mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-07-02 18:10:33 -05:00
Add server version to master api
Add IsEvadedOffense to EFPenalty Fix remote log reading in not Windows
This commit is contained in:
@ -0,0 +1,12 @@
|
||||
from .jwt_manager import JWTManager
|
||||
from .view_decorators import (
|
||||
jwt_required, fresh_jwt_required, jwt_refresh_token_required, jwt_optional
|
||||
)
|
||||
from .utils import (
|
||||
create_refresh_token, create_access_token, get_jwt_identity,
|
||||
get_jwt_claims, set_access_cookies, set_refresh_cookies,
|
||||
unset_jwt_cookies, get_raw_jwt, get_current_user, current_user,
|
||||
get_jti, decode_token, get_csrf_token
|
||||
)
|
||||
|
||||
__version__ = '3.8.1'
|
247
Master/env_master/Lib/site-packages/flask_jwt_extended/config.py
Normal file
247
Master/env_master/Lib/site-packages/flask_jwt_extended/config.py
Normal file
@ -0,0 +1,247 @@
|
||||
import datetime
|
||||
from warnings import warn
|
||||
|
||||
from flask import current_app
|
||||
|
||||
# Older versions of pyjwt do not have the requires_cryptography set. Also,
|
||||
# older versions will not be adding new algorithms to them, so I can hard code
|
||||
# the default version here and be safe. If there is a newer algorithm someone
|
||||
# wants to use, they will need newer versions of pyjwt and it will be included
|
||||
# in their requires_cryptography set, and if they attempt to use it in older
|
||||
# versions of pyjwt, it will kick it out as an unrecognized algorithm.
|
||||
try:
|
||||
from jwt.algorithms import requires_cryptography
|
||||
except ImportError: # pragma: no cover
|
||||
requires_cryptography = {'RS256', 'RS384', 'RS512', 'ES256', 'ES384',
|
||||
'ES521', 'ES512', 'PS256', 'PS384', 'PS512'}
|
||||
|
||||
|
||||
class _Config(object):
|
||||
"""
|
||||
Helper object for accessing and verifying options in this extension. This
|
||||
is meant for internal use of the application; modifying config options
|
||||
should be done with flasks ```app.config```.
|
||||
|
||||
Default values for the configuration options are set in the jwt_manager
|
||||
object. All of these values are read only. This is simply a loose wrapper
|
||||
with some helper functionality for flasks `app.config`.
|
||||
"""
|
||||
|
||||
@property
|
||||
def is_asymmetric(self):
|
||||
return self.algorithm in requires_cryptography
|
||||
|
||||
@property
|
||||
def encode_key(self):
|
||||
return self._private_key if self.is_asymmetric else self._secret_key
|
||||
|
||||
@property
|
||||
def decode_key(self):
|
||||
return self._public_key if self.is_asymmetric else self._secret_key
|
||||
|
||||
@property
|
||||
def token_location(self):
|
||||
locations = current_app.config['JWT_TOKEN_LOCATION']
|
||||
if not isinstance(locations, list):
|
||||
locations = [locations]
|
||||
for location in locations:
|
||||
if location not in ('headers', 'cookies'):
|
||||
raise RuntimeError('JWT_TOKEN_LOCATION can only contain '
|
||||
'"headers" and/or "cookies"')
|
||||
return locations
|
||||
|
||||
@property
|
||||
def jwt_in_cookies(self):
|
||||
return 'cookies' in self.token_location
|
||||
|
||||
@property
|
||||
def jwt_in_headers(self):
|
||||
return 'headers' in self.token_location
|
||||
|
||||
@property
|
||||
def header_name(self):
|
||||
name = current_app.config['JWT_HEADER_NAME']
|
||||
if not name:
|
||||
raise RuntimeError("JWT_ACCESS_HEADER_NAME cannot be empty")
|
||||
return name
|
||||
|
||||
@property
|
||||
def header_type(self):
|
||||
return current_app.config['JWT_HEADER_TYPE']
|
||||
|
||||
@property
|
||||
def access_cookie_name(self):
|
||||
return current_app.config['JWT_ACCESS_COOKIE_NAME']
|
||||
|
||||
@property
|
||||
def refresh_cookie_name(self):
|
||||
return current_app.config['JWT_REFRESH_COOKIE_NAME']
|
||||
|
||||
@property
|
||||
def access_cookie_path(self):
|
||||
return current_app.config['JWT_ACCESS_COOKIE_PATH']
|
||||
|
||||
@property
|
||||
def refresh_cookie_path(self):
|
||||
return current_app.config['JWT_REFRESH_COOKIE_PATH']
|
||||
|
||||
@property
|
||||
def cookie_secure(self):
|
||||
return current_app.config['JWT_COOKIE_SECURE']
|
||||
|
||||
@property
|
||||
def cookie_domain(self):
|
||||
return current_app.config['JWT_COOKIE_DOMAIN']
|
||||
|
||||
@property
|
||||
def session_cookie(self):
|
||||
return current_app.config['JWT_SESSION_COOKIE']
|
||||
|
||||
@property
|
||||
def cookie_samesite(self):
|
||||
return current_app.config['JWT_COOKIE_SAMESITE']
|
||||
|
||||
@property
|
||||
def csrf_protect(self):
|
||||
return self.jwt_in_cookies and current_app.config['JWT_COOKIE_CSRF_PROTECT']
|
||||
|
||||
@property
|
||||
def csrf_request_methods(self):
|
||||
return current_app.config['JWT_CSRF_METHODS']
|
||||
|
||||
@property
|
||||
def csrf_in_cookies(self):
|
||||
return current_app.config['JWT_CSRF_IN_COOKIES']
|
||||
|
||||
@property
|
||||
def access_csrf_cookie_name(self):
|
||||
return current_app.config['JWT_ACCESS_CSRF_COOKIE_NAME']
|
||||
|
||||
@property
|
||||
def refresh_csrf_cookie_name(self):
|
||||
return current_app.config['JWT_REFRESH_CSRF_COOKIE_NAME']
|
||||
|
||||
@property
|
||||
def access_csrf_cookie_path(self):
|
||||
return current_app.config['JWT_ACCESS_CSRF_COOKIE_PATH']
|
||||
|
||||
@property
|
||||
def refresh_csrf_cookie_path(self):
|
||||
return current_app.config['JWT_REFRESH_CSRF_COOKIE_PATH']
|
||||
|
||||
@staticmethod
|
||||
def _get_depreciated_csrf_header_name():
|
||||
# This used to be the same option for access and refresh header names.
|
||||
# This gives users a warning if they are still using the old behavior
|
||||
old_name = current_app.config.get('JWT_CSRF_HEADER_NAME', None)
|
||||
if old_name:
|
||||
msg = (
|
||||
"JWT_CSRF_HEADER_NAME is depreciated. Use JWT_ACCESS_CSRF_HEADER_NAME "
|
||||
"or JWT_REFRESH_CSRF_HEADER_NAME instead"
|
||||
)
|
||||
warn(msg, DeprecationWarning)
|
||||
return old_name
|
||||
|
||||
@property
|
||||
def access_csrf_header_name(self):
|
||||
return self._get_depreciated_csrf_header_name() or \
|
||||
current_app.config['JWT_ACCESS_CSRF_HEADER_NAME']
|
||||
|
||||
@property
|
||||
def refresh_csrf_header_name(self):
|
||||
return self._get_depreciated_csrf_header_name() or \
|
||||
current_app.config['JWT_REFRESH_CSRF_HEADER_NAME']
|
||||
|
||||
@property
|
||||
def access_expires(self):
|
||||
delta = current_app.config['JWT_ACCESS_TOKEN_EXPIRES']
|
||||
if not isinstance(delta, datetime.timedelta) and delta is not False:
|
||||
raise RuntimeError('JWT_ACCESS_TOKEN_EXPIRES must be a datetime.timedelta or False')
|
||||
return delta
|
||||
|
||||
@property
|
||||
def refresh_expires(self):
|
||||
delta = current_app.config['JWT_REFRESH_TOKEN_EXPIRES']
|
||||
if not isinstance(delta, datetime.timedelta) and delta is not False:
|
||||
raise RuntimeError('JWT_REFRESH_TOKEN_EXPIRES must be a datetime.timedelta or False')
|
||||
return delta
|
||||
|
||||
@property
|
||||
def algorithm(self):
|
||||
return current_app.config['JWT_ALGORITHM']
|
||||
|
||||
@property
|
||||
def blacklist_enabled(self):
|
||||
return current_app.config['JWT_BLACKLIST_ENABLED']
|
||||
|
||||
@property
|
||||
def blacklist_checks(self):
|
||||
check_type = current_app.config['JWT_BLACKLIST_TOKEN_CHECKS']
|
||||
if not isinstance(check_type, list):
|
||||
check_type = [check_type]
|
||||
for item in check_type:
|
||||
if item not in ('access', 'refresh'):
|
||||
raise RuntimeError('JWT_BLACKLIST_TOKEN_CHECKS must be "access" or "refresh"')
|
||||
return check_type
|
||||
|
||||
@property
|
||||
def blacklist_access_tokens(self):
|
||||
return 'access' in self.blacklist_checks
|
||||
|
||||
@property
|
||||
def blacklist_refresh_tokens(self):
|
||||
return 'refresh' in self.blacklist_checks
|
||||
|
||||
@property
|
||||
def _secret_key(self):
|
||||
key = current_app.config['JWT_SECRET_KEY']
|
||||
if not key:
|
||||
key = current_app.config.get('SECRET_KEY', None)
|
||||
if not key:
|
||||
raise RuntimeError('JWT_SECRET_KEY or flask SECRET_KEY '
|
||||
'must be set when using symmetric '
|
||||
'algorithm "{}"'.format(self.algorithm))
|
||||
return key
|
||||
|
||||
@property
|
||||
def _public_key(self):
|
||||
key = current_app.config['JWT_PUBLIC_KEY']
|
||||
if not key:
|
||||
raise RuntimeError('JWT_PUBLIC_KEY must be set to use '
|
||||
'asymmetric cryptography algorithm '
|
||||
'"{}"'.format(self.algorithm))
|
||||
return key
|
||||
|
||||
@property
|
||||
def _private_key(self):
|
||||
key = current_app.config['JWT_PRIVATE_KEY']
|
||||
if not key:
|
||||
raise RuntimeError('JWT_PRIVATE_KEY must be set to use '
|
||||
'asymmetric cryptography algorithm '
|
||||
'"{}"'.format(self.algorithm))
|
||||
return key
|
||||
|
||||
@property
|
||||
def cookie_max_age(self):
|
||||
# Returns the appropiate value for max_age for flask set_cookies. If
|
||||
# session cookie is true, return None, otherwise return a number of
|
||||
# seconds a long ways in the future
|
||||
return None if self.session_cookie else 2147483647 # 2^31
|
||||
|
||||
@property
|
||||
def identity_claim_key(self):
|
||||
return current_app.config['JWT_IDENTITY_CLAIM']
|
||||
|
||||
@property
|
||||
def user_claims_key(self):
|
||||
return current_app.config['JWT_USER_CLAIMS']
|
||||
|
||||
@property
|
||||
def exempt_methods(self):
|
||||
return {"OPTIONS"}
|
||||
|
||||
@property
|
||||
def json_encoder(self):
|
||||
return current_app.json_encoder
|
||||
|
||||
config = _Config()
|
@ -0,0 +1,100 @@
|
||||
"""
|
||||
These are the default methods implementations that are used in this extension.
|
||||
All of these can be updated on an app by app basis using the JWTManager
|
||||
loader decorators. For further information, check out the following links:
|
||||
|
||||
http://flask-jwt-extended.readthedocs.io/en/latest/changing_default_behavior.html
|
||||
http://flask-jwt-extended.readthedocs.io/en/latest/tokens_from_complex_object.html
|
||||
"""
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
def default_user_claims_callback(userdata):
|
||||
"""
|
||||
By default, we add no additional claims to the access tokens.
|
||||
|
||||
:param userdata: data passed in as the ```identity``` argument to the
|
||||
```create_access_token``` and ```create_refresh_token```
|
||||
functions
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
def default_user_identity_callback(userdata):
|
||||
"""
|
||||
By default, we use the passed in object directly as the jwt identity.
|
||||
See this for additional info:
|
||||
|
||||
:param userdata: data passed in as the ```identity``` argument to the
|
||||
```create_access_token``` and ```create_refresh_token```
|
||||
functions
|
||||
"""
|
||||
return userdata
|
||||
|
||||
|
||||
def default_expired_token_callback():
|
||||
"""
|
||||
By default, if an expired token attempts to access a protected endpoint,
|
||||
we return a generic error message with a 401 status
|
||||
"""
|
||||
return jsonify({'msg': 'Token has expired'}), 401
|
||||
|
||||
|
||||
def default_invalid_token_callback(error_string):
|
||||
"""
|
||||
By default, if an invalid token attempts to access a protected endpoint, we
|
||||
return the error string for why it is not valid with a 422 status code
|
||||
|
||||
:param error_string: String indicating why the token is invalid
|
||||
"""
|
||||
return jsonify({'msg': error_string}), 422
|
||||
|
||||
|
||||
def default_unauthorized_callback(error_string):
|
||||
"""
|
||||
By default, if a protected endpoint is accessed without a JWT, we return
|
||||
the error string indicating why this is unauthorized, with a 401 status code
|
||||
|
||||
:param error_string: String indicating why this request is unauthorized
|
||||
"""
|
||||
return jsonify({'msg': error_string}), 401
|
||||
|
||||
|
||||
def default_needs_fresh_token_callback():
|
||||
"""
|
||||
By default, if a non-fresh jwt is used to access a ```fresh_jwt_required```
|
||||
endpoint, we return a general error message with a 401 status code
|
||||
"""
|
||||
return jsonify({'msg': 'Fresh token required'}), 401
|
||||
|
||||
|
||||
def default_revoked_token_callback():
|
||||
"""
|
||||
By default, if a revoked token is used to access a protected endpoint, we
|
||||
return a general error message with a 401 status code
|
||||
"""
|
||||
return jsonify({'msg': 'Token has been revoked'}), 401
|
||||
|
||||
|
||||
def default_user_loader_error_callback(identity):
|
||||
"""
|
||||
By default, if a user_loader callback is defined and the callback
|
||||
function returns None, we return a general error message with a 401
|
||||
status code
|
||||
"""
|
||||
return jsonify({'msg': "Error loading the user {}".format(identity)}), 401
|
||||
|
||||
|
||||
def default_claims_verification_callback(user_claims):
|
||||
"""
|
||||
By default, we do not do any verification of the user claims.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
def default_claims_verification_failed_callback():
|
||||
"""
|
||||
By default, if the user claims verification failed, we return a generic
|
||||
error message with a 400 status code
|
||||
"""
|
||||
return jsonify({'msg': 'User claims verification failed'}), 400
|
@ -0,0 +1,72 @@
|
||||
class JWTExtendedException(Exception):
|
||||
"""
|
||||
Base except which all flask_jwt_extended errors extend
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class JWTDecodeError(JWTExtendedException):
|
||||
"""
|
||||
An error decoding a JWT
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidHeaderError(JWTExtendedException):
|
||||
"""
|
||||
An error getting header information from a request
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class NoAuthorizationError(JWTExtendedException):
|
||||
"""
|
||||
An error raised when no authorization token was found in a protected endpoint
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class CSRFError(JWTExtendedException):
|
||||
"""
|
||||
An error with CSRF protection
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class WrongTokenError(JWTExtendedException):
|
||||
"""
|
||||
Error raised when attempting to use a refresh token to access an endpoint
|
||||
or vice versa
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class RevokedTokenError(JWTExtendedException):
|
||||
"""
|
||||
Error raised when a revoked token attempt to access a protected endpoint
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class FreshTokenRequired(JWTExtendedException):
|
||||
"""
|
||||
Error raised when a valid, non-fresh JWT attempt to access an endpoint
|
||||
protected by fresh_jwt_required
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class UserLoadError(JWTExtendedException):
|
||||
"""
|
||||
Error raised when a user_loader callback function returns None, indicating
|
||||
that it cannot or will not load a user for the given identity.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class UserClaimsVerificationError(JWTExtendedException):
|
||||
"""
|
||||
Error raised when the claims_verification_callback function returns False,
|
||||
indicating that the expected user claims are invalid
|
||||
"""
|
||||
pass
|
@ -0,0 +1,403 @@
|
||||
import datetime
|
||||
|
||||
from jwt import ExpiredSignatureError, InvalidTokenError
|
||||
|
||||
from flask_jwt_extended.config import config
|
||||
from flask_jwt_extended.exceptions import (
|
||||
JWTDecodeError, NoAuthorizationError, InvalidHeaderError, WrongTokenError,
|
||||
RevokedTokenError, FreshTokenRequired, CSRFError, UserLoadError,
|
||||
UserClaimsVerificationError
|
||||
)
|
||||
from flask_jwt_extended.default_callbacks import (
|
||||
default_expired_token_callback, default_user_claims_callback,
|
||||
default_user_identity_callback, default_invalid_token_callback,
|
||||
default_unauthorized_callback, default_needs_fresh_token_callback,
|
||||
default_revoked_token_callback, default_user_loader_error_callback,
|
||||
default_claims_verification_callback,
|
||||
default_claims_verification_failed_callback
|
||||
)
|
||||
from flask_jwt_extended.tokens import (
|
||||
encode_refresh_token, encode_access_token
|
||||
)
|
||||
from flask_jwt_extended.utils import get_jwt_identity
|
||||
|
||||
|
||||
class JWTManager(object):
|
||||
"""
|
||||
An object used to hold JWT settings and callback functions for the
|
||||
Flask-JWT-Extended extension.
|
||||
|
||||
Instances of :class:`JWTManager` are *not* bound to specific apps, so
|
||||
you can create one in the main body of your code and then bind it
|
||||
to your app in a factory function.
|
||||
"""
|
||||
|
||||
def __init__(self, app=None):
|
||||
"""
|
||||
Create the JWTManager instance. You can either pass a flask application
|
||||
in directly here to register this extension with the flask app, or
|
||||
call init_app after creating this object (in a factory pattern).
|
||||
|
||||
:param app: A flask application
|
||||
"""
|
||||
# Register the default error handler callback methods. These can be
|
||||
# overridden with the appropriate loader decorators
|
||||
self._user_claims_callback = default_user_claims_callback
|
||||
self._user_identity_callback = default_user_identity_callback
|
||||
self._expired_token_callback = default_expired_token_callback
|
||||
self._invalid_token_callback = default_invalid_token_callback
|
||||
self._unauthorized_callback = default_unauthorized_callback
|
||||
self._needs_fresh_token_callback = default_needs_fresh_token_callback
|
||||
self._revoked_token_callback = default_revoked_token_callback
|
||||
self._user_loader_callback = None
|
||||
self._user_loader_error_callback = default_user_loader_error_callback
|
||||
self._token_in_blacklist_callback = None
|
||||
self._claims_verification_callback = default_claims_verification_callback
|
||||
self._claims_verification_failed_callback = default_claims_verification_failed_callback
|
||||
|
||||
# Register this extension with the flask app now (if it is provided)
|
||||
if app is not None:
|
||||
self.init_app(app)
|
||||
|
||||
def init_app(self, app):
|
||||
"""
|
||||
Register this extension with the flask app.
|
||||
|
||||
:param app: A flask application
|
||||
"""
|
||||
# Save this so we can use it later in the extension
|
||||
if not hasattr(app, 'extensions'): # pragma: no cover
|
||||
app.extensions = {}
|
||||
app.extensions['flask-jwt-extended'] = self
|
||||
|
||||
# Set all the default configurations for this extension
|
||||
self._set_default_configuration_options(app)
|
||||
self._set_error_handler_callbacks(app)
|
||||
|
||||
def _set_error_handler_callbacks(self, app):
|
||||
"""
|
||||
Sets the error handler callbacks used by this extension
|
||||
"""
|
||||
@app.errorhandler(NoAuthorizationError)
|
||||
def handle_auth_error(e):
|
||||
return self._unauthorized_callback(str(e))
|
||||
|
||||
@app.errorhandler(CSRFError)
|
||||
def handle_auth_error(e):
|
||||
return self._unauthorized_callback(str(e))
|
||||
|
||||
@app.errorhandler(ExpiredSignatureError)
|
||||
def handle_expired_error(e):
|
||||
return self._expired_token_callback()
|
||||
|
||||
@app.errorhandler(InvalidHeaderError)
|
||||
def handle_invalid_header_error(e):
|
||||
return self._invalid_token_callback(str(e))
|
||||
|
||||
@app.errorhandler(InvalidTokenError)
|
||||
def handle_invalid_token_error(e):
|
||||
return self._invalid_token_callback(str(e))
|
||||
|
||||
@app.errorhandler(JWTDecodeError)
|
||||
def handle_jwt_decode_error(e):
|
||||
return self._invalid_token_callback(str(e))
|
||||
|
||||
@app.errorhandler(WrongTokenError)
|
||||
def handle_wrong_token_error(e):
|
||||
return self._invalid_token_callback(str(e))
|
||||
|
||||
@app.errorhandler(RevokedTokenError)
|
||||
def handle_revoked_token_error(e):
|
||||
return self._revoked_token_callback()
|
||||
|
||||
@app.errorhandler(FreshTokenRequired)
|
||||
def handle_fresh_token_required(e):
|
||||
return self._needs_fresh_token_callback()
|
||||
|
||||
@app.errorhandler(UserLoadError)
|
||||
def handler_user_load_error(e):
|
||||
# The identity is already saved before this exception was raised,
|
||||
# otherwise a different exception would be raised, which is why we
|
||||
# can safely call get_jwt_identity() here
|
||||
identity = get_jwt_identity()
|
||||
return self._user_loader_error_callback(identity)
|
||||
|
||||
@app.errorhandler(UserClaimsVerificationError)
|
||||
def handle_failed_user_claims_verification(e):
|
||||
return self._claims_verification_failed_callback()
|
||||
|
||||
@staticmethod
|
||||
def _set_default_configuration_options(app):
|
||||
"""
|
||||
Sets the default configuration options used by this extension
|
||||
"""
|
||||
# Where to look for the JWT. Available options are cookies or headers
|
||||
app.config.setdefault('JWT_TOKEN_LOCATION', ['headers'])
|
||||
|
||||
# Options for JWTs when the TOKEN_LOCATION is headers
|
||||
app.config.setdefault('JWT_HEADER_NAME', 'Authorization')
|
||||
app.config.setdefault('JWT_HEADER_TYPE', 'Bearer')
|
||||
|
||||
# Option for JWTs when the TOKEN_LOCATION is cookies
|
||||
app.config.setdefault('JWT_ACCESS_COOKIE_NAME', 'access_token_cookie')
|
||||
app.config.setdefault('JWT_REFRESH_COOKIE_NAME', 'refresh_token_cookie')
|
||||
app.config.setdefault('JWT_ACCESS_COOKIE_PATH', '/')
|
||||
app.config.setdefault('JWT_REFRESH_COOKIE_PATH', '/')
|
||||
app.config.setdefault('JWT_COOKIE_SECURE', False)
|
||||
app.config.setdefault('JWT_COOKIE_DOMAIN', None)
|
||||
app.config.setdefault('JWT_SESSION_COOKIE', True)
|
||||
app.config.setdefault('JWT_COOKIE_SAMESITE', None)
|
||||
|
||||
# Options for using double submit csrf protection
|
||||
app.config.setdefault('JWT_COOKIE_CSRF_PROTECT', True)
|
||||
app.config.setdefault('JWT_CSRF_METHODS', ['POST', 'PUT', 'PATCH', 'DELETE'])
|
||||
app.config.setdefault('JWT_ACCESS_CSRF_HEADER_NAME', 'X-CSRF-TOKEN')
|
||||
app.config.setdefault('JWT_REFRESH_CSRF_HEADER_NAME', 'X-CSRF-TOKEN')
|
||||
app.config.setdefault('JWT_CSRF_IN_COOKIES', True)
|
||||
app.config.setdefault('JWT_ACCESS_CSRF_COOKIE_NAME', 'csrf_access_token')
|
||||
app.config.setdefault('JWT_REFRESH_CSRF_COOKIE_NAME', 'csrf_refresh_token')
|
||||
app.config.setdefault('JWT_ACCESS_CSRF_COOKIE_PATH', '/')
|
||||
app.config.setdefault('JWT_REFRESH_CSRF_COOKIE_PATH', '/')
|
||||
|
||||
# How long an a token will live before they expire.
|
||||
app.config.setdefault('JWT_ACCESS_TOKEN_EXPIRES', datetime.timedelta(minutes=15))
|
||||
app.config.setdefault('JWT_REFRESH_TOKEN_EXPIRES', datetime.timedelta(days=30))
|
||||
|
||||
# What algorithm to use to sign the token. See here for a list of options:
|
||||
# https://github.com/jpadilla/pyjwt/blob/master/jwt/api_jwt.py
|
||||
app.config.setdefault('JWT_ALGORITHM', 'HS256')
|
||||
|
||||
# Secret key to sign JWTs with. Only used if a symmetric algorithm is
|
||||
# used (such as the HS* algorithms). We will use the app secret key
|
||||
# if this is not set.
|
||||
app.config.setdefault('JWT_SECRET_KEY', None)
|
||||
|
||||
# Keys to sign JWTs with when use when using an asymmetric
|
||||
# (public/private key) algorithm, such as RS* or EC*
|
||||
app.config.setdefault('JWT_PRIVATE_KEY', None)
|
||||
app.config.setdefault('JWT_PUBLIC_KEY', None)
|
||||
|
||||
# Options for blacklisting/revoking tokens
|
||||
app.config.setdefault('JWT_BLACKLIST_ENABLED', False)
|
||||
app.config.setdefault('JWT_BLACKLIST_TOKEN_CHECKS', ['access', 'refresh'])
|
||||
|
||||
app.config.setdefault('JWT_IDENTITY_CLAIM', 'identity')
|
||||
app.config.setdefault('JWT_USER_CLAIMS', 'user_claims')
|
||||
|
||||
def user_claims_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function for adding custom claims to an
|
||||
access token when :func:`~flask_jwt_extended.create_access_token` is
|
||||
called. By default, no extra user claims will be added to the JWT.
|
||||
|
||||
The callback function must be a function that takes only one argument,
|
||||
which is the object passed into
|
||||
:func:`~flask_jwt_extended.create_access_token`, and returns the custom
|
||||
claims you want included in the access tokens. This returned claims
|
||||
must be JSON serializable.
|
||||
"""
|
||||
self._user_claims_callback = callback
|
||||
return callback
|
||||
|
||||
def user_identity_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function for getting the JSON
|
||||
serializable identity out of whatever object is passed into
|
||||
:func:`~flask_jwt_extended.create_access_token` and
|
||||
:func:`~flask_jwt_extended.create_refresh_token`. By default, this will
|
||||
return the unmodified object that is passed in as the `identity` kwarg
|
||||
to the above functions.
|
||||
|
||||
The callback function must be a function that takes only one argument,
|
||||
which is the object passed into
|
||||
:func:`~flask_jwt_extended.create_access_token` or
|
||||
:func:`~flask_jwt_extended.create_refresh_token`, and returns the
|
||||
JSON serializable identity of this token.
|
||||
"""
|
||||
self._user_identity_callback = callback
|
||||
return callback
|
||||
|
||||
def expired_token_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called if an
|
||||
expired JWT attempts to access a protected endpoint. The default
|
||||
implementation will return a 401 status code with the JSON:
|
||||
|
||||
{"msg": "Token has expired"}
|
||||
|
||||
The callback must be a function that takes zero arguments, and returns
|
||||
a Flask response.
|
||||
"""
|
||||
self._expired_token_callback = callback
|
||||
return callback
|
||||
|
||||
def invalid_token_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called if an
|
||||
invalid JWT attempts to access a protected endpoint. The default
|
||||
implementation will return a 422 status code with the JSON:
|
||||
|
||||
{"msg": "<error description>"}
|
||||
|
||||
The callback must be a function that takes only one argument, which is
|
||||
a string which contains the reason why a token is invalid, and returns
|
||||
a Flask response.
|
||||
"""
|
||||
self._invalid_token_callback = callback
|
||||
return callback
|
||||
|
||||
def unauthorized_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called if an
|
||||
no JWT can be found when attempting to access a protected endpoint.
|
||||
The default implementation will return a 401 status code with the JSON:
|
||||
|
||||
{"msg": "<error description>"}
|
||||
|
||||
The callback must be a function that takes only one argument, which is
|
||||
a string which contains the reason why a JWT could not be found, and
|
||||
returns a Flask response.
|
||||
"""
|
||||
self._unauthorized_callback = callback
|
||||
return callback
|
||||
|
||||
def needs_fresh_token_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called if a
|
||||
valid and non-fresh token attempts to access an endpoint protected with
|
||||
the :func:`~flask_jwt_extended.fresh_jwt_required` decorator. The
|
||||
default implementation will return a 401 status code with the JSON:
|
||||
|
||||
{"msg": "Fresh token required"}
|
||||
|
||||
The callback must be a function that takes no arguments, and returns
|
||||
a Flask response.
|
||||
"""
|
||||
self._needs_fresh_token_callback = callback
|
||||
return callback
|
||||
|
||||
def revoked_token_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called if a
|
||||
revoked token attempts to access a protected endpoint. The default
|
||||
implementation will return a 401 status code with the JSON:
|
||||
|
||||
{"msg": "Token has been revoked"}
|
||||
|
||||
The callback must be a function that takes no arguments, and returns
|
||||
a Flask response.
|
||||
"""
|
||||
self._revoked_token_callback = callback
|
||||
return callback
|
||||
|
||||
def user_loader_callback_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called to
|
||||
automatically load an object when a protected endpoint is accessed.
|
||||
By default this is not is not used.
|
||||
|
||||
The callback must take one argument which is the identity JWT accessing
|
||||
the protected endpoint, and it must return any object (which can then
|
||||
be accessed via the :attr:`~flask_jwt_extended.current_user` LocalProxy
|
||||
in the protected endpoint), or `None` in the case of a user not being
|
||||
able to be loaded for any reason. If this callback function returns
|
||||
`None`, the :meth:`~flask_jwt_extended.JWTManager.user_loader_error_loader`
|
||||
will be called.
|
||||
"""
|
||||
self._user_loader_callback = callback
|
||||
return callback
|
||||
|
||||
def user_loader_error_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called if `None`
|
||||
is returned from the
|
||||
:meth:`~flask_jwt_extended.JWTManager.user_loader_callback_loader`
|
||||
callback function. The default implementation will return
|
||||
a 401 status code with the JSON:
|
||||
|
||||
{"msg": "Error loading the user <identity>"}
|
||||
|
||||
The callback must be a function that takes one argument, which is the
|
||||
identity of the user who failed to load, and must return a Flask response.
|
||||
"""
|
||||
self._user_loader_error_callback = callback
|
||||
return callback
|
||||
|
||||
def token_in_blacklist_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called when
|
||||
a protected endpoint is accessed and will check if the JWT has been
|
||||
been revoked. By default, this callback is not used.
|
||||
|
||||
The callback must be a function that takes one argument, which is the
|
||||
decoded JWT (python dictionary), and returns `True` if the token
|
||||
has been blacklisted (or is otherwise considered revoked), or `False`
|
||||
otherwise.
|
||||
"""
|
||||
self._token_in_blacklist_callback = callback
|
||||
return callback
|
||||
|
||||
def claims_verification_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called when
|
||||
a protected endpoint is accessed, and will check if the custom claims
|
||||
in the JWT are valid. By default, this callback is not used. The
|
||||
error returned if the claims are invalid can be controlled via the
|
||||
:meth:`~flask_jwt_extended.JWTManager.claims_verification_failed_loader`
|
||||
decorator.
|
||||
|
||||
This callback must be a function that takes one argument, which is the
|
||||
custom claims (python dict) present in the JWT, and returns `True` if the
|
||||
claims are valid, or `False` otherwise.
|
||||
"""
|
||||
self._claims_verification_callback = callback
|
||||
return callback
|
||||
|
||||
def claims_verification_failed_loader(self, callback):
|
||||
"""
|
||||
This decorator sets the callback function that will be called if
|
||||
the :meth:`~flask_jwt_extended.JWTManager.claims_verification_loader`
|
||||
callback returns False, indicating that the user claims are not valid.
|
||||
The default implementation will return a 400 status code with the JSON:
|
||||
|
||||
{"msg": "User claims verification failed"}
|
||||
|
||||
This callback must be a function that takes no arguments, and returns
|
||||
a Flask response.
|
||||
"""
|
||||
self._claims_verification_failed_callback = callback
|
||||
return callback
|
||||
|
||||
def _create_refresh_token(self, identity, expires_delta=None):
|
||||
if expires_delta is None:
|
||||
expires_delta = config.refresh_expires
|
||||
|
||||
refresh_token = encode_refresh_token(
|
||||
identity=self._user_identity_callback(identity),
|
||||
secret=config.encode_key,
|
||||
algorithm=config.algorithm,
|
||||
expires_delta=expires_delta,
|
||||
csrf=config.csrf_protect,
|
||||
identity_claim_key=config.identity_claim_key,
|
||||
json_encoder=config.json_encoder
|
||||
)
|
||||
return refresh_token
|
||||
|
||||
def _create_access_token(self, identity, fresh=False, expires_delta=None):
|
||||
if expires_delta is None:
|
||||
expires_delta = config.access_expires
|
||||
|
||||
access_token = encode_access_token(
|
||||
identity=self._user_identity_callback(identity),
|
||||
secret=config.encode_key,
|
||||
algorithm=config.algorithm,
|
||||
expires_delta=expires_delta,
|
||||
fresh=fresh,
|
||||
user_claims=self._user_claims_callback(identity),
|
||||
csrf=config.csrf_protect,
|
||||
identity_claim_key=config.identity_claim_key,
|
||||
user_claims_key=config.user_claims_key,
|
||||
json_encoder=config.json_encoder
|
||||
)
|
||||
return access_token
|
||||
|
139
Master/env_master/Lib/site-packages/flask_jwt_extended/tokens.py
Normal file
139
Master/env_master/Lib/site-packages/flask_jwt_extended/tokens.py
Normal file
@ -0,0 +1,139 @@
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
from calendar import timegm
|
||||
|
||||
import jwt
|
||||
from werkzeug.security import safe_str_cmp
|
||||
|
||||
from flask_jwt_extended.exceptions import JWTDecodeError, CSRFError
|
||||
|
||||
|
||||
def _create_csrf_token():
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
def _encode_jwt(additional_token_data, expires_delta, secret, algorithm,
|
||||
json_encoder=None):
|
||||
uid = str(uuid.uuid4())
|
||||
now = datetime.datetime.utcnow()
|
||||
token_data = {
|
||||
'iat': now,
|
||||
'nbf': now,
|
||||
'jti': uid,
|
||||
}
|
||||
# If expires_delta is False, the JWT should never expire
|
||||
# and the 'exp' claim is not set.
|
||||
if expires_delta:
|
||||
token_data['exp'] = now + expires_delta
|
||||
token_data.update(additional_token_data)
|
||||
encoded_token = jwt.encode(token_data, secret, algorithm,
|
||||
json_encoder=json_encoder).decode('utf-8')
|
||||
return encoded_token
|
||||
|
||||
|
||||
def encode_access_token(identity, secret, algorithm, expires_delta, fresh,
|
||||
user_claims, csrf, identity_claim_key, user_claims_key,
|
||||
json_encoder=None):
|
||||
"""
|
||||
Creates a new encoded (utf-8) access token.
|
||||
|
||||
:param identity: Identifier for who this token is for (ex, username). This
|
||||
data must be json serializable
|
||||
:param secret: Secret key to encode the JWT with
|
||||
:param algorithm: Which algorithm to encode this JWT with
|
||||
:param expires_delta: How far in the future this token should expire
|
||||
(set to False to disable expiration)
|
||||
:type expires_delta: datetime.timedelta or False
|
||||
:param fresh: If this should be a 'fresh' token or not. If a
|
||||
datetime.timedelta is given this will indicate how long this
|
||||
token will remain fresh.
|
||||
:param user_claims: Custom claims to include in this token. This data must
|
||||
be json serializable
|
||||
:param csrf: Whether to include a csrf double submit claim in this token
|
||||
(boolean)
|
||||
:param identity_claim_key: Which key should be used to store the identity
|
||||
:param user_claims_key: Which key should be used to store the user claims
|
||||
:return: Encoded access token
|
||||
"""
|
||||
|
||||
if isinstance(fresh, datetime.timedelta):
|
||||
now = datetime.datetime.utcnow()
|
||||
fresh = timegm((now + fresh).utctimetuple())
|
||||
|
||||
token_data = {
|
||||
identity_claim_key: identity,
|
||||
'fresh': fresh,
|
||||
'type': 'access',
|
||||
}
|
||||
|
||||
# Don't add extra data to the token if user_claims is empty.
|
||||
if user_claims:
|
||||
token_data[user_claims_key] = user_claims
|
||||
|
||||
if csrf:
|
||||
token_data['csrf'] = _create_csrf_token()
|
||||
return _encode_jwt(token_data, expires_delta, secret, algorithm,
|
||||
json_encoder=json_encoder)
|
||||
|
||||
|
||||
def encode_refresh_token(identity, secret, algorithm, expires_delta, csrf,
|
||||
identity_claim_key, json_encoder=None):
|
||||
"""
|
||||
Creates a new encoded (utf-8) refresh token.
|
||||
|
||||
:param identity: Some identifier used to identify the owner of this token
|
||||
:param secret: Secret key to encode the JWT with
|
||||
:param algorithm: Which algorithm to use for the toek
|
||||
:param expires_delta: How far in the future this token should expire
|
||||
(set to False to disable expiration)
|
||||
:type expires_delta: datetime.timedelta or False
|
||||
:param csrf: Whether to include a csrf double submit claim in this token
|
||||
(boolean)
|
||||
:param identity_claim_key: Which key should be used to store the identity
|
||||
:return: Encoded refresh token
|
||||
"""
|
||||
token_data = {
|
||||
identity_claim_key: identity,
|
||||
'type': 'refresh',
|
||||
}
|
||||
if csrf:
|
||||
token_data['csrf'] = _create_csrf_token()
|
||||
return _encode_jwt(token_data, expires_delta, secret, algorithm,
|
||||
json_encoder=json_encoder)
|
||||
|
||||
|
||||
def decode_jwt(encoded_token, secret, algorithm, identity_claim_key,
|
||||
user_claims_key, csrf_value=None):
|
||||
"""
|
||||
Decodes an encoded JWT
|
||||
|
||||
:param encoded_token: The encoded JWT string to decode
|
||||
:param secret: Secret key used to encode the JWT
|
||||
:param algorithm: Algorithm used to encode the JWT
|
||||
:param identity_claim_key: expected key that contains the identity
|
||||
:param user_claims_key: expected key that contains the user claims
|
||||
:param csrf_value: Expected double submit csrf value
|
||||
:return: Dictionary containing contents of the JWT
|
||||
"""
|
||||
# This call verifies the ext, iat, and nbf claims
|
||||
data = jwt.decode(encoded_token, secret, algorithms=[algorithm])
|
||||
|
||||
# Make sure that any custom claims we expect in the token are present
|
||||
if 'jti' not in data:
|
||||
raise JWTDecodeError("Missing claim: jti")
|
||||
if identity_claim_key not in data:
|
||||
raise JWTDecodeError("Missing claim: {}".format(identity_claim_key))
|
||||
if 'type' not in data or data['type'] not in ('refresh', 'access'):
|
||||
raise JWTDecodeError("Missing or invalid claim: type")
|
||||
if data['type'] == 'access':
|
||||
if 'fresh' not in data:
|
||||
raise JWTDecodeError("Missing claim: fresh")
|
||||
if user_claims_key not in data:
|
||||
data[user_claims_key] = {}
|
||||
if csrf_value:
|
||||
if 'csrf' not in data:
|
||||
raise JWTDecodeError("Missing claim: csrf")
|
||||
if not safe_str_cmp(data['csrf'], csrf_value):
|
||||
raise CSRFError("CSRF double submit tokens do not match")
|
||||
return data
|
321
Master/env_master/Lib/site-packages/flask_jwt_extended/utils.py
Normal file
321
Master/env_master/Lib/site-packages/flask_jwt_extended/utils.py
Normal file
@ -0,0 +1,321 @@
|
||||
from flask import current_app
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
try:
|
||||
from flask import _app_ctx_stack as ctx_stack
|
||||
except ImportError: # pragma: no cover
|
||||
from flask import _request_ctx_stack as ctx_stack
|
||||
|
||||
from flask_jwt_extended.config import config
|
||||
from flask_jwt_extended.exceptions import (
|
||||
RevokedTokenError, UserClaimsVerificationError, WrongTokenError
|
||||
)
|
||||
from flask_jwt_extended.tokens import decode_jwt
|
||||
|
||||
|
||||
# Proxy to access the current user
|
||||
current_user = LocalProxy(lambda: get_current_user())
|
||||
|
||||
|
||||
def get_raw_jwt():
|
||||
"""
|
||||
In a protected endpoint, this will return the python dictionary which has
|
||||
all of the claims of the JWT that is accessing the endpoint. If no
|
||||
JWT is currently present, an empty dict is returned instead.
|
||||
"""
|
||||
return getattr(ctx_stack.top, 'jwt', {})
|
||||
|
||||
|
||||
def get_jwt_identity():
|
||||
"""
|
||||
In a protected endpoint, this will return the identity of the JWT that is
|
||||
accessing this endpoint. If no JWT is present,`None` is returned instead.
|
||||
"""
|
||||
return get_raw_jwt().get(config.identity_claim_key, None)
|
||||
|
||||
|
||||
def get_jwt_claims():
|
||||
"""
|
||||
In a protected endpoint, this will return the dictionary of custom claims
|
||||
in the JWT that is accessing the endpoint. If no custom user claims are
|
||||
present, an empty dict is returned instead.
|
||||
"""
|
||||
return get_raw_jwt().get(config.user_claims_key, {})
|
||||
|
||||
|
||||
def get_current_user():
|
||||
"""
|
||||
In a protected endpoint, this will return the user object for the JWT that
|
||||
is accessing this endpoint. This is only present if the
|
||||
:meth:`~flask_jwt_extended.JWTManager.user_loader_callback_loader` is
|
||||
being used. If the user loader callback is not being used, this will
|
||||
return `None`.
|
||||
"""
|
||||
return getattr(ctx_stack.top, 'jwt_user', None)
|
||||
|
||||
|
||||
def get_jti(encoded_token):
|
||||
"""
|
||||
Returns the JTI (unique identifier) of an encoded JWT
|
||||
|
||||
:param encoded_token: The encoded JWT to get the JTI from.
|
||||
"""
|
||||
return decode_token(encoded_token).get('jti')
|
||||
|
||||
|
||||
def decode_token(encoded_token, csrf_value=None):
|
||||
"""
|
||||
Returns the decoded token (python dict) from an encoded JWT. This does all
|
||||
the checks to insure that the decoded token is valid before returning it.
|
||||
|
||||
:param encoded_token: The encoded JWT to decode into a python dict.
|
||||
:param csrf_value: Expected CSRF double submit value (optional)
|
||||
"""
|
||||
return decode_jwt(
|
||||
encoded_token=encoded_token,
|
||||
secret=config.decode_key,
|
||||
algorithm=config.algorithm,
|
||||
identity_claim_key=config.identity_claim_key,
|
||||
user_claims_key=config.user_claims_key,
|
||||
csrf_value=csrf_value
|
||||
)
|
||||
|
||||
|
||||
def _get_jwt_manager():
|
||||
try:
|
||||
return current_app.extensions['flask-jwt-extended']
|
||||
except KeyError: # pragma: no cover
|
||||
raise RuntimeError("You must initialize a JWTManager with this flask "
|
||||
"application before using this method")
|
||||
|
||||
|
||||
def create_access_token(identity, fresh=False, expires_delta=None):
|
||||
"""
|
||||
Create a new access token.
|
||||
|
||||
:param identity: The identity of this token, which can be any data that is
|
||||
json serializable. It can also be a python object, in which
|
||||
case you can use the
|
||||
:meth:`~flask_jwt_extended.JWTManager.user_identity_loader`
|
||||
to define a callback function that will be used to pull a
|
||||
json serializable identity out of the object.
|
||||
:param fresh: If this token should be marked as fresh, and can thus access
|
||||
:func:`~flask_jwt_extended.fresh_jwt_required` endpoints.
|
||||
Defaults to `False`. This value can also be a
|
||||
`datetime.timedelta` in which case it will indicate how long
|
||||
this token will be considered fresh.
|
||||
:param expires_delta: A `datetime.timedelta` for how long this token should
|
||||
last before it expires. Set to False to disable
|
||||
expiration. If this is None, it will use the
|
||||
'JWT_ACCESS_TOKEN_EXPIRES` config value
|
||||
(see :ref:`Configuration Options`)
|
||||
:return: An encoded access token
|
||||
"""
|
||||
jwt_manager = _get_jwt_manager()
|
||||
return jwt_manager._create_access_token(identity, fresh, expires_delta)
|
||||
|
||||
|
||||
def create_refresh_token(identity, expires_delta=None):
|
||||
"""
|
||||
Creates a new refresh token.
|
||||
|
||||
:param identity: The identity of this token, which can be any data that is
|
||||
json serializable. It can also be a python object, in which
|
||||
case you can use the
|
||||
:meth:`~flask_jwt_extended.JWTManager.user_identity_loader`
|
||||
to define a callback function that will be used to pull a
|
||||
json serializable identity out of the object.
|
||||
:param expires_delta: A `datetime.timedelta` for how long this token should
|
||||
last before it expires. Set to False to disable
|
||||
expiration. If this is None, it will use the
|
||||
'JWT_REFRESH_TOKEN_EXPIRES` config value
|
||||
(see :ref:`Configuration Options`)
|
||||
:return: An encoded access token
|
||||
"""
|
||||
jwt_manager = _get_jwt_manager()
|
||||
return jwt_manager._create_refresh_token(identity, expires_delta)
|
||||
|
||||
|
||||
def has_user_loader():
|
||||
jwt_manager = _get_jwt_manager()
|
||||
return jwt_manager._user_loader_callback is not None
|
||||
|
||||
|
||||
def user_loader(*args, **kwargs):
|
||||
jwt_manager = _get_jwt_manager()
|
||||
return jwt_manager._user_loader_callback(*args, **kwargs)
|
||||
|
||||
|
||||
def has_token_in_blacklist_callback():
|
||||
jwt_manager = _get_jwt_manager()
|
||||
return jwt_manager._token_in_blacklist_callback is not None
|
||||
|
||||
|
||||
def token_in_blacklist(*args, **kwargs):
|
||||
jwt_manager = _get_jwt_manager()
|
||||
return jwt_manager._token_in_blacklist_callback(*args, **kwargs)
|
||||
|
||||
|
||||
def verify_token_type(decoded_token, expected_type):
|
||||
if decoded_token['type'] != expected_type:
|
||||
raise WrongTokenError('Only {} tokens are allowed'.format(expected_type))
|
||||
|
||||
|
||||
def verify_token_not_blacklisted(decoded_token, request_type):
|
||||
if not config.blacklist_enabled:
|
||||
return
|
||||
if not has_token_in_blacklist_callback():
|
||||
raise RuntimeError("A token_in_blacklist_callback must be provided via "
|
||||
"the '@token_in_blacklist_loader' if "
|
||||
"JWT_BLACKLIST_ENABLED is True")
|
||||
if config.blacklist_access_tokens and request_type == 'access':
|
||||
if token_in_blacklist(decoded_token):
|
||||
raise RevokedTokenError('Token has been revoked')
|
||||
if config.blacklist_refresh_tokens and request_type == 'refresh':
|
||||
if token_in_blacklist(decoded_token):
|
||||
raise RevokedTokenError('Token has been revoked')
|
||||
|
||||
|
||||
def verify_token_claims(jwt_data):
|
||||
jwt_manager = _get_jwt_manager()
|
||||
user_claims = jwt_data[config.user_claims_key]
|
||||
if not jwt_manager._claims_verification_callback(user_claims):
|
||||
raise UserClaimsVerificationError('User claims verification failed')
|
||||
|
||||
|
||||
def get_csrf_token(encoded_token):
|
||||
"""
|
||||
Returns the CSRF double submit token from an encoded JWT.
|
||||
|
||||
:param encoded_token: The encoded JWT
|
||||
:return: The CSRF double submit token
|
||||
"""
|
||||
token = decode_token(encoded_token)
|
||||
return token['csrf']
|
||||
|
||||
|
||||
def set_access_cookies(response, encoded_access_token, max_age=None):
|
||||
"""
|
||||
Takes a flask response object, and an encoded access token, and configures
|
||||
the response to set in the access token in a cookie. If `JWT_CSRF_IN_COOKIES`
|
||||
is `True` (see :ref:`Configuration Options`), this will also set the CSRF
|
||||
double submit values in a separate cookie.
|
||||
|
||||
:param response: The Flask response object to set the access cookies in.
|
||||
:param encoded_access_token: The encoded access token to set in the cookies.
|
||||
:param max_age: The max age of the cookie. If this is None, it will use the
|
||||
`JWT_SESSION_COOKIE` option (see :ref:`Configuration Options`).
|
||||
Otherwise, it will use this as the cookies `max-age`.
|
||||
Values should be the number of seconds (as an integer).
|
||||
"""
|
||||
if not config.jwt_in_cookies:
|
||||
raise RuntimeWarning("set_access_cookies() called without "
|
||||
"'JWT_TOKEN_LOCATION' configured to use cookies")
|
||||
|
||||
# Set the access JWT in the cookie
|
||||
response.set_cookie(config.access_cookie_name,
|
||||
value=encoded_access_token,
|
||||
max_age=max_age or config.cookie_max_age,
|
||||
secure=config.cookie_secure,
|
||||
httponly=True,
|
||||
domain=config.cookie_domain,
|
||||
path=config.access_cookie_path,
|
||||
samesite=config.cookie_samesite)
|
||||
|
||||
# If enabled, set the csrf double submit access cookie
|
||||
if config.csrf_protect and config.csrf_in_cookies:
|
||||
response.set_cookie(config.access_csrf_cookie_name,
|
||||
value=get_csrf_token(encoded_access_token),
|
||||
max_age=max_age or config.cookie_max_age,
|
||||
secure=config.cookie_secure,
|
||||
httponly=False,
|
||||
domain=config.cookie_domain,
|
||||
path=config.access_csrf_cookie_path,
|
||||
samesite=config.cookie_samesite)
|
||||
|
||||
|
||||
def set_refresh_cookies(response, encoded_refresh_token, max_age=None):
|
||||
"""
|
||||
Takes a flask response object, and an encoded refresh token, and configures
|
||||
the response to set in the refresh token in a cookie. If `JWT_CSRF_IN_COOKIES`
|
||||
is `True` (see :ref:`Configuration Options`), this will also set the CSRF
|
||||
double submit values in a separate cookie.
|
||||
|
||||
:param response: The Flask response object to set the refresh cookies in.
|
||||
:param encoded_refresh_token: The encoded refresh token to set in the cookies.
|
||||
:param max_age: The max age of the cookie. If this is None, it will use the
|
||||
`JWT_SESSION_COOKIE` option (see :ref:`Configuration Options`).
|
||||
Otherwise, it will use this as the cookies `max-age`.
|
||||
Values should be the number of seconds (as an integer).
|
||||
"""
|
||||
if not config.jwt_in_cookies:
|
||||
raise RuntimeWarning("set_refresh_cookies() called without "
|
||||
"'JWT_TOKEN_LOCATION' configured to use cookies")
|
||||
|
||||
# Set the refresh JWT in the cookie
|
||||
response.set_cookie(config.refresh_cookie_name,
|
||||
value=encoded_refresh_token,
|
||||
max_age=max_age or config.cookie_max_age,
|
||||
secure=config.cookie_secure,
|
||||
httponly=True,
|
||||
domain=config.cookie_domain,
|
||||
path=config.refresh_cookie_path,
|
||||
samesite=config.cookie_samesite)
|
||||
|
||||
# If enabled, set the csrf double submit refresh cookie
|
||||
if config.csrf_protect and config.csrf_in_cookies:
|
||||
response.set_cookie(config.refresh_csrf_cookie_name,
|
||||
value=get_csrf_token(encoded_refresh_token),
|
||||
max_age=max_age or config.cookie_max_age,
|
||||
secure=config.cookie_secure,
|
||||
httponly=False,
|
||||
domain=config.cookie_domain,
|
||||
path=config.refresh_csrf_cookie_path,
|
||||
samesite=config.cookie_samesite)
|
||||
|
||||
|
||||
def unset_jwt_cookies(response):
|
||||
"""
|
||||
Takes a flask response object, and configures it to unset (delete) JWTs
|
||||
stored in cookies.
|
||||
|
||||
:param response: The Flask response object to delete the JWT cookies in.
|
||||
"""
|
||||
if not config.jwt_in_cookies:
|
||||
raise RuntimeWarning("unset_refresh_cookies() called without "
|
||||
"'JWT_TOKEN_LOCATION' configured to use cookies")
|
||||
|
||||
response.set_cookie(config.refresh_cookie_name,
|
||||
value='',
|
||||
expires=0,
|
||||
secure=config.cookie_secure,
|
||||
httponly=True,
|
||||
domain=config.cookie_domain,
|
||||
path=config.refresh_cookie_path,
|
||||
samesite=config.cookie_samesite)
|
||||
response.set_cookie(config.access_cookie_name,
|
||||
value='',
|
||||
expires=0,
|
||||
secure=config.cookie_secure,
|
||||
httponly=True,
|
||||
domain=config.cookie_domain,
|
||||
path=config.access_cookie_path,
|
||||
samesite=config.cookie_samesite)
|
||||
|
||||
if config.csrf_protect and config.csrf_in_cookies:
|
||||
response.set_cookie(config.refresh_csrf_cookie_name,
|
||||
value='',
|
||||
expires=0,
|
||||
secure=config.cookie_secure,
|
||||
httponly=False,
|
||||
domain=config.cookie_domain,
|
||||
path=config.refresh_csrf_cookie_path,
|
||||
samesite=config.cookie_samesite)
|
||||
response.set_cookie(config.access_csrf_cookie_name,
|
||||
value='',
|
||||
expires=0,
|
||||
secure=config.cookie_secure,
|
||||
httponly=False,
|
||||
domain=config.cookie_domain,
|
||||
path=config.access_csrf_cookie_path,
|
||||
samesite=config.cookie_samesite)
|
@ -0,0 +1,196 @@
|
||||
from functools import wraps
|
||||
from datetime import datetime
|
||||
from calendar import timegm
|
||||
|
||||
from flask import request
|
||||
try:
|
||||
from flask import _app_ctx_stack as ctx_stack
|
||||
except ImportError: # pragma: no cover
|
||||
from flask import _request_ctx_stack as ctx_stack
|
||||
|
||||
from flask_jwt_extended.config import config
|
||||
from flask_jwt_extended.exceptions import (
|
||||
CSRFError, FreshTokenRequired, InvalidHeaderError, NoAuthorizationError,
|
||||
UserLoadError
|
||||
)
|
||||
from flask_jwt_extended.utils import (
|
||||
decode_token, has_user_loader, user_loader, verify_token_claims,
|
||||
verify_token_not_blacklisted, verify_token_type
|
||||
)
|
||||
|
||||
|
||||
def jwt_required(fn):
|
||||
"""
|
||||
A decorator to protect a Flask endpoint.
|
||||
|
||||
If you decorate an endpoint with this, it will ensure that the requester
|
||||
has a valid access token before allowing the endpoint to be called. This
|
||||
does not check the freshness of the access token.
|
||||
|
||||
See also: :func:`~flask_jwt_extended.fresh_jwt_required`
|
||||
"""
|
||||
@wraps(fn)
|
||||
def wrapper(*args, **kwargs):
|
||||
if request.method not in config.exempt_methods:
|
||||
jwt_data = _decode_jwt_from_request(request_type='access')
|
||||
ctx_stack.top.jwt = jwt_data
|
||||
verify_token_claims(jwt_data)
|
||||
_load_user(jwt_data[config.identity_claim_key])
|
||||
return fn(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def jwt_optional(fn):
|
||||
"""
|
||||
A decorator to optionally protect a Flask endpoint
|
||||
|
||||
If an access token in present in the request, this will call the endpoint
|
||||
with :func:`~flask_jwt_extended.get_jwt_identity` having the identity
|
||||
of the access token. If no access token is present in the request,
|
||||
this endpoint will still be called, but
|
||||
:func:`~flask_jwt_extended.get_jwt_identity` will return `None` instead.
|
||||
|
||||
If there is an invalid access token in the request (expired, tampered with,
|
||||
etc), this will still call the appropriate error handler instead of allowing
|
||||
the endpoint to be called as if there is no access token in the request.
|
||||
"""
|
||||
@wraps(fn)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
jwt_data = _decode_jwt_from_request(request_type='access')
|
||||
ctx_stack.top.jwt = jwt_data
|
||||
verify_token_claims(jwt_data)
|
||||
_load_user(jwt_data[config.identity_claim_key])
|
||||
except (NoAuthorizationError, InvalidHeaderError):
|
||||
pass
|
||||
return fn(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def fresh_jwt_required(fn):
|
||||
"""
|
||||
A decorator to protect a Flask endpoint.
|
||||
|
||||
If you decorate an endpoint with this, it will ensure that the requester
|
||||
has a valid and fresh access token before allowing the endpoint to be
|
||||
called.
|
||||
|
||||
See also: :func:`~flask_jwt_extended.jwt_required`
|
||||
"""
|
||||
@wraps(fn)
|
||||
def wrapper(*args, **kwargs):
|
||||
if request.method not in config.exempt_methods:
|
||||
jwt_data = _decode_jwt_from_request(request_type='access')
|
||||
ctx_stack.top.jwt = jwt_data
|
||||
fresh = jwt_data['fresh']
|
||||
if isinstance(fresh, bool):
|
||||
if not fresh:
|
||||
raise FreshTokenRequired('Fresh token required')
|
||||
else:
|
||||
now = timegm(datetime.utcnow().utctimetuple())
|
||||
if fresh < now:
|
||||
raise FreshTokenRequired('Fresh token required')
|
||||
verify_token_claims(jwt_data)
|
||||
_load_user(jwt_data[config.identity_claim_key])
|
||||
return fn(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def jwt_refresh_token_required(fn):
|
||||
"""
|
||||
A decorator to protect a Flask endpoint.
|
||||
|
||||
If you decorate an endpoint with this, it will ensure that the requester
|
||||
has a valid refresh token before allowing the endpoint to be called.
|
||||
"""
|
||||
@wraps(fn)
|
||||
def wrapper(*args, **kwargs):
|
||||
if request.method not in config.exempt_methods:
|
||||
jwt_data = _decode_jwt_from_request(request_type='refresh')
|
||||
ctx_stack.top.jwt = jwt_data
|
||||
_load_user(jwt_data[config.identity_claim_key])
|
||||
return fn(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def _load_user(identity):
|
||||
if has_user_loader():
|
||||
user = user_loader(identity)
|
||||
if user is None:
|
||||
raise UserLoadError("user_loader returned None for {}".format(identity))
|
||||
else:
|
||||
ctx_stack.top.jwt_user = user
|
||||
|
||||
|
||||
def _decode_jwt_from_headers():
|
||||
header_name = config.header_name
|
||||
header_type = config.header_type
|
||||
|
||||
# Verify we have the auth header
|
||||
jwt_header = request.headers.get(header_name, None)
|
||||
if not jwt_header:
|
||||
raise NoAuthorizationError("Missing {} Header".format(header_name))
|
||||
|
||||
# Make sure the header is in a valid format that we are expecting, ie
|
||||
# <HeaderName>: <HeaderType(optional)> <JWT>
|
||||
parts = jwt_header.split()
|
||||
if not header_type:
|
||||
if len(parts) != 1:
|
||||
msg = "Bad {} header. Expected value '<JWT>'".format(header_name)
|
||||
raise InvalidHeaderError(msg)
|
||||
encoded_token = parts[0]
|
||||
else:
|
||||
if parts[0] != header_type or len(parts) != 2:
|
||||
msg = "Bad {} header. Expected value '{} <JWT>'".format(header_name, header_type)
|
||||
raise InvalidHeaderError(msg)
|
||||
encoded_token = parts[1]
|
||||
|
||||
return decode_token(encoded_token)
|
||||
|
||||
|
||||
def _decode_jwt_from_cookies(request_type):
|
||||
if request_type == 'access':
|
||||
cookie_key = config.access_cookie_name
|
||||
csrf_header_key = config.access_csrf_header_name
|
||||
else:
|
||||
cookie_key = config.refresh_cookie_name
|
||||
csrf_header_key = config.refresh_csrf_header_name
|
||||
|
||||
encoded_token = request.cookies.get(cookie_key)
|
||||
if not encoded_token:
|
||||
raise NoAuthorizationError('Missing cookie "{}"'.format(cookie_key))
|
||||
|
||||
if config.csrf_protect and request.method in config.csrf_request_methods:
|
||||
csrf_value = request.headers.get(csrf_header_key, None)
|
||||
if not csrf_value:
|
||||
raise CSRFError("Missing CSRF token in headers")
|
||||
else:
|
||||
csrf_value = None
|
||||
|
||||
return decode_token(encoded_token, csrf_value=csrf_value)
|
||||
|
||||
|
||||
def _decode_jwt_from_request(request_type):
|
||||
# We have three cases here, having jwts in both cookies and headers is
|
||||
# valid, or the jwt can only be saved in one of cookies or headers. Check
|
||||
# all cases here.
|
||||
if config.jwt_in_cookies and config.jwt_in_headers:
|
||||
try:
|
||||
decoded_token = _decode_jwt_from_cookies(request_type)
|
||||
except NoAuthorizationError:
|
||||
try:
|
||||
decoded_token = _decode_jwt_from_headers()
|
||||
except NoAuthorizationError:
|
||||
raise NoAuthorizationError("Missing JWT in headers and cookies")
|
||||
elif config.jwt_in_headers:
|
||||
decoded_token = _decode_jwt_from_headers()
|
||||
else:
|
||||
decoded_token = _decode_jwt_from_cookies(request_type)
|
||||
|
||||
# Make sure the type of token we received matches the request type we expect
|
||||
verify_token_type(decoded_token, expected_type=request_type)
|
||||
|
||||
# If blacklisting is enabled, see if this token has been revoked
|
||||
verify_token_not_blacklisted(decoded_token, request_type)
|
||||
|
||||
return decoded_token
|
Reference in New Issue
Block a user