add fallback to id
This commit is contained in:
@@ -121,6 +121,8 @@ def _create_user(user: User, org: Org) -> bool:
|
|||||||
# 'org_id': {org.id},
|
# 'org_id': {org.id},
|
||||||
# 'created_at': now_,
|
# 'created_at': now_,
|
||||||
'createDate': now_,
|
'createDate': now_,
|
||||||
|
# Makes the email searchable
|
||||||
|
'emails': {user.email},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
transact.put(
|
transact.put(
|
||||||
@@ -137,8 +139,8 @@ def _create_user(user: User, org: Org) -> bool:
|
|||||||
'sk': f'emails#{user.email}',
|
'sk': f'emails#{user.email}',
|
||||||
'email_verified': email_verified,
|
'email_verified': email_verified,
|
||||||
'email_primary': True,
|
'email_primary': True,
|
||||||
'mx_record_exists': email_verified,
|
|
||||||
'created_at': now_,
|
'created_at': now_,
|
||||||
|
**({'mx_record_exists': True} if email_verified else {}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -20,13 +20,9 @@ router = Router()
|
|||||||
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
|
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/<user_id>/emails')
|
class ConflictError(ServiceError):
|
||||||
def get_emails(user_id: str, start_key: Annotated[str | None, Query] = None):
|
def __init__(self, msg: str | dict):
|
||||||
return dyn.collection.query(
|
super().__init__(HTTPStatus.CONFLICT, msg)
|
||||||
# Post-migration (users): rename `emails` to `EMAIL`
|
|
||||||
key=KeyPair(user_id, 'emails'),
|
|
||||||
start_key=start_key,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UserNotFoundError(NotFoundError): ...
|
class UserNotFoundError(NotFoundError): ...
|
||||||
@@ -35,9 +31,19 @@ class UserNotFoundError(NotFoundError): ...
|
|||||||
class EmailNotFoundError(NotFoundError): ...
|
class EmailNotFoundError(NotFoundError): ...
|
||||||
|
|
||||||
|
|
||||||
class EmailConflictError(ServiceError):
|
class EmailVerificationNotFoundError(NotFoundError): ...
|
||||||
def __init__(self, msg: str | dict):
|
|
||||||
super().__init__(HTTPStatus.CONFLICT, msg)
|
|
||||||
|
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')
|
@router.post('/<user_id>/emails')
|
||||||
@@ -68,7 +74,6 @@ def add(
|
|||||||
# Post-migration (users): rename `emails` to `EMAIL`
|
# Post-migration (users): rename `emails` to `EMAIL`
|
||||||
'sk': f'emails#{email}',
|
'sk': f'emails#{email}',
|
||||||
'email_verified': False,
|
'email_verified': False,
|
||||||
'mx_record_exists': False,
|
|
||||||
'email_primary': False,
|
'email_primary': False,
|
||||||
'created_at': now_,
|
'created_at': now_,
|
||||||
}
|
}
|
||||||
@@ -124,9 +129,6 @@ def request_verification(
|
|||||||
return JSONResponse(status_code=HTTPStatus.NO_CONTENT)
|
return JSONResponse(status_code=HTTPStatus.NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
class EmailVerificationNotFoundError(NotFoundError): ...
|
|
||||||
|
|
||||||
|
|
||||||
@router.post('/<user_id>/emails/<code>/verify')
|
@router.post('/<user_id>/emails/<code>/verify')
|
||||||
def verify(user_id: str, code: str):
|
def verify(user_id: str, code: str):
|
||||||
r = dyn.collection.get_items(
|
r = dyn.collection.get_items(
|
||||||
|
|||||||
@@ -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(), {
|
const r = await fetch(issuerUrl.toString(), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: new Headers([
|
headers: new Headers([
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ from aws_lambda_powertools.event_handler import (
|
|||||||
Response,
|
Response,
|
||||||
)
|
)
|
||||||
from aws_lambda_powertools.event_handler.api_gateway import Router
|
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.event_handler.openapi.params import Body
|
||||||
from aws_lambda_powertools.shared.cookies import Cookie
|
from aws_lambda_powertools.shared.cookies import Cookie
|
||||||
from layercake.dateutils import now, ttl
|
from layercake.dateutils import now, ttl
|
||||||
@@ -25,7 +28,7 @@ dyn = DynamoDBPersistenceLayer(OAUTH2_TABLE, dynamodb_client)
|
|||||||
idp = boto3.client('cognito-idp')
|
idp = boto3.client('cognito-idp')
|
||||||
|
|
||||||
|
|
||||||
class InvalidCredentialsError(ForbiddenError): ...
|
class InvalidCredentialsError(UnauthorizedError): ...
|
||||||
|
|
||||||
|
|
||||||
class UserNotFoundError(NotFoundError): ...
|
class UserNotFoundError(NotFoundError): ...
|
||||||
@@ -42,16 +45,22 @@ def authentication(
|
|||||||
_get_idp_user(user_id, username, password)
|
_get_idp_user(user_id, username, password)
|
||||||
else:
|
else:
|
||||||
if not pbkdf2_sha256.verify(password, password_hash):
|
if not pbkdf2_sha256.verify(password, password_hash):
|
||||||
raise InvalidCredentialsError('Invalid credentials')
|
|
||||||
|
|
||||||
dyn.update_item(
|
dyn.update_item(
|
||||||
key=KeyPair(user_id, '0'),
|
key=KeyPair(user_id, 'FAILED_ATTEMPTS'),
|
||||||
# Post-migration (users): uncomment the following line
|
update_expr='SET #count = if_not_exists(#count, :zero) + :one, \
|
||||||
# update_expr='SET last_login = :now',
|
updated_at = :now',
|
||||||
update_expr='SET lastLogin = :now',
|
expr_attr_names={
|
||||||
expr_attr_values={':now': now()},
|
'#count': 'failed_attempts',
|
||||||
|
},
|
||||||
|
expr_attr_values={
|
||||||
|
':zero': 0,
|
||||||
|
':one': 1,
|
||||||
|
':now': now(),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
raise InvalidCredentialsError('Invalid credentials')
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
status_code=HTTPStatus.OK,
|
status_code=HTTPStatus.OK,
|
||||||
cookies=[
|
cookies=[
|
||||||
@@ -146,6 +155,16 @@ def new_session(user_id: str) -> str:
|
|||||||
exp = ttl(start_dt=now_, seconds=SESSION_EXPIRES_IN)
|
exp = ttl(start_dt=now_, seconds=SESSION_EXPIRES_IN)
|
||||||
|
|
||||||
with dyn.transact_writer() as transact:
|
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(
|
transact.put(
|
||||||
item={
|
item={
|
||||||
'id': 'SESSION',
|
'id': 'SESSION',
|
||||||
|
|||||||
@@ -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
|
from ..conftest import HttpApiProxy, LambdaContext
|
||||||
|
|
||||||
@@ -29,3 +29,31 @@ def test_authentication(
|
|||||||
session = dynamodb_persistence_layer.collection.query(PartitionKey('SESSION'))
|
session = dynamodb_persistence_layer.collection.query(PartitionKey('SESSION'))
|
||||||
# One seesion if created from seeds
|
# One seesion if created from seeds
|
||||||
assert len(session['items']) == 2
|
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
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Globals:
|
|||||||
ENROLLMENT_TABLE: !Ref EnrollmentTable
|
ENROLLMENT_TABLE: !Ref EnrollmentTable
|
||||||
COURSE_TABLE: !Ref CourseTable
|
COURSE_TABLE: !Ref CourseTable
|
||||||
KONVIVA_API_URL: https://lms.saladeaula.digital
|
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:
|
Resources:
|
||||||
EventLog:
|
EventLog:
|
||||||
@@ -49,7 +49,7 @@ Resources:
|
|||||||
Type: AWS::Serverless::HttpApi
|
Type: AWS::Serverless::HttpApi
|
||||||
Properties:
|
Properties:
|
||||||
CorsConfiguration:
|
CorsConfiguration:
|
||||||
AllowOrigins: ["*"]
|
AllowOrigins: ['*']
|
||||||
AllowMethods: [POST, OPTIONS]
|
AllowMethods: [POST, OPTIONS]
|
||||||
AllowHeaders: [Content-Type, X-Requested-With]
|
AllowHeaders: [Content-Type, X-Requested-With]
|
||||||
|
|
||||||
@@ -90,12 +90,34 @@ Resources:
|
|||||||
detail-type: [INSERT]
|
detail-type: [INSERT]
|
||||||
detail:
|
detail:
|
||||||
new_image:
|
new_image:
|
||||||
sk: ["0"]
|
sk: ['0']
|
||||||
cnpj:
|
cnpj:
|
||||||
- exists: false
|
- exists: false
|
||||||
metadata__konviva_user_id:
|
metadata__konviva_user_id:
|
||||||
- exists: false
|
- 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:
|
EventUpdateUserFunction:
|
||||||
Type: AWS::Serverless::Function
|
Type: AWS::Serverless::Function
|
||||||
Properties:
|
Properties:
|
||||||
@@ -114,7 +136,7 @@ Resources:
|
|||||||
detail-type: [MODIFY]
|
detail-type: [MODIFY]
|
||||||
detail:
|
detail:
|
||||||
new_image:
|
new_image:
|
||||||
sk: ["0"]
|
sk: ['0']
|
||||||
metadata__konviva_user_id:
|
metadata__konviva_user_id:
|
||||||
- exists: true
|
- exists: true
|
||||||
changes: [name, email, cpf]
|
changes: [name, email, cpf]
|
||||||
@@ -159,7 +181,7 @@ Resources:
|
|||||||
detail-type: [MODIFY]
|
detail-type: [MODIFY]
|
||||||
detail:
|
detail:
|
||||||
new_image:
|
new_image:
|
||||||
sk: ["0"]
|
sk: ['0']
|
||||||
status: [CANCELED]
|
status: [CANCELED]
|
||||||
old_image:
|
old_image:
|
||||||
status: [PENDING]
|
status: [PENDING]
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
|||||||
|
|
||||||
user_layer.update_item(
|
user_layer.update_item(
|
||||||
key=KeyPair(new_image['sk'], '0'),
|
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={
|
expr_attr_values={
|
||||||
':tenant_id': {tenant},
|
':tenant_id': {tenant},
|
||||||
':updated_at': now_,
|
':updated_at': now_,
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ Resources:
|
|||||||
detail:
|
detail:
|
||||||
new_image:
|
new_image:
|
||||||
sk:
|
sk:
|
||||||
|
# Post-migration (users): rename `emails` to `EMAIL`
|
||||||
- prefix: emails#
|
- prefix: emails#
|
||||||
mx_record_exists:
|
mx_record_exists:
|
||||||
- exists: false
|
- exists: false
|
||||||
|
|||||||
Reference in New Issue
Block a user