Esempio n. 1
0
    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')
Esempio n. 2
0
    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
Esempio n. 3
0
    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
Esempio n. 4
0
    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
Esempio n. 5
0
    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)
Esempio n. 6
0
    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.'
Esempio n. 7
0
    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)
Esempio n. 8
0
    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
Esempio n. 9
0
    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