udata 9.1.5.dev31308__py2.py3-none-any.whl → 9.1.5.dev31332__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of udata might be problematic. Click here for more details.
- udata/api/oauth2.py +28 -13
- udata/tests/api/test_auth_api.py +40 -5
- {udata-9.1.5.dev31308.dist-info → udata-9.1.5.dev31332.dist-info}/METADATA +3 -2
- {udata-9.1.5.dev31308.dist-info → udata-9.1.5.dev31332.dist-info}/RECORD +8 -8
- {udata-9.1.5.dev31308.dist-info → udata-9.1.5.dev31332.dist-info}/LICENSE +0 -0
- {udata-9.1.5.dev31308.dist-info → udata-9.1.5.dev31332.dist-info}/WHEEL +0 -0
- {udata-9.1.5.dev31308.dist-info → udata-9.1.5.dev31332.dist-info}/entry_points.txt +0 -0
- {udata-9.1.5.dev31308.dist-info → udata-9.1.5.dev31332.dist-info}/top_level.txt +0 -0
udata/api/oauth2.py
CHANGED
|
@@ -15,6 +15,7 @@ As well as a sample application:
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import fnmatch
|
|
18
|
+
import time
|
|
18
19
|
from datetime import datetime, timedelta
|
|
19
20
|
|
|
20
21
|
from authlib.integrations.flask_oauth2 import AuthorizationServer, ResourceProtector
|
|
@@ -111,7 +112,7 @@ class OAuth2Client(ClientMixin, db.Datetimed, db.Document):
|
|
|
111
112
|
def check_client_secret(self, client_secret):
|
|
112
113
|
return self.secret == client_secret
|
|
113
114
|
|
|
114
|
-
def
|
|
115
|
+
def check_endpoint_auth_method(self, method, _endpoint):
|
|
115
116
|
if not self.has_client_secret():
|
|
116
117
|
return method == "none"
|
|
117
118
|
return method in ("client_secret_post", "client_secret_basic")
|
|
@@ -149,6 +150,9 @@ class OAuth2Token(db.Document):
|
|
|
149
150
|
def __str__(self):
|
|
150
151
|
return "<OAuth2Token({0.client.name})>".format(self)
|
|
151
152
|
|
|
153
|
+
def check_client(self, client):
|
|
154
|
+
return self.client == client
|
|
155
|
+
|
|
152
156
|
def get_scope(self):
|
|
153
157
|
return self.scope
|
|
154
158
|
|
|
@@ -161,6 +165,13 @@ class OAuth2Token(db.Document):
|
|
|
161
165
|
def get_client_id(self):
|
|
162
166
|
return str(self.client.id)
|
|
163
167
|
|
|
168
|
+
def is_expired(self):
|
|
169
|
+
now = time.time()
|
|
170
|
+
return self.get_expires_at() < now
|
|
171
|
+
|
|
172
|
+
def is_revoked(self):
|
|
173
|
+
return self.revoked
|
|
174
|
+
|
|
164
175
|
def is_refresh_token_valid(self):
|
|
165
176
|
if self.revoked:
|
|
166
177
|
return False
|
|
@@ -238,6 +249,8 @@ class PasswordGrant(grants.ResourceOwnerPasswordCredentialsGrant):
|
|
|
238
249
|
|
|
239
250
|
|
|
240
251
|
class RefreshTokenGrant(grants.RefreshTokenGrant):
|
|
252
|
+
INCLUDE_NEW_REFRESH_TOKEN = True
|
|
253
|
+
|
|
241
254
|
def authenticate_refresh_token(self, refresh_token):
|
|
242
255
|
item = OAuth2Token.objects(refresh_token=refresh_token).first()
|
|
243
256
|
if item and item.is_refresh_token_valid():
|
|
@@ -252,17 +265,17 @@ class RefreshTokenGrant(grants.RefreshTokenGrant):
|
|
|
252
265
|
|
|
253
266
|
|
|
254
267
|
class RevokeToken(RevocationEndpoint):
|
|
255
|
-
def query_token(self,
|
|
256
|
-
qs = OAuth2Token.objects(
|
|
268
|
+
def query_token(self, token_string, token_type_hint):
|
|
269
|
+
qs = OAuth2Token.objects()
|
|
257
270
|
if token_type_hint == "access_token":
|
|
258
|
-
return qs.filter(access_token=
|
|
271
|
+
return qs.filter(access_token=token_string).first()
|
|
259
272
|
elif token_type_hint == "refresh_token":
|
|
260
|
-
return qs.filter(refresh_token=
|
|
273
|
+
return qs.filter(refresh_token=token_string).first()
|
|
261
274
|
else:
|
|
262
|
-
qs = qs(db.Q(access_token=
|
|
275
|
+
qs = qs(db.Q(access_token=token_string) | db.Q(refresh_token=token_string))
|
|
263
276
|
return qs.first()
|
|
264
277
|
|
|
265
|
-
def revoke_token(self, token):
|
|
278
|
+
def revoke_token(self, token, _request):
|
|
266
279
|
token.revoked = True
|
|
267
280
|
token.save()
|
|
268
281
|
|
|
@@ -295,7 +308,7 @@ def revoke_token():
|
|
|
295
308
|
def authorize(*args, **kwargs):
|
|
296
309
|
if request.method == "GET":
|
|
297
310
|
try:
|
|
298
|
-
grant = oauth.
|
|
311
|
+
grant = oauth.get_consent_grant(end_user=current_user)
|
|
299
312
|
except OAuth2Error as error:
|
|
300
313
|
return error.error
|
|
301
314
|
# Bypass authorization screen for internal clients
|
|
@@ -324,13 +337,15 @@ def query_client(client_id):
|
|
|
324
337
|
|
|
325
338
|
def save_token(token, request):
|
|
326
339
|
scope = token.pop("scope", "")
|
|
340
|
+
client = request.client
|
|
341
|
+
user = request.user or client.owner
|
|
327
342
|
if request.grant_type == "refresh_token":
|
|
328
|
-
|
|
329
|
-
|
|
343
|
+
old_token = OAuth2Token.objects(
|
|
344
|
+
refresh_token=request.refresh_token.refresh_token, client=client, user=user, scope=scope
|
|
345
|
+
).first()
|
|
346
|
+
old_token.update(**token)
|
|
330
347
|
else:
|
|
331
|
-
client =
|
|
332
|
-
user = request.user or client.owner
|
|
333
|
-
OAuth2Token.objects.create(client=client, user=user.id, scope=scope, **token)
|
|
348
|
+
OAuth2Token.objects.create(client=client, user=user, scope=scope, **token)
|
|
334
349
|
|
|
335
350
|
|
|
336
351
|
def check_credentials():
|
udata/tests/api/test_auth_api.py
CHANGED
|
@@ -301,7 +301,7 @@ class APIAuthTest:
|
|
|
301
301
|
|
|
302
302
|
assert_status(response, 400)
|
|
303
303
|
assert "error" in response.json
|
|
304
|
-
assert "
|
|
304
|
+
assert "Redirect URI" in response.json["error_description"]
|
|
305
305
|
|
|
306
306
|
@pytest.mark.options(OAUTH2_ALLOW_WILDCARD_IN_REDIRECT_URI=True)
|
|
307
307
|
@pytest.mark.oauth(redirect_uris=["https://*.test.org/callback"])
|
|
@@ -327,7 +327,7 @@ class APIAuthTest:
|
|
|
327
327
|
|
|
328
328
|
assert_status(response, 400)
|
|
329
329
|
assert "error" in response.json
|
|
330
|
-
assert "
|
|
330
|
+
assert "Redirect URI" in response.json["error_description"]
|
|
331
331
|
|
|
332
332
|
def test_authorization_grant_token(self, client, oauth):
|
|
333
333
|
client.login()
|
|
@@ -360,6 +360,8 @@ class APIAuthTest:
|
|
|
360
360
|
assert200(response)
|
|
361
361
|
assert response.content_type == "application/json"
|
|
362
362
|
assert "access_token" in response.json
|
|
363
|
+
tokens = OAuth2Token.objects(access_token=response.json["access_token"])
|
|
364
|
+
assert len(tokens) == 1 # A token has been created and saved.
|
|
363
365
|
|
|
364
366
|
def test_s256_code_challenge_success_client_secret_basic(self, client, oauth):
|
|
365
367
|
code_verifier = generate_token(48)
|
|
@@ -582,23 +584,36 @@ class APIAuthTest:
|
|
|
582
584
|
)
|
|
583
585
|
|
|
584
586
|
assert_status(response, 400)
|
|
585
|
-
assert response.json["error"] == "
|
|
587
|
+
assert response.json["error"] == "unsupported_response_type"
|
|
586
588
|
|
|
587
589
|
@pytest.mark.oauth(confidential=True)
|
|
588
590
|
def test_refresh_token(self, client, oauth):
|
|
589
591
|
user = UserFactory()
|
|
590
|
-
|
|
592
|
+
token_to_be_refreshed = OAuth2Token.objects.create(
|
|
591
593
|
client=oauth,
|
|
592
594
|
user=user,
|
|
593
595
|
access_token="access-token",
|
|
594
596
|
refresh_token="refresh-token",
|
|
595
597
|
)
|
|
598
|
+
token_same_user_not_refreshed = OAuth2Token.objects.create(
|
|
599
|
+
client=oauth,
|
|
600
|
+
user=user,
|
|
601
|
+
access_token="same-user-access-token",
|
|
602
|
+
refresh_token="same-user-refresh-token",
|
|
603
|
+
)
|
|
604
|
+
other_token = OAuth2Token.objects.create(
|
|
605
|
+
client=oauth,
|
|
606
|
+
user=UserFactory(),
|
|
607
|
+
access_token="other-access-token",
|
|
608
|
+
refresh_token="other-refresh-token",
|
|
609
|
+
)
|
|
610
|
+
tokens_count = OAuth2Token.objects.count()
|
|
596
611
|
|
|
597
612
|
response = client.post(
|
|
598
613
|
url_for("oauth.token"),
|
|
599
614
|
{
|
|
600
615
|
"grant_type": "refresh_token",
|
|
601
|
-
"refresh_token":
|
|
616
|
+
"refresh_token": token_to_be_refreshed.refresh_token,
|
|
602
617
|
},
|
|
603
618
|
headers=basic_header(oauth),
|
|
604
619
|
)
|
|
@@ -607,6 +622,26 @@ class APIAuthTest:
|
|
|
607
622
|
assert response.content_type == "application/json"
|
|
608
623
|
assert "access_token" in response.json
|
|
609
624
|
|
|
625
|
+
# Reload from the DB.
|
|
626
|
+
token_to_be_refreshed.reload()
|
|
627
|
+
token_same_user_not_refreshed.reload()
|
|
628
|
+
other_token.reload()
|
|
629
|
+
|
|
630
|
+
assert tokens_count == OAuth2Token.objects.count() # No new token created.
|
|
631
|
+
|
|
632
|
+
# The access token has been refreshed.
|
|
633
|
+
assert token_to_be_refreshed.access_token != "access-token"
|
|
634
|
+
# The refresh token is also updated.
|
|
635
|
+
assert token_to_be_refreshed.refresh_token != "refresh-token"
|
|
636
|
+
|
|
637
|
+
# No change to the user's other token.
|
|
638
|
+
assert token_same_user_not_refreshed.access_token == "same-user-access-token"
|
|
639
|
+
assert token_same_user_not_refreshed.refresh_token == "same-user-refresh-token"
|
|
640
|
+
|
|
641
|
+
# No change to other token.
|
|
642
|
+
assert other_token.access_token == "other-access-token"
|
|
643
|
+
assert other_token.refresh_token == "other-refresh-token"
|
|
644
|
+
|
|
610
645
|
@pytest.mark.parametrize("token_type", ["access_token", "refresh_token"])
|
|
611
646
|
def test_revoke_token(self, client, oauth, token_type):
|
|
612
647
|
user = UserFactory()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: udata
|
|
3
|
-
Version: 9.1.5.
|
|
3
|
+
Version: 9.1.5.dev31332
|
|
4
4
|
Summary: Open data portal
|
|
5
5
|
Home-page: https://github.com/opendatateam/udata
|
|
6
6
|
Author: Opendata Team
|
|
@@ -25,7 +25,7 @@ Requires-Dist: amqp ==5.2.0
|
|
|
25
25
|
Requires-Dist: aniso8601 ==9.0.1
|
|
26
26
|
Requires-Dist: appdirs ==1.4.4
|
|
27
27
|
Requires-Dist: attrs ==23.2.0
|
|
28
|
-
Requires-Dist: authlib ==
|
|
28
|
+
Requires-Dist: authlib ==1.3.1
|
|
29
29
|
Requires-Dist: awesome-slugify ==1.6.5
|
|
30
30
|
Requires-Dist: babel ==2.12.1
|
|
31
31
|
Requires-Dist: bcrypt ==3.1.7
|
|
@@ -156,6 +156,7 @@ It is collectively taken care of by members of the
|
|
|
156
156
|
- Always add Vary even for non CORS requests [#3132](https://github.com/opendatateam/udata/pull/3132)
|
|
157
157
|
- Add acronym in organization csv catalog [#3134](https://github.com/opendatateam/udata/pull/3134)
|
|
158
158
|
- Limit the number of user suggestions [#3131](https://github.com/opendatateam/udata/pull/3131)
|
|
159
|
+
- Update authlib dependency from 0.14.3 to 1.3.1 [#3135](https://github.com/opendatateam/udata/pull/3135)
|
|
159
160
|
|
|
160
161
|
## 9.1.3 (2024-08-01)
|
|
161
162
|
|
|
@@ -29,7 +29,7 @@ udata/api/__init__.py,sha256=p2FcJ1I4k4HERFvYYQNitO23FnfBc-kXpMlWb7u__RM,10644
|
|
|
29
29
|
udata/api/commands.py,sha256=SwZYuANyGoI-lplWg18d0Ej8e7znmIA5EUKIU0jBA6U,3227
|
|
30
30
|
udata/api/errors.py,sha256=P_UigBf6HAL73LbXKULagmp5Cuw-H1Ms6LmXxOmFIeg,211
|
|
31
31
|
udata/api/fields.py,sha256=BO2bHPWN6CT2bNpEkkosBGVUSV383nl8MUed_wlxLmw,4176
|
|
32
|
-
udata/api/oauth2.py,sha256=
|
|
32
|
+
udata/api/oauth2.py,sha256=4emtZKqVoNrWBNQsg1AEW-K5BMcQabMp7GCY3sF6EO4,12013
|
|
33
33
|
udata/api/parsers.py,sha256=iQEwgMwdVz_gXlVZYaRCGrUFWvcf7_8uCIxw7Iw-hpw,1405
|
|
34
34
|
udata/api/signals.py,sha256=t8s7tF1KYeAygqbR4GdA76_UYvthAsyRbJaqsMmFsK4,162
|
|
35
35
|
udata/auth/__init__.py,sha256=RRMYs8rYIwXncx3Z62NFtVhuQzxQrFQpC1lBhl68WJI,1992
|
|
@@ -598,7 +598,7 @@ udata/tests/test_uris.py,sha256=MxafZ0SyzSNRomVpZnH1ChzWaHOi1MQiXe1gmKnBc6o,8517
|
|
|
598
598
|
udata/tests/test_utils.py,sha256=3BGnlvw-GOE6tLHQteo-uUeYuzq4rsjePOuytFGkpOg,7967
|
|
599
599
|
udata/tests/api/__init__.py,sha256=y4sL7LD1-KwONHF0q_Rhk2W6BmGUlp7Uz2JnX3e27sk,1218
|
|
600
600
|
udata/tests/api/test_activities_api.py,sha256=RjDDeNle3T-ydVnh6BRypqxAE_244-DXaKkuUCT0HVU,2823
|
|
601
|
-
udata/tests/api/test_auth_api.py,sha256=
|
|
601
|
+
udata/tests/api/test_auth_api.py,sha256=buWlvWOKLhEH-hS_bDyHePyyitRg8fX86hYy2Euo6AY,23200
|
|
602
602
|
udata/tests/api/test_base_api.py,sha256=2w_vz0eEuq3P3aN-ByvxGc3VZAo7XtgatFfcrzf2uEU,2244
|
|
603
603
|
udata/tests/api/test_contact_points.py,sha256=pyakkKnM4lH_asuEoXq9lQyJC9to6ZxSp4QQrHh1c1U,1041
|
|
604
604
|
udata/tests/api/test_dataservices_api.py,sha256=dfPonOmHcx83KLeUa2uqVaa2DJ_Xx91fHBqmFOlOP10,14636
|
|
@@ -697,9 +697,9 @@ udata/translations/pt/LC_MESSAGES/udata.mo,sha256=WpPzAqVd2Onv_kz45ULUySKPLrpjcc
|
|
|
697
697
|
udata/translations/pt/LC_MESSAGES/udata.po,sha256=18Op9RUITewoDRewlOdYzzq6gjsf1lsvepACV1d7zxs,44976
|
|
698
698
|
udata/translations/sr/LC_MESSAGES/udata.mo,sha256=NIYRNhVoETZUvIvWm3cCW7DtMBAnS2vXzZjMF5ZzD_c,28500
|
|
699
699
|
udata/translations/sr/LC_MESSAGES/udata.po,sha256=rQB-4V4WJ7bURj6g2j653vItr5TMHadcLQxec7_fDmg,51545
|
|
700
|
-
udata-9.1.5.
|
|
701
|
-
udata-9.1.5.
|
|
702
|
-
udata-9.1.5.
|
|
703
|
-
udata-9.1.5.
|
|
704
|
-
udata-9.1.5.
|
|
705
|
-
udata-9.1.5.
|
|
700
|
+
udata-9.1.5.dev31332.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
|
|
701
|
+
udata-9.1.5.dev31332.dist-info/METADATA,sha256=UFo6oo3kWJCPd8d-G6ZoEj4_0QyntfwPF4r881KPI7M,129939
|
|
702
|
+
udata-9.1.5.dev31332.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
|
|
703
|
+
udata-9.1.5.dev31332.dist-info/entry_points.txt,sha256=3SKiqVy4HUqxf6iWspgMqH8d88Htk6KoLbG1BU-UddQ,451
|
|
704
|
+
udata-9.1.5.dev31332.dist-info/top_level.txt,sha256=39OCg-VWFWOq4gCKnjKNu-s3OwFlZIu_dVH8Gl6ndHw,12
|
|
705
|
+
udata-9.1.5.dev31332.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|