def test_access_token_expires_after_30_min() -> None:
    thirtyone_mins_from_now = generate_time_from_now(1860)
    uow = register_account_helper()
    access_token = get_access_token_helper(uow)

    with freeze_time(thirtyone_mins_from_now):
        with pytest.raises(ExpiredSignatureError):
            Authentication.verify_token(access_token)
Exemple #2
0
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            if not authorization_header:
                raise AuthorizationError('No authorization header provided')

            access_token = authorization_header.split(' ')[1]

            if not access_token:
                raise AuthorizationError('No authorization token provided')

            try:
                Authentication.verify_token(access_token)
            except Exception as e:
                raise AuthorizationError(e) from e

            return func(*args, **kwargs)
def login_endpoint() -> Tuple[Response, int]:
    try:
        if not request.json:
            raise APIError('Missing request body', 400)

        uow = UnitOfWork()

        email = request.json['email']
        password = request.json['password']

        access_token = get_access_token(uow, email, password)
        token_store = TokenStore()

        user = Authentication().get_jwt_identity(access_token)
        user_id = int(user['user_id'])
        username = user['username']

        refresh_token = set_refresh_token(token_store, user_id, username)

        res = jsonify({'access_token': access_token})
        res.set_cookie('refresh_token',
                       refresh_token,
                       httponly=True,
                       domain='.talel.io',
                       secure=not current_app.config['DEBUG'])

        return res, 200
    except KeyError as error:
        raise APIError(f'Expected {error} key', 400) from error
    except AccountError as error:
        raise APIError(str(error), 401) from error
Exemple #4
0
def register_account(uow: UnitOfWork, email: str, password: str,
                     username: str) -> Account:
    whitelisted_emails = getenv('WHITELISTED_EMAILS')

    if whitelisted_emails is not None and email not in whitelisted_emails.split(
            ','):
        raise AccountRegistrationError('Email not whitelisted')

    with uow:
        if uow.account.get(Account, email=email) is not None:
            raise AccountRegistrationError(
                f"Account with the email '{email}' already exists")

        if uow.user.get(User, username=username):
            raise AccountRegistrationError(
                f"Account with the username '{username}' already exists")

        password_hash = Authentication().generate_password_hash(password)
        user = User(username)
        account = Account(email, password_hash, user)

        account_record = uow.account.add(account)
        uow.commit()

        account_record = uow.account.get(Account, email=email)
        verification_token = account.generate_verification_token
        account.send_registration_email(verification_token)

        return account_record
Exemple #5
0
 def validate_verification_token(token: str) -> Dict[str, str]:
     try:
         verified_token = Authentication.verify_token(token)
         return verified_token
     except InvalidSignatureError as error:
         raise InvalidSignatureError(
             'Invalid verification token') from error
def test_can_get_access_token_for_user() -> None:
    uow = register_account_helper()
    access_token = get_access_token_helper(uow)

    username = Authentication().get_jwt_identity(access_token)['username']

    assert username == USERNAME_TALEL
Exemple #7
0
def generate_access_token(user_id: int, username: str) -> str:
    thirty_mins_from_now = generate_time_from_now(seconds=1800)
    payload = {
        'user_id': user_id,
        'username': username,
        'exp': thirty_mins_from_now
    }

    return Authentication.generate_token(payload)
Exemple #8
0
    def test_cannot_verify_account_with_invalid_token(self) -> None:
        invalid_verification_token = Authentication.generate_token(
            {'email': EMAIL_TALEL}, 'invalidsecretkey')

        res = self.verify_account_request(invalid_verification_token)
        res_data = json.loads(res.data)

        assert res.status_code == 400
        assert res_data['error']['message'] == 'Invalid verification token'
Exemple #9
0
    def test_cannot_verify_non_registered_account(self) -> None:
        unknown_verification_token = Authentication.generate_token(
            {'email': INVALID_EMAIL})

        res = self.verify_account_request(unknown_verification_token)
        res_data = json.loads(res.data)

        assert res.status_code == 400
        assert res_data['error'][
            'message'] == f"No registered account with the email '{INVALID_EMAIL}'"
Exemple #10
0
def set_refresh_token(token_store: TokenStore, user_id: int,
                      username: str) -> str:
    now = generate_time_from_now(seconds=0)

    payload = {'user_id': user_id, 'username': username, 'iat': now}
    refresh_token = Authentication.generate_token(payload)

    token_store.set_token(user_id, refresh_token)

    return refresh_token
def test_can_set_refresh_token_for_user() -> None:
    token_store = FakeTokenStore()
    refresh_token = set_refresh_token(token_store, INITIAL_USER_ID,
                                      USERNAME_TALEL)  # type: ignore

    user_id, username, iat = Authentication().get_jwt_identity(
        refresh_token).values()

    assert username == USERNAME_TALEL
    assert user_id == INITIAL_USER_ID
    assert iat
Exemple #12
0
    def test_cannot_verify_already_verified_account(self) -> None:
        bianca_verification_token = Authentication.generate_token(
            {'email': EMAIL_BIANCA})

        self.register_account_request(bianca_registration_data)
        self.verify_account_request(bianca_verification_token)

        res = self.verify_account_request(bianca_verification_token)
        res_data = json.loads(res.data)

        assert res.status_code == 400
        assert res_data['error']['message'] == 'Account already verified'
