예제 #1
0
async def send_mail(msg: EmailMsg):
    '''Sends an email'''
    # Get settings
    settings = get_settings()

    # Build message
    smtp_msg = EmailMessage()
    smtp_msg['Subject'] = msg.subject
    # pylint: disable=no-member
    smtp_msg['From'] = get_address(settings.EMAIL_FROM.name,
                                   settings.EMAIL_FROM.email)
    smtp_msg['To'] = msg.recipient
    smtp_msg.set_content(msg.text)
    smtp_msg.add_alternative(msg.html, subtype='html')

    # Get security
    mail_sec = settings.EMAIL_SECURITY
    if mail_sec == EmailSecurity.TLS_SSL:
        sec_opts = {'use_tls': True}
    elif mail_sec == EmailSecurity.STARTTLS:
        sec_opts = {'start_tls': True}
    else:
        # Unsecure
        sec_opts = {}

    # Send mail
    await aiosmtplib.send(smtp_msg,
                          hostname=settings.EMAIL_HOSTNAME,
                          port=settings.EMAIL_PORT,
                          username=settings.EMAIL_USERNAME,
                          password=settings.EMAIL_PASSWORD.get_secret_value(),
                          **sec_opts)
예제 #2
0
async def test_create_access_token_with_expire(get_jwt_key, jwt_encode,
                                               freezer):
    '''Should return an access token'''
    # Create mocks
    get_jwt_key.return_value = 'test-private-key'
    jwt_encode.return_value = 'test-access-token'

    # Get settings
    settings = get_settings()

    # Call function
    delta = timedelta(minutes=123456)
    token = await auth.create_access_token(user_id='test-user-id',
                                           expires_delta=delta)

    # Assert results
    assert token == 'test-access-token'
    get_jwt_key.assert_called_with('private')
    jwt_encode.assert_called_with(
        {
            'sub': 'user:test-user-id',
            'exp': datetime.utcnow() + delta,
        },
        'test-private-key',
        algorithm=settings.JWT_ALG,
    )
async def validate_access_token(token: str) -> AccessTokenData:
    '''Validates and extracts User ID from token

    Raises
        InvalidTokenError: Provided token is invalid
    '''
    # Get settings
    settings = get_settings()

    # Decode JWT token
    try:
        jwt_key_public = get_jwt_key('public')
        payload = jwt.decode(token, jwt_key_public,
                             algorithms=[settings.JWT_ALG])
    except jwt.PyJWTError:
        raise InvalidTokenError(f'Unable to decode token "{token}"')

    # Extract user ID
    try:
        user_id = payload.get("sub").split(':')[1]
    except IndexError:
        raise InvalidTokenError(
            f"Token payload doesn't contain valid user ID. Payload: {payload!r}"
        )

    # Build access token data
    try:
        return AccessTokenData(user_id=user_id)
    except ValidationError:
        raise InvalidTokenError(
            f'JWT token contains invalid user ID: "{user_id!r}"'
        )
예제 #4
0
def assert_email_send(args, kwargs):
    '''Helper to assert the email.send mock'''
    # Get settings
    settings = get_settings()

    # Assert results
    smtp_msg = args[0]
    assert smtp_msg['Subject'] == 'test-subject'
    assert smtp_msg[
        'From'] == f'{settings.EMAIL_FROM.name} <{settings.EMAIL_FROM.email}>'
    assert smtp_msg['To'] == 'TestUser <*****@*****.**>'
    assert kwargs['hostname'] == settings.EMAIL_HOSTNAME
    assert kwargs['port'] == settings.EMAIL_PORT
    assert kwargs['username'] == settings.EMAIL_USERNAME
    assert kwargs['password'] == settings.EMAIL_PASSWORD.get_secret_value()
예제 #5
0
def prepare_register_verification(recipient: Address, secret: str) -> EmailMsg:
    '''Prepare mail with registration verification link'''
    # Get settings
    settings = get_settings()

    # Build message
    registration_link = f'{settings.FRONTEND_URL}/register/verify?token={secret}'
    msg = TEMPLATE_REGISTER_TEXT.format(registration_link=registration_link)
    msg_html = TEMPLATE_REGISTER_HTML.format(
        registration_link=registration_link)

    return EmailMsg(
        recipient=recipient,
        subject='Verify your Kinky Harbor account',
        text=msg,
        html=msg_html,
    )
