
    qh:                         S r SSKrSSKJr  SSKJr  SSKJrJr  SSK	J
r
  S	rS
rSrSrSrSrSr " S S5      r " S S\5      r " S S\
5      rg)z
Backend for SAML 2.0 support

Terminology:

"Service Provider" (SP): Your web app
"Identity Provider" (IdP): The third-party site that is authenticating
                           users via SAML
    N)OneLogin_Saml2_Auth)OneLogin_Saml2_Settings   )
AuthFailedAuthMissingParameter   )BaseAuthzurn:oid:2.5.4.3z urn:oid:1.3.6.1.4.1.5923.1.1.1.6z urn:oid:1.3.6.1.4.1.5923.1.1.1.7zurn:oid:2.5.4.42z!urn:oid:0.9.2342.19200300.100.1.3zurn:oid:2.5.4.4z!urn:oid:0.9.2342.19200300.100.1.1c                   p    \ rS rSrSrS rS rS rS r\	S 5       r
\	S 5       r\	S	 5       r\	S
 5       rSrg)SAMLIdentityProvider   z9Wrapper around configuration for a SAML Identity providerc                 j    Xl         SU R                   ;  a  SU R                   ;  d   S5       eX l        g)zLoad and parse configuration: z.IdP "name" should be a slug (short, no spaces)N)nameconf)selfr   kwargss      K/var/www/html/env/lib/python3.13/site-packages/social_core/backends/saml.py__init__SAMLIdentityProvider.__init__    s7    	 $))#499(< 	
<	
< 	    c                 ~    XR                   R                  S[        5         n[        U[        5      (       a  US   nU$ )z
The most important method: Get a permanent, unique identifier
for this user from the attributes supplied by the IdP.

If you want to use the NameID, it's available via
attributes['name_id']
attr_user_permanent_idr   )r   get
OID_USERID
isinstancelist)r   
attributesuids      r   get_user_permanent_id*SAMLIdentityProvider.get_user_permanent_id*   s7     '?LMc4  a&C
r   c           	          U R                  US[        5      U R                  US[        5      U R                  US[        5      U R                  US[        5      U R                  US[
        5      S.$ )zY
Given the SAML attributes extracted from the SSO response, get
the user data like name.
attr_full_nameattr_first_nameattr_last_nameattr_username
attr_email)fullname
first_name	last_nameusernameemail)get_attrOID_COMMON_NAMEOID_GIVEN_NAMEOID_SURNAMEr   OID_MAIL)r   r   s     r   get_user_details%SAMLIdentityProvider.get_user_details8   sa     j2BOT--
4E~Vz3C[Qj/:N]]:|XF
 	
r   c                     U R                   R                  X#5      nUR                  US5      n[        U[        5      (       a  U(       a  US   OSnU$ )z
Internal helper method.
Get the attribute 'default_attribute' out of the attributes,
unless self.conf[conf_key] overrides the default by specifying
another attribute to use.
Nr   )r   r   r   r   )r   r   conf_keydefault_attributekeyvalues         r   r-   SAMLIdentityProvider.get_attrE   sE     iimmH8sD)eT"" %E!H4Er   c                      U R                   S   $ )zGet the entity ID for this IdP	entity_idr   r   s    r   r;   SAMLIdentityProvider.entity_idR   s     yy%%r   c                      U R                   S   $ )zGet the SSO URL for this IdPurlr<   r=   s    r   sso_urlSAMLIdentityProvider.sso_urlX   s    
 yyr   c                 8    U R                   R                  S5      $ )zGet the SLO URL for this IdPslo_url)r   r   r=   s    r   rD   SAMLIdentityProvider.slo_url_   s     yy}}Y''r   c                 :   U R                   U R                  SS.S.nU R                  (       a  U R                  SS.US'   U R                  R	                  SS5      nU(       a  X!S'   U$ U R                  R	                  SS5      nU(       a  X!S'   U$ [        S5      e)	zDGet the IdP configuration dict in the format required by
python-samlz2urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirectr@   binding)entityIdsingleSignOnServicesingleLogoutServicex509certNx509certMultiz*IDP must contain x509cert or x509certMulti)r;   rA   rD   r   r   KeyError)r   resultcerts      r   saml_config_dict%SAMLIdentityProvider.saml_config_dictd   s    
 ||O$
 <<||O-F()
 yy}}Z.!%:Myy}}_d3&*?#MCDDr   )r   r   N)__name__
__module____qualname____firstlineno____doc__r   r    r2   r-   propertyr;   rA   rD   rQ   __static_attributes__ r   r   r   r      sk    C
 & &
     ( ( E Er   r   c                   ,   ^  \ rS rSrSrU 4S jrSrU =r$ )DummySAMLIdentityProvider   z
A placeholder IdP used when we must specify something, e.g. when
generating SP metadata.

If OneLogin_Saml2_Auth is modified to not always require IdP
config, this can be removed.
c                 &   > [         TU ]  SSSSS9  g )Ndummyzhttps://dummy.none/saml2zhttps://dummy.none/SSO )r;   r@   rL   )superr   )r   	__class__s    r   r   "DummySAMLIdentityProvider.__init__   s!    0(	 	 	
r   rZ   )rS   rT   rU   rV   rW   r   rY   __classcell__rb   s   @r   r\   r\      s    
 
