add fallback to id

This commit is contained in:
2025-12-03 13:31:28 -03:00
parent af45be7083
commit 1b4cbbce6c
8 changed files with 112 additions and 33 deletions

View File

@@ -121,6 +121,8 @@ def _create_user(user: User, org: Org) -> bool:
# 'org_id': {org.id},
# 'created_at': now_,
'createDate': now_,
# Makes the email searchable
'emails': {user.email},
},
)
transact.put(
@@ -137,8 +139,8 @@ def _create_user(user: User, org: Org) -> bool:
'sk': f'emails#{user.email}',
'email_verified': email_verified,
'email_primary': True,
'mx_record_exists': email_verified,
'created_at': now_,
**({'mx_record_exists': True} if email_verified else {}),
}
)

View File

@@ -20,13 +20,9 @@ router = Router()
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
@router.get('/<user_id>/emails')
def get_emails(user_id: str, start_key: Annotated[str | None, Query] = None):
return dyn.collection.query(
# Post-migration (users): rename `emails` to `EMAIL`
key=KeyPair(user_id, 'emails'),
start_key=start_key,
)
class ConflictError(ServiceError):
def __init__(self, msg: str | dict):
super().__init__(HTTPStatus.CONFLICT, msg)
class UserNotFoundError(NotFoundError): ...
@@ -35,9 +31,19 @@ class UserNotFoundError(NotFoundError): ...
class EmailNotFoundError(NotFoundError): ...
class EmailConflictError(ServiceError):
def __init__(self, msg: str | dict):
super().__init__(HTTPStatus.CONFLICT, msg)
class EmailVerificationNotFoundError(NotFoundError): ...
class EmailConflictError(ConflictError): ...
@router.get('/<user_id>/emails')
def get_emails(user_id: str, start_key: Annotated[str | None, Query] = None):
return dyn.collection.query(
# Post-migration (users): rename `emails` to `EMAIL`
key=KeyPair(user_id, 'emails'),
start_key=start_key,
)
@router.post('/<user_id>/emails')
@@ -68,7 +74,6 @@ def add(
# Post-migration (users): rename `emails` to `EMAIL`
'sk': f'emails#{email}',
'email_verified': False,
'mx_record_exists': False,
'email_primary': False,
'created_at': now_,
}
@@ -124,9 +129,6 @@ def request_verification(
return JSONResponse(status_code=HTTPStatus.NO_CONTENT)
class EmailVerificationNotFoundError(NotFoundError): ...
@router.post('/<user_id>/emails/<code>/verify')
def verify(user_id: str, code: str):
r = dyn.collection.get_items(

View File

@@ -20,6 +20,10 @@ export async function loader({ request, context }: Route.LoaderArgs) {
})
}
if (!url.searchParams.has('client_id')) {
throw redirect('https://scorm.eduseg.workers.dev/')
}
const r = await fetch(issuerUrl.toString(), {
method: 'GET',
headers: new Headers([

View File

@@ -7,7 +7,10 @@ from aws_lambda_powertools.event_handler import (
Response,
)
from aws_lambda_powertools.event_handler.api_gateway import Router
from aws_lambda_powertools.event_handler.exceptions import ForbiddenError, NotFoundError
from aws_lambda_powertools.event_handler.exceptions import (
NotFoundError,
UnauthorizedError,
)
from aws_lambda_powertools.event_handler.openapi.params import Body
from aws_lambda_powertools.shared.cookies import Cookie
from layercake.dateutils import now, ttl
@@ -25,7 +28,7 @@ dyn = DynamoDBPersistenceLayer(OAUTH2_TABLE, dynamodb_client)
idp = boto3.client('cognito-idp')
class InvalidCredentialsError(ForbiddenError): ...
class InvalidCredentialsError(UnauthorizedError): ...
class UserNotFoundError(NotFoundError): ...
@@ -42,15 +45,21 @@ def authentication(
_get_idp_user(user_id, username, password)
else:
if not pbkdf2_sha256.verify(password, password_hash):
raise InvalidCredentialsError('Invalid credentials')
dyn.update_item(
key=KeyPair(user_id, 'FAILED_ATTEMPTS'),
update_expr='SET #count = if_not_exists(#count, :zero) + :one, \
updated_at = :now',
expr_attr_names={
'#count': 'failed_attempts',
},
expr_attr_values={
':zero': 0,
':one': 1,
':now': now(),
},
)
dyn.update_item(
key=KeyPair(user_id, '0'),
# Post-migration (users): uncomment the following line
# update_expr='SET last_login = :now',
update_expr='SET lastLogin = :now',
expr_attr_values={':now': now()},
)
raise InvalidCredentialsError('Invalid credentials')
return Response(
status_code=HTTPStatus.OK,
@@ -146,6 +155,16 @@ def new_session(user_id: str) -> str:
exp = ttl(start_dt=now_, seconds=SESSION_EXPIRES_IN)
with dyn.transact_writer() as transact:
transact.delete(key=KeyPair(user_id, 'FAILED_ATTEMPTS'))
transact.update(
key=KeyPair(user_id, '0'),
# Post-migration (users): uncomment the following line
# update_expr='SET last_login = :now',
update_expr='SET lastLogin = :now',
expr_attr_values={
':now': now_,
},
)
transact.put(
item={
'id': 'SESSION',

View File

@@ -1,6 +1,6 @@
from http import HTTPMethod
from http import HTTPMethod, HTTPStatus
from layercake.dynamodb import DynamoDBPersistenceLayer, PartitionKey
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, PartitionKey
from ..conftest import HttpApiProxy, LambdaContext
@@ -29,3 +29,31 @@ def test_authentication(
session = dynamodb_persistence_layer.collection.query(PartitionKey('SESSION'))
# One seesion if created from seeds
assert len(session['items']) == 2
def test_invalid_password(
app,
seeds,
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
http_api_proxy: HttpApiProxy,
lambda_context: LambdaContext,
):
r = app.lambda_handler(
http_api_proxy(
raw_path='/authentication',
method=HTTPMethod.POST,
body={
'username': '07879819908',
'password': '123333',
},
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.UNAUTHORIZED
failed = dynamodb_persistence_layer.collection.get_item(
KeyPair('357db1c5-7442-4075-98a3-fbe5c938a419', 'FAILED_ATTEMPTS')
)
assert 'failed_attempts' in failed

View File

@@ -32,7 +32,7 @@ Globals:
ENROLLMENT_TABLE: !Ref EnrollmentTable
COURSE_TABLE: !Ref CourseTable
KONVIVA_API_URL: https://lms.saladeaula.digital
KONVIVA_SECRET_KEY: "{{resolve:ssm:/betaeducacao/konviva/secret_key/str}}"
KONVIVA_SECRET_KEY: '{{resolve:ssm:/betaeducacao/konviva/secret_key/str}}'
Resources:
EventLog:
@@ -49,7 +49,7 @@ Resources:
Type: AWS::Serverless::HttpApi
Properties:
CorsConfiguration:
AllowOrigins: ["*"]
AllowOrigins: ['*']
AllowMethods: [POST, OPTIONS]
AllowHeaders: [Content-Type, X-Requested-With]
@@ -90,12 +90,34 @@ Resources:
detail-type: [INSERT]
detail:
new_image:
sk: ["0"]
sk: ['0']
cnpj:
- exists: false
metadata__konviva_user_id:
- exists: false
EventCreateOrgFunction:
Type: AWS::Serverless::Function
Properties:
Handler: events.create_org.lambda_handler
LoggingConfig:
LogGroup: !Ref EventLog
Policies:
- DynamoDBWritePolicy:
TableName: !Ref UserTable
Events:
DynamoDBEvent:
Type: EventBridgeRule
Properties:
Pattern:
resources: [!Ref UserTable]
detail-type: [INSERT]
detail:
new_image:
sk: ['0']
cnpj:
- exists: true
EventUpdateUserFunction:
Type: AWS::Serverless::Function
Properties:
@@ -114,7 +136,7 @@ Resources:
detail-type: [MODIFY]
detail:
new_image:
sk: ["0"]
sk: ['0']
metadata__konviva_user_id:
- exists: true
changes: [name, email, cpf]
@@ -159,7 +181,7 @@ Resources:
detail-type: [MODIFY]
detail:
new_image:
sk: ["0"]
sk: ['0']
status: [CANCELED]
old_image:
status: [PENDING]

View File

@@ -20,7 +20,8 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
user_layer.update_item(
key=KeyPair(new_image['sk'], '0'),
update_expr='ADD tenant_id :tenant_id SET updated_at = :updated_at',
update_expr='ADD tenant_id :tenant_id \
SET updated_at = :updated_at',
expr_attr_values={
':tenant_id': {tenant},
':updated_at': now_,

View File

@@ -242,6 +242,7 @@ Resources:
detail:
new_image:
sk:
# Post-migration (users): rename `emails` to `EMAIL`
- prefix: emails#
mx_record_exists:
- exists: false