def _extract_sid_and_expiry_ts_from( self, session_key: str ) -> Union[Tuple[str, None], Tuple[str, int]]: """Given the session key, we decode it using the `itsdangerous` package. Args: session_key: an encoded session key with the `itsdangerous.TimedJSONWebSignatureSerializer` Returns: A Tuple of (str, None) if there is any errors or (str, int). """ s = itsdangerous.TimedJSONWebSignatureSerializer( secret_key=self._secret_key ) try: res = s.loads(session_key, salt=self._salt) except BadSignature: logger.exception('Bad Signature.') apm.capture_exception() return 'BadSignature', None except SignatureExpired: logger.exception('Signature Expired') apm.capture_exception() return 'SignatureExpired', None return res.get('sid'), res.get('expiry_seconds')
def encode_password_reset_token(user_id: ObjectId) -> Union[bytes, None]: """Encode a timed JWT token of 30 minutes to send to the client-side so we can verify which user has requested to reset their password. Args: user_id: the valid ObjectId user id. Returns: A valid JWT token or None if there is any kind of generic exception. """ # noinspection PyBroadException try: payload = { 'exp': datetime.utcnow() + timedelta(minutes=30), 'iat': datetime.utcnow(), 'sub': user_id } return jwt.encode(payload=payload, key=app.config.get('SECRET_KEY', None), algorithm='HS256', json_encoder=JSONEncoder) except Exception: logger.exception('Encode authentication token error.', exc_info=True) apm.capture_exception() return None
def encode_auth_token(user_id: ObjectId, role: str = 'user') -> Union[bytes, None]: """Generates JWT auth token that is returned as bytes. Args: user_id: an ObjectId for the User. role: the role of user. Must be one of: `admin` or `user`. Returns: A valid JWT token as bytes. """ try: payload = { 'exp': datetime.utcnow() + timedelta( days=app.config.get('TOKEN_EXPIRATION_DAYS', 0), seconds=app.config.get('TOKEN_EXPIRATION_SECONDS', 0)), 'iat': datetime.utcnow(), 'sub': user_id, 'role': role } return jwt.encode(payload=payload, key=app.config.get('SECRET_KEY', None), algorithm='HS256', json_encoder=JSONEncoder) except Exception as e: logger.exception('Encode auth token error: {}'.format(e)) apm.capture_exception() return None
def _expiry_timestamp_not_match(expiry_timestamp, redis_key_ttl): datetime_from_ttl = datetime.utcnow() + timedelta( seconds=redis_key_ttl ) timestamp_from_ttl = utctimestamp_by_second(datetime_from_ttl) try: return abs(int(expiry_timestamp) - timestamp_from_ttl) > 10 except (ValueError, TypeError): apm.capture_exception() return True
def decorated_func(*args, **kwargs): response = {'status': 'fail', 'message': 'Session token is not valid.'} # Get the session key from the cookies session_key = request.cookies.get(SESSION_COOKIE_NAME) if not session_key: return response, 401 if not session: response['message'] = 'Session is invalid.' return response, 401 # Extract the JWT from the session which we stored at login auth_token = session.get('jwt', None) if auth_token is None: response['message'] = 'No JWT stored in Session.' return response, 500 # Decode either returns bson.ObjectId if successful or a string from an # exception resp = AuthService().decode_auth_token(auth_token=auth_token) # Either returns an ObjectId User ID or a string response. if not isinstance(resp, ObjectId): response['message'] = resp return response, 401 # Validate the user is active try: user = User.objects.get(id=resp) except DoesNotExist as e: response['error'] = str(e) response['message'] = 'User does not exist.' logger.exception(response['message']) apm.capture_exception() return response, 404 if not user.active: response['message'] = 'This user account has been disabled.' logger.info( json.dumps({ "message": response['message'], "user": user.email })) # Must capture message because there is no exception in this # case which is a bug if Python APM Agent. # https://github.com/elastic/apm-agent-python/issues/599 apm.capture_message('Unauthorised access.') return response, 403 return f(user, *args, **kwargs)
def decode_auth_token( auth_token: Union[bytes, str]) -> Union[ObjectId, str]: """Decodes the JWT auth token. Args: auth_token: a bytes type that was encoded by Returns: An integer or string. """ try: payload = jwt.decode(jwt=auth_token, key=app.config.get('SECRET_KEY', None)) return ObjectId(payload['sub']) except jwt.ExpiredSignatureError: return 'Signature expired. Please login again.' except jwt.InvalidTokenError: logger.info('Invalid token error.') apm.capture_exception() return 'Invalid token. Please log in again.'
def write_wrapper(self, write_method, *args, **kwargs) -> None: """A basic wrapper to call redis-py methods but allows us to set a retry of 3 attempts if there are Redis ReadOnlyErrors. Args: write_method: the `redis-py` method to use. *args: arguments. **kwargs: keyword arguments. Returns: None """ for i in range(3): try: write_method(*args, **kwargs) break except ReadOnlyError: logger.exception('Redis Read error.') apm.capture_exception() self.redis.connection_pool.reset() time.sleep(1)
def create_alloys_many(self, alloys: list) -> Union[list, str]: """We use the PyMongo interface insert_many() method with an instance of a Python list with many alloy dictionaries. Args: alloys: a list of Python dict object that conforms with the AlloySchema. Returns: A list of ObjectId object of the newly created alloy or a str. """ try: alloy_id_list = self.client.create_many( self.load(alloys, many=True)) except ValidationError as e: logger.exception({'msg': e.messages}) apm.capture_exception() return e.messages except DuplicateKeyError as e: logger.exception('Duplicate Key Error') apm.capture_exception() return str(e) return alloy_id_list
def create_alloy(self, alloy: dict) -> Union[ObjectId, str]: """We use the PyMongo interface insert_one() method with an instance of a Python dictionary. Args: alloy: a Python dict object that conforms with the AlloySchema. Returns: The ObjectId object of the newly created alloy or a str. """ try: alloy_id = self.client.create(self.load(alloy)) except ValidationError as e: logger.exception({'msg': e.messages, 'data': str(alloy)}) apm.capture_exception() return e.messages except DuplicateKeyError: logger.exception({ 'msg': 'Duplicate Key Error', 'data': str(alloy) }) apm.capture_exception() return 'Alloy already exists.' return alloy_id