r   r\   c                      ^  \ rS rSrSrSr/ rS rSS jrS r	S r
S rS	 rS
 rS rSU 4S jjrSS jrS rS rSrU =r$ )SAMLAuth   a  
PSA Backend that implements SAML 2.0 Service Provider (SP) functionality.

Unlike all of the other backends, this one can be configured to work with
many identity providers (IdPs). For example, a University that belongs to a
Shibboleth federation may support authentication via ~100 partner
universities. Also, the IdP configuration can be changed at runtime if you
require that functionality - just subclass this and override `get_idp()`.

Several settings are required. Here's an example:

SOCIAL_AUTH_SAML_SP_ENTITY_ID = "https://saml.example.com/"
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = "... X.509 certificate string ..."
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = "... private key ..."
SOCIAL_AUTH_SAML_ORG_INFO = {
    "en-US": {
        "name": "example",
        "displayname": "Example Inc.",
        "url": "http://example.com"
    }
}
SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = {
    "givenName": "Tech Gal",
    "emailAddress": "technical@example.com"
}
SOCIAL_AUTH_SAML_SUPPORT_CONTACT = {
    "givenName": "Support Guy",
    "emailAddress": "support@example.com"
}
SOCIAL_AUTH_SAML_ENABLED_IDPS = {
    "testshib": {
        "entity_id": "https://idp.testshib.org/idp/shibboleth",
        "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO",
        "x509cert": "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0B...
                    ...8Bbnl+ev0peYzxFyF5sQA==",
    }
}

