Skip to content

Commit 7bfb80e

Browse files
committed
refactor: rename AuthInfoDict to ActiveUserIdAndRole and update related methods
1 parent 22122e9 commit 7bfb80e

File tree

3 files changed

+36
-24
lines changed

3 files changed

+36
-24
lines changed

services/web/server/src/simcore_service_webserver/security/_authz_policy.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
has_access_by_role,
2626
)
2727
from ._authz_repository import (
28-
AuthInfoDict,
28+
ActiveUserIdAndRole,
2929
get_active_user_or_none,
3030
is_user_in_product_name,
3131
)
@@ -72,7 +72,9 @@ def __init__(self, app: web.Application, access_model: RoleBasedAccessModel):
7272
namespace=__name__,
7373
key_builder=lambda f, *ag, **kw: f"{f.__name__}/{kw['email']}",
7474
)
75-
async def _get_auth_or_none(self, *, email: str) -> AuthInfoDict | None:
75+
async def _get_authorized_user_or_none(
76+
self, *, email: str
77+
) -> ActiveUserIdAndRole | None:
7678
"""
7779
Raises:
7880
web.HTTPServiceUnavailable: if database raises an exception
@@ -105,7 +107,7 @@ def access_model(self) -> RoleBasedAccessModel:
105107

106108
async def clear_cache(self):
107109
# pylint: disable=no-member
108-
for fun in (self._get_auth_or_none, self._has_access_to_product):
110+
for fun in (self._get_authorized_user_or_none, self._has_access_to_product):
109111
autz_cache: BaseCache = fun.cache
110112
await autz_cache.clear()
111113

@@ -119,7 +121,9 @@ async def authorized_userid(self, identity: IdentityStr) -> int | None:
119121
Return the user_id of the user identified by the identity
120122
or "None" if no user exists related to the identity.
121123
"""
122-
user_info: AuthInfoDict | None = await self._get_auth_or_none(email=identity)
124+
user_info: ActiveUserIdAndRole | None = await self._get_authorized_user_or_none(
125+
email=identity
126+
)
123127
if user_info is None:
124128
return None
125129

