add reset password
This commit is contained in:
@@ -12,7 +12,7 @@ from aws_lambda_powertools.event_handler.exceptions import ServiceError
|
||||
from aws_lambda_powertools.logging import correlation_paths
|
||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||
|
||||
from middlewares import AuthorizerMiddleware
|
||||
from middlewares import AuthenticationMiddleware
|
||||
from routes import courses, enrollments, lookup, orders, orgs, settings, users, webhooks
|
||||
|
||||
tracer = Tracer()
|
||||
@@ -28,7 +28,7 @@ app = APIGatewayHttpResolver(
|
||||
cors=cors,
|
||||
debug='AWS_SAM_LOCAL' in os.environ,
|
||||
)
|
||||
app.use(middlewares=[AuthorizerMiddleware()])
|
||||
app.use(middlewares=[AuthenticationMiddleware()])
|
||||
app.include_router(courses.router, prefix='/courses')
|
||||
app.include_router(enrollments.router, prefix='/enrollments')
|
||||
app.include_router(orders.router, prefix='/orders')
|
||||
|
||||
@@ -36,3 +36,40 @@ def admin_get_user(
|
||||
return None
|
||||
else:
|
||||
return user
|
||||
|
||||
|
||||
def admin_set_user_password(
|
||||
username: str,
|
||||
password: str,
|
||||
*,
|
||||
user_pool_id: str,
|
||||
permanent: bool = False,
|
||||
idp_client,
|
||||
) -> bool:
|
||||
"""Sets the specified user's password in a user pool as an administrator.
|
||||
Works on any user.
|
||||
|
||||
The password can be temporary or permanent. If it is temporary, the user
|
||||
status enters the FORCE_CHANGE_PASSWORD state.
|
||||
|
||||
When the user next tries to sign in, the InitiateAuth/AdminInitiateAuth
|
||||
response will contain the NEW_PASSWORD_REQUIRED challenge.
|
||||
|
||||
If the user doesn't sign in before it expires, the user won't be able
|
||||
to sign in, and an administrator must reset their password.
|
||||
|
||||
Once the user has set a new password, or the password is permanent,
|
||||
the user status is set to Confirmed.
|
||||
"""
|
||||
try:
|
||||
idp_client.admin_set_user_password(
|
||||
UserPoolId=user_pool_id,
|
||||
Username=username,
|
||||
Password=password,
|
||||
Permanent=permanent,
|
||||
)
|
||||
except idp_client.exceptions as err:
|
||||
logger.exception(err)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from .audit_log_middleware import AuditLogMiddleware
|
||||
from .authorizer_middleware import AuthorizerMiddleware, User
|
||||
from .authentication_middleware import AuthenticationMiddleware, User
|
||||
from .tenant_middelware import Tenant, TenantMiddleware
|
||||
|
||||
__all__ = [
|
||||
'AuthorizerMiddleware',
|
||||
'AuthenticationMiddleware',
|
||||
'AuditLogMiddleware',
|
||||
'TenantMiddleware',
|
||||
'User',
|
||||
|
||||
@@ -17,7 +17,7 @@ from layercake.dynamodb import (
|
||||
)
|
||||
from layercake.funcs import pick
|
||||
|
||||
from .authorizer_middleware import User
|
||||
from .authentication_middleware import User
|
||||
|
||||
YEAR_DAYS = 365
|
||||
LOG_RETENTION_DAYS = YEAR_DAYS * 2
|
||||
@@ -29,14 +29,14 @@ class AuditLogMiddleware(BaseMiddlewareHandler):
|
||||
|
||||
Parameters
|
||||
----------
|
||||
action : str
|
||||
action: str
|
||||
The identifier for the audit log action.
|
||||
collect : DynamoDBCollection
|
||||
collect: DynamoDBCollection
|
||||
The collection instance used to persist the audit log data.
|
||||
audit_attrs : tuple of str, optional
|
||||
audit_attrs: tuple of str, optional
|
||||
A tuple of attribute names to extract from the response body for logging.
|
||||
These represent the specific fields to include in the audit log.
|
||||
retention_days : int or None, optional
|
||||
retention_days: int or None, optional
|
||||
The number of days the log is retained on the server.
|
||||
If None, no time-to-live (TTL) will be applied.
|
||||
"""
|
||||
@@ -64,10 +64,13 @@ class AuditLogMiddleware(BaseMiddlewareHandler):
|
||||
ip_addr = req_context.http.source_ip
|
||||
response = next_middleware(app)
|
||||
|
||||
print(app.context['_route'])
|
||||
|
||||
# Successful response
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status#successful_responses
|
||||
if 200 <= response.status_code < 300 and user:
|
||||
now_ = now()
|
||||
author = pick(('id', 'name'), dict(user))
|
||||
data = (
|
||||
pick(self.audit_attrs, extract_event_from_common_models(response.body))
|
||||
if response.is_json()
|
||||
@@ -89,7 +92,7 @@ class AuditLogMiddleware(BaseMiddlewareHandler):
|
||||
action=self.action,
|
||||
data=data,
|
||||
ip=ip_addr,
|
||||
author='himself',
|
||||
author=author,
|
||||
ttl=retention_days,
|
||||
)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class CognitoUser(User):
|
||||
sub: UUID4
|
||||
|
||||
|
||||
class AuthorizerMiddleware(BaseMiddlewareHandler):
|
||||
class AuthenticationMiddleware(BaseMiddlewareHandler):
|
||||
"""This middleware extracts user authentication details from the Lambda authorizer context
|
||||
and makes them available in the application context."""
|
||||
|
||||
@@ -18,7 +18,7 @@ from pydantic import UUID4, BaseModel
|
||||
|
||||
from auth import AuthFlowType
|
||||
|
||||
from .authorizer_middleware import User
|
||||
from .authentication_middleware import User
|
||||
|
||||
|
||||
class Tenant(BaseModel):
|
||||
|
||||
@@ -15,7 +15,7 @@ import elastic
|
||||
from boto3clients import dynamodb_client
|
||||
from enrollment import set_status_as_canceled
|
||||
from middlewares.audit_log_middleware import AuditLogMiddleware
|
||||
from middlewares.authorizer_middleware import User
|
||||
from middlewares.authentication_middleware import User
|
||||
from settings import ELASTIC_CONN, ENROLLMENT_TABLE, USER_TABLE
|
||||
|
||||
router = Router()
|
||||
|
||||
@@ -24,6 +24,7 @@ from pydantic import UUID4, BaseModel, EmailStr, StringConstraints
|
||||
|
||||
import cognito
|
||||
import elastic
|
||||
import middlewares
|
||||
from boto3clients import dynamodb_client, idp_client
|
||||
from middlewares import AuditLogMiddleware
|
||||
from models import User
|
||||
@@ -70,9 +71,31 @@ class Password(BaseModel):
|
||||
new_password: Annotated[str, StringConstraints(min_length=6)]
|
||||
|
||||
|
||||
@router.post('/<id>/password', compress=True, tags=['User'], include_in_schema=False)
|
||||
def new_password(id: str, payload: Password):
|
||||
return Response(status_code=HTTPStatus.OK)
|
||||
@router.post(
|
||||
'/<id>/password',
|
||||
compress=True,
|
||||
tags=['User'],
|
||||
include_in_schema=False,
|
||||
middlewares=[
|
||||
AuditLogMiddleware('PASSWORD_RESET', user_collect, ('id', 'cognito_sub'))
|
||||
],
|
||||
)
|
||||
def password(id: str, payload: Password):
|
||||
cognito.admin_set_user_password(
|
||||
username=str(payload.cognito_sub),
|
||||
password=payload.new_password,
|
||||
user_pool_id=USER_POOOL_ID,
|
||||
idp_client=idp_client,
|
||||
)
|
||||
|
||||
return Response(
|
||||
body={
|
||||
'id': id,
|
||||
'cognito_sub': payload.cognito_sub,
|
||||
},
|
||||
content_type=content_types.APPLICATION_JSON,
|
||||
status_code=HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@router.get('/<id>', compress=True, tags=['User'], summary='Get user')
|
||||
@@ -80,10 +103,10 @@ def get_user(id: str):
|
||||
return user_collect.get_item(KeyPair(id, '0'))
|
||||
|
||||
|
||||
@router.get('/<id>/idp', compress=True, include_in_schema=False)
|
||||
def get_idp(id: str):
|
||||
@router.get('/<sub>/idp', compress=True, include_in_schema=False)
|
||||
def get_idp(sub: str):
|
||||
return cognito.admin_get_user(
|
||||
sub=id,
|
||||
sub=sub,
|
||||
user_pool_id=USER_POOOL_ID,
|
||||
idp_client=idp_client,
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ from ..conftest import HttpApiProxy, LambdaContext
|
||||
YEAR_DAYS = 365
|
||||
|
||||
|
||||
def test_get_course(
|
||||
def test_get_courses(
|
||||
mock_app,
|
||||
dynamodb_seeds,
|
||||
http_api_proxy: HttpApiProxy,
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import json
|
||||
from http import HTTPMethod, HTTPStatus
|
||||
|
||||
from layercake.dynamodb import (
|
||||
DynamoDBCollection,
|
||||
DynamoDBPersistenceLayer,
|
||||
KeyPair,
|
||||
)
|
||||
from layercake.dynamodb import DynamoDBCollection, DynamoDBPersistenceLayer, KeyPair
|
||||
|
||||
from ..conftest import HttpApiProxy, LambdaContext
|
||||
|
||||
@@ -32,7 +28,7 @@ def test_get_policies(
|
||||
}
|
||||
|
||||
|
||||
def test_put_org(
|
||||
def test_put_policies(
|
||||
mock_app,
|
||||
dynamodb_seeds,
|
||||
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
|
||||
@@ -44,7 +40,7 @@ def test_put_org(
|
||||
raw_path='/orgs/cJtK9SsnJhKPyxESe7g3DG/policies',
|
||||
method=HTTPMethod.PUT,
|
||||
headers={'X-Tenant': '*'},
|
||||
body={},
|
||||
body={'payment_policy': None},
|
||||
),
|
||||
lambda_context,
|
||||
)
|
||||
|
||||
@@ -31,7 +31,16 @@ def test_get_emails(
|
||||
'id': '5OxmMjL-ujoR5IMGegQz',
|
||||
'create_date': '2019-03-25T00:00:00-03:00',
|
||||
'update_date': '2023-11-09T12:13:04.308986-03:00',
|
||||
}
|
||||
},
|
||||
{
|
||||
'email_verified': True,
|
||||
'mx_record_exists': True,
|
||||
'sk': 'osergiosiqueira@gmail.com',
|
||||
'email_primary': False,
|
||||
'id': '5OxmMjL-ujoR5IMGegQz',
|
||||
'create_date': '2019-03-25T00:00:00-03:00',
|
||||
'update_date': '2023-11-09T12:13:04.308986-03:00',
|
||||
},
|
||||
],
|
||||
'last_key': None,
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import pytest
|
||||
from aws_lambda_powertools.event_handler.api_gateway import APIGatewayHttpResolver
|
||||
from layercake.dynamodb import DynamoDBCollection, DynamoDBPersistenceLayer
|
||||
|
||||
from middlewares import AuthorizerMiddleware, TenantMiddleware
|
||||
from middlewares import AuthenticationMiddleware, TenantMiddleware
|
||||
|
||||
from .conftest import HttpApiProxy, LambdaContext
|
||||
|
||||
@@ -13,7 +13,7 @@ from .conftest import HttpApiProxy, LambdaContext
|
||||
def mock_app(dynamodb_persistence_layer: DynamoDBPersistenceLayer):
|
||||
collect = DynamoDBCollection(dynamodb_persistence_layer)
|
||||
app = APIGatewayHttpResolver()
|
||||
app.use(middlewares=[AuthorizerMiddleware(), TenantMiddleware(collect)])
|
||||
app.use(middlewares=[AuthenticationMiddleware(), TenantMiddleware(collect)])
|
||||
|
||||
@app.get('/')
|
||||
def index():
|
||||
|
||||
@@ -67,7 +67,7 @@ def del_email(
|
||||
key=KeyPair('email', email),
|
||||
)
|
||||
transact.delete(
|
||||
key=KeyPair(id, ComposeKey(email, 'emails')),
|
||||
key=KeyPair(id, ComposeKey(email, prefix='emails')),
|
||||
cond_expr='email_primary <> :primary',
|
||||
expr_attr_values={':primary': True},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user