Optional settings:
SOCIAL_AUTH_SAML_SP_EXTRA = {}
SOCIAL_AUTH_SAML_SECURITY_CONFIG = {}
samlc                 B    U R                  S5      U   n[        U40 UD6$ )z=Given the name of an IdP, get a SAMLIdentityProvider instanceENABLED_IDPS)settingr   )r   idp_name
idp_configs      r   get_idpSAMLAuth.get_idp   s%    \\.1(;
#H;
;;r   c                    U R                   nU R                  S5      U R                  S5      S.SU(       a  UR                  O0 U R                  S5      SSS.US	S
.U R                  S5      U R                  S5      U R                  S5      S.SS.nUS   R                  U R                  S0 5      5        US   R                  U R                  S0 5      5        U$ )zH
Generate the configuration required to instantiate OneLogin_Saml2_Auth
TECHNICAL_CONTACTSUPPORT_CONTACT)	technicalsupportTORG_INFOr`   P10D)metadataValidUntilmetadataCacheDurationz.urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POSTrG   SP_ENTITY_IDSP_PUBLIC_CERTSP_PRIVATE_KEY)assertionConsumerServicerI   rL   
privateKey)contactPersondebugidporganizationsecurityspstrictr   SECURITY_CONFIGr   SP_EXTRA)redirect_urirl   rQ   update)r   r   abs_completion_urlconfigs       r   generate_saml_configSAMLAuth.generate_saml_config   s     ".. "\\*=><<(9: +.3''B LL4&()/ .O-
 !LL8 LL)9:"ll+;<	 -
0 	z!!$,,/@""EFtDLLR89r   c                 ~    U R                  5       n[        USS9nUR                  5       nUR                  U5      nX44$ )a'  
Helper method that can be used from your web app to generate the XML
metadata required to link your web app as a Service Provider.

Returns (metadata XML string, list of errors)

Example usage (Django):
    from ..apps.django_app.utils import load_strategy,                                                      load_backend
    def saml_metadata_view(request):
        complete_url = reverse('social:complete', args=("saml", ))
        saml_backend = load_backend(load_strategy(request), "saml",
                                    complete_url)
        metadata, errors = saml_backend.generate_metadata_xml()
        if not errors:
            return HttpResponse(content=metadata,
                                content_type='text/xml')
        return HttpResponseServerError(content=', '.join(errors))
T)sp_validation_only)r   r   get_sp_metadatavalidate_metadata)r   r   saml_settingsmetadataerrorss        r   generate_metadata_xmlSAMLAuth.generate_metadata_xml   sE    ( **,/4P 00200:r   c                 L   U R                  U5      nU R                  R                  5       (       a  SOSU R                  R                  5       U R                  R	                  5       U R                  R                  5       U R                  R                  5       S.n[        X25      $ )z&Get an instance of OneLogin_Saml2_Authonoff)https	http_hostscript_nameget_data	post_data)r   strategyrequest_is_securerequest_hostrequest_pathrequest_getrequest_postr   )r   r   r   request_infos       r   _create_saml_authSAMLAuth._create_saml_auth  s}    **3/!]]<<>>TE335==557113335
 #<88r   c                 0    U R                   R                  5       S   nU R	                  U R                  U5      S9nUU R                  R                  S5      S.nUR                  [        R                  " U5      S9$ ! [         a    [        U S5      ef = f)zGGet the URL to which we must redirect in order to
authenticate the userr   )r   next)r   r   )	return_to)r   request_datarN   r   r   ro   datar   loginjsondumps)r   rm   authrelay_states       r   auth_urlSAMLAuth.auth_url  s    	4}}113E:H %%$,,x*@%A IIMM&)
 zzDJJ{$;z<<  	4&tU33	4s   A> >Bc                 R    U R                  US   5      nUR                  US   5      $ )zRGet user details like full name, email, etc. from the
response - see auth_completerm   r   )ro   r2   )r   responser   s      r   r2   SAMLAuth.get_user_details#  s-     ll8J/0##H\$:;;r   c                 t    U R                  US   5      nUR                  US   5      nUR                   SU 3$ )z
Get the permanent ID for this user from the response.
We prefix each ID with the name of the IdP so that we can
connect multiple IdPs to this user.
rm   r   r   )ro   r    r   )r   detailsr   r   r   s        r   get_user_idSAMLAuth.get_user_id)  sA     ll8J/0''(>?((1SE""r   c                     U R                   R                  5       S   n [        R
                  " U5      n[        U[        5      (       a  SU;  a  [        S5      e US   nUR                  S5      =n(       a,  U R                   R                  UR                  SS5      U5        U R                  U5      nU R                  U5      nUR                  5         UR                  5       n	U	(       d  UR                  5       (       d#  UR!                  5       n
[#        U SU	 SU
 S35      eUR%                  5       nUR'                  5       US	'   U R)                  X{5        UUUR+                  5       S
.nUR-                  XS.5        U R                   R.                  " U0 UD6$ ! [         a    [        U S5      ef = f! [         a    Un GN!f = f)zi
The user has been redirected back from the IdP and we should
now log them in, if everything checks out.

RelayStater   zARelayState is expected to contain a JSON object with an 'idp' keyr   redirect_namezSAML login failed: z ()name_id)rm   r   session_index)r   backend)r   r   rN   r   r   loadsr   dict
ValueErrorr   session_setro   r   process_response
get_errorsis_authenticatedget_last_error_reasonr   get_attributes
get_nameid_check_entitlementsget_session_indexr   authenticate)r   argsr   relay_state_strr   rm   next_urlr   r   r   reasonr   r   s                r   auth_completeSAMLAuth.auth_complete3  s   
	;"mm88:<HO	Y**_5Kk400E4L W  5M #5)H&??622x2))&**_f*MxXll8$%%c*"..00//1FT%86(!#LMM((*
 $ 1
9  1 $!335

 	8=>}}))4:6::I  	;&t\::	;  	' 'H	's   F <F8 F58GGc                 b   > [         TU ]  " XUS   /UQ7SU0UD6nUS   US'   US   S   US'   U$ )Nr   r   r   r   )ra   
extra_data)	r   userr   r   r   r   r   r   rb   s	           r   r   SAMLAuth.extra_data`  s_    W'x-
AE
7>
IO

 '/&?
?# ( 6y A
9r   c                     U R                  U5      nU R                  U5      nUR                  S   nUR                  S   nUR                  XgUS9$ )Nr   r   )r   r   r   )ro   r   r   logout)r   rm   social_authr   r   r   r   r   s           r   request_logoutSAMLAuth.request_logouth  s[    ll8$%%c*((3#..?{{I  
 	
r   c                     U R                  U5      nU R                  U5      nUR                  US9nUR                  5       nXV4$ )N)delete_session_cb)ro   r   process_slor   )r   rm   r   r   r   r@   r   s          r   process_logoutSAMLAuth.process_logoutq  sH    ll8$%%c*1BC"{r   c                     g)a]  
Additional verification of a SAML response before
authenticating the user.

Subclasses can override this method if they need custom
validation code, such as requiring the presence of an
eduPersonEntitlement.

raise social_core.exceptions.AuthForbidden if the user should not
be authenticated, or do nothing to allow the login pipeline to
continue.
NrZ   )r   r   r   s      r   r   SAMLAuth._check_entitlementsx  s    r   rZ   )N)rS   rT   rU   rV   rW   r   
EXTRA_DATAro   r   r   r   r   r2   r   r   r   r   r   r   rY   rd   re   s   @r   rg   rg      sX    )V DJ<
!F 4
9=$<#+;Z
 r   rg   )rW   r   onelogin.saml2.authr   onelogin.saml2.settingsr   
exceptionsr   r   baser	   r.   OID_EDU_PERSON_PRINCIPAL_NAMEOID_EDU_PERSON_ENTITLEMENTr/   r1   r0   r   r   r\   rg   rZ   r   r   <module>r      sl     3 ; 9  $ B ? #.0
bE bEJ
 4 
$px pr   