fix set email verified
This commit is contained in:
@@ -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_,
|
||||||
|
|||||||
@@ -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(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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"}
|
||||||
|
|||||||
@@ -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')
|
||||||
]),
|
]),
|
||||||
|
|||||||
@@ -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_,
|
||||||
|
|||||||
@@ -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>
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -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>
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user