11from datetime import datetime
22from logging import getLogger
3- from typing import Any , Literal , TypedDict , cast
4-
5- import asyncpg
6- from aiohttp import web
7- from servicelib .utils_secrets import generate_passcode
8-
9- from . import _login_repository_legacy_sql
3+ from typing import Literal , TypedDict
104
115_logger = getLogger (__name__ )
126
13- APP_LOGIN_STORAGE_KEY = f"{ __name__ } .APP_LOGIN_STORAGE_KEY"
14-
157
168## MODELS
179
@@ -31,105 +23,3 @@ class ConfirmationTokenDict(BaseConfirmationTokenDict):
3123 created_at : datetime
3224 # SEE handlers_confirmation.py::email_confirmation to determine what type is associated to each action
3325 data : str | None
34-
35-
36- ## REPOSITORY
37-
38-
39- class AsyncpgStorage :
40- def __init__ (
41- self ,
42- pool : asyncpg .Pool ,
43- * ,
44- user_table_name : str = "users" ,
45- confirmation_table_name : str = "confirmations" ,
46- ):
47- self .pool = pool
48- self .user_tbl = user_table_name
49- self .confirm_tbl = confirmation_table_name
50-
51- #
52- # CRUD confirmation
53- #
54- async def create_confirmation (
55- self , user_id : int , action : ActionLiteralStr , data : str | None = None
56- ) -> ConfirmationTokenDict :
57- async with self .pool .acquire () as conn :
58- # generate different code
59- while True :
60- # NOTE: use only numbers (i.e. avoid generate_password) since front-end does not handle well url encoding
61- numeric_code : str = generate_passcode (20 )
62- if not await _login_repository_legacy_sql .find_one (
63- conn , self .confirm_tbl , {"code" : numeric_code }
64- ):
65- break
66- # insert confirmation
67- # NOTE: returns timestamp generated at the server-side
68- confirmation = ConfirmationTokenDict (
69- code = numeric_code ,
70- action = action ,
71- user_id = user_id ,
72- data = data ,
73- created_at = datetime .utcnow (),
74- )
75- c = await _login_repository_legacy_sql .insert (
76- conn , self .confirm_tbl , dict (confirmation ), returning = "code"
77- )
78- assert numeric_code == c # nosec
79- return confirmation
80-
81- async def get_confirmation (
82- self , filter_dict : dict [str , Any ]
83- ) -> ConfirmationTokenDict | None :
84- if "user" in filter_dict :
85- filter_dict ["user_id" ] = filter_dict .pop ("user" )["id" ]
86- async with self .pool .acquire () as conn :
87- confirmation = await _login_repository_legacy_sql .find_one (
88- conn , self .confirm_tbl , filter_dict
89- )
90- confirmation_token : ConfirmationTokenDict | None = (
91- ConfirmationTokenDict (** confirmation ) if confirmation else None # type: ignore[typeddict-item]
92- )
93- return confirmation_token
94-
95- async def delete_confirmation (self , confirmation : ConfirmationTokenDict ):
96- async with self .pool .acquire () as conn :
97- await _login_repository_legacy_sql .delete (
98- conn , self .confirm_tbl , {"code" : confirmation ["code" ]}
99- )
100-
101- #
102- # Transactions that guarantee atomicity. This avoids
103- # inconsistent states of confirmation and users rows
104- #
105-
106- async def delete_confirmation_and_user (
107- self , user_id : int , confirmation : ConfirmationTokenDict
108- ):
109- async with self .pool .acquire () as conn , conn .transaction ():
110- await _login_repository_legacy_sql .delete (
111- conn , self .confirm_tbl , {"code" : confirmation ["code" ]}
112- )
113- await _login_repository_legacy_sql .delete (
114- conn , self .user_tbl , {"id" : user_id }
115- )
116-
117- async def delete_confirmation_and_update_user (
118- self , user_id : int , updates : dict [str , Any ], confirmation : ConfirmationTokenDict
119- ):
120- async with self .pool .acquire () as conn , conn .transaction ():
121- await _login_repository_legacy_sql .delete (
122- conn , self .confirm_tbl , {"code" : confirmation ["code" ]}
123- )
124- await _login_repository_legacy_sql .update (
125- conn , self .user_tbl , {"id" : user_id }, updates
126- )
127-
128- # NOTE: This class is deprecated. Use ConfirmationRepository instead.
129- # Keeping for backwards compatibility during migration.
130-
131-
132- def get_plugin_storage (app : web .Application ) -> AsyncpgStorage :
133- storage = cast (AsyncpgStorage , app .get (APP_LOGIN_STORAGE_KEY ))
134- assert storage , "login plugin was not initialized" # nosec
135- return storage
0 commit comments