예제 #6
0
def prepare_reset_password(recipient: Address, user_id: str,
                           token: str) -> EmailMsg:
    '''Prepare mail with link to reset your password'''
    # Get settings
    settings = get_settings()

    # Build message
    link = f'{settings.FRONTEND_URL}/login/reset-password?user={user_id}&token={token}'
    msg = TEMPLATE_RESET_PASSWORD_TEXT.format(reset_password_link=link)
    msg_html = TEMPLATE_RESET_PASSWORD_HTML.format(reset_password_link=link)

    return EmailMsg(
        recipient=recipient,
        subject='Password reset for Kinky Harbor',
        text=msg,
        html=msg_html,
    )
예제 #7
0
def prepare_register_email_exist(recipient: Address) -> EmailMsg:
    '''Prepare mail with link to request a password reset'''
    # Get settings
    settings = get_settings()

    # Build message
    reset_password_link = f'{settings.FRONTEND_URL}/login/request-reset/'
    msg = TEMPLATE_REGISTER_EMAIL_EXISTS_TEXT.format(
        reset_password_link=reset_password_link)
    msg_html = TEMPLATE_REGISTER_EMAIL_EXISTS_HTML.format(
        reset_password_link=reset_password_link)

    return EmailMsg(
        recipient=recipient,
        subject='Registration attempt at Kinky Harbor',
        text=msg,
        html=msg_html,
    )
async def create_access_token(*, user_id: str, expires_delta: timedelta = None):
    '''Generates an access token containing provided data'''
    # Get settings
    settings = get_settings()

    # Prepare contents
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        minutes = settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES
        expire = datetime.utcnow() + timedelta(minutes=minutes)
    data = {
        "sub": f"user:{user_id}",
        "exp": expire,
    }

    # Generate token
    jwt_key_private = get_jwt_key('private')
    return jwt.encode(data, jwt_key_private, algorithm=settings.JWT_ALG)
예제 #9
0
async def test_success_validate_access_token(get_jwt_key, jwt_decode):
    '''Should return an access token'''
    # Create mocks
    get_jwt_key.return_value = 'test-public-key'
    jwt_decode.return_value = {'sub': 'user:507f1f77bcf86cd799439011'}

    # Get settings
    settings = get_settings()

    # Call function
    data = await auth.validate_access_token(token='test-jwt-token')

    # Assert results
    get_jwt_key.assert_called_with('public')
    jwt_decode.assert_called_with(
        'test-jwt-token',
        'test-public-key',
        algorithms=[settings.JWT_ALG],
    )
    assert data == AccessTokenData(user_id='507f1f77bcf86cd799439011', )
예제 #10
0
async def test_fail_invalid_token_other(get_jwt_key, jwt_decode, payload):
    '''Should throw InvalidTokenError'''
    # Create mocks
    get_jwt_key.return_value = 'test-public-key'
    jwt_decode.return_value = payload

    # Get settings
    settings = get_settings()

    # Call function
    with pytest.raises(auth.InvalidTokenError):
        await auth.validate_access_token(token='test-jwt-token')

    # Assert results
    get_jwt_key.assert_called_with('public')
    jwt_decode.assert_called_with(
        'test-jwt-token',
        'test-public-key',
        algorithms=[settings.JWT_ALG],
    )
예제 #11
0
# Close database connections
@app.on_event("shutdown")
async def close_repos():
    '''Close DB client of repositories on application shutdown'''
    logging.info("Database repositories: Closing ...")
    await app.state.repos['refresh_token'].close()
    await app.state.repos['stats'].close()
    await app.state.repos['user'].close()
    await app.state.repos['verif_token'].close()
    logging.info("Database repositories: Closed")


# Add CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=get_settings().CORS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get('/', include_in_schema=False)
async def redirect_to_docs():
    '''Redirect root page to docs.'''
    return RedirectResponse(url='/docs')


# Allows debugging of the application
# https://fastapi.tiangolo.com/tutorial/debugging/
if __name__ == '__main__':
예제 #12
0
def get_default_db(client: motor.AsyncIOMotorClient):
    '''Returns client for default database'''
    return client[get_settings().MONGO_DATABASE]
예제 #13
0
def create_db_client():
    '''Returns instance of database client'''
    return motor.AsyncIOMotorClient(get_settings().MONGO_HOST)