@@ -142,25 +146,33 @@ async def permits(
142146
if identity is None or permission is None:
143147
return False
144148

145-
auth_info = await self._get_auth_or_none(email=identity)
146-
if auth_info is None:
149+
# authorized user info
150+
authorized_user_info = await self._get_authorized_user_or_none(email=identity)
151+
if authorized_user_info is None:
147152
return False
148153

149-
# make sure context is a copy to avoid side effects
150-
context = deepcopy(context) if context else AuthContextDict()
154+
user_id = authorized_user_info["id"]
155+
user_role = authorized_user_info["role"]
156+
157+
# context info: product_name
158+
context = context or AuthContextDict()
159+
product_name = context.get("product_name")
160+
161+
assert user_id == context.get( # nosec
162+
"authorized_uid"
163+
), f"{user_id}!={context.get('authorized_uid')}"
151164

152165
# product access
153166
if permission == PERMISSION_PRODUCT_LOGIN_KEY:
154-
product_name = context.get("product_name")
155167
ok: bool = product_name is not None and await self._has_access_to_product(
156-
user_id=auth_info["id"], product_name=product_name
168+
user_id=user_id, product_name=product_name
157169
)
158170
return ok
159171

160172
# role-based access
161173
return await has_access_by_role(
162174
self._access_model,
163-
role=auth_info["role"],
175+
role=user_role,
164176
operations=permission,
165177
context=context,
166178
)

services/web/server/src/simcore_service_webserver/security/_authz_repository.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
_logger = logging.getLogger(__name__)
1717

1818

19-
class AuthInfoDict(TypedDict, total=True):
19+
class ActiveUserIdAndRole(TypedDict, total=True):
2020
id: IdInt
2121
role: UserRole
2222

2323

2424
async def get_active_user_or_none(
2525
engine: AsyncEngine, *, email: str
26-
) -> AuthInfoDict | None:
26+
) -> ActiveUserIdAndRole | None:
2727
"""Gets a user with email if ACTIVE othewise return None
2828
2929
Raises:
@@ -46,7 +46,7 @@ async def get_active_user_or_none(
4646
row is None or TypeAdapter(UserRole).validate_python(row.role) is not None
4747
)
4848

49-
return AuthInfoDict(id=row.id, role=row.role) if row else None
49+
return ActiveUserIdAndRole(id=row.id, role=row.role) if row else None
5050

5151

5252
async def is_user_in_product_name(

services/web/server/tests/unit/isolated/test_security__authz.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
UserRole,
3030
)
3131
from simcore_service_webserver.security._authz_policy import AuthorizationPolicy
32-
from simcore_service_webserver.security._authz_repository import AuthInfoDict
32+
from simcore_service_webserver.security._authz_repository import ActiveUserIdAndRole
3333

3434

3535
@pytest.fixture
@@ -248,9 +248,9 @@ def mock_db(mocker: MockerFixture) -> MagicMock:
248248
return_value="FAKE-ENGINE",
249249
)
250250

251-
users_db: dict[str, AuthInfoDict] = {
252-
"[email protected]": AuthInfoDict(id=1, role=UserRole.GUEST),
253-
"[email protected]": AuthInfoDict(id=55, role=UserRole.GUEST),
251+
users_db: dict[str, ActiveUserIdAndRole] = {
252+
"[email protected]": ActiveUserIdAndRole(id=1, role=UserRole.GUEST),
253+
"[email protected]": ActiveUserIdAndRole(id=55, role=UserRole.GUEST),
254254
}
255255

256256
async def _fake_db(engine, email):
@@ -280,11 +280,11 @@ async def test_authorization_policy_cache(mocker: MockerFixture, mock_db: MagicM
280280
# cache under test
281281

282282
# pylint: disable=no-member
283-
autz_cache: BaseCache = authz_policy._get_auth_or_none.cache
283+
autz_cache: BaseCache = authz_policy._get_authorized_user_or_none.cache
284284

285285
assert not (await autz_cache.exists("_get_auth_or_none/[email protected]"))
286286
for _ in range(3):
287-
got = await authz_policy._get_auth_or_none(email="[email protected]")
287+
got = await authz_policy._get_authorized_user_or_none(email="[email protected]")
288288
assert mock_db.call_count == 1
289289
assert got["id"] == 1
290290

@@ -295,15 +295,15 @@ async def test_authorization_policy_cache(mocker: MockerFixture, mock_db: MagicM
295295
assert (await autz_cache.get("_get_auth_or_none/[email protected]"))["id"] == 1
296296

297297
# gets cache, db is NOT called
298-
got = await authz_policy._get_auth_or_none(email="[email protected]")
298+
got = await authz_policy._get_authorized_user_or_none(email="[email protected]")
299299
assert mock_db.call_count == 1
300300
assert got["id"] == 1
301301

302302
# clear cache
303303
await authz_policy.clear_cache()
304304

305305
# gets new value
306-
got = await authz_policy._get_auth_or_none(email="[email protected]")
306+
got = await authz_policy._get_authorized_user_or_none(email="[email protected]")
307307
assert mock_db.call_count == 2
308308
assert got["id"] == 2
309309

@@ -312,10 +312,10 @@ async def test_authorization_policy_cache(mocker: MockerFixture, mock_db: MagicM
312312

313313
for _ in range(4):
314314
# NOTE: None
315-
assert await authz_policy._get_auth_or_none(email="[email protected]")
315+
assert await authz_policy._get_authorized_user_or_none(email="[email protected]")
316316
assert await autz_cache.exists("_get_auth_or_none/[email protected]")
317317
assert mock_db.call_count == 3
318318

319319
# should raise web.HTTPServiceUnavailable on db failure
320320
with pytest.raises(web.HTTPServiceUnavailable):
321-
await authz_policy._get_auth_or_none(email="[email protected]")
321+
await authz_policy._get_authorized_user_or_none(email="[email protected]")

0 commit comments

Comments
 (0)