fix set email verified

This commit is contained in:
2025-11-28 21:28:13 -03:00
parent c48f289514
commit b5f3648f44
8 changed files with 66 additions and 20 deletions

View File

@@ -139,7 +139,6 @@ def _create_user(user: User, org: Org) -> bool:
'fresh_user': True, 'fresh_user': True,
'name': user.name, 'name': user.name,
'email': user.email, 'email': user.email,
'email_primary': True,
'org_name': org.name, 'org_name': org.name,
'ttl': ttl(start_dt=now_, days=30), 'ttl': ttl(start_dt=now_, days=30),
'created_at': now_, 'created_at': now_,

View File

@@ -8,8 +8,7 @@ from aws_lambda_powertools.event_handler.exceptions import (
) )
from aws_lambda_powertools.event_handler.openapi.params import Body, Path, Query from aws_lambda_powertools.event_handler.openapi.params import Body, Path, Query
from layercake.dateutils import now, ttl from layercake.dateutils import now, ttl
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey, TransactKey
from layercake.funcs import pick
from pydantic import EmailStr from pydantic import EmailStr
from typing_extensions import Annotated from typing_extensions import Annotated
@@ -33,6 +32,9 @@ def get_emails(user_id: str, start_key: Annotated[str | None, Query] = None):
class UserNotFoundError(NotFoundError): ... class UserNotFoundError(NotFoundError): ...
class EmailNotFoundError(NotFoundError): ...
class EmailConflictError(ServiceError): class EmailConflictError(ServiceError):
def __init__(self, msg: str | dict): def __init__(self, msg: str | dict):
super().__init__(HTTPStatus.CONFLICT, msg) super().__init__(HTTPStatus.CONFLICT, msg)
@@ -82,7 +84,6 @@ def add(
'sk': f'EMAIL_VERIFICATION#{uuid4()}', 'sk': f'EMAIL_VERIFICATION#{uuid4()}',
'name': name, 'name': name,
'email': email, 'email': email,
'user_id': user_id,
'ttl': ttl(start_dt=now_, days=30), 'ttl': ttl(start_dt=now_, days=30),
'created_at': now_, 'created_at': now_,
} }
@@ -110,7 +111,6 @@ def request_verification(
'sk': f'EMAIL_VERIFICATION#{uuid4()}', 'sk': f'EMAIL_VERIFICATION#{uuid4()}',
'name': name, 'name': name,
'email': email, 'email': email,
'user_id': user_id,
'ttl': ttl(start_dt=now_, days=30), 'ttl': ttl(start_dt=now_, days=30),
'created_at': now_, 'created_at': now_,
} }
@@ -121,20 +121,31 @@ def request_verification(
class EmailVerificationNotFoundError(NotFoundError): ... class EmailVerificationNotFoundError(NotFoundError): ...
@router.post('/<user_id>/emails/<hash>/verify') @router.post('/<user_id>/emails/<code>/verify')
def verify(user_id: str, hash: str): def verify(user_id: str, code: str):
verification = dyn.collection.get_item( r = dyn.collection.get_items(
KeyPair( TransactKey(user_id)
pk=user_id, + SortKey(
sk=f'EMAIL_VERIFICATION#{hash}', sk='0',
rename_key='email_primary',
path_spec='email',
)
+ SortKey(
sk=f'EMAIL_VERIFICATION#{code}',
rename_key='email',
path_spec='email',
), ),
exc_cls=EmailVerificationNotFoundError, flatten_top=False,
) )
email, primary = pick(('email', 'email_primary'), verification, default=False)
if 'email' not in r:
raise EmailVerificationNotFoundError('Verification code not found')
email, email_primary = r['email'], r['email_primary']
with dyn.transact_writer() as transact: with dyn.transact_writer() as transact:
transact.delete( transact.delete(
key=KeyPair(user_id, f'EMAIL_VERIFICATION#{hash}'), key=KeyPair(user_id, f'EMAIL_VERIFICATION#{code}'),
) )
transact.update( transact.update(
# Post-migration (users): rename `emails` to `EMAIL` # Post-migration (users): rename `emails` to `EMAIL`
@@ -144,15 +155,16 @@ def verify(user_id: str, hash: str):
':true': True, ':true': True,
':now': now(), ':now': now(),
}, },
cond_expr='attribute_exists(sk)',
exc_cls=EmailNotFoundError,
) )
if primary: if email == email_primary:
transact.update( transact.update(
key=KeyPair(user_id, '0'), key=KeyPair(user_id, '0'),
update_expr='SET email_verified = :true, \ update_expr='SET email_verified = :true, \
updated_at = :now', updated_at = :now',
expr_attr_values={ expr_attr_values={
':email': email,
':true': True, ':true': True,
':now': now(), ':now': now(),
}, },

View File

@@ -76,6 +76,40 @@ def test_email_as_primary(
assert r['statusCode'] == HTTPStatus.NO_CONTENT assert r['statusCode'] == HTTPStatus.NO_CONTENT
def test_verify_email(
app,
seeds,
http_api_proxy: HttpApiProxy,
lambda_context: LambdaContext,
):
r = app.lambda_handler(
http_api_proxy(
raw_path='/users/15bacf02-1535-4bee-9022-19d106fd7518/emails/0d29c753-55f8-42d2-908b-e4976aafc183/verify',
method=HTTPMethod.POST,
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.NO_CONTENT
def test_verify_email_notfound(
app,
seeds,
http_api_proxy: HttpApiProxy,
lambda_context: LambdaContext,
):
r = app.lambda_handler(
http_api_proxy(
raw_path='/users/15bacf02-1535-4bee-9022-19d106fd7518/emails/abc/verify',
method=HTTPMethod.POST,
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.NOT_FOUND
def test_remove_emal( def test_remove_emal(
app, app,
seeds, seeds,

View File

@@ -2,7 +2,8 @@
{"id": "213a6682-2c59-4404-9189-12eec0a846d4", "sk": "orgs#f6000f79-6e5c-49a0-952f-3bda330ef278", "name": "Banco do Brasil", "cnpj": "00000000000191"} {"id": "213a6682-2c59-4404-9189-12eec0a846d4", "sk": "orgs#f6000f79-6e5c-49a0-952f-3bda330ef278", "name": "Banco do Brasil", "cnpj": "00000000000191"}
{"id": "15bacf02-1535-4bee-9022-19d106fd7518", "sk": "0", "name": "Sérgio R Siqueira", "email": "sergio@somosbeta.com.br", "cpf": "07879819908"} {"id": "15bacf02-1535-4bee-9022-19d106fd7518", "sk": "0", "name": "Sérgio R Siqueira", "email": "sergio@somosbeta.com.br", "cpf": "07879819908"}
{"id": "15bacf02-1535-4bee-9022-19d106fd7518", "sk": "emails#sergio@somosbeta.com.br", "email_primary": true, "mx_record_exists": true} {"id": "15bacf02-1535-4bee-9022-19d106fd7518", "sk": "emails#sergio@somosbeta.com.br", "email_primary": true, "mx_record_exists": true}
{"id": "15bacf02-1535-4bee-9022-19d106fd7518", "sk": "emails#osergiosiqueira@gmail.com", "mx_record_exists": true} {"id": "15bacf02-1535-4bee-9022-19d106fd7518", "sk": "emails#osergiosiqueira@gmail.com", "email_verified": false, "mx_record_exists": true}
{"id": "15bacf02-1535-4bee-9022-19d106fd7518", "sk": "EMAIL_VERIFICATION#0d29c753-55f8-42d2-908b-e4976aafc183", "email": "osergiosiqueira@gmail.com", "name": "Sérgio Rafael de Siqueira"}
// User orgs // User orgs
{"id": "15bacf02-1535-4bee-9022-19d106fd7518", "sk": "orgs#f6000f79-6e5c-49a0-952f-3bda330ef278", "name": "Banco do Brasil", "cnpj": "00000000000191"} {"id": "15bacf02-1535-4bee-9022-19d106fd7518", "sk": "orgs#f6000f79-6e5c-49a0-952f-3bda330ef278", "name": "Banco do Brasil", "cnpj": "00000000000191"}

View File

@@ -13,6 +13,7 @@ export default [
route('settings', 'routes/settings/layout.tsx', [ route('settings', 'routes/settings/layout.tsx', [
index('routes/settings/profile.tsx'), index('routes/settings/profile.tsx'),
route('emails', 'routes/settings/emails/index.tsx'), route('emails', 'routes/settings/emails/index.tsx'),
route('emails/:code/verify', 'routes/settings/emails/verify.tsx'),
route('password', 'routes/settings/password.tsx'), route('password', 'routes/settings/password.tsx'),
route('orgs', 'routes/settings/orgs.tsx') route('orgs', 'routes/settings/orgs.tsx')
]), ]),

View File

@@ -155,7 +155,6 @@ def _create_user(rawuser: dict, context: dict) -> None:
'fresh_user': True, 'fresh_user': True,
'name': user.name, 'name': user.name,
'email': user.email, 'email': user.email,
'email_primary': True,
'org_name': org.name, 'org_name': org.name,
'ttl': ttl(start_dt=now_, days=30), 'ttl': ttl(start_dt=now_, days=30),
'created_at': now_, 'created_at': now_,

View File

@@ -17,7 +17,7 @@ Oi {first_name}, tudo bem?<br/><br/>
Para proteger sua conta na EDUSEG, precisamos apenas verificar seu Para proteger sua conta na EDUSEG, precisamos apenas verificar seu
endereço de email: {email}.<br/><br/> endereço de email: {email}.<br/><br/>
<a href="https://saladeaula.digital/settings/emails/{code}/verify"> <a href="https://scorm.eduseg.workers.dev/settings/emails/{code}/verify">
👉 Clique aqui para verificar endereço de email 👉 Clique aqui para verificar endereço de email
</a> </a>
""" """

View File

@@ -16,7 +16,7 @@ Oi {first_name}, tudo bem?<br/><br/>
Sua conta foi criada na EDUSEG pela empresa <b>{org_name}</b>.<br/><br/> Sua conta foi criada na EDUSEG pela empresa <b>{org_name}</b>.<br/><br/>
<a href="https://id.saladeaula.digital/signup?uid={user_id}&code={code}"> <a href="https://scorm.eduseg.workers.dev/signup?uid={user_id}&code={code}">
👉 Clique aqui para fazer seu primeiro acesso 👉 Clique aqui para fazer seu primeiro acesso
</a> </a>
""" """