basic resource model
This commit is contained in:
parent
44d55487be
commit
56ebabad7f
4 changed files with 174 additions and 1 deletions
|
|
@ -5,9 +5,10 @@ Django models for the gooyal-restrict-scopes package.
|
||||||
import requests
|
import requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from oauth2_provider.models import AbstractApplication
|
from oauth2_provider.models import AbstractApplication, AbstractAccessToken
|
||||||
from oauth2_provider.scopes import get_scopes_backend
|
from oauth2_provider.scopes import get_scopes_backend
|
||||||
from oauth2_provider.settings import oauth2_settings
|
from oauth2_provider.settings import oauth2_settings
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
class Application(AbstractApplication):
|
class Application(AbstractApplication):
|
||||||
|
|
@ -95,3 +96,25 @@ class Scope(models.Model):
|
||||||
defaults={'description': description, 'is_default': is_default}
|
defaults={'description': description, 'is_default': is_default}
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class Resource(AbstractAccessToken):
|
||||||
|
source_refresh_token = None
|
||||||
|
id = None
|
||||||
|
application = None
|
||||||
|
|
||||||
|
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True)
|
||||||
|
name = models.CharField(max_length=255, blank=True)
|
||||||
|
|
||||||
|
user = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True,
|
||||||
|
related_name="resources"
|
||||||
|
)
|
||||||
|
expires = models.DateTimeField()
|
||||||
|
token = models.CharField(max_length=255, unique=True, ) # introspect token
|
||||||
|
|
||||||
|
scope = 'introspection'
|
||||||
|
scopes = {'introspection': 'Introspect token scope'}
|
||||||
|
|
||||||
|
def allow_scopes(self, scopes):
|
||||||
|
return scopes == [self.scope]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
from oauth2_provider.oauth2_validators import OAuth2Validator
|
from oauth2_provider.oauth2_validators import OAuth2Validator
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from oauth2_provider.settings import oauth2_settings
|
||||||
|
|
||||||
|
from apps.gooyal_oauth2.models import Resource
|
||||||
|
|
||||||
USER_MODEL = get_user_model()
|
USER_MODEL = get_user_model()
|
||||||
|
|
||||||
|
|
@ -45,3 +48,44 @@ class MultiGatewayOAuth2Validator(OAuth2Validator): # pylint: disable=w0223
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class IntrospectOAuth2Validator(OAuth2Validator):
|
||||||
|
def validate_bearer_token(self, token, scopes, request):
|
||||||
|
"""
|
||||||
|
When users try to access resources, check that provided token is valid
|
||||||
|
"""
|
||||||
|
if not token:
|
||||||
|
return False
|
||||||
|
|
||||||
|
introspection_url = oauth2_settings.RESOURCE_SERVER_INTROSPECTION_URL
|
||||||
|
introspection_token = oauth2_settings.RESOURCE_SERVER_AUTH_TOKEN
|
||||||
|
introspection_credentials = oauth2_settings.RESOURCE_SERVER_INTROSPECTION_CREDENTIALS
|
||||||
|
|
||||||
|
try:
|
||||||
|
access_token = Resource.objects.select_related("application", "user").get(token=token)
|
||||||
|
except Resource.DoesNotExist:
|
||||||
|
access_token = None
|
||||||
|
|
||||||
|
# if there is no token or it's invalid then introspect the token if there's an external OAuth server
|
||||||
|
if not access_token or not access_token.is_valid(scopes):
|
||||||
|
if introspection_url and (introspection_token or introspection_credentials):
|
||||||
|
access_token = self._get_token_from_authentication_server(
|
||||||
|
token,
|
||||||
|
introspection_url,
|
||||||
|
introspection_token,
|
||||||
|
introspection_credentials
|
||||||
|
)
|
||||||
|
|
||||||
|
if access_token and access_token.is_valid(scopes):
|
||||||
|
request.client = access_token.application
|
||||||
|
request.user = access_token.user
|
||||||
|
request.scopes = scopes
|
||||||
|
|
||||||
|
# this is needed by django rest framework
|
||||||
|
request.access_token = access_token
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self._set_oauth2_error_on_request(request, access_token, scopes)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
|
||||||
103
apps/gooyal_oauth2/views.py
Normal file
103
apps/gooyal_oauth2/views.py
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
"""
|
||||||
|
Django views for the gooyal-dynamic-scopes package.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import functools
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.http import HttpResponse, HttpResponseForbidden
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from django.views.decorators.http import require_http_methods, require_POST
|
||||||
|
|
||||||
|
from oauthlib.oauth2 import Server
|
||||||
|
|
||||||
|
from oauth2_provider.oauth2_backends import OAuthLibCore
|
||||||
|
from oauth2_provider.views import IntrospectTokenView
|
||||||
|
|
||||||
|
from apps.gooyal_oauth2.validators import IntrospectOAuth2Validator
|
||||||
|
from .models import Scope
|
||||||
|
|
||||||
|
|
||||||
|
def protected_resource(scopes=None):
|
||||||
|
"""
|
||||||
|
Implementation of protected_resource decorator that saves the client on the
|
||||||
|
request for the view function to use.
|
||||||
|
|
||||||
|
Cribbed from django-oauth-toolkit.
|
||||||
|
"""
|
||||||
|
_scopes = scopes or []
|
||||||
|
|
||||||
|
def decorator(view_func):
|
||||||
|
@functools.wraps(view_func)
|
||||||
|
def _validate(request, *args, **kwargs):
|
||||||
|
validator = IntrospectOAuth2Validator()
|
||||||
|
core = OAuthLibCore(Server(validator))
|
||||||
|
valid, oauthlib_req = core.verify_request(request, scopes=_scopes)
|
||||||
|
if valid:
|
||||||
|
request.client = oauthlib_req.client
|
||||||
|
request.resource_owner = oauthlib_req.user
|
||||||
|
return view_func(request, *args, **kwargs)
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
return _validate
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
@require_http_methods(['GET', 'POST'])
|
||||||
|
@csrf_exempt
|
||||||
|
@protected_resource(scopes=[settings.INTROSPECT_SCOPE])
|
||||||
|
def introspect_token(request):
|
||||||
|
"""
|
||||||
|
Version of the introspection view protected by a regular scope instead of
|
||||||
|
read-write scopes.
|
||||||
|
|
||||||
|
Also allows for the required scope to be changed using a setting.
|
||||||
|
"""
|
||||||
|
if request.method == 'GET':
|
||||||
|
token = request.GET.get("token", None)
|
||||||
|
else:
|
||||||
|
token = request.POST.get("token", None)
|
||||||
|
return IntrospectTokenView.get_token_response(token)
|
||||||
|
|
||||||
|
|
||||||
|
# @require_POST
|
||||||
|
# @csrf_exempt
|
||||||
|
# @protected_resource(scopes=[settings.REGISTER_SCOPE_SCOPE])
|
||||||
|
# def register_scope(request):
|
||||||
|
# """
|
||||||
|
# Implements an endpoint for registering a scope.
|
||||||
|
# """
|
||||||
|
# # Get the scope data from the request body
|
||||||
|
# scope_data = json.loads(request.body) if request.body else {}
|
||||||
|
# try:
|
||||||
|
# try:
|
||||||
|
# # If a scope with the given name already exists, find it
|
||||||
|
# scope = Scope.objects.get(name=scope_data['name'])
|
||||||
|
# except Scope.DoesNotExist:
|
||||||
|
# # If no scope with the given name exists, create it
|
||||||
|
# _ = Scope.objects.create(
|
||||||
|
# application=request.client,
|
||||||
|
# name=scope_data['name'],
|
||||||
|
# description=scope_data['description'],
|
||||||
|
# is_default=scope_data.get('is_default', False)
|
||||||
|
# )
|
||||||
|
# # Respond with a 201 Created
|
||||||
|
# return HttpResponse(status=201)
|
||||||
|
# except KeyError as exc:
|
||||||
|
# # A key missing in the data should be reported as a bad request
|
||||||
|
# return HttpResponse(
|
||||||
|
# status=400,
|
||||||
|
# content="'{}' must be given in request data".format(exc.args[0]),
|
||||||
|
# content_type='text/plain'
|
||||||
|
# )
|
||||||
|
# # If the scope does exist, check that the current application is the
|
||||||
|
# # owner of the scope before updating it
|
||||||
|
# if scope.application and scope.application == request.client:
|
||||||
|
# scope.description = scope_data['description']
|
||||||
|
# scope.is_default = scope_data.get('is_default', False)
|
||||||
|
# scope.save()
|
||||||
|
# return HttpResponse(status=200)
|
||||||
|
# else:
|
||||||
|
# return HttpResponse(status=403)
|
||||||
|
|
@ -20,6 +20,7 @@ from django.contrib.auth.views import LogoutView
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from apps.gooyal_oauth2.views import introspect_token
|
||||||
from apps.transactions.views import TransactionList, TransactionDetail, TransactionPay, TransactionReceipt, \
|
from apps.transactions.views import TransactionList, TransactionDetail, TransactionPay, TransactionReceipt, \
|
||||||
ServiceTransactionVerify, ServiceTransactionSubmit
|
ServiceTransactionVerify, ServiceTransactionSubmit
|
||||||
from apps.users.views import UserListView, UserDetailView, AccountView, RequestOTPView, ChangePasswordView, \
|
from apps.users.views import UserListView, UserDetailView, AccountView, RequestOTPView, ChangePasswordView, \
|
||||||
|
|
@ -34,6 +35,8 @@ urlpatterns = [
|
||||||
path('logout/', LogoutView.as_view(), name='logout'),
|
path('logout/', LogoutView.as_view(), name='logout'),
|
||||||
path('', home, name='home'),
|
path('', home, name='home'),
|
||||||
|
|
||||||
|
path('oauth2/introspect', introspect_token),
|
||||||
|
path('oauth2/introspect/', introspect_token, name='introspect'),
|
||||||
path('oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')),
|
path('oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')),
|
||||||
# url(r'^oauth2/register_scope/$', register_scope, name = 'register-scope'),
|
# url(r'^oauth2/register_scope/$', register_scope, name = 'register-scope'),
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue