Skip to content

Commit e2f609b

Browse files
committed
separete web from service interfaces
1 parent 6963644 commit e2f609b

File tree

16 files changed

+109
-85
lines changed

16 files changed

+109
-85
lines changed

packages/pytest-simcore/src/pytest_simcore/helpers/webserver_login.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
get_plugin_storage,
1818
)
1919
from simcore_service_webserver.products.products_service import list_products
20-
from simcore_service_webserver.security import security_web
20+
from simcore_service_webserver.security import security_service
2121
from yarl import URL
2222

2323
from .assert_checks import assert_status
@@ -187,7 +187,7 @@ async def __aexit__(self, *args):
187187
assert self.client.app
188188
# NOTE: cache key is based on an email. If the email is
189189
# reused during the test, then it creates quite some noise
190-
await security_web.clean_auth_policy_cache(self.client.app)
190+
await security_service.clean_auth_policy_cache(self.client.app)
191191
return await super().__aexit__(*args)
192192

193193

services/web/server/src/simcore_service_webserver/garbage_collector/_tasks_users.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from tenacity.wait import wait_exponential
1616

1717
from ..login import login_service
18-
from ..security import security_web
18+
from ..security import security_service
1919
from ..users.api import update_expired_users
2020

2121
_logger = logging.getLogger(__name__)
@@ -62,7 +62,7 @@ async def _update_expired_users(app: web.Application):
6262
if updated := await update_expired_users(app):
6363
# expired users might be cached in the auth. If so, any request
6464
# with this user-id will get thru producing unexpected side-effects
65-
await security_web.clean_auth_policy_cache(app)
65+
await security_service.clean_auth_policy_cache(app)
6666

6767
# broadcast force logout of user_id
6868
for user_id in updated:

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
from simcore_postgres_database.utils_repos import transaction_context
88
from simcore_postgres_database.utils_users import UsersRepo
99
from simcore_service_webserver.db.plugin import get_asyncpg_engine
10+
from simcore_service_webserver.security import security_service
1011

1112
from ..groups import api as groups_service
1213
from ..products.models import Product
13-
from ..security import security_web
1414
from . import _login_service
1515
from ._constants import MSG_UNKNOWN_EMAIL, MSG_WRONG_PASSWORD
1616
from ._login_repository_legacy import AsyncpgStorage, get_plugin_storage
@@ -35,7 +35,7 @@ async def create_user(
3535
user = await UsersRepo.new_user(
3636
conn,
3737
email=email,
38-
password_hash=security_web.encrypt_password(password),
38+
password_hash=security_service.encrypt_password(password),
3939
status=status_upon_creation,
4040
expires_at=expires_at,
4141
)
@@ -58,7 +58,7 @@ async def check_authorized_user_credentials_or_raise(
5858

5959
_login_service.validate_user_status(user=user, support_email=product.support_email)
6060

61-
if not security_web.check_password(password, user["password_hash"]):
61+
if not security_service.check_password(password, user["password_hash"]):
6262
raise web.HTTPUnauthorized(
6363
reason=MSG_WRONG_PASSWORD, content_type=MIMETYPE_APPLICATION_JSON
6464
)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from ...._meta import API_VTAG
1616
from ....products import products_web
1717
from ....products.models import Product
18-
from ....security import security_web
18+
from ....security import security_service
1919
from ....users import api as users_service
2020
from ....utils import HOUR
2121
from ....utils_rate_limiting import global_rate_limit_route
@@ -293,7 +293,7 @@ async def change_password(request: web.Request):
293293
user = await db.get_user({"id": request[RQT_USERID_KEY]})
294294
assert user # nosec
295295

296-
if not security_web.check_password(
296+
if not security_service.check_password(
297297
passwords.current.get_secret_value(), user["password_hash"]
298298
):
299299
raise web.HTTPUnprocessableEntity(
@@ -303,7 +303,7 @@ async def change_password(request: web.Request):
303303
await db.update_user(
304304
dict(user),
305305
{
306-
"password_hash": security_web.encrypt_password(
306+
"password_hash": security_service.encrypt_password(
307307
passwords.new.get_secret_value()
308308
)
309309
},

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@
2424
from servicelib.logging_errors import create_troubleshotting_log_kwargs
2525
from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON
2626
from simcore_postgres_database.aiopg_errors import UniqueViolation
27+
from simcore_service_webserver.security import security_service
2728
from yarl import URL
2829

2930
from ....products import products_web
3031
from ....products.models import Product
31-
from ....security import security_web
3232
from ....session.access_policies import session_access_required
3333
from ....utils import HOUR, MINUTE
3434
from ....utils_aiohttp import create_redirect_to_page_response
@@ -302,7 +302,7 @@ async def complete_reset_password(request: web.Request):
302302
await db.update_user(
303303
user={"id": user["id"]},
304304
updates={
305-
"password_hash": security_web.encrypt_password(
305+
"password_hash": security_service.encrypt_password(
306306
request_body.password.get_secret_value()
307307
)
308308
},

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from ....constants import RQ_PRODUCT_KEY
2121
from ....products import products_web
2222
from ....products.models import Product
23-
from ....security import security_web
23+
from ....security import security_service, security_web
2424
from ....security.decorators import permission_required
2525
from ....session.api import get_session
2626
from ....users.api import get_user_credentials, set_user_as_deleted
@@ -109,7 +109,7 @@ async def unregister_account(request: web.Request):
109109

110110
# checks before deleting
111111
credentials = await get_user_credentials(request.app, user_id=req_ctx.user_id)
112-
if body.email != credentials.email.lower() or not security_web.check_password(
112+
if body.email != credentials.email.lower() or not security_service.check_password(
113113
body.password.get_secret_value(), credentials.password_hash
114114
):
115115
raise web.HTTPConflict(

services/web/server/src/simcore_service_webserver/projects/_security_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import jsondiff # type: ignore[import-untyped]
22
from aiohttp import web
33
from simcore_postgres_database.models.users import UserRole
4+
from simcore_service_webserver.security import security_service
45

5-
from ..security import security_web
66
from ._projects_repository_legacy import ProjectDBAPI
77
from .api import check_user_project_permission
88

@@ -52,7 +52,7 @@ def setup_projects_access(app: web.Application):
5252
"""
5353
security - access : Inject permissions to rest API resources
5454
"""
55-
hrba = security_web.get_access_model(app)
55+
hrba = security_service.get_access_model(app)
5656

5757
hrba.roles[UserRole.GUEST].check[
5858
"project.workbench.node.inputs.update"

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

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
import aiohttp_security.api # type: ignore[import-untyped]
55
import passlib.hash
66
from aiohttp import web
7-
from models_library.users import UserID
87

9-
from ._authz_access_model import AuthContextDict, OptionalContext, RoleBasedAccessModel
8+
from ._authz_access_model import RoleBasedAccessModel
109
from ._authz_policy import AuthorizationPolicy
1110
from ._constants import PERMISSION_PRODUCT_LOGIN_KEY
1211

@@ -23,51 +22,6 @@ async def clean_auth_policy_cache(app: web.Application) -> None:
2322
await autz_policy.clear_cache()
2423

2524

26-
async def is_anonymous(request: web.Request) -> bool:
27-
"""
28-
User is considered anonymous if there is not verified identity in request.
29-
"""
30-
is_user_id_none: bool = await aiohttp_security.api.is_anonymous(request)
31-
return is_user_id_none
32-
33-
34-
async def check_user_authorized(request: web.Request) -> UserID:
35-
"""
36-
Raises:
37-
web.HTTPUnauthorized: for anonymous user (i.e. user_id is None)
38-
39-
"""
40-
# NOTE: Same as aiohttp_security.api.check_authorized
41-
user_id: UserID | None = await aiohttp_security.api.authorized_userid(request)
42-
if user_id is None:
43-
raise web.HTTPUnauthorized
44-
return user_id
45-
46-
47-
async def check_user_permission(
48-
request: web.Request, permission: str, *, context: OptionalContext = None
49-
) -> None:
50-
"""Checker that passes only to authoraised users with given permission.
51-
52-
Raises:
53-
web.HTTPUnauthorized: If user is not authorized
54-
web.HTTPForbidden: If user is authorized and does not have permission
55-
"""
56-
# NOTE: Same as aiohttp_security.api.check_permission
57-
context = context or AuthContextDict()
58-
if not context.get("authorized_uid"):
59-
context["authorized_uid"] = await check_user_authorized(request)
60-
61-
allowed = await aiohttp_security.api.permits(request, permission, context)
62-
if not allowed:
63-
msg = "You do not have sufficient access rights for"
64-
if permission == PERMISSION_PRODUCT_LOGIN_KEY:
65-
msg += f" {context.get('product_name')}"
66-
else:
67-
msg += f" {permission}"
68-
raise web.HTTPForbidden(reason=msg)
69-
70-
7125
#
7226
# utils (i.e. independent from setup)
7327
#
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# mypy: disable-error-code=truthy-function
2+
3+
4+
import aiohttp_security.api # type: ignore[import-untyped]
5+
from aiohttp import web
6+
from models_library.users import UserID
7+
8+
from ._authz_access_model import AuthContextDict, OptionalContext
9+
from ._constants import PERMISSION_PRODUCT_LOGIN_KEY
10+
11+
assert PERMISSION_PRODUCT_LOGIN_KEY # nosec
12+
13+
14+
async def is_anonymous(request: web.Request) -> bool:
15+
"""
16+
User is considered anonymous if there is not verified identity in request.
17+
"""
18+
is_user_id_none: bool = await aiohttp_security.api.is_anonymous(request)
19+
return is_user_id_none
20+
21+
22+
async def check_user_authorized(request: web.Request) -> UserID:
23+
"""
24+
Raises:
25+
web.HTTPUnauthorized: for anonymous user (i.e. user_id is None)
26+
27+
"""
28+
# NOTE: Same as aiohttp_security.api.check_authorized
29+
user_id: UserID | None = await aiohttp_security.api.authorized_userid(request)
30+
if user_id is None:
31+
raise web.HTTPUnauthorized
32+
return user_id
33+
34+
35+
async def check_user_permission(
36+
request: web.Request, permission: str, *, context: OptionalContext = None
37+
) -> None:
38+
"""Checker that passes only to authoraised users with given permission.
39+
40+
Raises:
41+
web.HTTPUnauthorized: If user is not authorized
42+
web.HTTPForbidden: If user is authorized and does not have permission
43+
"""
44+
# NOTE: Same as aiohttp_security.api.check_permission
45+
context = context or AuthContextDict()
46+
if not context.get("authorized_uid"):
47+
context["authorized_uid"] = await check_user_authorized(request)
48+
49+
allowed = await aiohttp_security.api.permits(request, permission, context)
50+
if not allowed:
51+
msg = "You do not have sufficient access rights for"
52+
if permission == PERMISSION_PRODUCT_LOGIN_KEY:
53+
msg += f" {context.get('product_name')}"
54+
else:
55+
msg += f" {permission}"
56+
raise web.HTTPForbidden(reason=msg)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Service-layer interface
2+
3+
NOTE: Must not include functions that depend on aiohttp.web.Request (use *_web.py instead)
4+
"""
5+
6+
from ._authz_service import (
7+
check_password,
8+
clean_auth_policy_cache,
9+
encrypt_password,
10+
get_access_model,
11+
)
12+
13+
__all__: tuple[str, ...] = (
14+
"check_password",
15+
"clean_auth_policy_cache",
16+
"encrypt_password",
17+
"get_access_model",
18+
)
19+
20+
# nopycln: file

0 commit comments

Comments
 (0)