File: decorators.py

package info (click to toggle)
django-oauth-toolkit 3.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 2,156 kB
  • sloc: python: 11,100; makefile: 159; javascript: 9; sh: 6
file content (87 lines) | stat: -rw-r--r-- 3,096 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
from functools import wraps

from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponseForbidden
from oauthlib.oauth2 import Server

from .oauth2_backends import OAuthLibCore
from .oauth2_validators import OAuth2Validator
from .scopes import get_scopes_backend
from .settings import oauth2_settings


def protected_resource(scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
    """
    Decorator to protect views by providing OAuth2 authentication out of the box,
    optionally with scope handling.

        @protected_resource()
        def my_view(request):
            # An access token is required to get here...
            # ...
            pass
    """
    _scopes = scopes or []

    def decorator(view_func):
        @wraps(view_func)
        def _validate(request, *args, **kwargs):
            validator = validator_cls()
            core = OAuthLibCore(server_cls(validator))
            valid, oauthlib_req = core.verify_request(request, scopes=_scopes)
            if valid:
                request.resource_owner = oauthlib_req.user
                return view_func(request, *args, **kwargs)
            return HttpResponseForbidden()

        return _validate

    return decorator


def rw_protected_resource(scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
    """
    Decorator to protect views by providing OAuth2 authentication and read/write scopes
    out of the box.
    GET, HEAD, OPTIONS http methods require "read" scope. Otherwise "write" scope is required.

        @rw_protected_resource()
        def my_view(request):
            # If this is a POST, you have to provide 'write' scope to get here...
            # ...
            pass

    """
    _scopes = scopes or []

    def decorator(view_func):
        @wraps(view_func)
        def _validate(request, *args, **kwargs):
            # Check if provided scopes are acceptable
            provided_scopes = get_scopes_backend().get_all_scopes()
            read_write_scopes = [oauth2_settings.READ_SCOPE, oauth2_settings.WRITE_SCOPE]

            if not set(read_write_scopes).issubset(set(provided_scopes)):
                raise ImproperlyConfigured(
                    "rw_protected_resource decorator requires following scopes {0}"
                    " to be in OAUTH2_PROVIDER['SCOPES'] list in settings".format(read_write_scopes)
                )

            # Check if method is safe
            if request.method.upper() in ["GET", "HEAD", "OPTIONS"]:
                _scopes.append(oauth2_settings.READ_SCOPE)
            else:
                _scopes.append(oauth2_settings.WRITE_SCOPE)

            # proceed with validation
            validator = validator_cls()
            core = OAuthLibCore(server_cls(validator))
            valid, oauthlib_req = core.verify_request(request, scopes=_scopes)
            if valid:
                request.resource_owner = oauthlib_req.user
                return view_func(request, *args, **kwargs)
            return HttpResponseForbidden()

        return _validate

    return decorator