Source code for simple_openid_connect.integrations.django.apps
"""Django AppConfig for this app"""importinspectimportloggingfromfunctoolsimportcached_propertyfromtypingimportTYPE_CHECKING,List,Optional,Unionfromdjango.appsimportAppConfig,appsfromdjango.confimportsettingsfromdjango.core.cacheimportcachefromdjango.core.checksimportError,Warning,registerfromdjango.core.exceptionsimportImproperlyConfiguredfromdjango.httpimportHttpRequestfromdjango.shortcutsimportresolve_urlfromdjango.utils.module_loadingimportimport_stringfrompydanticimportBaseModel,ConfigDictfromsimple_openid_connect.clientimportOpenidClientifTYPE_CHECKING:fromsimple_openid_connect.integrations.django.user_mappingimportUserMapperlogger=logging.getLogger(__name__)
[docs]classSettingsModel(BaseModel):""" A pydantic model used to validate django settings """model_config=ConfigDict(from_attributes=True)OPENID_ISSUER:str"The Openid issuer to use. This is required to be an `https` url and an Openid discovery document should be served under ``{issuer}/.well-known/openid-configuration``."OPENID_CLIENT_ID:str"The client id that was issued to you from your OpenId provider."OPENID_CLIENT_SECRET:Optional[str]=None"The client secret that was issued to you from your OpenId provider if this is a confidential client."OPENID_SCOPE:str="openid""The Openid scopes which are requested from the provider when a user logs in. It should be a list of scopes as space separated string and should contain the ``openid`` scope."OPENID_REDIRECT_URI:Optional[str]="simple_openid_connect:login-callback""The URL or ``named URL pattern <https://docs.djangoproject.com/en/dev/topics/http/urls/#naming-url-patterns>``_ where users are redirected to from the OpenId provider and at which the authentication result is processed.\nThis is sometimes also called a callback uri."OPENID_BASE_URI:Optional[str]=None"The absolute base uri of this application. This is used to construct valid redirect urls to the current application."OPENID_USER_MAPPER:str=("simple_openid_connect.integrations.django.user_mapping.UserMapper")"A string specifying a class that inherits from :class:`simple_openid_connect.integrations.django.user_mapping.UserMapper`."OPENID_LOGIN_TIMEOUT:int=60*5"Time in seconds which a login procedure is allowed to take at maximum. If a user takes more than this time between initiating a login and completing it, the login process fails and they have to redo it."
[docs]classOpenidAppConfig(AppConfig):""" The Django AppConfig for simple_openid_connect """name="simple_openid_connect.integrations.django"label="simple_openid_connect_django"default_auto_field="django.db.models.BigAutoField"
[docs]defready(self)->None:""" Called when django starts. Performs settings validation and raises ImproperlyConfigured if necessary. """super().ready()# assert that are settings are as requiredtry:_=self.safe_settings_=self.user_mapperexceptExceptionase:raiseImproperlyConfigured(f"django settings are invalid for openid usage: {e}")frome
[docs]@classmethoddefget_instance(cls)->"OpenidAppConfig":""" Retrieve the currently used instance from django's app registry """instance=apps.get_app_config(cls.label)assertisinstance(instance,OpenidAppConfig)returninstance
@cached_propertydefsafe_settings(self)->SettingsModel:""" type-validated version of django settings """returnSettingsModel.from_orm(settings)@cached_propertydefuser_mapper(self)->"UserMapper":""" A UserMapper instance of which the actual implementation is configured via django settings. """fromsimple_openid_connect.integrations.django.user_mappingimportUserMappercls=import_string(self.safe_settings.OPENID_USER_MAPPER)ifnotissubclass(cls,UserMapper):raiseImproperlyConfigured("OPENID_USER_MAPPER setting does not point to a subclass of UserMapper",cls,)returncls()# type: ignore
[docs]defget_client(self,own_base_uri:Union[HttpRequest,str,None]=None)->OpenidClient:""" Get an `OpenidClient` instance that is appropriate for usage in django. It is automatically configured via django settings. :param own_base_uri: The base url of this application which will be used to construct a redirect_uri back to it. Can also be the current request in which case `{scheme}://{host}` of it will be used as the base url. If this parameter is not given, only the `OPENID_BASE_URI` setting is used. In any case, if the `OPENID_BASE_URI` setting is set, it will be used instead. :raises ImproperlyConfigured: when no *own_base_uri* is given and the `OPENID_BASE_URI` is also None """# use a cached client instance if one exists or create a new one if notclient=cache.get("openid_client")# type: OpenidClientifclientisNone:# determine base_uri of this appifself.safe_settings.OPENID_REDIRECT_URIisnotNone:ifself.safe_settings.OPENID_BASE_URIisnotNone:own_base_uri=self.safe_settings.OPENID_BASE_URIelse:ifown_base_uriisNone:raiseImproperlyConfigured("either a value for own_base_uri must be given or the django setting OPENID_BASE_URI must be filled")elifisinstance(own_base_uri,HttpRequest):own_base_uri=(f"{own_base_uri.scheme}://{own_base_uri.get_host()}")relative_redirect_uri=resolve_url(self.safe_settings.OPENID_REDIRECT_URI)redirect_uri=f"{own_base_uri}{relative_redirect_uri}"else:redirect_uri=None# create a new client instance and cache itclient=OpenidClient.from_issuer_url(url=self.safe_settings.OPENID_ISSUER,authentication_redirect_uri=redirect_uri,client_id=self.safe_settings.OPENID_CLIENT_ID,client_secret=self.safe_settings.OPENID_CLIENT_SECRET,scope=self.safe_settings.OPENID_SCOPE,)cache.set("openid_client",client)returnclient
@register# type: ignoredefcheck_middleware(*args,**kwargs)->List[Union[Warning,Error]]:fromsimple_openid_connect.integrations.django.middlewareimport(TokenVerificationMiddleware,)if(hasattr(settings,"OPENID_REDIRECT_URI")andsettings.OPENID_REDIRECT_URIisNone# type: ignore):# redirect uri is set to None,# so the library is probably used as a resource server,# therefore we don't need the middlewarereturn[]has_token_verification_middleware=Falseformiddlewareinsettings.MIDDLEWARE:try:middleware_cls=import_string(middleware)exceptImportError:continueifinspect.isclass(middleware_cls)andissubclass(middleware_cls,TokenVerificationMiddleware):has_token_verification_middleware=Truebreakifnothas_token_verification_middleware:return[Warning("Access tokens are not checked for expiry and users are not automatically logged out when their access token expires.",hint="Add 'simple_openid_connect.integrations.django.middleware.TokenVerificationMiddleware' to MIDDLEWARE.",obj="simple_openid_connect",id="simple_openid_connect.W001",)]else:return[]