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 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 check_token_endpoint_auth_method(self, method):
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, token, token_type_hint, client):
256
- qs = OAuth2Token.objects(client=client)
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=token).first()
271
+ return qs.filter(access_token=token_string).first()
259
272
  elif token_type_hint == "refresh_token":
260
- return qs.filter(refresh_token=token).first()
273
+ return qs.filter(refresh_token=token_string).first()
261
274
  else:
262
- qs = qs(db.Q(access_token=token) | db.Q(refresh_token=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.validate_consent_request(end_user=current_user)
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
- credential = request.credential
329
- credential.update(scope=scope, **token)
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 = request.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():
@@ -301,7 +301,7 @@ class APIAuthTest:
301
301
 
302
302
  assert_status(response, 400)
303
303
  assert "error" in response.json
304
- assert "redirect_uri" in response.json["error_description"]
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 "redirect_uri" in response.json["error_description"]
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"] == "invalid_grant"
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
- token = OAuth2Token.objects.create(
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": token.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.dev31308
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 ==0.14.3
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=80NtFfQpd-DAhRDN-4h0zi5gB2qHoBIrF8NXzbCnMS4,11621
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=0Y_JXQ4x8jvc_pp4g3H6gZ_X0QVGM6TaZ2IN9LeOWEg,21657
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.dev31308.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
701
- udata-9.1.5.dev31308.dist-info/METADATA,sha256=zM6nwCbri71NX69yALYHV8N6Yq4wcQTYeVIw0JR9W50,129834
702
- udata-9.1.5.dev31308.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
703
- udata-9.1.5.dev31308.dist-info/entry_points.txt,sha256=3SKiqVy4HUqxf6iWspgMqH8d88Htk6KoLbG1BU-UddQ,451
704
- udata-9.1.5.dev31308.dist-info/top_level.txt,sha256=39OCg-VWFWOq4gCKnjKNu-s3OwFlZIu_dVH8Gl6ndHw,12
705
- udata-9.1.5.dev31308.dist-info/RECORD,,
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,,