33
44from typing import Any , Callable , Dict , Mapping , Optional , Union
55
6- from dictor import dictor
6+ from dictor import dictor # type: ignore
77from django .conf import settings
88from django .http import HttpRequest , HttpResponse , HttpResponseRedirect
99from django .urls import NoReverseMatch
10- from django_saml2_auth .errors import *
10+ from django_saml2_auth .errors import (ERROR_CREATING_SAML_CONFIG_OR_CLIENT ,
11+ INVALID_METADATA_URL ,
12+ NO_ISSUER_IN_SAML_RESPONSE ,
13+ NO_METADATA_URL_ASSOCIATED ,
14+ NO_METADATA_URL_OR_FILE ,
15+ NO_NAME_ID_IN_SAML_RESPONSE ,
16+ NO_SAML_CLIENT ,
17+ NO_SAML_RESPONSE_FROM_CLIENT ,
18+ NO_SAML_RESPONSE_FROM_IDP ,
19+ NO_TOKEN_SPECIFIED ,
20+ NO_USER_IDENTITY_IN_SAML_RESPONSE ,
21+ NO_USERNAME_OR_EMAIL_SPECIFIED )
1122from django_saml2_auth .exceptions import SAMLAuthError
1223from django_saml2_auth .utils import get_reverse , run_hook
1324from saml2 import BINDING_HTTP_POST , BINDING_HTTP_REDIRECT , entity
@@ -67,7 +78,7 @@ def validate_metadata_url(url: str) -> bool:
6778 http_client = HTTPBase ()
6879 metadata = MetaDataExtern (None , url = url , http = http_client )
6980 metadata .load ()
70- except :
81+ except Exception :
7182 return False
7283
7384 return True
@@ -94,7 +105,7 @@ def get_metadata(user_id: Optional[str] = None) -> Mapping[str, Any]:
94105 saml2_auth_settings = settings .SAML2_AUTH
95106 get_metadata_trigger = dictor (saml2_auth_settings , "TRIGGER.GET_METADATA_AUTO_CONF_URLS" )
96107 if get_metadata_trigger :
97- metadata_urls = run_hook (get_metadata_trigger , user_id )
108+ metadata_urls = run_hook (get_metadata_trigger , user_id ) # type: ignore
98109 if metadata_urls :
99110 # Filter invalid metadata URLs
100111 filtered_metadata_urls = list (
@@ -127,21 +138,25 @@ def get_metadata(user_id: Optional[str] = None) -> Mapping[str, Any]:
127138
128139def get_saml_client (domain : str ,
129140 acs : Callable [..., HttpResponse ],
130- user_id : str = None ) -> Optional [Saml2Client ]:
141+ user_id : Optional [ str ] = None ) -> Optional [Saml2Client ]:
131142 """Create a new Saml2Config object with the given config and return an initialized Saml2Client
132143 using the config object. The settings are read from django settings key: SAML2_AUTH.
133144
134145 Args:
135146 domain (str): Domain name to get SAML config for
136147 acs (Callable[..., HttpResponse]): The acs endpoint
148+ user_id (str, optional): If passed, it will be further processed by the
149+ GET_METADATA_AUTO_CONF_URLS trigger, which will return the metadata URL corresponding
150+ to the given user identifier, either email or username. Defaults to None.
137151
138152 Raises:
139153 SAMLAuthError: Re-raise any exception raised by Saml2Config or Saml2Client
140154
141155 Returns:
142156 Optional[Saml2Client]: A Saml2Client or None
143157 """
144- acs_url = domain + get_reverse ([acs , "acs" , "django_saml2_auth:acs" ])
158+ # get_reverse raises an exception if the view is not found, so we can safely ignore type errors
159+ acs_url = domain + get_reverse ([acs , "acs" , "django_saml2_auth:acs" ]) # type: ignore
145160 metadata = get_metadata (user_id )
146161 if (("local" in metadata and not metadata ["local" ]) or
147162 ("remote" in metadata and not metadata ["remote" ])):
@@ -154,7 +169,7 @@ def get_saml_client(domain: str,
154169
155170 saml2_auth_settings = settings .SAML2_AUTH
156171
157- saml_settings = {
172+ saml_settings : Dict [ str , Any ] = {
158173 "metadata" : metadata ,
159174 "allow_unknown_attributes" : True ,
160175 "debug" : saml2_auth_settings .get ("DEBUG" , False ),
@@ -208,7 +223,8 @@ def get_saml_client(domain: str,
208223
209224def decode_saml_response (
210225 request : HttpRequest ,
211- acs : Callable [..., HttpResponse ]) -> Union [HttpResponseRedirect , Optional [AuthnResponse ]]:
226+ acs : Callable [..., HttpResponse ]) -> Union [
227+ HttpResponseRedirect , Optional [AuthnResponse ], None ]:
212228 """Given a request, the authentication response inside the SAML response body is parsed,
213229 decoded and returned. If there are any issues parsing the request, the identity or the issuer,
214230 an exception is raised.
@@ -225,8 +241,8 @@ def decode_saml_response(
225241 SAMLAuthError: No user identity in SAML response.
226242
227243 Returns:
228- Union[HttpResponseRedirect, Optional[AuthnResponse]]: Returns an AuthnResponse object for
229- extracting user identity from.
244+ Union[HttpResponseRedirect, Optional[AuthnResponse], None ]: Returns an AuthnResponse
245+ object for extracting user identity from.
230246 """
231247 saml_client = get_saml_client (get_assertion_url (request ), acs )
232248 if not saml_client :
@@ -313,8 +329,8 @@ def extract_user_identity(user_identity: Dict[str, Any]) -> Dict[str, Optional[A
313329 user ["first_name" ] = dictor (user_identity , f"{ firstname_field } /0" , pathsep = "/" )
314330 user ["last_name" ] = dictor (user_identity , f"{ lastname_field } /0" , pathsep = "/" )
315331
316- TOKEN_REQUIRED = dictor (saml2_auth_settings , "TOKEN_REQUIRED" , default = True )
317- if TOKEN_REQUIRED :
332+ token_required = dictor (saml2_auth_settings , "TOKEN_REQUIRED" , default = True )
333+ if token_required :
318334 token_field = dictor (saml2_auth_settings , "ATTRIBUTES_MAP.token" , default = "token" )
319335 user ["token" ] = dictor (user_identity , f"{ token_field } .0" )
320336
@@ -334,7 +350,7 @@ def extract_user_identity(user_identity: Dict[str, Any]) -> Dict[str, Optional[A
334350 "status_code" : 422
335351 })
336352
337- if TOKEN_REQUIRED and not user .get ("token" ):
353+ if token_required and not user .get ("token" ):
338354 raise SAMLAuthError ("No token specified." , extra = {
339355 "exc_type" : ValueError ,
340356 "error_code" : NO_TOKEN_SPECIFIED ,
0 commit comments