File: requests.py

package info (click to toggle)
flask-dance 7.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 932 kB
  • sloc: python: 6,342; makefile: 162
file content (200 lines) | stat: -rw-r--r-- 7,154 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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
from functools import wraps

from flask import redirect, url_for
from oauthlib.common import to_unicode
from requests_oauthlib import OAuth1Session as BaseOAuth1Session
from requests_oauthlib import OAuth2Session as BaseOAuth2Session
from urlobject import URLObject
from werkzeug.utils import cached_property


class OAuth1Session(BaseOAuth1Session):
    """
    A :class:`requests.Session` subclass that can do some special things:

    * lazy-loads OAuth1 tokens from the storage via the blueprint
    * handles OAuth1 authentication
      (from :class:`requests_oauthlib.OAuth1Session` superclass)
    * has a ``base_url`` property used for relative URL resolution

    Note that this is a session between the consumer (your website) and the
    provider (e.g. Google), and *not* a session between a user of your website
    and your website.
    """

    def __init__(self, blueprint=None, base_url=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.blueprint = blueprint
        self.base_url = URLObject(base_url)

    @cached_property
    def token(self):
        """
        Get and set the values in the OAuth token, structured as a dictionary.
        """
        return self.blueprint.token

    def load_token(self):
        t = self.token
        if t and "oauth_token" in t and "oauth_token_secret" in t:
            # This really, really violates the Law of Demeter, but
            # I don't see a better way to set these parameters. :(
            self.auth.client.resource_owner_key = to_unicode(t["oauth_token"])
            self.auth.client.resource_owner_secret = to_unicode(t["oauth_token_secret"])
            return True
        return False

    @property
    def authorized(self):
        """This is the property used when you have a statement in your code
        that reads "if <provider>.authorized:", e.g. "if google.authorized:".

        The way it works is kind of complicated: this function just tries
        to load the token, and then the 'super()' statement basically just
        tests if the token exists (see BaseOAuth1Session.authorized).

        To load the token, it calls the load_token() function within this class,
        which in turn checks the 'token' property of this class (another
        function), which in turn checks the 'token' property of the blueprint
        (see base.py), which calls 'storage.get()' to actually try to load
        the token from the cache/db (see the 'get()' function in
        storage/sqla.py).
        """
        self.load_token()
        return super().authorized

    @property
    def authorization_required(self):
        """
        .. versionadded:: 1.3.0

        This is a decorator for a view function. If the current user does not
        have an OAuth token, then they will be redirected to the
        :meth:`~flask_dance.consumer.oauth1.OAuth1ConsumerBlueprint.login`
        view to obtain one.
        """

        def wrapper(func):
            @wraps(func)
            def check_authorization(*args, **kwargs):
                if not self.authorized:
                    endpoint = f"{self.blueprint.name}.login"
                    return redirect(url_for(endpoint))
                return func(*args, **kwargs)

            return check_authorization

        return wrapper

    def prepare_request(self, request):
        if self.base_url:
            request.url = self.base_url.relative(request.url)
        return super().prepare_request(request)

    def request(
        self, method, url, data=None, headers=None, should_load_token=True, **kwargs
    ):
        if should_load_token:
            self.load_token()
        return super().request(
            method=method, url=url, data=data, headers=headers, **kwargs
        )


class OAuth2Session(BaseOAuth2Session):
    """
    A :class:`requests.Session` subclass that can do some special things:

    * lazy-loads OAuth2 tokens from the storage via the blueprint
    * handles OAuth2 authentication
      (from :class:`requests_oauthlib.OAuth2Session` superclass)
    * has a ``base_url`` property used for relative URL resolution

    Note that this is a session between the consumer (your website) and the
    provider (e.g. Google), and *not* a session between a user of your website
    and your website.
    """

    def __init__(self, blueprint=None, base_url=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.blueprint = blueprint
        self.base_url = URLObject(base_url)
        del self.token

    @cached_property
    def token(self):
        """
        Get and set the values in the OAuth token, structured as a dictionary.
        """
        return self.blueprint.token

    def load_token(self):
        self._client.token = self.token
        if self.token:
            self._client.populate_token_attributes(self.token)
            return True
        return False

    @property
    def access_token(self):
        """
        Returns the ``access_token`` from the OAuth token.
        """
        return self.token and self.token.get("access_token")

    @property
    def authorized(self):
        """This is the property used when you have a statement in your code
        that reads "if <provider>.authorized:", e.g. "if google.authorized:".

        The way it works is kind of complicated: this function just tries
        to load the token, and then the 'super()' statement basically just
        tests if the token exists (see BaseOAuth1Session.authorized).

        To load the token, it calls the load_token() function within this class,
        which in turn checks the 'token' property of this class (another
        function), which in turn checks the 'token' property of the blueprint
        (see base.py), which calls 'storage.get()' to actually try to load
        the token from the cache/db (see the 'get()' function in
        storage/sqla.py).
        """
        self.load_token()
        return super().authorized

    @property
    def authorization_required(self):
        """
        .. versionadded:: 1.3.0

        This is a decorator for a view function. If the current user does not
        have an OAuth token, then they will be redirected to the
        :meth:`~flask_dance.consumer.oauth2.OAuth2ConsumerBlueprint.login`
        view to obtain one.
        """

        def wrapper(func):
            @wraps(func)
            def check_authorization(*args, **kwargs):
                if not self.authorized:
                    endpoint = f"{self.blueprint.name}.login"
                    return redirect(url_for(endpoint))
                return func(*args, **kwargs)

            return check_authorization

        return wrapper

    def request(self, method, url, data=None, headers=None, **kwargs):
        if self.base_url:
            url = self.base_url.relative(url)

        self.load_token()
        return super().request(
            method=method,
            url=url,
            data=data,
            headers=headers,
            client_id=self.blueprint.client_id,
            client_secret=self.blueprint.client_secret,
            **kwargs,
        )