Exemple #13
0
    def test_can_verify_account(self) -> None:
        talel_verification_token = Authentication.generate_token(
            {'email': EMAIL_TALEL})

        res_account = self.register_account_request(talel_registration_data)
        res_account_data = json.loads(res_account.data)

        assert not res_account_data['verified']

        res_verify = self.verify_account_request(talel_verification_token)
        res_verify_data = json.loads(res_verify.data)

        assert res_verify.status_code == 200
        assert res_verify_data['verified']
    def protected_logout_endpoint() -> Tuple[Response, int]:
        try:
            access_token = extract_access_token_from_authorization_header(
                cast(str, authorization_header))
            token_store = TokenStore()

            user = Authentication().get_jwt_identity(access_token)
            user_id = int(user['user_id'])

            assert delete_refresh_token(token_store, user_id)

            return jsonify({'message': 'Successfully logged out'}), 200
        except TokenError as error:
            raise APIError(str(error), 409) from error
Exemple #15
0
    def test_cannot_generate_new_access_token_for_user_with_no_stored_refresh_token(
            self) -> None:
        invalid_refresh_token = Authentication.generate_token({
            'user_id':
            INITIAL_USER_ID + 1986,
            'username':
            USERNAME_TALEL
        })

        res = self.new_access_token_request(invalid_refresh_token)
        res_data = json.loads(res.data)

        assert res.status_code == 401
        assert res_data['error'][
            'message'] == 'No stored refresh token for user'
Exemple #16
0
def get_access_token(uow: UnitOfWork, email: str, password: str) -> str:
    error_msg = 'Invalid username or password'

    with uow:
        account_record = uow.account.get(Account, email=email)

        if account_record is None:
            raise AccountError(error_msg)

        password_hash = account_record.password

        if not Authentication().check_password_hash(password, password_hash):
            raise AccountError(error_msg)

        access_token = generate_access_token(account_record.user.id,
                                             account_record.user.username)

        return access_token
Exemple #17
0
def generate_authorization_header(user_id: int = INITIAL_USER_ID,
                                  username: str = USERNAME_TALEL,
                                  access_token: Union[str, None] = None,
                                  no_token: bool = False,
                                  invalid_token: bool = False) -> Dict[str, str]:
    if not access_token:
        access_token = Authentication.generate_token({'user_id': user_id, 'username': username})

    if no_token:
        authorization_header = {'Authorization': 'Bearer '}
        return authorization_header

    if invalid_token:
        authorization_header = {'Authorization': f'Bearer {access_token + "1986"}'}
        return authorization_header

    authorization_header = {'Authorization': f'Bearer {access_token}'}

    return authorization_header
def new_access_token_endpoint() -> Tuple[Response, int]:
    try:
        refresh_token = request.cookies.get('refresh_token') or ''
        token_store = TokenStore()

        user = Authentication().get_jwt_identity(refresh_token)
        user_id = int(user['user_id'])
        username = user['username']

        assert verify_refresh_token(token_store, user_id, refresh_token)
        access_token = generate_access_token(user_id, username)

        return jsonify({'access_token': access_token}), 200
    except InvalidSignatureError as error:
        raise APIError(str(error), 400) from error
    except DecodeError as error:
        raise APIError(str(error), 400) from error
    except TokenError as error:
        raise APIError(str(error), 401) from error
Exemple #19
0
    def protected_upload_images_endpoint() -> Tuple[Response, int]:
        try:
            access_token = extract_access_token_from_authorization_header(
                cast(str, authorization_header))
            user = Authentication().get_jwt_identity(access_token)

            asset_store = AssetStore()
            image_streams = get_streams_from_request_files(request.files)
            user_id = int(user['user_id'])
            bucket = current_app.config['S3_BUCKET']

            image_objects_urls = upload_images(asset_store, image_streams,
                                               user_id, bucket)

            return jsonify(image_objects_urls), 200
        except ImageError as error:
            raise APIError(str(error), 400) from error
        except client('s3').exceptions.ClientError as error:
            raise APIError(str(error), 400) from error
Exemple #20
0
    def protected_create_article_endpoint() -> Tuple[Response, int]:
        try:
            if not request.json:
                raise APIError('Missing request body', 400)

            access_token = extract_access_token_from_authorization_header(
                cast(str, authorization_header))
            uow = UnitOfWork()

            user = Authentication().get_jwt_identity(access_token)
            user_id = int(user['user_id'])
            title = request.json['title']
            body = request.json['body']

            created_article = create_article(uow, user_id, title, body)
            res_body = ArticleSchema().dump(created_article)

            return res_body, 201
        except KeyError as error:
            raise APIError(f'Expected {error} key', 400) from error
def verify_account_helper(uow: FakeUnitOfWork,
                          email: str = EMAIL_TALEL) -> None:
    verification_token = Authentication.generate_token({'email': email})
    verify_account(uow, verification_token)  # type: ignore
Exemple #22
0
 def generate_verification_token(self) -> str:
     token = Authentication.generate_token({'email': self.email})
     return token
Exemple #23
0
from talelio_backend.identity_and_access.authentication import Authentication
from talelio_backend.tests.constants import PASSWORD

SALT = 'talel'

auth = Authentication()


def test_can_generate_password_hash_with_new_salt() -> None:
    password_hash = auth.generate_password_hash(PASSWORD)
    salt = password_hash.split(',')[1]

    assert len(salt) == 5
    assert salt.isascii()


def test_can_generate_password_hash_when_provided_salt() -> None:
    password_hash = auth.generate_password_hash(PASSWORD, SALT)
    salt = password_hash.split(',')[1]

    assert salt == SALT


def test_valid_password_returns_true_when_checking_hash() -> None:
    password_hash = auth.generate_password_hash(PASSWORD)
    password = auth.check_password_hash(PASSWORD, password_hash)

    assert password


def test_invalid_password_returns_false_when_checking_hash() -> None: