From 81fbd9f96a5f2c2f0ef54bec5245a06ebdedcaee Mon Sep 17 00:00:00 2001 From: Pedro Crespo <32402063+pcrespov@users.noreply.github.com> Date: Thu, 8 Dec 2022 14:29:02 +0100 Subject: [PATCH] fixes check_other_registrations --- .../login/_registration.py | 79 ++++++++----------- .../login/handlers_registration.py | 4 +- .../login/storage.py | 4 +- 3 files changed, 37 insertions(+), 50 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/login/_registration.py b/services/web/server/src/simcore_service_webserver/login/_registration.py index a939d24d180..55a3e7714bc 100644 --- a/services/web/server/src/simcore_service_webserver/login/_registration.py +++ b/services/web/server/src/simcore_service_webserver/login/_registration.py @@ -17,14 +17,12 @@ Json, PositiveInt, ValidationError, - parse_obj_as, parse_raw_as, validator, ) from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from yarl import URL -from ..db_models import UserStatus from ._confirmation import ( ConfirmationAction, get_expiration_date, @@ -34,6 +32,7 @@ from ._constants import MSG_EMAIL_EXISTS from .settings import LoginOptions from .storage import AsyncpgStorage, ConfirmationTokenDict +from .utils import CONFIRMATION_PENDING log = logging.getLogger(__name__) @@ -75,55 +74,45 @@ def ensure_enum(cls, v): } -def validate_email(email): - try: - parse_obj_as(EmailStr, email) - except ValidationError as err: - raise web.HTTPUnprocessableEntity( - reason="Invalid email", content_type=MIMETYPE_APPLICATION_JSON - ) from err - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422 - - -async def validate_registration( +async def check_other_registrations( email: str, db: AsyncpgStorage, cfg: LoginOptions, ) -> None: - # email : required & formats - # password: required & secure[min length, ...] - # - # NOTE: Extra requirements on passwords - # SEE https://github.com/ITISFoundation/osparc-simcore/issues/2480 - # - - # The email is already taken - if user := await db.get_user({"email": email}): - if ( - user["status"] == UserStatus.CONFIRMATION_PENDING.value - and ( - _confirmation := await db.get_confirmation( - filter_dict={ - "user": user, - "action": ConfirmationAction.REGISTRATION.value, - } - ) + user = await db.get_user({"email": email}) + if not user: + # The email is already taken + + # RULE: drop_previous_registration + # An unconfirmed account w/o confirmation or w/ an expired confirmation + # will get deleted and the email can be overtaken by + # this new registration + # + if user["status"] == CONFIRMATION_PENDING: + _confirmation = await db.get_confirmation( + filter_dict={ + "user": user, + "action": ConfirmationAction.REGISTRATION.value, + } ) - and is_confirmation_expired(cfg, _confirmation) - ): - # - # An unconfirmed account with an expired confirmation - # will get deleted and the email is associated to this new registration - # - await db.delete_confirmation_and_user(user=user, confirmation=_confirmation) - - log.warning( - "Re-registration of %s with expired %s" - "Deleting user and proceeding to a new registration", - f"{user=}", - f"{_confirmation=}", + drop_previous_registration = not _confirmation or is_confirmation_expired( + cfg, _confirmation ) - return + if drop_previous_registration: + if not _confirmation: + await db.delete_user(user=user) + else: + await db.delete_confirmation_and_user( + user=user, confirmation=_confirmation + ) + + log.warning( + "Re-registration of %s with expired %s" + "Deleting user and proceeding to a new registration", + f"{user=}", + f"{_confirmation=}", + ) + return raise web.HTTPConflict( reason=MSG_EMAIL_EXISTS, content_type=MIMETYPE_APPLICATION_JSON diff --git a/services/web/server/src/simcore_service_webserver/login/handlers_registration.py b/services/web/server/src/simcore_service_webserver/login/handlers_registration.py index e211007afd6..a6d1a1118fd 100644 --- a/services/web/server/src/simcore_service_webserver/login/handlers_registration.py +++ b/services/web/server/src/simcore_service_webserver/login/handlers_registration.py @@ -18,7 +18,7 @@ from ._confirmation import make_confirmation_link from ._constants import MSG_2FA_CODE_SENT, MSG_CANT_SEND_MAIL from ._models import InputSchema, check_confirm_password_match -from ._registration import check_and_consume_invitation, validate_registration +from ._registration import check_and_consume_invitation, check_other_registrations from ._security import login_granted_response from .settings import ( LoginOptions, @@ -86,7 +86,7 @@ async def register(request: web.Request): registration = await parse_request_body_as(RegisterBody, request) - await validate_registration(email=registration.email, db=db, cfg=cfg) + await check_other_registrations(email=registration.email, db=db, cfg=cfg) expires_at = None # = does not expire if settings.LOGIN_REGISTRATION_INVITATION_REQUIRED: diff --git a/services/web/server/src/simcore_service_webserver/login/storage.py b/services/web/server/src/simcore_service_webserver/login/storage.py index d9266767959..09d15f21192 100644 --- a/services/web/server/src/simcore_service_webserver/login/storage.py +++ b/services/web/server/src/simcore_service_webserver/login/storage.py @@ -95,9 +95,7 @@ async def get_confirmation(self, filter_dict) -> Optional[ConfirmationTokenDict] filter_dict["user_id"] = filter_dict.pop("user")["id"] async with self.pool.acquire() as conn: confirmation = await _sql.find_one(conn, self.confirm_tbl, filter_dict) - return ( - ConfirmationTokenDict(**confirmation) if confirmation else confirmation - ) + return ConfirmationTokenDict(**confirmation) if confirmation else None async def delete_confirmation(self, confirmation: ConfirmationTokenDict): async with self.pool.acquire() as conn: