From 69ef3a05a3fe302415e7bc44361148ee55a15917 Mon Sep 17 00:00:00 2001 From: vpsx <19900057+vpsx@users.noreply.github.com> Date: Mon, 7 Jun 2021 10:54:40 -0500 Subject: [PATCH 1/2] PXP-6617 Remove scopes from aud claim in tokens (#839) * fix(jwt-aud): Rm scopes from aud claim in id tokens * Don't pass scope to audiences arg in generate_token_response * In generate_id_token itself: * Don't append to audiences itself; instead make a copy * Check if client_id is None * Don't include aud claim in token if aud is empty * feat(scope): Add JWT scope validation * New scope arg in validate_jwt defaults to {'openid'} but allows None * No longer use aud claim for scopes * fix(aud): Pass aud to validate_jwt as string, not set * feat(scope): pass scope=None when validating session tokens * feat(scopes): Add scope claim to oidc tokens * add separate scope claim to id, refresh tkns (already in access tkns) * rm scopes from aud claim in all tokens * set aud claim to client_id in all oidc tokens * also do all of the above for user API key * update docstrings some fence.jwt.token functions * feat(scope): pass scopes to generate_[implicit,token]_response * fix(aud): Validate aud claim in refresh token grant flow * feat(scope): add scope claim to claims_supported * fix(aud): Look for scopes in scope not aud claim when validating refresh token * fix(aud): Skip aud validation in login_required fn * see TECHDEBT.md for context * also rm unused has_oauth import * fix(aud-to-scope): authutils require_auth_header now takes scope arg not aud arg * fix(aud-scope): Don't include aud in API keys; fix API key validation * fix(scope): change scope param to set in require_auth_header * fix(aud-scope): Fix validate_request argument * fix(aud-scope): Don't validate aud when blacklisting tokens * previously passed 'openid' only to appease validation code, bc using aud for scopes * fix(aud-scope): Get scope from new scope claim in refresh token grant * fix(aud): Skip aud validation for refresh tokens * see TECHDEBT.md for context * test(aud-scope): Update aud and scope claims in bunch of fixtures * test(aud-scope): Update validate_jwt calls in bunch of tests * Temporarily install authutils requirement from branch * fix(aud): Include issuer in aud claim for all tokens * (session, refresh, access, id, API key) * fix(aud): In validate_jwt, set aud argument default to issuer * test(aud): Switch test claims fixtures to have iss in aud, not client_id * fix(aud): Enable aud claim validation in has_oauth/login_required * Remove corresponding TECHDEBT entry * Reverts fc6fc6c04eab9a978e7e24abdf2d55c150f008ba, minus the unusued import cleanup * fix(aud): Enable aud validation for refresh tokens * Expect iss, not client_id, in refresh token aud claim * Otherwise reverts f3a87ba037075ef5c661e7cf3915febea13bbb81 * fix(aud): Update remaining validate_jwt calls * Most updates are to rm aud argument and use the default value * docs(aud): Remove techdebt item! about using aud claim for scopes * fix(aud): Update validate_request calls * fix(aud-scope): Use isinstance() not type() * chore(aud-scope): bump authutils to ^6.0.0 * chore(black): pre-commit autoupdate and fix black * fix(aud-scope): Give up and keep scopes in aud claim in access tkns for bkwd cmp * fix(aud-scope): Allow old style refresh tokens in this tag * To help transition * fix(aud-scope): Allow old style API keys in this tag * To help transition --- .pre-commit-config.yaml | 6 +- .secrets.baseline | 131 ++---- TECHDEBT.md | 21 - fence/auth.py | 5 +- fence/blueprints/data/blueprint.py | 10 +- fence/blueprints/data/indexd.py | 5 +- fence/blueprints/link.py | 4 +- fence/blueprints/login/fence_login.py | 2 +- fence/blueprints/well_known.py | 1 + fence/jwt/blacklist.py | 10 +- fence/jwt/token.py | 72 +++- fence/jwt/validate.py | 79 +++- fence/oidc/grants/refresh_token_grant.py | 25 +- fence/oidc/jwt_generator.py | 4 +- fence/resources/storage/cdis_jwt.py | 19 +- fence/resources/user/user_session.py | 10 +- poetry.lock | 388 ++++++------------ pyproject.toml | 2 +- tests/admin/test_admin_users_endpoints.py | 2 +- tests/conftest.py | 1 - tests/oidc/core/authorization/test_max_age.py | 2 +- tests/oidc/core/token/test_id_token.py | 4 +- tests/oidc/core/token/test_refresh.py | 7 +- tests/oidc/core/token/test_token_response.py | 4 +- tests/scripting/test_fence-create.py | 5 +- tests/utils/__init__.py | 34 +- 26 files changed, 406 insertions(+), 447 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 75a6a6198..a29604a6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,18 @@ repos: - repo: git@github.com:Yelp/detect-secrets - rev: v0.13.1 + rev: v1.1.0 hooks: - id: detect-secrets args: ['--baseline', '.secrets.baseline'] exclude: poetry.lock - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.5.0 + rev: v4.0.1 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: no-commit-to-branch args: [--branch, develop, --branch, master, --pattern, release/.*] - repo: https://github.com/psf/black - rev: stable + rev: 21.5b1 hooks: - id: black diff --git a/.secrets.baseline b/.secrets.baseline index 696fada09..5139a8121 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1,9 +1,5 @@ { - "exclude": { - "files": "poetry.lock", - "lines": null - }, - "generated_at": "2021-05-25T21:22:19Z", + "version": "1.1.0", "plugins_used": [ { "name": "ArtifactoryDetector" @@ -80,6 +76,12 @@ { "path": "detect_secrets.filters.heuristic.is_likely_id_string" }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, { "path": "detect_secrets.filters.heuristic.is_potential_uuid" }, @@ -89,6 +91,9 @@ { "path": "detect_secrets.filters.heuristic.is_sequential_string" }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, { "path": "detect_secrets.filters.heuristic.is_templated_secret" } @@ -110,6 +115,13 @@ "hashed_secret": "98c144f5ecbb4dbe575147a39698b6be1a5649dd", "is_verified": false, "line_number": 66 + }, + { + "type": "Secret Keyword", + "filename": "fence/blueprints/storage_creds/other.py", + "hashed_secret": "98c144f5ecbb4dbe575147a39698b6be1a5649dd", + "is_verified": false, + "line_number": 66 } ], "fence/config-default.yaml": [ @@ -119,13 +131,6 @@ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "is_verified": false, "line_number": 31 - }, - { - "type": "Secret Keyword", - "filename": "fence/config-default.yaml", - "hashed_secret": "dd29ecf524b030a65261e3059c48ab9e1ecb2585", - "is_verified": false, - "line_number": 101 } ], "fence/local_settings.example.py": [ @@ -167,91 +172,52 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, "line_number": 248 - } - ], - "openapis/swagger.yaml": [ - { - "type": "Private Key", - "filename": "openapis/swagger.yaml", - "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", - "is_verified": false, - "line_number": 1927 }, { "type": "Secret Keyword", - "filename": "openapis/swagger.yaml", - "hashed_secret": "8cb81a55dff48721f04fe341f33ee5b623dd9144", - "is_verified": false, - "line_number": 1927 - }, - { - "type": "Secret Keyword", - "filename": "openapis/swagger.yaml", - "hashed_secret": "41c39979fd01095376b3e9456f1058c33483dbbe", - "is_verified": false, - "line_number": 1994 - }, - { - "type": "JSON Web Token", - "filename": "openapis/swagger.yaml", - "hashed_secret": "d6b66ddd9ea7dbe760114bfe9a97352a5e139134", + "filename": "fence/utils.py", + "hashed_secret": "8954f53c9dc3f57137230a016d65bfaee24f8bc5", "is_verified": false, - "line_number": 1994 - }, + "line_number": 249 + } + ], + "tests/conftest.py": [ { - "type": "Base64 High Entropy String", - "filename": "openapis/swagger.yaml", - "hashed_secret": "98c144f5ecbb4dbe575147a39698b6be1a5649dd", + "type": "Private Key", + "filename": "tests/conftest.py", + "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", "is_verified": false, - "line_number": 2041 + "line_number": 1176 }, { "type": "Base64 High Entropy String", - "filename": "openapis/swagger.yaml", - "hashed_secret": "2f58edc671a89190115ecebddf4c70bdd87e3267", + "filename": "tests/conftest.py", + "hashed_secret": "227dea087477346785aefd575f91dd13ab86c108", "is_verified": false, - "line_number": 2084 + "line_number": 1199 } ], - "poetry.lock": [ + "tests/credentials/google/test_credentials.py": [ { - "type": "Hex High Entropy String", - "filename": "poetry.lock", - "hashed_secret": "640e60795f08744221f6816fe9dc949c58465256", - "is_verified": false, - "line_number": 1177, - "type": "Private Key" - }, - { - "type": "Hex High Entropy String", - "filename": "poetry.lock", - "hashed_secret": "6642e431aaa417100a91214385af6657acb3fab7", + "type": "Secret Keyword", + "filename": "tests/credentials/google/test_credentials.py", + "hashed_secret": "a06bdb09c0106ab559bd6acab2f1935e19f7e939", "is_verified": false, - "line_number": 1368 + "line_number": 381 }, { - "type": "Hex High Entropy String", - "filename": "poetry.lock", - "hashed_secret": "205b95ce89ff252c6045d78ca9d007e73b45dc00", - "is_verified": false, - "line_number": 1200, - "type": "Base64 High Entropy String" - } - ], - "tests/conftest.py": [ - { - "type": "Private Key", - "filename": "tests/conftest.py", - "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", + "type": "Secret Keyword", + "filename": "tests/credentials/google/test_credentials.py", + "hashed_secret": "93aa43c580f5347782e17fba5091f944767b15f0", "is_verified": false, - "line_number": 1151 + "line_number": 474 }, { - "type": "Base64 High Entropy String", - "filename": "tests/conftest.py", - "hashed_secret": "227dea087477346785aefd575f91dd13ab86c108", + "type": "Secret Keyword", + "filename": "tests/credentials/google/test_credentials.py", + "hashed_secret": "768b7fe00de4fd233c0c72375d12f87ce9670144", "is_verified": false, - "line_number": 1174 + "line_number": 476 } ], "tests/keys/2018-05-01T21:29:02Z/jwt_private_key.pem": [ @@ -287,7 +253,7 @@ "filename": "tests/scripting/test_fence-create.py", "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4", "is_verified": false, - "line_number": 1117 + "line_number": 1120 } ], "tests/test-fence-config.yaml": [ @@ -298,13 +264,6 @@ "is_verified": false, "line_number": 31 }, - { - "type": "Secret Keyword", - "filename": "tests/test-fence-config.yaml", - "hashed_secret": "dd29ecf524b030a65261e3059c48ab9e1ecb2585", - "is_verified": false, - "line_number": 85 - }, { "type": "Secret Keyword", "filename": "tests/test-fence-config.yaml", @@ -314,5 +273,5 @@ } ] }, - "generated_at": "2021-05-26T14:24:12Z" + "generated_at": "2021-05-27T15:18:42Z" } diff --git a/TECHDEBT.md b/TECHDEBT.md index 863659c7e..83ac5d87f 100644 --- a/TECHDEBT.md +++ b/TECHDEBT.md @@ -1,22 +1 @@ # Tech debt - -### Using 'aud' claim for scopes -- Observed: July 2020 -- Impact: (If this tech debt affected your work somehow, add a +1 here with a date and note) - - +1 Zoe 2020 July 15 This is an example of a +1 - - +1 Vahid Oct 2020 - -##### Problem: -Fence puts OAuth2 scopes into the 'aud' claim of access tokens. -##### Why it was done this way: -We don't know. -##### Why this way is problematic: -Per RFC7519 the aud claim [is not meant for scopes](https://tools.ietf.org/html/rfc7519#section-4.1.3). -##### What the solution might be: -GA4GH AAI [already requires](https://github.com/ga4gh/data-security/blob/master/AAI/AAIConnectProfile.md#access_token-issued-by-broker) that a 'scope' claim be included in access tokens issued by Passport Brokers. So as of July 2020 we will put scopes in the 'scope' claim. However, this is in addition to keeping them in the 'aud' claim. Ideally we would only have the scopes in the 'scope' claim. -##### Why we aren't already doing the above: -Fence presently guards several endpoints (e.g. /data, signed urls, SA registration) by checking the scopes in the 'aud' claim of the JWT. This code would need to be changed. -##### Next steps: -Address above. -##### Other notes: -n/a diff --git a/fence/auth.py b/fence/auth.py index 0c79231de..b7e86b729 100644 --- a/fence/auth.py +++ b/fence/auth.py @@ -209,7 +209,10 @@ def has_oauth(scope=None): scope = scope or set() scope.update({"openid"}) try: - access_token_claims = validate_jwt(aud=scope, purpose="access") + access_token_claims = validate_jwt( + scope=scope, + purpose="access", + ) except JWTError as e: raise Unauthorized("failed to validate token: {}".format(e)) user_id = access_token_claims["sub"] diff --git a/fence/blueprints/data/blueprint.py b/fence/blueprints/data/blueprint.py index 9114a0441..95798f1e0 100644 --- a/fence/blueprints/data/blueprint.py +++ b/fence/blueprints/data/blueprint.py @@ -19,7 +19,7 @@ @blueprint.route("/", methods=["DELETE"]) -@require_auth_header(aud={"data"}) +@require_auth_header(scope={"data"}) @login_required({"data"}) def delete_data_file(file_id): """ @@ -106,7 +106,7 @@ def delete_data_file(file_id): @blueprint.route("/upload", methods=["POST"]) -@require_auth_header(aud={"data"}) +@require_auth_header(scope={"data"}) @login_required({"data"}) def upload_data_file(): """ @@ -181,7 +181,7 @@ def upload_data_file(): @blueprint.route("/multipart/init", methods=["POST"]) -@require_auth_header(aud={"data"}) +@require_auth_header(scope={"data"}) @login_required({"data"}) @check_arborist_auth(resource="/data_file", method="file_upload") def init_multipart_upload(): @@ -212,7 +212,7 @@ def init_multipart_upload(): @blueprint.route("/multipart/upload", methods=["POST"]) -@require_auth_header(aud={"data"}) +@require_auth_header(scope={"data"}) @login_required({"data"}) @check_arborist_auth(resource="/data_file", method="file_upload") def generate_multipart_upload_presigned_url(): @@ -246,7 +246,7 @@ def generate_multipart_upload_presigned_url(): @blueprint.route("/multipart/complete", methods=["POST"]) -@require_auth_header(aud={"data"}) +@require_auth_header(scope={"data"}) @login_required({"data"}) @check_arborist_auth(resource="/data_file", method="file_upload") def complete_multipart_upload(): diff --git a/fence/blueprints/data/indexd.py b/fence/blueprints/data/indexd.py index 08cbad473..c122bb3b7 100644 --- a/fence/blueprints/data/indexd.py +++ b/fence/blueprints/data/indexd.py @@ -14,7 +14,6 @@ from fence.auth import ( get_jwt, - has_oauth, current_token, login_required, set_current_token, @@ -1148,7 +1147,9 @@ def _get_user_info(sub_to_string=True): populated information about an anonymous user. """ try: - set_current_token(validate_request(aud={"user"})) + set_current_token( + validate_request(scope={"user"}, audience=config.get("BASE_URL")) + ) user_id = current_token["sub"] if sub_to_string: user_id = str(user_id) diff --git a/fence/blueprints/link.py b/fence/blueprints/link.py index 988a52816..b77c587bf 100644 --- a/fence/blueprints/link.py +++ b/fence/blueprints/link.py @@ -274,7 +274,9 @@ def get(self): # if we're mocking google auth, mock response to include the email # from the provided access token try: - token = validate_request({"user"}) + token = validate_request( + scope={"user"}, audience=config.get("BASE_URL") + ) email = get_user_from_claims(token).username except Exception as exc: logger.info( diff --git a/fence/blueprints/login/fence_login.py b/fence/blueprints/login/fence_login.py index fbc7eeb0c..dd12e7917 100644 --- a/fence/blueprints/login/fence_login.py +++ b/fence/blueprints/login/fence_login.py @@ -92,7 +92,7 @@ def get(self): redirect_uri, **flask.request.args.to_dict() ) id_token_claims = validate_jwt( - tokens["id_token"], aud={"openid"}, purpose="id", attempt_refresh=True + tokens["id_token"], scope="openid", purpose="id", attempt_refresh=True ) username = id_token_claims["context"]["user"]["name"] login_user( diff --git a/fence/blueprints/well_known.py b/fence/blueprints/well_known.py index 3046bc166..93504a342 100644 --- a/fence/blueprints/well_known.py +++ b/fence/blueprints/well_known.py @@ -78,6 +78,7 @@ def openid_configuration(): "jti", "auth_time", "azp", + "scope", "nonce", "context", ] diff --git a/fence/jwt/blacklist.py b/fence/jwt/blacklist.py index 873f117ee..cbccb10e3 100644 --- a/fence/jwt/blacklist.py +++ b/fence/jwt/blacklist.py @@ -89,7 +89,10 @@ def blacklist_encoded_token(encoded_token, public_key=None): public_key = public_key or keys.default_public_key() try: claims = jwt.decode( - encoded_token, public_key, algorithm="RS256", audience="openid" + encoded_token, + public_key, + algorithm="RS256", + options={"verify_aud": False}, ) except jwt.InvalidTokenError as e: raise BlacklistingError("failed to decode token: {}".format(e)) @@ -138,7 +141,10 @@ def is_token_blacklisted(encoded_token, public_key=None): public_key = public_key or keys.default_public_key() try: token = jwt.decode( - encoded_token, public_key, algorithm="RS256", audience="openid" + encoded_token, + public_key, + algorithm="RS256", + options={"verify_aud": False}, ) except jwt.exceptions.InvalidTokenError as e: raise JWTError("could not decode token to check blacklisting: {}".format(e)) diff --git a/fence/jwt/token.py b/fence/jwt/token.py index 8ce6f5b46..747228e46 100644 --- a/fence/jwt/token.py +++ b/fence/jwt/token.py @@ -178,7 +178,7 @@ def generate_signed_session_token(kid, private_key, expires_in, context=None): claims = { "pur": "session", - "aud": ["fence"], + "aud": ["fence", issuer], "sub": context.get("user_id", ""), "iss": issuer, "iat": iat, @@ -204,6 +204,7 @@ def generate_signed_id_token( expires_in, client_id, audiences=None, + scopes=None, auth_time=None, max_age=None, nonce=None, @@ -222,7 +223,10 @@ def generate_signed_id_token( user (fence.models.User): User to generate ID token for expires_in (int): seconds token should last client_id (str, optional): Client identifier - audiences (List(str), optional): Description + audiences (List(str), optional): + audiences the ID token is intended for (the aud claim). + client_id will get appended to this. + scopes (List[str], optional): oauth scopes for user auth_time (int, optional): Last time user authN'd in number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time @@ -230,6 +234,13 @@ def generate_signed_id_token( max number of seconds allowed since last user AuthN nonce (str, optional): string value used to associate a Client session with an ID Token + include_project_access (bool, optional): + whether to include user.project_access in the token context.user.projects + auth_flow_type (AuthFlowTypes, optional): + which auth flow (Auth Code or Implicit) is issuing this token + (token validation will be different for each flow) + access_token (string, optional): + the access token tied to this id token; used to generate the at_hash claim Return: str: encoded JWT ID token signed with ``private_key`` @@ -240,6 +251,7 @@ def generate_signed_id_token( client_id, include_project_access=include_project_access, audiences=audiences, + scopes=scopes, auth_time=auth_time, max_age=max_age, nonce=nonce, @@ -282,15 +294,19 @@ def generate_signed_refresh_token( ) claims = { "pur": "refresh", - "aud": scopes, "sub": sub, "iss": iss, + "aud": [iss], "iat": iat, "exp": exp, "jti": jti, "azp": client_id or "", + "scope": scopes, } + if client_id: + claims["aud"].append(client_id) + logger.info("issuing JWT refresh token with id [{}] to [{}]".format(jti, sub)) logger.debug("issuing JWT refresh token\n" + json.dumps(claims, indent=4)) @@ -319,15 +335,17 @@ def generate_api_key(kid, private_key, user_id, expires_in, scopes, client_id): iat, exp = issued_and_expiration_times(expires_in) jti = str(uuid.uuid4()) sub = str(user_id) + iss = config.get("BASE_URL") claims = { "pur": "api_key", - "aud": scopes, "sub": sub, - "iss": config.get("BASE_URL"), + "iss": iss, + "aud": [iss], "iat": iat, "exp": exp, "jti": jti, "azp": client_id or "", + "scope": scopes, } logger.info("issuing JWT API key with id [{}] to [{}]".format(jti, sub)) logger.debug("issuing JWT API key\n" + json.dumps(claims, indent=4)) @@ -377,18 +395,12 @@ def generate_signed_access_token( "must provide value for `iss` (issuer) field if" " running outside of flask application" ) - audiences = [] - if client_id: - audiences.append(client_id) - # append scopes for backwards compatibility - # eventual goal is to remove scopes from `aud` - audiences = audiences + scopes claims = { "pur": "access", - "aud": audiences, "sub": sub, "iss": iss, + "aud": [iss], "iat": iat, "exp": exp, "jti": jti, @@ -403,6 +415,13 @@ def generate_signed_access_token( "azp": client_id or "", } + if client_id: + claims["aud"].append(client_id) + + # Keep scopes in aud claim in access tokens for backwards comp.... + if scopes: + claims["aud"] += scopes + if include_project_access: # NOTE: "THIS IS A TERRIBLE STOP-GAP SOLUTION SO THAT USERS WITH # MINIMAL ACCESS CAN STILL USE LATEST VERSION OF FENCE @@ -452,6 +471,7 @@ def generate_id_token( expires_in, client_id, audiences=None, + scopes=None, auth_time=None, max_age=None, nonce=None, @@ -468,7 +488,10 @@ def generate_id_token( user (fence.models.User): User to generate ID token for expires_in (int): seconds token should last client_id (str, optional): Client identifier - audiences (List(str), optional): Description + audiences (List(str), optional): + audiences the ID token is intended for (the aud claim). + client_id will get appended to this. + scopes (List[str], optional): oauth scopes for user auth_time (int, optional): Last time user authN'd in number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time @@ -476,6 +499,13 @@ def generate_id_token( max number of seconds allowed since last user AuthN nonce (str, optional): string value used to associate a Client session with an ID Token + include_project_access (bool, optional): + whether to include user.project_access in the token context.user.projects + auth_flow_type (AuthFlowTypes, optional): + which auth flow (Auth Code or Implicit) is issuing this token + (token validation will be different for each flow) + access_token (string, optional): + the access token tied to this id token; used to generate the at_hash claim Returns: UnsignedIDToken: Unsigned ID token @@ -484,13 +514,6 @@ def generate_id_token( iat, exp = issued_and_expiration_times(expires_in) issuer = config.get("BASE_URL") - # include client_id if not already in audiences - if audiences: - if client_id not in audiences: - audiences.append(client_id) - else: - audiences = [client_id] - # If not provided, assume auth time is time this ID token is issued auth_time = auth_time or iat @@ -500,7 +523,6 @@ def generate_id_token( # ``fence/blueprints/well_known.py``. claims = { "pur": "id", - "aud": audiences, "sub": str(user.id), "iss": issuer, "iat": iat, @@ -508,6 +530,7 @@ def generate_id_token( "jti": str(uuid.uuid4()), "auth_time": auth_time, "azp": client_id, + "scope": scopes, "context": { "user": { "name": user.username, @@ -518,6 +541,13 @@ def generate_id_token( } }, } + aud = audiences.copy() if audiences else [] + if client_id and client_id not in aud: + aud.append(client_id) + if issuer not in aud: + aud.append(issuer) + claims["aud"] = aud + if user.tags: claims["context"]["user"]["tags"] = {tag.key: tag.value for tag in user.tags} diff --git a/fence/jwt/validate.py b/fence/jwt/validate.py index bf64e0c81..74dd4027c 100644 --- a/fence/jwt/validate.py +++ b/fence/jwt/validate.py @@ -40,6 +40,7 @@ def validate_purpose(claims, pur): def validate_jwt( encoded_token=None, aud=None, + scope={"openid"}, purpose=None, public_key=None, attempt_refresh=False, @@ -54,9 +55,16 @@ def validate_jwt( Args: encoded_token (str): the base64 encoding of the token - aud (Optional[Iterable[str]]): - list of audiences that the token must satisfy; defaults to - ``{'openid'}`` (minimum expected by OpenID provider) + aud (Optional[str]): + audience as which the app identifies, which the JWT will be + expected to include in its ``aud`` claim. + Optional; will default to issuer (config["BASE_URL"]). + To skip aud validation, pass the following as a kwarg: + options={"verify_aud": False} + scope (Optional[Iterable[str]]): + list of scopes each of which the token must satisfy; defaults + to ``{'openid'}`` (minimum expected by OpenID provider). + Explicitly set this to None to skip scope validation. purpose (Optional[str]): which purpose the token is supposed to be used for (access, refresh, or id) @@ -77,8 +85,14 @@ def validate_jwt( except Unauthorized as e: raise JWTError(e.message) - aud = aud or {"openid"} - aud = set(aud) + assert ( + isinstance(scope, set) or isinstance(scope, list) or scope is None + ), "scope argument must be set or list or None" + + # Can't set arg default to config[x] in fn def, so doing it this way. + if aud is None: + aud = config["BASE_URL"] + iss = config["BASE_URL"] issuers = [iss] oidc_iss = ( @@ -98,6 +112,7 @@ def validate_jwt( claims = authutils.token.validate.validate_jwt( encoded_token=encoded_token, aud=aud, + scope=scope, purpose=purpose, issuers=issuers, public_key=public_key, @@ -105,11 +120,57 @@ def validate_jwt( **kwargs ) except authutils.errors.JWTError as e: - msg = "Invalid token : {}".format(str(e)) + + ##### begin refresh token and API key patch block ##### + # TODO: In the next release, remove this if/elif block and take the else block + # back out of the else. + # Old refresh tokens and API keys are not compatible with new validation, so to smooth + # the transition, allow old style refresh tokens/API keys with this patch; + # remove patch in next tag. Refresh tokens and API keys have default TTL of 30 days. + from authutils.errors import JWTAudienceError + unverified_claims = jwt.decode(encoded_token, verify=False) - if "" in unverified_claims["aud"]: - msg += "; was OIDC client configured with scopes?" - raise JWTError(msg) + if unverified_claims.get("pur") == "refresh" and isinstance( + e, JWTAudienceError + ): + # Check everything else is fine minus the audience + try: + claims = authutils.token.validate.validate_jwt( + encoded_token=encoded_token, + aud="openid", + scope=None, + purpose="refresh", + issuers=issuers, + public_key=public_key, + attempt_refresh=attempt_refresh, + **kwargs + ) + except Error as e: + raise JWTError("Invalid refresh token: {}".format(e)) + elif unverified_claims.get("pur") == "api_key" and isinstance( + e, JWTAudienceError + ): + # Check everything else is fine minus the audience + try: + claims = authutils.token.validate.validate_jwt( + encoded_token=encoded_token, + aud="fence", + scope=None, + purpose="api_key", + issuers=issuers, + public_key=public_key, + attempt_refresh=attempt_refresh, + **kwargs + ) + except Error as e: + raise JWTError("Invalid API key: {}".format(e)) + else: + ##### end refresh token, API key patch block ##### + msg = "Invalid token : {}".format(str(e)) + unverified_claims = jwt.decode(encoded_token, verify=False) + if not unverified_claims.get("scope") or "" in unverified_claims["scope"]: + msg += "; was OIDC client configured with scopes?" + raise JWTError(msg) if purpose: validate_purpose(claims, purpose) if "pur" not in claims: diff --git a/fence/oidc/grants/refresh_token_grant.py b/fence/oidc/grants/refresh_token_grant.py index 026002b87..7e3d18437 100644 --- a/fence/oidc/grants/refresh_token_grant.py +++ b/fence/oidc/grants/refresh_token_grant.py @@ -41,7 +41,10 @@ def authenticate_refresh_token(self, refresh_token): Return: dict: the claims from the validated token """ - return validate_jwt(refresh_token, purpose="refresh") + return validate_jwt( + refresh_token, + purpose="refresh", + ) def create_access_token(self, token, client, authenticated_token): """ @@ -126,7 +129,7 @@ def create_token_response(self): scope = self.request.scope if not scope: - scope = credential["aud"] + scope = credential["scope"] client = self.request.client expires_in = credential["exp"] @@ -164,10 +167,22 @@ def _validate_token_scope(self, token): return # token is dict so just get the scope, don't use get_scope() - original_scope = token.get("aud") + original_scope = token.get("scope") + + ##### begin refresh token patch block ##### + # TODO: In the next release, remove this if block + # Old refresh tokens are not compatible with new validation, so to smooth + # the transition, allow old style refresh tokens with this patch; + # remove patch in next tag. Refresh tokens have default TTL of 30 days. if not original_scope: - raise InvalidScopeError() + original_scope = token.get("aud") + ##### end refresh token patch block ##### + + if not original_scope: + raise InvalidScopeError("No scope claim found in original refresh token.") original_scope = set(scope_to_list(original_scope)) if not original_scope.issuperset(set(scope_to_list(scope))): - raise InvalidScopeError() + raise InvalidScopeError( + "Cannot request scopes that were not in original refresh token." + ) diff --git a/fence/oidc/jwt_generator.py b/fence/oidc/jwt_generator.py index 19a1e826a..28dcb18d8 100644 --- a/fence/oidc/jwt_generator.py +++ b/fence/oidc/jwt_generator.py @@ -115,7 +115,7 @@ def generate_implicit_response( user=user, expires_in=config["ACCESS_TOKEN_EXPIRES_IN"], client_id=client.client_id, - audiences=scope, + scopes=scope, nonce=nonce, linked_google_email=linked_google_email, linked_google_account_exp=linked_google_account_exp, @@ -192,7 +192,7 @@ def generate_token_response( user=user, expires_in=config["ACCESS_TOKEN_EXPIRES_IN"], client_id=client.client_id, - audiences=scope, + scopes=scope, nonce=nonce, linked_google_email=linked_google_email, linked_google_account_exp=linked_google_account_exp, diff --git a/fence/resources/storage/cdis_jwt.py b/fence/resources/storage/cdis_jwt.py index ca3e70ac8..45c89cb47 100644 --- a/fence/resources/storage/cdis_jwt.py +++ b/fence/resources/storage/cdis_jwt.py @@ -10,8 +10,8 @@ def create_access_token(user, keypair, api_key, expires_in, scopes): try: - claims = validate_jwt(api_key, aud=scopes, purpose="api_key") - if not set(claims["aud"]).issuperset(scopes): + claims = validate_jwt(api_key, scope=scopes, purpose="api_key") + if not set(claims["scope"]).issuperset(scopes): raise JWTError("cannot issue access token with scope beyond refresh token") except Exception as e: return flask.jsonify({"errors": str(e)}) @@ -53,8 +53,19 @@ def create_user_access_token(keypair, api_key, expires_in): access token """ try: - claims = validate_jwt(api_key, aud={"fence"}, purpose="api_key") - scopes = claims["aud"] + claims = validate_jwt(api_key, scope={"fence"}, purpose="api_key") + # scopes = claims["scope"] + + ##### begin api key patch block ##### + # TODO: In the next release, remove this block and uncomment line above. + # Old API keys are not compatible with new validation + # This is to help transition + try: + scopes = claims["scope"] + except KeyError as e: + scopes = claims["aud"] + ##### end api key patch block ##### + user = get_user_from_claims(claims) except Exception as e: raise Unauthorized(str(e)) diff --git a/fence/resources/user/user_session.py b/fence/resources/user/user_session.py index 01475d400..8f3703b29 100644 --- a/fence/resources/user/user_session.py +++ b/fence/resources/user/user_session.py @@ -48,7 +48,12 @@ def __init__(self, session_token): if session_token: try: - jwt_info = validate_jwt(session_token, aud={"fence"}) + jwt_info = validate_jwt( + session_token, + scope=None, + purpose="session", + public_key=default_public_key(), + ) except JWTError: # if session token is invalid, create a new # empty one silently @@ -70,9 +75,10 @@ def _get_initial_session_token(self): expires_in=config.get("SESSION_TIMEOUT"), ).token self._encoded_token = session_token + initial_token = validate_jwt( session_token, - aud={"fence"}, + scope=None, purpose="session", public_key=default_public_key(), ) diff --git a/poetry.lock b/poetry.lock index dfde7a00c..7cf88e9a6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -61,7 +61,7 @@ requests = "*" [[package]] name = "authutils" -version = "5.0.5" +version = "6.0.1" description = "Gen3 auth utility functions" category = "main" optional = false @@ -79,19 +79,6 @@ xmltodict = ">=0.9,<1.0" flask = ["Flask (>=0.10.1)"] fastapi = ["fastapi (>=0.54.1,<0.55.0)"] -[[package]] -name = "aws-xray-sdk" -version = "0.95" -description = "The AWS X-Ray SDK for Python (the SDK) enables Python developers to record and emit information from within their applications to the AWS X-Ray service." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -jsonpickle = "*" -requests = "*" -wrapt = "*" - [[package]] name = "backoff" version = "1.10.0" @@ -113,7 +100,7 @@ cffi = ">=1.1" six = ">=1.4.1" [package.extras] -tests = ["pytest (>=3.2.1,!=3.3.0)"] +tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)"] typecheck = ["mypy"] [[package]] @@ -214,7 +201,6 @@ description = "Collection of test data and tools" category = "dev" optional = false python-versions = "*" -develop = false [package.source] type = "git" @@ -251,14 +237,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "click" -version = "8.0.0" +version = "7.1.2" description = "Composable command line interface toolkit" category = "main" optional = false -python-versions = ">=3.6" - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "codacy-coverage" @@ -279,7 +262,7 @@ test = ["coverage", "nosetests"] name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." -category = "main" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" @@ -334,15 +317,15 @@ cffi = ">=1.8,<1.11.3 || >1.11.3" six = ">=1.4.1" [package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0)", "sphinx-rtd-theme"] +docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0)", "sphinx-rtd-theme"] docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] idna = ["idna (>=2.1)"] pep8test = ["flake8", "flake8-import-order", "pep8-naming"] -test = ["pytest (>=3.6.0,!=3.9.0,!=3.9.1,!=3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"] [[package]] name = "decorator" -version = "5.0.7" +version = "5.0.9" description = "Decorators for Humans" category = "main" optional = false @@ -363,23 +346,6 @@ idna = ["idna (>=2.1)"] curio = ["curio (>=1.2)", "sniffio (>=1.1)"] trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"] -[[package]] -name = "docker" -version = "5.0.0" -description = "A Python library for the Docker Engine API." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pywin32 = {version = "227", markers = "sys_platform == \"win32\""} -requests = ">=2.14.2,<2.18.0 || >2.18.0" -websocket-client = ">=0.32.0" - -[package.extras] -ssh = ["paramiko (>=2.4.2)"] -tls = ["pyOpenSSL (>=17.5.0)", "cryptography (>=3.4.7)", "idna (>=2.0.0)"] - [[package]] name = "docopt" version = "0.6.2" @@ -425,17 +391,17 @@ idna = ">=2.0.0" [[package]] name = "flask" -version = "1.1.2" +version = "1.1.4" description = "A simple framework for building complex web applications." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -click = ">=5.1" -itsdangerous = ">=0.24" -Jinja2 = ">=2.10.1" -Werkzeug = ">=0.15" +click = ">=5.1,<8.0" +itsdangerous = ">=0.24,<2.0" +Jinja2 = ">=2.10.1,<3.0" +Werkzeug = ">=0.15,<2.0" [package.extras] dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] @@ -456,7 +422,7 @@ Six = "*" [[package]] name = "flask-restful" -version = "0.3.8" +version = "0.3.9" description = "Simple framework for creating REST APIs" category = "main" optional = false @@ -528,7 +494,7 @@ oauth2client = ">=2.0.0,<4.0dev" [[package]] name = "gen3config" -version = "0.1.8" +version = "0.1.9" description = "Gen3 Configuration Library" category = "main" optional = false @@ -556,14 +522,14 @@ PyYAML = ">=5.1,<6.0" [[package]] name = "google-api-core" -version = "1.26.3" +version = "1.28.0" description = "Google API client core library" category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" [package.dependencies] -google-auth = ">=1.21.1,<2.0dev" +google-auth = ">=1.25.0,<2.0dev" googleapis-common-protos = ">=1.6.0,<2.0dev" packaging = ">=14.3" protobuf = ">=3.12.0" @@ -594,7 +560,7 @@ uritemplate = ">=3.0.0,<4dev" [[package]] name = "google-auth" -version = "1.30.0" +version = "1.30.1" description = "Google Authentication Library" category = "main" optional = false @@ -670,7 +636,7 @@ testing = ["pytest"] [[package]] name = "google-resumable-media" -version = "1.2.0" +version = "1.3.0" description = "Utilities for Google Media Downloads and Resumable Uploads" category = "main" optional = false @@ -772,7 +738,7 @@ test = ["flake8 (>=3.8.4,<3.9.0)", "pycodestyle (>=2.6.0,<2.7.0)"] [[package]] name = "importlib-metadata" -version = "4.0.1" +version = "4.2.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -788,11 +754,11 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [[package]] name = "itsdangerous" -version = "2.0.0" -description = "Safely pass data to untrusted environments and back." +version = "1.1.0" +description = "Various helpers to pass data to untrusted environments and back." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "jinja2" @@ -816,30 +782,6 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "jsondiff" -version = "1.1.1" -description = "Diff JSON and JSON-like structures in Python" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "jsonpickle" -version = "2.0.0" -description = "Python library for serializing any arbitrary object graph into JSON" -category = "dev" -optional = false -python-versions = ">=2.7" - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["coverage (<5)", "pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov", "ecdsa", "feedparser", "numpy", "pandas", "pymongo", "sklearn", "sqlalchemy", "enum34", "jsonlib"] -"testing.libs" = ["demjson", "simplejson", "ujson", "yajl"] - [[package]] name = "markdown" version = "3.3.4" @@ -856,11 +798,11 @@ testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" -version = "2.0.0" +version = "1.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" [[package]] name = "mock" @@ -880,7 +822,7 @@ test = ["unittest2 (>=1.1.0)"] [[package]] name = "more-itertools" -version = "8.7.0" +version = "8.8.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -888,34 +830,41 @@ python-versions = ">=3.5" [[package]] name = "moto" -version = "1.3.7" +version = "1.3.15" description = "A library that allows your python tests to easily mock out the boto library" category = "dev" optional = false python-versions = "*" [package.dependencies] -aws-xray-sdk = ">=0.93,<0.96" boto = ">=2.36.0" -boto3 = ">=1.6.16" -botocore = ">=1.12.13" -cryptography = ">=2.3.0" -docker = ">=2.5.1" -Jinja2 = ">=2.7.3" -jsondiff = "1.1.1" +boto3 = ">=1.9.201" +botocore = ">=1.12.201" +Jinja2 = ">=2.10.1" +MarkupSafe = "<2.0" mock = "*" -pyaml = "*" +more-itertools = "*" python-dateutil = ">=2.1,<3.0.0" -python-jose = "<3.0.0" pytz = "*" requests = ">=2.5" responses = ">=0.9.0" six = ">1.9" werkzeug = "*" xmltodict = "*" +zipp = "*" [package.extras] +acm = ["cryptography (>=2.3.0)"] +all = ["cryptography (>=2.3.0)", "PyYAML (>=5.1)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "ecdsa (<0.15)", "docker (>=2.5.1)", "jsondiff (>=1.1.2)", "aws-xray-sdk (>=0.93,<0.96 || >0.96)", "idna (>=2.5,<3)", "cfn-lint (>=0.4.0)", "sshpubkeys (>=3.1.0,<4.0)", "sshpubkeys (>=3.1.0)"] +awslambda = ["docker (>=2.5.1)"] +batch = ["docker (>=2.5.1)"] +cloudformation = ["PyYAML (>=5.1)", "cfn-lint (>=0.4.0)"] +cognitoidp = ["python-jose[cryptography] (>=3.1.0,<4.0.0)", "ecdsa (<0.15)"] +ec2 = ["cryptography (>=2.3.0)", "sshpubkeys (>=3.1.0,<4.0)", "sshpubkeys (>=3.1.0)"] +iam = ["cryptography (>=2.3.0)"] +iotdata = ["jsondiff (>=1.1.2)"] server = ["flask"] +xray = ["aws-xray-sdk (>=0.93,<0.96 || >0.96)"] [[package]] name = "oauth2client" @@ -1009,7 +958,7 @@ prometheus_client = "*" [[package]] name = "protobuf" -version = "3.16.0" +version = "3.17.1" description = "Protocol Buffers" category = "main" optional = false @@ -1034,17 +983,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[[package]] -name = "pyaml" -version = "20.4.0" -description = "PyYAML-based module to produce pretty and readable YAML-serialized data" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -PyYAML = "*" - [[package]] name = "pyasn1" version = "0.4.8" @@ -1110,7 +1048,7 @@ six = "*" [package.extras] docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] -tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] +tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)", "hypothesis (>=3.27.0)"] [[package]] name = "pyparsing" @@ -1150,7 +1088,7 @@ coverage = ">=4.4" pytest = ">=3.6" [package.extras] -testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-flask" @@ -1205,14 +1143,6 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "pywin32" -version = "227" -description = "Python for Window Extensions" -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "pyyaml" version = "5.4.1" @@ -1237,7 +1167,7 @@ urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] [[package]] name = "responses" @@ -1349,7 +1279,6 @@ description = "" category = "main" optional = false python-versions = "*" -develop = false [package.dependencies] boto = ">=2.36.0,<3.0.0" @@ -1394,7 +1323,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] name = "userdatamodel" @@ -1408,17 +1337,6 @@ python-versions = "*" cdislogging = "*" sqlalchemy = ">=1.3.3,<1.4.0" -[[package]] -name = "websocket-client" -version = "0.59.0" -description = "WebSocket client for Python with low level API options" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -six = "*" - [[package]] name = "werkzeug" version = "0.16.1" @@ -1432,14 +1350,6 @@ dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-i termcolor = ["termcolor"] watchdog = ["watchdog"] -[[package]] -name = "wrapt" -version = "1.12.1" -description = "Module for decorators, wrappers and monkey patching." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "xmltodict" version = "0.12.0" @@ -1463,7 +1373,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "98d14d525da527c84f4c65c75dd40e42318bc99643cf147e417b11bb45be6829" +content-hash = "e43c1eed7e8a3b3a4e771bdd76315471030e23377ba5b0df910b9d1600effa83" [metadata.files] addict = [ @@ -1491,12 +1401,8 @@ authlib = [ {file = "Authlib-0.11.tar.gz", hash = "sha256:9741db6de2950a0a5cefbdb72ec7ab12f7e9fd530ff47219f1530e79183cbaaf"}, ] authutils = [ - {file = "authutils-5.0.5-py3-none-any.whl", hash = "sha256:91e81838b8ba419d5fd92550747f8f60a1f5e91fee4146109e35728bea140034"}, - {file = "authutils-5.0.5.tar.gz", hash = "sha256:ffe8f0425f065353106f5fbe8eb2d38284d34406724e175c3aa2f1806723f361"}, -] -aws-xray-sdk = [ - {file = "aws-xray-sdk-0.95.tar.gz", hash = "sha256:9e7ba8dd08fd2939376c21423376206bff01d0deaea7d7721c6b35921fed1943"}, - {file = "aws_xray_sdk-0.95-py2.py3-none-any.whl", hash = "sha256:72791618feb22eaff2e628462b0d58f398ce8c1bacfa989b7679817ab1fad60c"}, + {file = "authutils-6.0.1-py3-none-any.whl", hash = "sha256:53aedffabb3225c528e48e97767ec5d88b552aaf45bd036c525db8978f164c8e"}, + {file = "authutils-6.0.1.tar.gz", hash = "sha256:3466ef7f22538a8d4d1581174bf24492ccc814449d1ac6daa129a48381ae07dc"}, ] backoff = [ {file = "backoff-1.10.0-py2.py3-none-any.whl", hash = "sha256:5e73e2cbe780e1915a204799dba0a01896f45f4385e636bcca7a0614d879d0cd"}, @@ -1590,8 +1496,8 @@ chardet = [ {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] click = [ - {file = "click-8.0.0-py3-none-any.whl", hash = "sha256:e90e62ced43dc8105fb9a26d62f0d9340b5c8db053a814e25d95c19873ae87db"}, - {file = "click-8.0.0.tar.gz", hash = "sha256:7d8c289ee437bcb0316820ccee14aefcb056e58d31830ecab8e47eda6540e136"}, + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, + {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] codacy-coverage = [ {file = "codacy-coverage-1.3.11.tar.gz", hash = "sha256:b94651934745c638a980ad8d67494077e60f71e19e29aad1c275b66e0a070cbc"}, @@ -1686,17 +1592,13 @@ cryptography = [ {file = "cryptography-2.8.tar.gz", hash = "sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651"}, ] decorator = [ - {file = "decorator-5.0.7-py3-none-any.whl", hash = "sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"}, - {file = "decorator-5.0.7.tar.gz", hash = "sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060"}, + {file = "decorator-5.0.9-py3-none-any.whl", hash = "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323"}, + {file = "decorator-5.0.9.tar.gz", hash = "sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5"}, ] dnspython = [ {file = "dnspython-2.1.0-py3-none-any.whl", hash = "sha256:95d12f6ef0317118d2a1a6fc49aac65ffec7eb8087474158f42f26a639135216"}, {file = "dnspython-2.1.0.zip", hash = "sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"}, ] -docker = [ - {file = "docker-5.0.0-py2.py3-none-any.whl", hash = "sha256:fc961d622160e8021c10d1bcabc388c57d55fb1f917175afbe24af442e6879bd"}, - {file = "docker-5.0.0.tar.gz", hash = "sha256:3e8bc47534e0ca9331d72c32f2881bb13b93ded0bcdeab3c833fb7cf61c0a9a5"}, -] docopt = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] @@ -1714,16 +1616,16 @@ email-validator = [ {file = "email_validator-1.1.2-py2.py3-none-any.whl", hash = "sha256:094b1d1c60d790649989d38d34f69e1ef07792366277a2cf88684d03495d018f"}, ] flask = [ - {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, - {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, + {file = "Flask-1.1.4-py2.py3-none-any.whl", hash = "sha256:c34f04500f2cbbea882b1acb02002ad6fe6b7ffa64a6164577995657f50aed22"}, + {file = "Flask-1.1.4.tar.gz", hash = "sha256:0fbeb6180d383a9186d0d6ed954e0042ad9f18e0e8de088b2b419d526927d196"}, ] flask-cors = [ {file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"}, {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, ] flask-restful = [ - {file = "Flask-RESTful-0.3.8.tar.gz", hash = "sha256:5ea9a5991abf2cb69b4aac19793faac6c032300505b325687d7c305ffaa76915"}, - {file = "Flask_RESTful-0.3.8-py2.py3-none-any.whl", hash = "sha256:d891118b951921f1cec80cabb4db98ea6058a35e6404788f9e70d5b243813ec2"}, + {file = "Flask-RESTful-0.3.9.tar.gz", hash = "sha256:ccec650b835d48192138c85329ae03735e6ced58e9b2d9c2146d6c84c06fa53e"}, + {file = "Flask_RESTful-0.3.9-py2.py3-none-any.whl", hash = "sha256:4970c49b6488e46c520b325f54833374dc2b98e211f1b272bd4b0c516232afe2"}, ] flask-sqlalchemy-session = [ {file = "Flask-SQLAlchemy-Session-1.1.tar.gz", hash = "sha256:0fbc520d587c891c2a0d58aa7952bc37edd73660f10006ff4732a72aa16d8157"}, @@ -1739,22 +1641,22 @@ gen3cirrus = [ {file = "gen3cirrus-1.3.0.tar.gz", hash = "sha256:e73c9a77d2e98adee066e6547af6c2a7d49bf5d44d11eaca7fbb6549184e9635"}, ] gen3config = [ - {file = "gen3config-0.1.8.tar.gz", hash = "sha256:33d49b7f291146be12651aa4abe4612e436c9068548c4eb309b7837fa8e48d3b"}, + {file = "gen3config-0.1.9.tar.gz", hash = "sha256:2b2fa3e5f3d9a417d5173de26303d06e4b03f5f7d7005e44d4ba91f55e7b0fec"}, ] gen3users = [ {file = "gen3users-0.6.0.tar.gz", hash = "sha256:3b9b56798a7d8b34712389dbbab93c00b0f92524f890513f899c31630ea986da"}, ] google-api-core = [ - {file = "google-api-core-1.26.3.tar.gz", hash = "sha256:b914345c7ea23861162693a27703bab804a55504f7e6e9abcaff174d80df32ac"}, - {file = "google_api_core-1.26.3-py2.py3-none-any.whl", hash = "sha256:099762d4b4018cd536bcf85136bf337957da438807572db52f21dc61251be089"}, + {file = "google-api-core-1.28.0.tar.gz", hash = "sha256:02646803bd728e12dd1f45ee1dcc31c12614c9a1ac451b9a1ce26aa65df9b957"}, + {file = "google_api_core-1.28.0-py2.py3-none-any.whl", hash = "sha256:a658a4e367511a444c7daf2c58855ff6fd7f7d138c154e5b17186c1f8154c8cb"}, ] google-api-python-client = [ {file = "google-api-python-client-1.11.0.tar.gz", hash = "sha256:caf4015800ef1a18d06d117f47f0219c0c0641f21978f6b1bb5ede7912fab97b"}, {file = "google_api_python_client-1.11.0-py2.py3-none-any.whl", hash = "sha256:4f596894f702736da84cf89490a810b55ca02a81f0cddeacb3022e2900b11ec6"}, ] google-auth = [ - {file = "google-auth-1.30.0.tar.gz", hash = "sha256:9ad25fba07f46a628ad4d0ca09f38dcb262830df2ac95b217f9b0129c9e42206"}, - {file = "google_auth-1.30.0-py2.py3-none-any.whl", hash = "sha256:588bdb03a41ecb4978472b847881e5518b5d9ec6153d3d679aa127a55e13b39f"}, + {file = "google-auth-1.30.1.tar.gz", hash = "sha256:044d81b1e58012f8ebc71cc134e191c1fa312f543f1fbc99973afe28c25e3228"}, + {file = "google_auth-1.30.1-py2.py3-none-any.whl", hash = "sha256:b3ca7a8ff9ab3bdefee3ad5aefb11fc6485423767eee016f5942d8e606ca23fb"}, ] google-auth-httplib2 = [ {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, @@ -1800,8 +1702,8 @@ google-crc32c = [ {file = "google_crc32c-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:78cf5b1bd30f3a6033b41aa4ce8c796870bc4645a15d3ef47a4b05d31b0a6dc1"}, ] google-resumable-media = [ - {file = "google-resumable-media-1.2.0.tar.gz", hash = "sha256:ee98b1921e5bda94867a08c864e55b4763d63887664f49ee1c231988f56b9d43"}, - {file = "google_resumable_media-1.2.0-py2.py3-none-any.whl", hash = "sha256:dbe670cd7f02f3586705fd5a108c8ab8552fa36a1cad8afbc5a54e982cf34f0c"}, + {file = "google-resumable-media-1.3.0.tar.gz", hash = "sha256:030a650e6dd18faad1b86c8f64be8b6cd59a90dbc22937d25631576f0c23a305"}, + {file = "google_resumable_media-1.3.0-py2.py3-none-any.whl", hash = "sha256:e2075b40a645965e4312fe6dac20a274b6718801630017b7b75afe7f1081bd70"}, ] googleapis-common-protos = [ {file = "googleapis-common-protos-1.53.0.tar.gz", hash = "sha256:a88ee8903aa0a81f6c3cec2d5cf62d3c8aa67c06439b0496b49048fb1854ebf4"}, @@ -1845,12 +1747,12 @@ immutables = [ {file = "immutables-0.15.tar.gz", hash = "sha256:3713ab1ebbb6946b7ce1387bb9d1d7f5e09c45add58c2a2ee65f963c171e746b"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, - {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, + {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, + {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, ] itsdangerous = [ - {file = "itsdangerous-2.0.0-py3-none-any.whl", hash = "sha256:e2cb4ae918f07ab2a2f9a91dec2695bd1f25a19d31861a70015ad537ccb5e807"}, - {file = "itsdangerous-2.0.0.tar.gz", hash = "sha256:99b1053ccce68066dfc0b4465ef8779027e6d577377c8270e21a3d6289cac111"}, + {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, + {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, ] jinja2 = [ {file = "Jinja2-2.10.3-py2.py3-none-any.whl", hash = "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f"}, @@ -1860,64 +1762,56 @@ jmespath = [ {file = "jmespath-0.9.2-py2.py3-none-any.whl", hash = "sha256:3f03b90ac8e0f3ba472e8ebff083e460c89501d8d41979771535efe9a343177e"}, {file = "jmespath-0.9.2.tar.gz", hash = "sha256:54c441e2e08b23f12d7fa7d8e6761768c47c969e6aed10eead57505ba760aee9"}, ] -jsondiff = [ - {file = "jsondiff-1.1.1.tar.gz", hash = "sha256:2d0437782de9418efa34e694aa59f43d7adb1899bd9a793f063867ddba8f7893"}, -] -jsonpickle = [ - {file = "jsonpickle-2.0.0-py2.py3-none-any.whl", hash = "sha256:c1010994c1fbda87a48f8a56698605b598cb0fc6bb7e7927559fc1100e69aeac"}, - {file = "jsonpickle-2.0.0.tar.gz", hash = "sha256:0be49cba80ea6f87a168aa8168d717d00c6ca07ba83df3cec32d3b30bfe6fb9a"}, -] markdown = [ {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, ] markupsafe = [ - {file = "MarkupSafe-2.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2efaeb1baff547063bad2b2893a8f5e9c459c4624e1a96644bbba08910ae34e0"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:441ce2a8c17683d97e06447fcbccbdb057cbf587c78eb75ae43ea7858042fe2c"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:45535241baa0fc0ba2a43961a1ac7562ca3257f46c4c3e9c0de38b722be41bd1"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:90053234a6479738fd40d155268af631c7fca33365f964f2208867da1349294b"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3b54a9c68995ef4164567e2cd1a5e16db5dac30b2a50c39c82db8d4afaf14f63"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f58b5ba13a5689ca8317b98439fccfbcc673acaaf8241c1869ceea40f5d585bf"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-win32.whl", hash = "sha256:a00dce2d96587651ef4fa192c17e039e8cfab63087c67e7d263a5533c7dad715"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:007dc055dbce5b1104876acee177dbfd18757e19d562cd440182e1f492e96b95"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a08cd07d3c3c17cd33d9e66ea9dee8f8fc1c48e2d11bd88fd2dc515a602c709b"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3c352ff634e289061711608f5e474ec38dbaa21e3e168820d53d5f4015e5b91b"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:32200f562daaab472921a11cbb63780f1654552ae49518196fc361ed8e12e901"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:fef86115fdad7ae774720d7103aa776144cf9b66673b4afa9bcaa7af990ed07b"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e79212d09fc0e224d20b43ad44bb0a0a3416d1e04cf6b45fed265114a5d43d20"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:79b2ae94fa991be023832e6bcc00f41dbc8e5fe9d997a02db965831402551730"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-win32.whl", hash = "sha256:3261fae28155e5c8634dd7710635fe540a05b58f160cef7713c7700cb9980e66"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e4570d16f88c7f3032ed909dc9e905a17da14a1c4cfd92608e3fda4cb1208bbd"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f806bfd0f218477d7c46a11d3e52dc7f5fdfaa981b18202b7dc84bbc287463b"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e77e4b983e2441aff0c0d07ee711110c106b625f440292dfe02a2f60c8218bd6"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:031bf79a27d1c42f69c276d6221172417b47cb4b31cdc73d362a9bf5a1889b9f"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:83cf0228b2f694dcdba1374d5312f2277269d798e65f40344964f642935feac1"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4cc563836f13c57f1473bc02d1e01fc37bab70ad4ee6be297d58c1d66bc819bf"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d00a669e4a5bec3ee6dbeeeedd82a405ced19f8aeefb109a012ea88a45afff96"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-win32.whl", hash = "sha256:161d575fa49395860b75da5135162481768b11208490d5a2143ae6785123e77d"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:58bc9fce3e1557d463ef5cee05391a05745fd95ed660f23c1742c711712c0abb"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3fb47f97f1d338b943126e90b79cad50d4fcfa0b80637b5a9f468941dbbd9ce5"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dab0c685f21f4a6c95bfc2afd1e7eae0033b403dd3d8c1b6d13a652ada75b348"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:664832fb88b8162268928df233f4b12a144a0c78b01d38b81bdcf0fc96668ecb"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:df561f65049ed3556e5b52541669310e88713fdae2934845ec3606f283337958"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:24bbc3507fb6dfff663af7900a631f2aca90d5a445f272db5fc84999fa5718bc"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:87de598edfa2230ff274c4de7fcf24c73ffd96208c8e1912d5d0fee459767d75"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a19d39b02a24d3082856a5b06490b714a9d4179321225bbf22809ff1e1887cc8"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-win32.whl", hash = "sha256:4aca81a687975b35e3e80bcf9aa93fe10cd57fac37bf18b2314c186095f57e05"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:70820a1c96311e02449591cbdf5cd1c6a34d5194d5b55094ab725364375c9eb2"}, - {file = "MarkupSafe-2.0.0.tar.gz", hash = "sha256:4fae0677f712ee090721d8b17f412f1cbceefbf0dc180fe91bab3232f38b4527"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, ] mock = [ {file = "mock-2.0.0-py2.py3-none-any.whl", hash = "sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1"}, {file = "mock-2.0.0.tar.gz", hash = "sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba"}, ] more-itertools = [ - {file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"}, - {file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"}, + {file = "more-itertools-8.8.0.tar.gz", hash = "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a"}, + {file = "more_itertools-8.8.0-py3-none-any.whl", hash = "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d"}, ] moto = [ - {file = "moto-1.3.7-py2.py3-none-any.whl", hash = "sha256:4df37936ff8d6a4b8229aab347a7b412cd2ca4823ff47bd1362ddfbc6c5e4ecf"}, - {file = "moto-1.3.7.tar.gz", hash = "sha256:129de2e04cb250d9f8b2c722ec152ed1b5426ef179b4ebb03e9ec36e6eb3fcc5"}, + {file = "moto-1.3.15-py2.py3-none-any.whl", hash = "sha256:3be7e1f406ef7e9c222dbcbfd8cefa2cb1062200e26deae49b5df446e17be3df"}, + {file = "moto-1.3.15.tar.gz", hash = "sha256:fd98f7b219084ba8aadad263849c4dbe8be73979e035d8dc5c86e11a86f11b7f"}, ] oauth2client = [ {file = "oauth2client-3.0.0.tar.gz", hash = "sha256:5b5b056ec6f2304e7920b632885bd157fa71d1a7f3ddd00a43b1541a8d1a2460"}, @@ -1946,26 +1840,29 @@ prometheus-flask-exporter = [ {file = "prometheus_flask_exporter-0.18.2.tar.gz", hash = "sha256:fc487e385d95cb5efd045d6a315c4ecf68c42661e7bfde0526af75ed3c4f8c1b"}, ] protobuf = [ - {file = "protobuf-3.16.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:d3b9988f1a3591d900a090887ae4c41592e252bef5b249ad7e1fc46c21617534"}, - {file = "protobuf-3.16.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8ec649186f5443cab12692438190988bb9058dbfa5851d10d59a1c7214159a5f"}, - {file = "protobuf-3.16.0-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:9191e97d92b62424423ce5a5604047fd76c80a4f463fbd10c9d8b82928f152cf"}, - {file = "protobuf-3.16.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:49dd3550bb42050d1676378c3fef91ff058d7023b77ac6f3179eb2a1c6c562d7"}, - {file = "protobuf-3.16.0-cp35-cp35m-win32.whl", hash = "sha256:675d8e7463e03cf8343792935d62b80d90839d56a228c941dfdddda946eea066"}, - {file = "protobuf-3.16.0-cp35-cp35m-win_amd64.whl", hash = "sha256:b85ad5fe54163350067a53c0c170211c9f752dd4b4d8f339eb5aea8e987614a9"}, - {file = "protobuf-3.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:bc1ba824ff750c2ead1e7b8dd049bb5ddf8658d056cf4b989f04c68b049a47a7"}, - {file = "protobuf-3.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:80b233553ff500378becc372721541902c567e51b2654b68513d7b89c43dbc4b"}, - {file = "protobuf-3.16.0-cp36-cp36m-win32.whl", hash = "sha256:2cec059f4821c8f58890920a5c8a828ea027d46d5b18cb5e9dd4c727c65a2aa0"}, - {file = "protobuf-3.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:ef9c2d0b3c0935725b547175745ceacb86f4d410b1e984d47e320c9efb1936c5"}, - {file = "protobuf-3.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0f760adc1dd3dfe6d13af529b2ab42bb3fff1a2a00a6873b583b4ce0048ddff"}, - {file = "protobuf-3.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c94ee9832fded92a11920b10d75c5aa7f4ef910b0bd463c039e102c8d7eb2c46"}, - {file = "protobuf-3.16.0-cp37-cp37m-win32.whl", hash = "sha256:8c0d3a5aa3412a440a9384349f6095991e8a5012e619cf5f57f042829f65cdb3"}, - {file = "protobuf-3.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:11301f1993f67dc81fc5c4756623652c899f7b5574b1e095d63bfc78347b11f3"}, - {file = "protobuf-3.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4bb7064727953d9187f6806230968b98e1ce4ec03bd737600e8380a9e5a6ac15"}, - {file = "protobuf-3.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bf9a5caa0e0093552c2cc6051facef15b9c9ad4b1bde70430964edf99eaf2dad"}, - {file = "protobuf-3.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c356d038a4e4cf52ccd7aff8022fc6a76daa0be5ecf2517ed75b87d32be0405d"}, - {file = "protobuf-3.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:648178381d9dbbc736849443151533ab958e7b8bcbd658a62ad10906552fddcc"}, - {file = "protobuf-3.16.0-py2.py3-none-any.whl", hash = "sha256:be831508b9207156309a88b9d115dbae0caa4a987b05f1aa4fed4c5ac53ec51b"}, - {file = "protobuf-3.16.0.tar.gz", hash = "sha256:228eecbedd46d75010f1e0f8ce34dbcd11ae5a40c165a9fc9d330a58aa302818"}, + {file = "protobuf-3.17.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:6e48c9b26d8e262331c79b208c4bf6b499a71912b1213d77b63c5ca248fc2a4e"}, + {file = "protobuf-3.17.1-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d06ef0f7873e04e2d1a2bfa0701d063e4dde5242b2946c6f071a442e4cb3be0b"}, + {file = "protobuf-3.17.1-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:437d4681160ac5310457c4dc8ab973ec56769a236c479393c53fa71a26dfb38f"}, + {file = "protobuf-3.17.1-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4ef4865ee740d5b45adfc96481adf1fcbfbb454bd51b86f4c4a5065262d0b08b"}, + {file = "protobuf-3.17.1-cp35-cp35m-win32.whl", hash = "sha256:5057744a363d65d2780dc01fa751bb14e860c507b2598b22af241aabb28a0ae9"}, + {file = "protobuf-3.17.1-cp35-cp35m-win_amd64.whl", hash = "sha256:94be4d5c2ba372ff159b2cc804d274acbaa7ebf361966f5619f99880c7c09a3c"}, + {file = "protobuf-3.17.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fea786428b34385b073ef619d896282889b432b87dc043c0b815c72d35d64025"}, + {file = "protobuf-3.17.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2c6ad26f079301bcf9f1b5d5b4b2dbac0e2e7c0111c6fbecf94f558952c78ee0"}, + {file = "protobuf-3.17.1-cp36-cp36m-win32.whl", hash = "sha256:ad17d3dd80f3377fc70a9dd677ec51231a892f9f65783cf7d2d24ee381f0feca"}, + {file = "protobuf-3.17.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0f237c1e84e46747397a3e8173edb3116e81163b2878fc944a5193b05876eaee"}, + {file = "protobuf-3.17.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c1631570af7aae611f1cd7c6918fe4a7154507169ef30e8c5f380eba36ee2659"}, + {file = "protobuf-3.17.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:8a509097ba25452c9b44198843b0df67f921bd36f37adcbcfaaf6ee5cbe09132"}, + {file = "protobuf-3.17.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cbd132f0aa7180f099d3c47fecfbcdca1590ef3b7da8346146192ba580c9b24e"}, + {file = "protobuf-3.17.1-cp37-cp37m-win32.whl", hash = "sha256:762faa8873d0569466cf66128bdbadd5e500600bdf2f87cf039493ed9d2725b6"}, + {file = "protobuf-3.17.1-cp37-cp37m-win_amd64.whl", hash = "sha256:27c7c933b838a0837eb1f429e7994310ee8577a7b69abc38e84d5db97a2650a2"}, + {file = "protobuf-3.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fcace96670b8512e805d6e275bd59b860967a7648e05cfad35ec1e7c03d7262"}, + {file = "protobuf-3.17.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2a88be54fce118d69f083b847554afd1852053c0869bdac396678ec9ec6a94d5"}, + {file = "protobuf-3.17.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ad8cdbc594c886483970ac274b5af98483b272e873b7c5dac60aa3a7222301b3"}, + {file = "protobuf-3.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b92f95994ff27a6992ea2cf3f369476ad0065986eb00252b760d0bc55c5454a8"}, + {file = "protobuf-3.17.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:40bf764b63f06a816b1c079f7718184fdd8dbfd9dad3cd1a446382492ca00b3e"}, + {file = "protobuf-3.17.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a15898d88307dba0363767b30960396a2dec74b8ffe7bcb4e885312a569eda3f"}, + {file = "protobuf-3.17.1-py2.py3-none-any.whl", hash = "sha256:36d306dda3c159a5fe0025ba0e9728aac00dd69523dd12ee3fb7b1717cd80e7d"}, + {file = "protobuf-3.17.1.tar.gz", hash = "sha256:25bc4f1c23aced9b3a9e70eef7f03e63bcbd6cfbd881a91b5688412dce8992e1"}, ] psycopg2 = [ {file = "psycopg2-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725"}, @@ -1988,10 +1885,6 @@ py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] -pyaml = [ - {file = "pyaml-20.4.0-py2.py3-none-any.whl", hash = "sha256:67081749a82b72c45e5f7f812ee3a14a03b3f5c25ff36ec3b290514f8c4c4b99"}, - {file = "pyaml-20.4.0.tar.gz", hash = "sha256:29a5c2a68660a799103d6949167bd6c7953d031449d08802386372de1db6ad71"}, -] pyasn1 = [ {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, @@ -2110,20 +2003,6 @@ pytz = [ {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, ] -pywin32 = [ - {file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"}, - {file = "pywin32-227-cp27-cp27m-win_amd64.whl", hash = "sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116"}, - {file = "pywin32-227-cp35-cp35m-win32.whl", hash = "sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa"}, - {file = "pywin32-227-cp35-cp35m-win_amd64.whl", hash = "sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4"}, - {file = "pywin32-227-cp36-cp36m-win32.whl", hash = "sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be"}, - {file = "pywin32-227-cp36-cp36m-win_amd64.whl", hash = "sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2"}, - {file = "pywin32-227-cp37-cp37m-win32.whl", hash = "sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507"}, - {file = "pywin32-227-cp37-cp37m-win_amd64.whl", hash = "sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511"}, - {file = "pywin32-227-cp38-cp38-win32.whl", hash = "sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc"}, - {file = "pywin32-227-cp38-cp38-win_amd64.whl", hash = "sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e"}, - {file = "pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"}, - {file = "pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"}, -] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, @@ -2232,17 +2111,10 @@ urllib3 = [ userdatamodel = [ {file = "userdatamodel-2.3.3.tar.gz", hash = "sha256:b846b7efd2d002a653474fa3bd7bf2a2c964277ff5f8d9bde8e9d975aca8d130"}, ] -websocket-client = [ - {file = "websocket-client-0.59.0.tar.gz", hash = "sha256:d376bd60eace9d437ab6d7ee16f4ab4e821c9dae591e1b783c58ebd8aaf80c5c"}, - {file = "websocket_client-0.59.0-py2.py3-none-any.whl", hash = "sha256:2e50d26ca593f70aba7b13a489435ef88b8fc3b5c5643c1ce8808ff9b40f0b32"}, -] werkzeug = [ {file = "Werkzeug-0.16.1-py2.py3-none-any.whl", hash = "sha256:1e0dedc2acb1f46827daa2e399c1485c8fa17c0d8e70b6b875b4e7f54bf408d2"}, {file = "Werkzeug-0.16.1.tar.gz", hash = "sha256:b353856d37dec59d6511359f97f6a4b2468442e454bd1c98298ddce53cac1f04"}, ] -wrapt = [ - {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, -] xmltodict = [ {file = "xmltodict-0.12.0-py2.py3-none-any.whl", hash = "sha256:8bbcb45cc982f48b2ca8fe7e7827c5d792f217ecf1792626f808bf41c3b86051"}, {file = "xmltodict-0.12.0.tar.gz", hash = "sha256:50d8c638ed7ecb88d90561beedbf720c9b4e851a9fa6c47ebd64e99d166d8a21"}, diff --git a/pyproject.toml b/pyproject.toml index 91130f6ee..1763d2c5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ include = [ [tool.poetry.dependencies] python = "^3.6" authlib = "^0.11" -authutils = "^5.0.5" +authutils = "^6.0.0" bcrypt = "^3.1.4" boto3 = "~1.9.91" botocore = "^1.12.253" diff --git a/tests/admin/test_admin_users_endpoints.py b/tests/admin/test_admin_users_endpoints.py index 6719dc652..2d8b15f01 100644 --- a/tests/admin/test_admin_users_endpoints.py +++ b/tests/admin/test_admin_users_endpoints.py @@ -53,10 +53,10 @@ def encoded_admin_jwt(kid, rsa_private_key): headers = {"kid": kid} claims = utils.default_claims() claims["context"]["user"]["name"] = "admin_user@fake.com" - claims["aud"].append("admin") claims["sub"] = "5678" claims["iss"] = config["BASE_URL"] claims["exp"] += 600 + claims["scope"].append("admin") return jwt.encode( claims, key=rsa_private_key, headers=headers, algorithm="RS256" ).decode("utf-8") diff --git a/tests/conftest.py b/tests/conftest.py index 5fb46dcc4..bf5b4002e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -74,7 +74,6 @@ def mock_assume_role(self, role_arn, duration_seconds, config=None): def claims_refresh(): new_claims = tests.utils.default_claims() new_claims["pur"] = "refresh" - new_claims["aud"].append("fence") return new_claims diff --git a/tests/oidc/core/authorization/test_max_age.py b/tests/oidc/core/authorization/test_max_age.py index 39d75e87a..a5c94b34b 100644 --- a/tests/oidc/core/authorization/test_max_age.py +++ b/tests/oidc/core/authorization/test_max_age.py @@ -29,5 +29,5 @@ def test_id_token_contains_auth_time(oauth_test_client): data = {"confirm": "yes", "max_age": 3600} oauth_test_client.authorize(data=data) id_token = oauth_test_client.token().id_token - id_token_claims = validate_jwt(id_token, {"openid"}) + id_token_claims = validate_jwt(id_token) assert "auth_time" in id_token_claims diff --git a/tests/oidc/core/token/test_id_token.py b/tests/oidc/core/token/test_id_token.py index e6ea886ab..6c780d368 100644 --- a/tests/oidc/core/token/test_id_token.py +++ b/tests/oidc/core/token/test_id_token.py @@ -135,7 +135,7 @@ def test_id_token_has_nonce(oauth_test_client): data = {"confirm": "yes", "nonce": nonce} oauth_test_client.authorize(data=data) response_json = oauth_test_client.token(data=data).response.json - id_token = validate_jwt(response_json["id_token"], {"openid"}) + id_token = validate_jwt(response_json["id_token"]) assert "nonce" in id_token assert nonce == id_token["nonce"] @@ -144,6 +144,6 @@ def test_aud(client, oauth_client, id_token): """ Test that the audiences of the ID token contain the OAuth client id. """ - id_claims = validate_jwt(id_token, {"openid"}) + id_claims = validate_jwt(id_token) assert "aud" in id_claims assert oauth_client.client_id in id_claims["aud"] diff --git a/tests/oidc/core/token/test_refresh.py b/tests/oidc/core/token/test_refresh.py index 2efe2ef4d..c1b6db47f 100644 --- a/tests/oidc/core/token/test_refresh.py +++ b/tests/oidc/core/token/test_refresh.py @@ -27,13 +27,13 @@ def test_same_claims(oauth_test_client, token_response_json): original_id_token = token_response_json["id_token"] - original_claims = validate_jwt(original_id_token, {"openid"}) + original_claims = validate_jwt(original_id_token) refresh_token = token_response_json["refresh_token"] refresh_token_response = oauth_test_client.refresh( refresh_token=refresh_token ).response assert "id_token" in refresh_token_response.json - new_claims = validate_jwt(refresh_token_response.json["id_token"], {"openid"}) + new_claims = validate_jwt(refresh_token_response.json["id_token"]) assert original_claims["iss"] == new_claims["iss"] assert original_claims["sub"] == new_claims["sub"] assert original_claims["iat"] <= new_claims["iat"] @@ -42,3 +42,6 @@ def test_same_claims(oauth_test_client, token_response_json): assert original_claims["azp"] == new_claims["azp"] else: assert "azp" not in new_claims + + # Also test that custom (non-OIDC) scope claim is unchanged + assert original_claims["scope"] == new_claims["scope"] diff --git a/tests/oidc/core/token/test_token_response.py b/tests/oidc/core/token/test_token_response.py index 74468fb7b..fccb5f348 100644 --- a/tests/oidc/core/token/test_token_response.py +++ b/tests/oidc/core/token/test_token_response.py @@ -38,7 +38,7 @@ def test_id_token_required_fields(token_response): """ assert "id_token" in token_response.json # Check that the ID token is a valid JWT. - id_token = validate_jwt(token_response.json["id_token"], {"openid"}) + id_token = validate_jwt(token_response.json["id_token"], scope={"openid"}) # Check for required fields. assert "pur" in id_token and id_token["pur"] == "id" @@ -62,7 +62,7 @@ def test_access_token_correct_fields(token_response): expected fields. """ encoded_access_token = token_response.json["access_token"] - access_token = validate_jwt(encoded_access_token, {"openid"}) + access_token = validate_jwt(encoded_access_token, scope={"openid"}) access_token_fields = set(access_token.keys()) expected_fields = { "pur", diff --git a/tests/scripting/test_fence-create.py b/tests/scripting/test_fence-create.py index 4a87e678d..88a7fa73d 100644 --- a/tests/scripting/test_fence-create.py +++ b/tests/scripting/test_fence-create.py @@ -367,7 +367,10 @@ def test_create_refresh_token_with_found_user( refresh_token=jwt_result.token ).response - ret_claims = validate_jwt(refresh_token_response.json["id_token"], {"openid"}) + ret_claims = validate_jwt( + refresh_token_response.json["id_token"], + scope={"openid"}, + ) assert jwt_result.claims["iss"] == ret_claims["iss"] assert jwt_result.claims["sub"] == ret_claims["sub"] assert jwt_result.claims["iat"] <= ret_claims["iat"] diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index a2a4e0a64..c7193a4f3 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -235,19 +235,19 @@ def default_claims(): Return: dict: dictionary of claims """ - aud = ["openid", "user"] - iss = "https://user-api.test.net" + iss = config["BASE_URL"] jti = new_jti() iat, exp = iat_and_exp() return { "pur": "access", - "aud": aud, + "aud": [iss], "sub": "1234", "iss": iss, "iat": iat, "exp": exp, "jti": jti, "azp": "", + "scope": ["openid", "user"], "context": { "user": { "name": "test-user", @@ -265,12 +265,11 @@ def unauthorized_context_claims(user_name, user_id): Return: dict: dictionary of claims """ - aud = ["access", "data", "user", "openid"] iss = config["BASE_URL"] jti = new_jti() iat, exp = iat_and_exp() return { - "aud": aud, + "aud": [iss], "sub": user_id, "pur": "access", "iss": iss, @@ -278,6 +277,7 @@ def unauthorized_context_claims(user_name, user_id): "exp": exp, "jti": jti, "azp": "", + "scope": ["access", "data", "user", "openid"], "context": { "user": { "name": "test", @@ -298,12 +298,11 @@ def authorized_download_context_claims(user_name, user_id): Return: dict: dictionary of claims """ - aud = ["access", "data", "user", "openid"] iss = config["BASE_URL"] jti = new_jti() iat, exp = iat_and_exp() return { - "aud": aud, + "aud": [iss], "sub": user_id, "iss": iss, "iat": iat, @@ -311,6 +310,7 @@ def authorized_download_context_claims(user_name, user_id): "jti": jti, "azp": "", "pur": "access", + "scope": ["access", "data", "user", "openid"], "context": { "user": { "name": user_name, @@ -331,12 +331,11 @@ def authorized_service_account_management_claims(user_name, user_id, client_id): Return: dict: dictionary of claims """ - aud = ["access", "data", "user", "openid", "google_link", "google_service_account"] iss = config["BASE_URL"] jti = new_jti() iat, exp = iat_and_exp() return { - "aud": aud, + "aud": [iss], "sub": user_id, "iss": iss, "iat": iat, @@ -344,6 +343,14 @@ def authorized_service_account_management_claims(user_name, user_id, client_id): "jti": jti, "azp": client_id, "pur": "access", + "scope": [ + "access", + "data", + "user", + "openid", + "google_link", + "google_service_account", + ], "context": { "user": { "name": user_name, @@ -365,7 +372,7 @@ def authorized_download_credentials_context_claims( Return: dict: dictionary of claims """ - aud = [ + scope = [ "access", "data", "user", @@ -378,7 +385,7 @@ def authorized_download_credentials_context_claims( jti = new_jti() iat, exp = iat_and_exp() return { - "aud": aud, + "aud": [iss], "sub": user_id, "iss": iss, "iat": iat, @@ -386,6 +393,7 @@ def authorized_download_credentials_context_claims( "jti": jti, "azp": client_id, "pur": "access", + "scope": scope, "context": { "user": { "name": user_name, @@ -406,12 +414,11 @@ def authorized_upload_context_claims(user_name, user_id): Return: dict: dictionary of claims """ - aud = ["access", "data", "user", "openid"] iss = config["BASE_URL"] jti = new_jti() iat, exp = iat_and_exp() return { - "aud": aud, + "aud": [iss], "sub": user_id, "iss": iss, "pur": "access", @@ -419,6 +426,7 @@ def authorized_upload_context_claims(user_name, user_id): "exp": exp, "jti": jti, "azp": "test-client", + "scope": ["access", "data", "user", "openid"], "context": { "user": { "name": user_name, From c94f4930fc7bbf90bd804a8edb54d8ce7b040ae6 Mon Sep 17 00:00:00 2001 From: vpsx <19900057+vpsx@users.noreply.github.com> Date: Mon, 7 Jun 2021 15:18:57 -0500 Subject: [PATCH 2/2] chore(tag): update pyproject version to 5.0.0 (#929) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1763d2c5e..73ed84bac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "fence" -version = "4.29.1" +version = "5.0.0" description = "Gen3 AuthN/AuthZ OIDC Service" authors = ["CTDS UChicago "] license = "Apache-2.0"