-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Chore/python3 #39
Merged
Merged
Chore/python3 #39
Changes from 10 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
5fd502f
chore(python3): 2to3
paulineribeyre 93b991a
chore(python3): add wool
paulineribeyre 03b6cc4
chore(python3): update pipfile
paulineribeyre ae9d5d2
chore(python3): fix jwt encoding errors in tests
paulineribeyre 1827c60
chore(python3): fix encoding before hashing error
paulineribeyre 45714ac
chore(python3): fix harakiri uwsgi import
paulineribeyre 117a20a
chore(python3): black
paulineribeyre 73e0056
PR template
paulineribeyre ae79cdc
fix wool
paulineribeyre 043f8de
use wool check instead of travis
paulineribeyre 7b44918
chore(python3): remove py2 support
paulineribeyre 738a1b9
chore(python3): add back GH_TOKEN
paulineribeyre 2a1aedd
chore(python3): small fixes
paulineribeyre b189628
chore(python3): remove six
paulineribeyre File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
workflow "Run python formatter" { | ||
on = "pull_request" | ||
resolves = ["Run wool"] | ||
} | ||
|
||
action "Run wool" { | ||
uses = "uc-cdis/wool@master" | ||
secrets = ["GITHUB_TOKEN"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,2 @@ | ||
from .errors import JWTValidationError | ||
from .jwt_validation import ( | ||
get_public_key_for_kid, | ||
validate_jwt, | ||
validate_request_jwt, | ||
) | ||
from .jwt_validation import get_public_key_for_kid, validate_jwt, validate_request_jwt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,10 +19,7 @@ | |
import jwt | ||
import requests | ||
|
||
from .errors import ( | ||
JWTValidationError, | ||
JWTAudienceError, | ||
) | ||
from .errors import JWTValidationError, JWTAudienceError | ||
|
||
|
||
def refresh_jwt_public_keys(user_api=None): | ||
|
@@ -65,14 +62,13 @@ def refresh_jwt_public_keys(user_api=None): | |
Raises: | ||
ValueError: if user_api is not provided or set in app config | ||
""" | ||
user_api = user_api or flask.current_app.config.get('USER_API') | ||
user_api = user_api or flask.current_app.config.get("USER_API") | ||
if not user_api: | ||
raise ValueError('no URL provided for user API') | ||
path = '/'.join(path.strip('/') for path in [user_api, 'jwt', 'keys']) | ||
jwt_public_keys = requests.get(path).json()['keys'] | ||
raise ValueError("no URL provided for user API") | ||
path = "/".join(path.strip("/") for path in [user_api, "jwt", "keys"]) | ||
jwt_public_keys = requests.get(path).json()["keys"] | ||
flask.current_app.logger.info( | ||
'refreshing public keys; updated to:\n' | ||
+ json.dumps(jwt_public_keys, indent=4) | ||
"refreshing public keys; updated to:\n" + json.dumps(jwt_public_keys, indent=4) | ||
) | ||
flask.current_app.jwt_public_keys = OrderedDict(jwt_public_keys) | ||
|
||
|
@@ -112,24 +108,22 @@ def get_public_key_for_kid(kid, attempt_refresh=True): | |
JWTValidationError: | ||
if the key id is provided and public key with that key id is found | ||
""" | ||
need_refresh = ( | ||
not hasattr(flask.current_app, 'jwt_public_keys') | ||
or (kid and kid not in flask.current_app.jwt_public_keys) | ||
need_refresh = not hasattr(flask.current_app, "jwt_public_keys") or ( | ||
kid and kid not in flask.current_app.jwt_public_keys | ||
) | ||
if need_refresh and attempt_refresh: | ||
refresh_jwt_public_keys() | ||
if kid: | ||
try: | ||
return flask.current_app.jwt_public_keys[kid] | ||
except KeyError: | ||
raise JWTValidationError('no key exists with this key id') | ||
raise JWTValidationError("no key exists with this key id") | ||
else: | ||
# Grab the key from the first in the list of keys. | ||
return flask.current_app.jwt_public_keys.items()[0][1] | ||
return list(flask.current_app.jwt_public_keys.items())[0][1] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about: next(iter(flask.current_app.jwt_public_keys.values())) |
||
|
||
|
||
def validate_request_jwt( | ||
aud, request=None, user_api=None, attempt_refresh=True): | ||
def validate_request_jwt(aud, request=None, user_api=None, attempt_refresh=True): | ||
""" | ||
Verify the JWT authorization header from a Flask request. | ||
|
||
|
@@ -167,32 +161,30 @@ def validate_request_jwt( | |
- from ``validate_jwt``, if any step of the validation fails | ||
""" | ||
|
||
|
||
aud = set(aud) | ||
iss = user_api or flask.current_app.config['USER_API'] | ||
iss = user_api or flask.current_app.config["USER_API"] | ||
if not aud: | ||
raise JWTAudienceError('no audiences provided') | ||
raise JWTAudienceError("no audiences provided") | ||
request = request or flask.request | ||
try: | ||
encoded_token = request.headers['Authorization'].split(' ')[1] | ||
encoded_token = request.headers["Authorization"].split(" ")[1] | ||
except IndexError: | ||
raise JWTValidationError('could not parse authorization header') | ||
raise JWTValidationError("could not parse authorization header") | ||
except KeyError: | ||
raise JWTValidationError('no authorization token provided') | ||
raise JWTValidationError("no authorization token provided") | ||
|
||
try: | ||
token_headers = jwt.get_unverified_header(encoded_token) | ||
except jwt.DecodeError as e: | ||
raise JWTValidationError('invalid user token') | ||
raise JWTValidationError("invalid user token") | ||
|
||
public_key = get_public_key_for_kid( | ||
token_headers.get('kid'), attempt_refresh=attempt_refresh | ||
token_headers.get("kid"), attempt_refresh=attempt_refresh | ||
) | ||
|
||
return validate_jwt(encoded_token, public_key, aud, iss) | ||
|
||
|
||
|
||
def require_jwt(aud): | ||
""" | ||
Define a decorator for utility which calls ``validate_request_jwt`` using | ||
|
@@ -204,6 +196,7 @@ def require_jwt(aud): | |
Return: | ||
Callable[[Any], Any]: decorated function | ||
""" | ||
|
||
def decorator(f): | ||
""" | ||
Define the actual decorator, which takes the decorated function as an | ||
|
@@ -215,12 +208,15 @@ def decorator(f): | |
Return: | ||
Callable[[Any], Any]: the same type as the function | ||
""" | ||
|
||
@functools.wraps(f) | ||
def wrapper(*args, **kwargs): | ||
"""Validate the JWT and call the actual function here.""" | ||
validate_request_jwt(aud) | ||
return f(*args, **kwargs) | ||
|
||
return wrapper | ||
|
||
return decorator | ||
|
||
|
||
|
@@ -256,8 +252,7 @@ def validate_jwt(encoded_token, public_key, aud, iss): | |
random_aud = list(aud)[0] | ||
try: | ||
token = jwt.decode( | ||
encoded_token, key=public_key, algorithms=['RS256'], | ||
audience=random_aud | ||
encoded_token, key=public_key, algorithms=["RS256"], audience=random_aud | ||
) | ||
except jwt.InvalidTokenError as e: | ||
raise JWTValidationError(e) | ||
|
@@ -267,16 +262,16 @@ def validate_jwt(encoded_token, public_key, aud, iss): | |
|
||
# iss | ||
# Check that the issuer of the token is the `USER_API` of the current app. | ||
if token['iss'] != iss: | ||
msg = 'invalid issuer {}; expected {}'.format(token['iss'], iss) | ||
if token["iss"] != iss: | ||
msg = "invalid issuer {}; expected {}".format(token["iss"], iss) | ||
raise JWTValidationError(msg) | ||
|
||
# aud | ||
# The audiences listed in the token must completely satisfy all the | ||
# required audiences provided. Note that this is stricter than the | ||
# specification suggested in RFC 7519. | ||
missing = aud - set(token['aud']) | ||
missing = aud - set(token["aud"]) | ||
if missing: | ||
raise JWTAudienceError('missing audiences: ' + str(missing)) | ||
raise JWTAudienceError("missing audiences: " + str(missing)) | ||
|
||
return token |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,17 @@ | ||
BIONIMBUS_REQUEST = 'bionimbus_request' | ||
AWS4_REQUEST = 'aws4_request' | ||
ALGORITHM = 'HMAC-SHA256' | ||
AWS_ALGORITHM = 'AWS4-HMAC-SHA256' | ||
REQUEST_DATE_HEADER = 'x-amz-date' | ||
HASHED_REQUEST_CONTENT = 'x-amz-content-sha256' | ||
REQUEST_HEADER_PREFIX = 'x-amz-' | ||
AUTHORIZATION_HEADER = 'Authorization' | ||
CLIENT_CONTEXT_HEADER = 'x-amz-client-context' | ||
FULL_DATE_TIME_FORMAT = '%Y%m%dT%H%M%SZ' | ||
ABRIDGED_DATE_TIME_FORMAT = '%Y%m%d' | ||
AWS_ALGORITHM_KEY = 'X-Amz-Algorithm' | ||
AWS_CREDENTIAL_KEY = 'X-Amz-Credential' | ||
AWS_DATE_KEY = 'X-Amz-Date' | ||
AWS_EXPIRES_KEY = 'X-Amz-Expires' | ||
AWS_SIGNED_HEADERS_KEY = 'X-Amz-SignedHeaders' | ||
AWS_SIGNATURE_KEY = 'X-Amz-Signature' | ||
BIONIMBUS_REQUEST = "bionimbus_request" | ||
AWS4_REQUEST = "aws4_request" | ||
ALGORITHM = "HMAC-SHA256" | ||
AWS_ALGORITHM = "AWS4-HMAC-SHA256" | ||
REQUEST_DATE_HEADER = "x-amz-date" | ||
HASHED_REQUEST_CONTENT = "x-amz-content-sha256" | ||
REQUEST_HEADER_PREFIX = "x-amz-" | ||
AUTHORIZATION_HEADER = "Authorization" | ||
CLIENT_CONTEXT_HEADER = "x-amz-client-context" | ||
FULL_DATE_TIME_FORMAT = "%Y%m%dT%H%M%SZ" | ||
ABRIDGED_DATE_TIME_FORMAT = "%Y%m%d" | ||
AWS_ALGORITHM_KEY = "X-Amz-Algorithm" | ||
AWS_CREDENTIAL_KEY = "X-Amz-Credential" | ||
AWS_DATE_KEY = "X-Amz-Date" | ||
AWS_EXPIRES_KEY = "X-Amz-Expires" | ||
AWS_SIGNED_HEADERS_KEY = "X-Amz-SignedHeaders" | ||
AWS_SIGNATURE_KEY = "X-Amz-Signature" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,20 @@ | ||
|
||
class DateFormatError(Exception): | ||
pass | ||
|
||
|
||
class HMAC4Error(Exception): | ||
def __init__(self, message='', json=None): | ||
def __init__(self, message="", json=None): | ||
self.message = message | ||
self.json = json | ||
|
||
|
||
class UnauthorizedError(HMAC4Error): | ||
def __init__(self, message='', json=None): | ||
def __init__(self, message="", json=None): | ||
super(UnauthorizedError, self).__init__(message, json) | ||
self.code = 401 | ||
|
||
|
||
class ExpiredTimeError(HMAC4Error): | ||
def __init__(self, message='', json=None): | ||
def __init__(self, message="", json=None): | ||
super(ExpiredTimeError, self).__init__(message, json) | ||
self.code = 401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot decrypt, but I think it is a change like this:
We actually need
bothGH_TOKEN
-gen3git
needs the first one (perhaps it is even better to make gen3git support both)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right 🤦♀