def test_scopes_class_setting_is_set_to_capabilities_scopes(self): """ Test that the SCOPES_BACKEND_CLASS has been set to dot_ext.scopes.CapabilitiesScopes. """ assert settings.OAUTH2_PROVIDER['SCOPES_BACKEND_CLASS'] == 'apps.dot_ext.scopes.CapabilitiesScopes' assert isinstance(get_scopes_backend(), CapabilitiesScopes)
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): available_scopes = get_scopes_backend().get_available_scopes(application=client, request=request) for scope in scopes: if not self.is_scope_valid(scope, available_scopes): return False return True
class RestrictedApplicationForm(forms.ModelForm): """ Form for creating or updating a restricted application. """ # allowed_scope is a space-delimited list, but we want to present # a selection of valid scopes with checkboxes allowed_scope = DelimitedListField( label='Allowed scopes', # The choices and initial values are callables, because the scopes might # not be available at import type, e.g. if coming from the database choices=lambda: get_scopes_backend().get_all_scopes().items(), initial=lambda: get_scopes_backend().get_default_scopes(), delimiter=' ', widget=forms.CheckboxSelectMultiple) class Meta: exclude = ()
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): """ Ensure required scopes are permitted (as specified in the settings file) """ available_scopes = get_scopes_backend().get_available_scopes( application=client, request=request) return set(scopes).issubset(set(available_scopes))
def test_scopes_class_setting_is_set_to_capabilities_scopes(self): """ Test that the SCOPES_BACKEND_CLASS has been set to dot_ext.scopes.CapabilitiesScopes. """ assert settings.OAUTH2_PROVIDER[ 'SCOPES_BACKEND_CLASS'] == 'apps.dot_ext.scopes.CapabilitiesScopes' assert isinstance(get_scopes_backend(), CapabilitiesScopes)
def clean_scope(self): scopes = self.cleaned_data['scope'] for def_scope in get_scopes_backend().get_default_scopes(): # Prevent users from removing default scopes if def_scope not in scopes: scopes.append(def_scope) if not scopes and self.cleaned_data['allow']: raise forms.ValidationError( "Você deve escolher pelo menos uma permissão") return ' '.join(scopes)
def scopes(self): """ Returns a dictionary of allowed scope names (as keys) with their descriptions (as values) """ all_scopes = get_scopes_backend().get_all_scopes() token_scopes = self.scope.split() return { name: desc for name, desc in all_scopes.items() if name in token_scopes }
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): available_scopes = get_scopes_backend().get_available_scopes( application=client, request=request) request.scopes = list(set(available_scopes) & set(scopes)) # https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest # unexpected scopes should be ignored return True
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): available_scopes = get_scopes_backend().get_available_scopes( application=client, request=request) for scope in scopes: if not self.is_scope_valid(scope, available_scopes): return False return True
def valid_scopes_access(self) -> Dict[str, str]: """ Returns a dictionary of allowed scope names (as keys) with their descriptions (as values). """ all_scopes = get_scopes_backend().get_all_scopes() token_scopes = self.allowed_scopes.split( ) if self.allowed_scopes else [] return { name: desc for name, desc in all_scopes.items() if name in token_scopes }
def validate_authorization_request(self, request): """ By intercepting this call, I can make sure that the default scopes are always included in the original call """ extra_scopes, credentials = super().validate_authorization_request( request) scopes = get_scopes_backend().get_default_scopes() for extra in extra_scopes: if extra not in scopes: scopes.append(extra) return scopes, credentials
def __init__(self, *args, **kwargs): application = kwargs.pop('application', None) if application is None: super(AllowForm, self).__init__(*args, **kwargs) else: # we use the application instance to get the list of available scopes # because it is needed to create the choices list for the `scope` field. available_scopes = get_scopes_backend().get_available_scopes(application) # set the available_scopes as the initial value so that # all checkboxes are checked kwargs['initial']['scope'] = available_scopes # init the form to create self.fields super(AllowForm, self).__init__(*args, **kwargs) # get the list of all the scopes available in the system # to get the description of each available scope. all_scopes = get_scopes_backend().get_all_scopes() choices = [(scope, all_scopes[scope]) for scope in available_scopes] self.fields['scope'].choices = choices
def _create_test_token(self, user, application): now = timezone.now() expires = now + timedelta(days=1) scope = get_scopes_backend().get_available_scopes(application) t = AccessToken.objects.create(user=user, application=application, token="sample-token-string", expires=expires, scope=' '.join(scope)) return t
def __init__(self, *args, **kwargs): application = kwargs.pop('application', None) if application is None: super(AllowForm, self).__init__(*args, **kwargs) else: # we use the application instance to get the list of available scopes # because it is needed to create the choices list for the `scope` field. available_scopes = get_scopes_backend().get_available_scopes( application) # set the available_scopes as the initial value so that # all checkboxes are checked kwargs['initial']['scope'] = available_scopes # init the form to create self.fields super(AllowForm, self).__init__(*args, **kwargs) # get the list of all the scopes available in the system # to get the description of each available scope. all_scopes = get_scopes_backend().get_all_scopes() choices = [(scope, all_scopes[scope]) for scope in available_scopes] self.fields['scope'].choices = choices
def get_schema(self, request=None, public=False): schema = super().get_schema(request, public) if "components" in schema: schema["components"]["securitySchemes"] = { "oauth2": { "type": "oauth2", "description": "OAuth2", "flows": { "implicit": { "authorizationUrl": reverse("oauth2_provider:authorize"), "scopes": get_scopes_backend().get_all_scopes(), } }, } } return schema
def validate_offline_access(self, request, user, client, by_scope=BY_SCOPE): """Ensure client is authorized for offline access to resources. Client credentials grant type applications are automatically authorized for offline access since the client is the resource owner. For all other grant types: The existence of refresh tokens granted to the client for the resource owner creates an implicit offline access authorization. Trusted clients can be granted explicit offline access by_scope and either setting the client's 'skip_authorization' attribute to True or adding the client app owner to the TRUSTED_APP_GROUP. """ if client.authorization_grant_type == GRANT_CLIENT_CREDENTIALS: valid_offline_auth = True else: refresh_tokens = RefreshToken.objects.filter(user=user, application=client) if refresh_tokens: request.refresh_tokens = refresh_tokens request.original_scopes = set( scope for token in refresh_tokens for scope in utils.scope_to_list(token.access_token.scope)) valid_offline_auth = True elif by_scope: client_user_groups = client.user.groups.values_list("name", flat=True) skip_auth = getattr(client, 'skip_authorization', False) available_scopes = get_scopes_backend().get_available_scopes( application=client, request=request) if TRUSTED_APP_GROUP in client_user_groups or skip_auth: valid_offline_auth = any('offline' in scope for scope in available_scopes) else: valid_offline_auth = False else: valid_offline_auth = False return valid_offline_auth
def get_security_definition(self, auto_schema): from oauth2_provider.scopes import get_scopes_backend from drf_spectacular.settings import spectacular_settings flows = {} for flow_type in spectacular_settings.OAUTH2_FLOWS: flows[flow_type] = {} if flow_type in ('implicit', 'authorizationCode'): flows[flow_type][ 'authorizationUrl'] = spectacular_settings.OAUTH2_AUTHORIZATION_URL if flow_type in ('password', 'clientCredentials', 'authorizationCode'): flows[flow_type][ 'tokenUrl'] = spectacular_settings.OAUTH2_TOKEN_URL if spectacular_settings.OAUTH2_REFRESH_URL: flows[flow_type][ 'refreshUrl'] = spectacular_settings.OAUTH2_REFRESH_URL scope_backend = get_scopes_backend() flows[flow_type]['scopes'] = scope_backend.get_all_scopes() return {'type': 'oauth2', 'flows': flows}
class ApplicationForm(forms.ModelForm): scope = forms.CharField( label="Permissões", widget=forms.CheckboxSelectMultiple( choices=scopes.get_scopes_backend().get_all_scopes().items()) ) logo = VersatileImageFormField(label="Logo (opcional)", help_text="Quadrado, entre 512x512 e 1024x1024 px", required=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.fields['scope'].initial: self.fields['scope'].initial = self.fields['scope'].initial.split() def clean_scope(self): scopes = eval(self.cleaned_data['scope']) for def_scope in get_scopes_backend().get_default_scopes(): if def_scope not in scopes: scopes.append(def_scope) return ' '.join(scopes) class Meta: model = models.get_application_model() fields = ['name', 'description', 'client_type', 'authorization_grant_type', 'platform', 'scope', 'redirect_uris', 'website', 'logo', 'webhook_url'] labels = { 'name': 'Nome do aplicativo', 'description': 'Descrição', 'client_type': 'Tipo de cliente', 'authorization_grant_type': 'Tipo de autorização', 'platform': 'Plataforma', 'redirect_uris': 'URIs de redirecionamento (uma por linha)', 'website': 'Página do app/Link para Download (opcional)', 'logo': 'URL do logo (opcional)', 'webhook_url': 'URL para receber Webhooks (opcional)', }
def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) all_scopes = get_scopes_backend().get_all_scopes() for t in ctx['tokens']: t.scopes_descriptions = [all_scopes[scope] for scope in t.scopes] return ctx
def get_default_scopes(self, client_id, request, *args, **kwargs): default_scopes = get_scopes_backend().get_default_scopes( application=request.client, request=request) return default_scopes
def get(self, request, *args, **kwargs): # Copy/Pasta'd from oauth2_provider.views.BaseAuthorizationView.get try: scopes, credentials = self.validate_authorization_request(request) # all_scopes = get_scopes_backend().get_all_scopes() # kwargs["scopes"] = scopes # kwargs["scopes_descriptions"] = [all_scopes[scope] for scope in scopes] # at this point we know an Application instance with such client_id exists in the database # TODO: Cache this! application = get_application_model().objects.get(client_id=credentials["client_id"]) kwargs["client_id"] = credentials["client_id"] kwargs["redirect_uri"] = credentials["redirect_uri"] kwargs["response_type"] = credentials["response_type"] kwargs["state"] = credentials["state"] try: kwargs["application"] = { "name": application.applicationinfo.get_visible_name(), } if application.applicationinfo.icon: kwargs["application"]['image'] = application.applicationinfo.icon.url if application.applicationinfo.website_url: kwargs["application"]["url"] = application.applicationinfo.website_url app_scopes = [s for s in re.split(r'[\s\n]+', application.applicationinfo.allowed_scopes) if s] except ApplicationInfo.DoesNotExist: app_scopes = ["r:profile"] kwargs["application"] = dict( name=application.name, scopes=app_scopes ) filtered_scopes = set(app_scopes) & set(scopes) kwargs['scopes'] = list(filtered_scopes) all_scopes = get_scopes_backend().get_all_scopes() kwargs['scopes_descriptions'] = {scope: all_scopes[scope] for scope in scopes} self.oauth2_data = kwargs # Check to see if the user has already granted access and return # a successful response depending on "approval_prompt" url parameter require_approval = request.GET.get("approval_prompt", oauth2_settings.REQUEST_APPROVAL_PROMPT) # If skip_authorization field is True, skip the authorization screen even # if this is the first use of the application and there was no previous authorization. # This is useful for in-house applications-> assume an in-house applications # are already approved. if application.skip_authorization: success_url = self.get_authorization_redirect_url(" ".join(kwargs['scopes']), credentials) return Response({ 'success_url': success_url }) elif require_approval == "auto" and not request.user.is_anonymous: tokens = get_access_token_model().objects.filter( user=request.user, application=application, expires__gt=timezone.now() ).all() # check past authorizations regarded the same scopes as the current one for token in tokens: if token.allow_scopes(scopes): success_url = self.get_authorization_redirect_url(" ".join(kwargs['scopes']), credentials) return Response({ 'success_url': success_url }) return Response(kwargs) except OAuthToolkitError as error: return Response({ 'error': error.oauthlib_error.description }, status=HTTP_400_BAD_REQUEST)
from oauth2_provider import scopes from oauth2_provider.exceptions import OAuthToolkitError from oauth2_provider.models import get_application_model from oauth2_provider.settings import oauth2_settings from oauth2_provider.views.base import ( AuthorizationView as OAuth2AuthorizationView ) from .settings import oidc_settings from .claims import get_claims_provider from .jwt import get_jwt_builder from .forms import NonceAllowForm from .exceptions import AuthenticationRequired log = logging.getLogger(__name__) Scopes = scopes.get_scopes_backend() ClaimsProvider = get_claims_provider() class Wellknown(APIView): renderer_classes = [JSONRenderer] def get(self, request, format=None): return Response({ # TODO get this from oauth server config "grant_types_supported": [ "authorization_code", "implicit", "refresh_token", ],
def get(self, request, *args, **kwargs): # Note: This code is copied from https://github.com/evonove/django-oauth-toolkit/blob/34f3b7b3511c15686039079026165feaadb1b87d/oauth2_provider/views/base.py#L111 # Places that we have changed are noted with ***. try: # *** Moved code to get the require_approval value earlier on so we can # circumvent our custom code in the case when auto_even_if_expired # isn't required. require_approval = request.GET.get( "approval_prompt", oauth2_settings.REQUEST_APPROVAL_PROMPT, ) if require_approval != 'auto_even_if_expired': return super(EdxOAuth2AuthorizationView, self).get(request, *args, **kwargs) scopes, credentials = self.validate_authorization_request(request) all_scopes = get_scopes_backend().get_all_scopes() kwargs["scopes_descriptions"] = [all_scopes[scope] for scope in scopes] kwargs['scopes'] = scopes # at this point we know an Application instance with such client_id exists in the database application = get_application_model().objects.get(client_id=credentials['client_id']) content_orgs = ApplicationOrganization.get_related_org_names( application, relation_type=ApplicationOrganization.RELATION_TYPE_CONTENT_ORG ) kwargs['application'] = application kwargs['content_orgs'] = content_orgs kwargs['client_id'] = credentials['client_id'] kwargs['redirect_uri'] = credentials['redirect_uri'] kwargs['response_type'] = credentials['response_type'] kwargs['state'] = credentials['state'] self.oauth2_data = kwargs # following two loc are here only because of https://code.djangoproject.com/ticket/17795 form = self.get_form(self.get_form_class()) kwargs['form'] = form # If skip_authorization field is True, skip the authorization screen even # if this is the first use of the application and there was no previous authorization. # This is useful for in-house applications-> assume an in-house applications # are already approved. if application.skip_authorization: uri, headers, body, status = self.create_authorization_response( request=self.request, scopes=" ".join(scopes), credentials=credentials, allow=True) return HttpResponseUriRedirect(uri) # *** Changed the if statement that checked for require_approval to an assert. assert require_approval == 'auto_even_if_expired' tokens = request.user.accesstoken_set.filter( application=kwargs['application'], # *** Purposefully keeping this commented out code to highlight that # our version of the implementation does NOT filter by expiration date. # expires__gt=timezone.now(), ).all() # check past authorizations regarded the same scopes as the current one for token in tokens: if token.allow_scopes(scopes): uri, headers, body, status = self.create_authorization_response( request=self.request, scopes=" ".join(scopes), credentials=credentials, allow=True) return HttpResponseUriRedirect(uri) # render an authorization prompt so the user can approve # the application's requested scopes return self.render_to_response(self.get_context_data(**kwargs)) except OAuthToolkitError as error: return self.error_response(error)
def get(self, request, *args, **kwargs): # pylint: disable=line-too-long # Note: This code is copied from https://github.com/evonove/django-oauth-toolkit/blob/34f3b7b3511c15686039079026165feaadb1b87d/oauth2_provider/views/base.py#L111 # Places that we have changed are noted with ***. application = None try: # *** Moved code to get the require_approval value earlier on so we can # circumvent our custom code in the case when auto_even_if_expired # isn't required. require_approval = request.GET.get( "approval_prompt", oauth2_settings.REQUEST_APPROVAL_PROMPT, ) if require_approval != 'auto_even_if_expired': return super().get(request, *args, **kwargs) scopes, credentials = self.validate_authorization_request(request) all_scopes = get_scopes_backend().get_all_scopes() kwargs["scopes_descriptions"] = [all_scopes[scope] for scope in scopes] kwargs['scopes'] = scopes # at this point we know an Application instance with such client_id exists in the database application = get_application_model().objects.get(client_id=credentials['client_id']) try: content_orgs = list(ApplicationAccess.get_filter_values(application, ApplicationAccess.CONTENT_ORG_FILTER_NAME)) except ApplicationAccess.DoesNotExist: # No application access policy for this application exists. # so we have no content orgs. content_orgs = [] kwargs['application'] = application kwargs['content_orgs'] = content_orgs kwargs['client_id'] = credentials['client_id'] kwargs['redirect_uri'] = credentials['redirect_uri'] kwargs['response_type'] = credentials['response_type'] kwargs['state'] = credentials['state'] self.oauth2_data = kwargs # lint-amnesty, pylint: disable=attribute-defined-outside-init # following two loc are here only because of https://code.djangoproject.com/ticket/17795 form = self.get_form(self.get_form_class()) kwargs['form'] = form # If skip_authorization field is True, skip the authorization screen even # if this is the first use of the application and there was no previous authorization. # This is useful for in-house applications-> assume an in-house applications # are already approved. if application.skip_authorization: uri, headers, body, status = self.create_authorization_response( # lint-amnesty, pylint: disable=unused-variable request=self.request, scopes=" ".join(scopes), credentials=credentials, allow=True) return OAuth2ResponseRedirect(uri, application.get_allowed_schemes()) # *** Changed the if statement that checked for require_approval to an assert. assert require_approval == 'auto_even_if_expired' tokens = get_access_token_model().objects.filter( user=request.user, application=kwargs['application'], # *** Purposefully keeping this commented out code to highlight that # our version of the implementation does NOT filter by expiration date. # expires__gt=timezone.now(), ).all() # check past authorizations regarded the same scopes as the current one for token in tokens: if token.allow_scopes(scopes): uri, headers, body, status = self.create_authorization_response( request=self.request, scopes=" ".join(scopes), credentials=credentials, allow=True) return OAuth2ResponseRedirect(uri, application.get_allowed_schemes()) # render an authorization prompt so the user can approve # the application's requested scopes return self.render_to_response(self.get_context_data(**kwargs)) except OAuthToolkitError as error: return self.error_response(error, application)
def clean_scope(self): scopes = eval(self.cleaned_data['scope']) for def_scope in get_scopes_backend().get_default_scopes(): if def_scope not in scopes: scopes.append(def_scope) return ' '.join(scopes)
def get(self, request, *args, **kwargs): # Copy/Pasta'd from oauth2_provider.views.BaseAuthorizationView.get try: scopes, credentials = self.validate_authorization_request(request) # all_scopes = get_scopes_backend().get_all_scopes() # kwargs["scopes"] = scopes # kwargs["scopes_descriptions"] = [all_scopes[scope] for scope in scopes] # at this point we know an Application instance with such client_id exists in the database # TODO: Cache this! application = get_application_model().objects.get( client_id=credentials["client_id"]) kwargs["client_id"] = credentials["client_id"] kwargs["redirect_uri"] = credentials["redirect_uri"] kwargs["response_type"] = credentials["response_type"] kwargs["state"] = credentials["state"] try: kwargs["application"] = { "name": application.applicationinfo.get_visible_name(), } if application.applicationinfo.icon: kwargs["application"][ 'image'] = application.applicationinfo.icon.url if application.applicationinfo.website_url: kwargs["application"][ "url"] = application.applicationinfo.website_url app_scopes = [ s for s in re.split( r'[\s\n]+', application.applicationinfo.allowed_scopes) if s ] except ApplicationInfo.DoesNotExist: app_scopes = ["r:profile"] kwargs["application"] = dict(name=application.name, scopes=app_scopes) filtered_scopes = set(app_scopes) & set(scopes) kwargs['scopes'] = list(filtered_scopes) all_scopes = get_scopes_backend().get_all_scopes() kwargs['scopes_descriptions'] = { scope: all_scopes[scope] for scope in scopes } self.oauth2_data = kwargs # Check to see if the user has already granted access and return # a successful response depending on "approval_prompt" url parameter require_approval = request.GET.get( "approval_prompt", oauth2_settings.REQUEST_APPROVAL_PROMPT) # If skip_authorization field is True, skip the authorization screen even # if this is the first use of the application and there was no previous authorization. # This is useful for in-house applications-> assume an in-house applications # are already approved. if application.skip_authorization: success_url = self.get_authorization_redirect_url( " ".join(kwargs['scopes']), credentials) return Response({'success_url': success_url}) elif require_approval == "auto" and not request.user.is_anonymous: tokens = get_access_token_model().objects.filter( user=request.user, application=application, expires__gt=timezone.now()).all() # check past authorizations regarded the same scopes as the current one for token in tokens: if token.allow_scopes(scopes): success_url = self.get_authorization_redirect_url( " ".join(kwargs['scopes']), credentials) return Response({'success_url': success_url}) return Response(kwargs) except OAuthToolkitError as error: return Response({'error': error.oauthlib_error.description}, status=HTTP_400_BAD_REQUEST)
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): """ Ensure required scopes are permitted (as specified in the settings file) """ available_scopes = get_scopes_backend().get_available_scopes(application=client, request=request) return set(scopes).issubset(set(available_scopes))