Skip to content

Commit e5fd903

Browse files
committed
✨ Refactor invitation service: integrate confirmation service and remove legacy database dependencies
1 parent b50eaae commit e5fd903

File tree

2 files changed

+42
-29
lines changed

2 files changed

+42
-29
lines changed

services/web/server/src/simcore_service_webserver/login/_controller/rest/registration.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@
3939
check_other_registrations,
4040
extract_email_from_invitation,
4141
)
42-
from ..._login_repository_legacy import (
43-
AsyncpgStorage,
44-
get_plugin_storage,
45-
)
4642
from ..._login_service import (
4743
notify_user_confirmation,
4844
)
@@ -57,7 +53,6 @@
5753
MSG_WEAK_PASSWORD,
5854
)
5955
from ...settings import (
60-
LoginOptions,
6156
LoginSettingsForProduct,
6257
get_plugin_options,
6358
get_plugin_settings,
@@ -129,13 +124,11 @@ async def register(request: web.Request):
129124
settings: LoginSettingsForProduct = get_plugin_settings(
130125
request.app, product_name=product.name
131126
)
132-
db: AsyncpgStorage = get_plugin_storage(request.app)
133-
cfg: LoginOptions = get_plugin_options(request.app)
134127

135128
registration = await parse_request_body_as(RegisterBody, request)
136129

137130
await check_other_registrations(
138-
request.app, email=registration.email, current_product=product, db=db, cfg=cfg
131+
request.app, email=registration.email, current_product=product
139132
)
140133

141134
# Check for weak passwords
@@ -184,8 +177,6 @@ async def register(request: web.Request):
184177
invitation_code,
185178
product=product,
186179
guest_email=registration.email,
187-
db=db,
188-
cfg=cfg,
189180
app=request.app,
190181
)
191182
if invitation.trial_account_days:

services/web/server/src/simcore_service_webserver/login/_invitations_service.py

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@
4141
)
4242
from ..products.models import Product
4343
from ..users import users_service
44-
from . import _auth_service, _confirmation_service
44+
from . import _auth_service
45+
from ._confirmation_repository import ConfirmationRepository
46+
from ._confirmation_service import ConfirmationService
4547
from ._login_repository_legacy import (
46-
AsyncpgStorage,
4748
BaseConfirmationTokenDict,
4849
ConfirmationTokenDict,
4950
)
@@ -52,11 +53,19 @@
5253
MSG_INVITATIONS_CONTACT_SUFFIX,
5354
MSG_USER_DISABLED,
5455
)
55-
from .settings import LoginOptions
56+
from .settings import get_plugin_options
5657

5758
_logger = logging.getLogger(__name__)
5859

5960

61+
def _get_confirmation_service(app: web.Application) -> ConfirmationService:
62+
"""Get confirmation service instance from app."""
63+
engine = app["postgres_db_engine"]
64+
repository = ConfirmationRepository(engine)
65+
options = get_plugin_options(app)
66+
return ConfirmationService(repository, options)
67+
68+
6069
class ConfirmationTokenInfoDict(ConfirmationTokenDict):
6170
expires: datetime
6271
url: str
@@ -112,8 +121,6 @@ async def check_other_registrations(
112121
app: web.Application,
113122
email: str,
114123
current_product: Product,
115-
db: AsyncpgStorage,
116-
cfg: LoginOptions,
117124
) -> None:
118125

119126
# An account is already registered with this email
@@ -133,23 +140,24 @@ async def check_other_registrations(
133140
# w/ an expired confirmation will get deleted and its account (i.e. email)
134141
# can be overtaken by this new registration
135142
#
136-
_confirmation = await db.get_confirmation(
143+
confirmation_service = _get_confirmation_service(app)
144+
_confirmation = await confirmation_service.get_confirmation(
137145
filter_dict={
138-
"user": user,
146+
"user_id": user["id"],
139147
"action": ConfirmationAction.REGISTRATION.value,
140148
}
141149
)
142150
drop_previous_registration = (
143151
not _confirmation
144-
or _confirmation_service.is_confirmation_expired(cfg, _confirmation)
152+
or confirmation_service.is_confirmation_expired(_confirmation)
145153
)
146154
if drop_previous_registration:
147155
if not _confirmation:
148156
await users_service.delete_user_without_projects(
149157
app, user_id=user["id"], clean_cache=False
150158
)
151159
else:
152-
await db.delete_confirmation_and_user(
160+
await confirmation_service.delete_confirmation_and_user(
153161
user_id=user["id"], confirmation=_confirmation
154162
)
155163

@@ -175,7 +183,7 @@ async def check_other_registrations(
175183

176184

177185
async def create_invitation_token(
178-
db: AsyncpgStorage,
186+
app: web.Application,
179187
*,
180188
user_id: IdInt,
181189
user_email: LowerCaseEmailStr | None = None,
@@ -198,11 +206,19 @@ async def create_invitation_token(
198206
trial_account_days=trial_days,
199207
extra_credits_in_usd=extra_credits_in_usd,
200208
)
201-
return await db.create_confirmation(
209+
confirmation_service = _get_confirmation_service(app)
210+
confirmation = await confirmation_service.create_confirmation(
202211
user_id=user_id,
203212
action=ConfirmationAction.INVITATION.name,
204213
data=data_model.model_dump_json(),
205214
)
215+
return {
216+
"code": confirmation.code,
217+
"user_id": confirmation.user_id,
218+
"action": confirmation.action,
219+
"data": confirmation.data,
220+
"created_at": confirmation.created_at,
221+
}
206222

207223

208224
@contextmanager
@@ -270,8 +286,6 @@ async def check_and_consume_invitation(
270286
invitation_code: str,
271287
guest_email: str,
272288
product: Product,
273-
db: AsyncpgStorage,
274-
cfg: LoginOptions,
275289
app: web.Application,
276290
) -> ConfirmedInvitationData:
277291
"""Consumes invitation: the code is validated, the invitation retrieives and then deleted
@@ -305,26 +319,34 @@ async def check_and_consume_invitation(
305319
)
306320

307321
# database-type invitations
308-
if confirmation_token := await _confirmation_service.validate_confirmation_code(
309-
invitation_code, db, cfg
322+
confirmation_service = _get_confirmation_service(app)
323+
if confirmation := await confirmation_service.validate_confirmation_code(
324+
invitation_code
310325
):
311326
try:
327+
confirmation_token_dict = {
328+
"code": confirmation.code,
329+
"user_id": confirmation.user_id,
330+
"action": confirmation.action,
331+
"data": confirmation.data,
332+
"created_at": confirmation.created_at,
333+
}
312334
invitation_data: ConfirmedInvitationData = (
313-
_InvitationValidator.model_validate(confirmation_token).data
335+
_InvitationValidator.model_validate(confirmation_token_dict).data
314336
)
315337
return invitation_data
316338

317339
except ValidationError as err:
318340
_logger.warning(
319341
"%s is associated with an invalid %s.\nDetails: %s",
320342
f"{invitation_code=}",
321-
f"{confirmation_token=}",
343+
f"{confirmation=}",
322344
f"{err=}",
323345
)
324346

325347
finally:
326-
await db.delete_confirmation(confirmation_token)
327-
_logger.info("Invitation with %s was consumed", f"{confirmation_token=}")
348+
await confirmation_service.delete_confirmation(confirmation)
349+
_logger.info("Invitation with %s was consumed", f"{confirmation=}")
328350

329351
raise web.HTTPForbidden(
330352
text=(

0 commit comments

Comments
 (0)