4242 new_session_id /0 ,
4343 get_resp_headers /1 ,
4444 acting_on_behalf /1 ,
45- init_auth /1 ]).
45+ init_auth /1 ,
46+ on_behalf_context /1 ,
47+ get_authn_res_from_on_behalf_of /3 ]).
4648
4749% % rpc from ns_couchdb node
4850-export ([authenticate /1 ,
@@ -501,7 +503,7 @@ verify_rest_auth(Req, Permission) ->
501503 {ok , # authn_res {} = AuthnRes ,
502504 RespHeaders } ->
503505 Req2 = append_resp_headers (RespHeaders , Req ),
504- case apply_on_behalf_of_identity (AuthnRes , Req2 ) of
506+ case apply_on_behalf_of_authn_res (AuthnRes , Req2 ) of
505507 error ->
506508 Req3 = maybe_store_rejected_user (
507509 get_rejected_user (Auth ), Req2 ),
@@ -526,15 +528,41 @@ verify_rest_auth(Req, Permission) ->
526528 {auth_failure , Req2 }
527529 end .
528530
529- -spec apply_on_behalf_of_identity (# authn_res {}, mochiweb_request ()) ->
530- error | # authn_res {}.
531- apply_on_behalf_of_identity (AuthnRes , Req ) ->
532- case extract_on_behalf_of_identity (Req ) of
531+ % % Specify authentication context for SAML (and later JWT) in on-behalf-of
532+ % % extras. {User, Domain} aren't sufficient to determine the full set of
533+ % % privileges available to the user.
534+ -spec on_behalf_context (# authn_res {}) -> {string (), boolean ()}.
535+ on_behalf_context (# authn_res {session_id = Id }) when is_binary (Id ) ->
536+ {" context:ui=" ++ binary_to_list (Id ), true };
537+ on_behalf_context (_ ) -> {" " , false }.
538+
539+ -spec get_authn_res_from_on_behalf_of (User :: rbac_user_id (),
540+ Domain :: rbac_identity_type (),
541+ Context :: string () | undefined ) ->
542+ # authn_res {}.
543+ get_authn_res_from_on_behalf_of (User , Domain , Context ) ->
544+ AuthnRes0 = # authn_res {identity = {User , Domain }},
545+ case Context of
546+ undefined -> AuthnRes0 ;
547+ " ui=" ++ Id ->
548+ UiAuthnRes = menelaus_ui_auth :get_authn_res_from_ui_session (Id ),
549+ case UiAuthnRes of
550+ undefined -> AuthnRes0 ;
551+ # authn_res {identity = {User0 , Domain0 }}
552+ when User0 =:= User , Domain0 =:= Domain -> UiAuthnRes ;
553+ _ -> AuthnRes0
554+ end
555+ end .
556+
557+ -spec apply_on_behalf_of_authn_res (# authn_res {}, mochiweb_request ()) ->
558+ error | # authn_res {}.
559+ apply_on_behalf_of_authn_res (AuthnRes , Req ) ->
560+ case extract_on_behalf_of_authn_res (Req ) of
533561 error ->
534562 error ;
535563 undefined ->
536564 AuthnRes ;
537- {ok , RealIdentity } ->
565+ {User , Domain , Context } ->
538566 % % The permission is formed the way that it is currently granted
539567 % % to full admins only. We might consider to reformulate it
540568 % % like {[onbehalf], impersonate} or, such in the upcoming
@@ -547,9 +575,7 @@ apply_on_behalf_of_identity(AuthnRes, Req) ->
547575 case menelaus_roles :is_allowed (
548576 {[admin , security , admin ], impersonate }, AuthnRes ) of
549577 true ->
550- AuthnRes # authn_res {identity = RealIdentity ,
551- extra_groups = [],
552- extra_roles = []};
578+ get_authn_res_from_on_behalf_of (User , Domain , Context );
553579 false ->
554580 error
555581 end
@@ -559,17 +585,23 @@ apply_on_behalf_of_identity(AuthnRes, Req) ->
559585acting_on_behalf (Req ) ->
560586 get_authenticated_identity (Req ) =/= get_identity (Req ).
561587
562- -spec extract_on_behalf_of_identity (mochiweb_request ()) ->
563- error | undefined
564- | { ok , rbac_identity () }.
565- extract_on_behalf_of_identity (Req ) ->
588+ -spec extract_on_behalf_of_authn_res (mochiweb_request ()) ->
589+ error | undefined |
590+ { rbac_user_id (), rbac_identity_type (), string () | undefined }.
591+ extract_on_behalf_of_authn_res (Req ) ->
566592 case read_on_behalf_of_header (Req ) of
567593 Header when is_list (Header ) ->
568594 case parse_on_behalf_of_header (Header ) of
569595 {User , Domain } ->
570- try
571- ExistingDomain = list_to_existing_atom (Domain ),
572- {ok , {User , ExistingDomain }}
596+ try list_to_existing_atom (Domain ) of
597+ ExistingDomain ->
598+ case parse_on_behalf_of_extras (Req ) of
599+ error -> error ;
600+ Context when is_list (Context ) ->
601+ {User , ExistingDomain , Context };
602+ undefined ->
603+ {User , ExistingDomain , undefined }
604+ end
573605 catch
574606 error :badarg ->
575607 ? log_debug (" Invalid domain in cb-on-behalf-of: ~s " ,
@@ -582,12 +614,21 @@ extract_on_behalf_of_identity(Req) ->
582614 error
583615 end ;
584616 undefined ->
585- undefined
617+ case read_on_behalf_of_extras (Req ) of
618+ undefined -> undefined ;
619+ Hdr ->
620+ ? log_debug (" Unexpected cb-on-behalf-extras: ~s " ,
621+ [ns_config_log :tag_user_name (Hdr )]),
622+ undefined
623+ end
586624 end .
587625
588626read_on_behalf_of_header (Req ) ->
589627 mochiweb_request :get_header_value (" cb-on-behalf-of" , Req ).
590628
629+ read_on_behalf_of_extras (Req ) ->
630+ mochiweb_request :get_header_value (" cb-on-behalf-extras" , Req ).
631+
591632parse_on_behalf_of_header (Header ) ->
592633 case (catch base64 :decode_to_string (Header )) of
593634 UserDomainStr when is_list (UserDomainStr ) ->
@@ -602,6 +643,29 @@ parse_on_behalf_of_header(Header) ->
602643 error
603644 end .
604645
646+ parse_on_behalf_of_extras (Req ) ->
647+ case read_on_behalf_of_extras (Req ) of
648+ undefined -> undefined ;
649+ Extras when is_list (Extras ) ->
650+ Status =
651+ case (catch base64 :decode_to_string (Extras )) of
652+ ContextStr when is_list (ContextStr ) ->
653+ case ContextStr of
654+ " context:" ++ X -> X ;
655+ _ -> error
656+ end ;
657+ _ -> error
658+ end ,
659+ case Status of
660+ error ->
661+ ? log_debug (" Invalid context in cb-on-behalf-extras:~s " ,
662+ [ns_config_log :tag_user_name (Extras )]),
663+ error ;
664+ S -> S
665+ end ;
666+ _ -> error
667+ end .
668+
605669-spec extract_identity_from_cert (binary ()) ->
606670 tuple () | auth_failure | temporary_failure .
607671extract_identity_from_cert (CertDer ) ->
0 commit comments