OAuth: Add profile-only access

This commit is contained in:
Raphael Michel
2020-09-28 16:01:59 +02:00
parent ae0637a3d6
commit 3cbcf663e5
8 changed files with 92 additions and 24 deletions

View File

@@ -84,3 +84,15 @@ class EventCRUDPermission(EventPermission):
return False
return True
class ProfilePermission(BasePermission):
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False
if isinstance(request.auth, OAuthAccessToken):
if not (request.auth.allow_scopes(['read']) or request.auth.allow_scopes(['profile'])) and request.method in SAFE_METHODS:
return False
return True

View File

@@ -9,7 +9,7 @@ from oauth2_provider.settings import oauth2_settings
class Validator(OAuth2Validator):
def save_authorization_code(self, client_id, code, request, *args, **kwargs):
if not getattr(request, 'organizers', None):
if not getattr(request, 'organizers', None) and request.scopes != ['profile']:
raise FatalClientError('No organizers selected.')
expires = timezone.now() + timedelta(
@@ -18,7 +18,8 @@ class Validator(OAuth2Validator):
expires=expires, redirect_uri=request.redirect_uri,
scope=" ".join(request.scopes))
g.save()
g.organizers.add(*request.organizers.all())
if request.scopes != ['profile']:
g.organizers.add(*request.organizers.all())
def validate_code(self, client_id, code, client, request, *args, **kwargs):
try:
@@ -34,12 +35,14 @@ class Validator(OAuth2Validator):
return False
def _create_access_token(self, expires, request, token, source_refresh_token=None):
if not getattr(request, 'organizers', None) and not getattr(source_refresh_token, 'access_token'):
if not getattr(request, 'organizers', None) and not getattr(source_refresh_token, 'access_token', None) and token["scope"] != 'profile':
raise FatalClientError('No organizers selected.')
if hasattr(request, 'organizers'):
orgs = list(request.organizers.all())
else:
orgs = list(source_refresh_token.access_token.organizers.all())
if token['scope'] != 'profile':
if hasattr(request, 'organizers'):
orgs = list(request.organizers.all())
else:
orgs = list(source_refresh_token.access_token.organizers.all())
access_token = super()._create_access_token(expires, request, token, source_refresh_token=None)
access_token.organizers.add(*orgs)
if token['scope'] != 'profile':
access_token.organizers.add(*orgs)
return access_token

View File

@@ -3,8 +3,9 @@ import logging
from django import forms
from django.conf import settings
from django.utils.translation import gettext as _
from oauth2_provider.exceptions import OAuthToolkitError
from oauth2_provider.exceptions import OAuthToolkitError, FatalClientError
from oauth2_provider.forms import AllowForm
from oauth2_provider.settings import oauth2_settings
from oauth2_provider.views import (
AuthorizationView as BaseAuthorizationView,
RevokeTokenView as BaseRevokeTokenView, TokenView as BaseTokenView,
@@ -24,9 +25,12 @@ class OAuthAllowForm(AllowForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
scope = kwargs.pop('scope')
super().__init__(*args, **kwargs)
self.fields['organizers'].queryset = Organizer.objects.filter(
pk__in=user.teams.values_list('organizer', flat=True))
if scope == 'profile':
del self.fields['organizers']
class AuthorizationView(BaseAuthorizationView):
@@ -36,6 +40,7 @@ class AuthorizationView(BaseAuthorizationView):
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
kwargs['scope'] = self.request.GET.get('scope')
return kwargs
def get_context_data(self, **kwargs):
@@ -43,8 +48,14 @@ class AuthorizationView(BaseAuthorizationView):
ctx['settings'] = settings
return ctx
def create_authorization_response(self, request, scopes, credentials, allow, organizers):
credentials["organizers"] = organizers
def validate_authorization_request(self, request):
require_approval = request.GET.get("approval_prompt", oauth2_settings.REQUEST_APPROVAL_PROMPT)
if require_approval != 'force' and request.GET.get('scope') != 'profile':
raise FatalClientError('Combnination of require_approval and scope values not allowed.')
return super().validate_authorization_request(request)
def create_authorization_response(self, request, scopes, credentials, allow, organizers=None):
credentials["organizers"] = organizers or []
return super().create_authorization_response(request, scopes, credentials, allow)
def form_valid(self, form):

View File

@@ -3,14 +3,18 @@ from rest_framework.authentication import SessionAuthentication
from rest_framework.response import Response
from rest_framework.views import APIView
from pretix.api.auth.permission import ProfilePermission
class MeView(APIView):
authentication_classes = (SessionAuthentication, OAuth2Authentication)
permission_classes = (ProfilePermission,)
def get(self, request, format=None):
return Response({
'email': request.user.email,
'fullname': request.user.fullname,
'locale': request.user.locale,
'is_staff': request.user.is_staff,
'timezone': request.user.timezone
})