Reputation: 1141
I'm using drf and oauth toolkit with IsAuthenticatedOrTokenHasScope permissions as default. I have a view that contains scopes
required_scopes = ['mod', 'admin']
When users logs into the app he have special groups which define his permission scope. So when the moderator logs into the app he gets mod
scope. When he calls my view he gets 403 because allow_scopes
in AccessToken model returns False. That is because the resource_scopes
is ['mod', 'admin'] and provided_scopes is 'mod'. When method allow_scopes
checks resource_scopes.issubset(provided_scopes)
she returns False which is not intentional in my case.
Is there any other option without overwriting allow_scopes in AccessToken model to define that this view needs scope mod
or scope admin
. ?
Upvotes: 1
Views: 2069
Reputation: 1032
I think I found a way to get this to work. The oauth2_provider
doesn't provided any function to achieve this. So, what I did was I defined my own custom permission which is similar to the TokenHasScope
. So, create a file called permissions.py
and paste the code
from rest_framework import permissions
from django.core.exceptions import ImproperlyConfigured
from rest_framework.exceptions import PermissionDenied
from oauth2_provider.settings import oauth2_settings
class TokenHasAtLeastOneScope(permissions.BasePermission):
"""
The request is authenticated as a user and the token used has at least one of the right scope
"""
def has_permission(self, request, view):
token = request.auth
if not token:
return False
if hasattr(token, "scope"): # OAuth 2
required_scopes = self.get_scopes(request, view)
log.debug("Required scopes to access resource: {0}".format(required_scopes))
# If any scope in the list of required_scopes is valid, return True.
for given_scope in required_scopes:
if token.is_valid([given_scope]):
return True
# Provide information about required scope?
include_required_scope = (
oauth2_settings.ERROR_RESPONSE_WITH_SCOPES
and required_scopes
and not token.is_expired()
and not token.allow_scopes(required_scopes)
)
if include_required_scope:
self.message = {
"detail": PermissionDenied.default_detail,
"required_scopes": list(required_scopes),
}
return False
assert False, (
"TokenHasAtLeastOneScope requires the"
"`oauth2_provider.rest_framework.OAuth2Authentication` authentication "
"class to be used."
)
Then in your view, import permissions and set it accordingly
permission_classes = (permissions.TokenHasAtLeastOneScope)
required_scopes = ['mod', 'admin']
In the custom TokenHasAtLeastOneScope
above, the code is similar to TokenHasScope
. The only line that changes is
for given_scope in required_scopes:
if token.is_valid([given_scope]):
return True
Which loop through the items in your required_scopes
list and if it finds a valid scope, it returns True.
Upvotes: 1