Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
e70b7c3
moving pre-registration
pcrespov Jul 8, 2025
6f79acd
splitting login logic
pcrespov Jul 8, 2025
690585a
rest exceptions
pcrespov Jul 8, 2025
46d7f5b
cleanup schemas
pcrespov Jul 8, 2025
1f8d3cd
updates models
pcrespov Jul 8, 2025
44355ca
fixes
pcrespov Jul 8, 2025
a44b5cd
rename
pcrespov Jul 8, 2025
d87c805
rename tests
pcrespov Jul 8, 2025
f4a9518
minor
pcrespov Jul 8, 2025
3cb66a8
utils
pcrespov Jul 8, 2025
2d727ed
refactor: clean up auth.py and move schemas to auth_schemas.py
pcrespov Jul 8, 2025
f33ce73
split confirmation schemas
pcrespov Jul 8, 2025
acee3e0
schema split
pcrespov Jul 8, 2025
51c493c
simplified
pcrespov Jul 8, 2025
f4abe7a
refactor
pcrespov Jul 8, 2025
dae2d9c
feat: implement captcha creation and account registration endpoints
pcrespov Jul 8, 2025
fdc0b7d
fixes pylint
pcrespov Jul 8, 2025
3163325
fixes import
pcrespov Jul 8, 2025
aca0e4c
importing user_services
pcrespov Jul 8, 2025
e914dd8
importing user_services
pcrespov Jul 8, 2025
74bcae8
tokens
pcrespov Jul 9, 2025
f476ac0
preferences
pcrespov Jul 9, 2025
c551036
rename
pcrespov Jul 9, 2025
007c535
rename
pcrespov Jul 9, 2025
3568d1d
mv user preferences
pcrespov Jul 9, 2025
913c3b6
thining user notifiactions
pcrespov Jul 9, 2025
b235b17
user rest
pcrespov Jul 9, 2025
4ce6eba
rest exception handlers
pcrespov Jul 9, 2025
5987720
user preferences rest
pcrespov Jul 9, 2025
25708f4
rest
pcrespov Jul 9, 2025
a7c277e
rest
pcrespov Jul 9, 2025
f1e172d
cleanup
pcrespov Jul 9, 2025
a6f8d34
models
pcrespov Jul 9, 2025
e0a747e
foler
pcrespov Jul 9, 2025
bae0667
fixing imports
pcrespov Jul 9, 2025
9162ad3
mypy fixes
pcrespov Jul 9, 2025
bdacf61
fix: correct function name from setup_login_account to setup_login_ac…
pcrespov Jul 9, 2025
ed70d7c
rest
pcrespov Jul 9, 2025
2a3ba75
rest
pcrespov Jul 9, 2025
a6b8d04
rename tests
pcrespov Jul 9, 2025
f7c94f3
uypdate repo
pcrespov Jul 9, 2025
d764bc7
rename
pcrespov Jul 9, 2025
faab1db
token upgraded
pcrespov Jul 9, 2025
cfb22d8
refactor: update user preferences repository methods to use async con…
pcrespov Jul 9, 2025
3d8a505
minor
pcrespov Jul 9, 2025
aa1498a
minor
pcrespov Jul 9, 2025
a256806
upgrades notifications
pcrespov Jul 9, 2025
67fe045
fixes
pcrespov Jul 9, 2025
c8ba806
fixes test
pcrespov Jul 9, 2025
834e5bb
Merge branch 'master' into is64/users-refactoring
pcrespov Jul 9, 2025
95ccd3b
rm prefix
pcrespov Jul 9, 2025
30e931f
cleanup
pcrespov Jul 9, 2025
a0e7634
sonar
pcrespov Jul 9, 2025
1e07b7e
sonar
pcrespov Jul 9, 2025
d434c4e
rename
pcrespov Jul 9, 2025
885ae2e
Merge branch 'master' into is64/users-refactoring
pcrespov Jul 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions api/specs/web-server/_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@
MyProfilePatch,
MyTokenCreate,
MyTokenGet,
TokenPathParams,
UserGet,
UsersSearch,
)
from models_library.api_schemas_webserver.users_preferences import PatchRequestBody
from models_library.generics import Envelope
from models_library.user_preferences import PreferenceIdentifier
from simcore_service_webserver._meta import API_VTAG
from simcore_service_webserver.users._notifications import (
from simcore_service_webserver.user_notifications._controller.rest.user_notification_rest import (
NotificationPathParams,
)
from simcore_service_webserver.user_notifications._models import (
UserNotification,
UserNotificationCreate,
UserNotificationPatch,
)
from simcore_service_webserver.users._notifications_rest import _NotificationPathParams
from simcore_service_webserver.users._tokens_rest import _TokenPathParams

router = APIRouter(prefix=f"/{API_VTAG}", tags=["users"])

Expand Down Expand Up @@ -76,15 +78,15 @@ async def create_token(_body: MyTokenCreate): ...
response_model=Envelope[MyTokenGet],
)
async def get_token(
_path: Annotated[_TokenPathParams, Depends()],
_path: Annotated[TokenPathParams, Depends()],
): ...


@router.delete(
"/me/tokens/{service}",
status_code=status.HTTP_204_NO_CONTENT,
)
async def delete_token(_path: Annotated[_TokenPathParams, Depends()]): ...
async def delete_token(_path: Annotated[TokenPathParams, Depends()]): ...


@router.get(
Expand All @@ -108,7 +110,7 @@ async def create_user_notification(
status_code=status.HTTP_204_NO_CONTENT,
)
async def mark_notification_as_read(
_path: Annotated[_NotificationPathParams, Depends()],
_path: Annotated[NotificationPathParams, Depends()],
_body: UserNotificationPatch,
): ...

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from models_library.rest_filters import Filters
from models_library.rest_pagination import PageQueryParameters
from pydantic import (
BaseModel,
ConfigDict,
EmailStr,
Field,
Expand Down Expand Up @@ -334,6 +335,10 @@ def _consistency_check(cls, v, info: ValidationInfo):
#


class TokenPathParams(BaseModel):
service: str


class MyTokenCreate(InputSchemaWithoutCamelCase):
service: Annotated[
IDStr,
Expand Down
5 changes: 3 additions & 2 deletions packages/service-library/src/servicelib/aiohttp/observer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from collections.abc import Callable

from aiohttp import web
from servicelib.aiohttp.application_setup import ensure_single_setup

from ..utils import logged_gather

Expand All @@ -17,10 +18,10 @@
_APP_OBSERVER_EVENTS_REGISTRY_KEY = "{__name__}.event_registry"


class ObserverRegistryNotFoundError(RuntimeError):
...
class ObserverRegistryNotFoundError(RuntimeError): ...


@ensure_single_setup(__name__, logger=log)
def setup_observer_registry(app: web.Application):
# only once
app.setdefault(_APP_OBSERVER_EVENTS_REGISTRY_KEY, defaultdict(list))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from models_library.users import UserID

# Import or define SocketMessageDict
from ..users.api import get_user_primary_group_id
from ..users import users_service
from . import _conversation_message_repository
from ._conversation_service import _get_recipients
from ._socketio import (
Expand All @@ -39,7 +39,7 @@ async def create_message(
content: str,
type_: ConversationMessageType,
) -> ConversationMessageGetDB:
_user_group_id = await get_user_primary_group_id(app, user_id=user_id)
_user_group_id = await users_service.get_user_primary_group_id(app, user_id=user_id)

created_message = await _conversation_message_repository.create(
app,
Expand Down Expand Up @@ -110,7 +110,7 @@ async def delete_message(
message_id=message_id,
)

_user_group_id = await get_user_primary_group_id(app, user_id=user_id)
_user_group_id = await users_service.get_user_primary_group_id(app, user_id=user_id)

await notify_conversation_message_deleted(
app,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
notify_conversation_updated,
)
from ..projects._groups_repository import list_project_groups
from ..users import users_service
from ..users._users_service import get_users_in_group
from ..users.api import get_user_primary_group_id
from . import _conversation_repository

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -52,7 +52,7 @@ async def create_conversation(
if project_uuid is None:
raise NotImplementedError

_user_group_id = await get_user_primary_group_id(app, user_id=user_id)
_user_group_id = await users_service.get_user_primary_group_id(app, user_id=user_id)

created_conversation = await _conversation_repository.create(
app,
Expand Down Expand Up @@ -121,7 +121,7 @@ async def delete_conversation(
conversation_id=conversation_id,
)

_user_group_id = await get_user_primary_group_id(app, user_id=user_id)
_user_group_id = await users_service.get_user_primary_group_id(app, user_id=user_id)

await notify_conversation_deleted(
app,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from ..products import products_service
from ..products.models import Product
from ..projects import projects_wallets_service
from ..users import preferences_api as user_preferences_service
from ..user_preferences import user_preferences_service
from ..users.exceptions import UserDefaultWalletNotFoundError
from ..wallets import api as wallets_service
from ._client import DirectorV2RestClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from ..projects._projects_service import create_user_notification_cb
from ..redis import get_redis_lock_manager_client_sdk
from ..security.decorators import permission_required
from ..users.api import get_user_fullname
from ..users import users_service
from ._formatter.archive import get_sds_archive_path
from .exceptions import SDSException
from .utils import CleanupFileResponse
Expand Down Expand Up @@ -52,7 +52,8 @@ async def export_project(request: web.Request):
project_uuid=project_uuid,
status=ProjectStatus.EXPORTING,
owner=Owner(
user_id=user_id, **await get_user_fullname(request.app, user_id=user_id)
user_id=user_id,
**await users_service.get_user_fullname(request.app, user_id=user_id),
),
notification_cb=create_user_notification_cb(
user_id, ProjectID(f"{project_uuid}"), request.app
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from pydantic import NonNegativeInt

from ..projects._projects_service import delete_project_by_user
from ..users.api import get_user
from ..users.users_service import get_user
from ..workspaces.api import check_user_workspace_access
from ..workspaces.errors import (
WorkspaceAccessForbiddenError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ..projects import _groups_repository as projects_groups_repository
from ..projects import _projects_repository as _projects_repository
from ..projects._access_rights_service import check_user_project_permission
from ..users.api import get_user
from ..users import users_service
from ..workspaces.api import check_user_workspace_access
from . import _folders_repository

Expand Down Expand Up @@ -122,7 +122,7 @@ async def move_folder_into_workspace(
)

# 9. Remove all project permissions, leave only the user who moved the project
user = await get_user(app, user_id=user_id)
user = await users_service.get_user(app, user_id=user_id)
for project_id in project_ids:
await projects_groups_repository.delete_all_project_groups(
app, connection=conn, project_id=project_id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@

from ..db.plugin import get_asyncpg_engine
from ..groups.api import list_all_user_groups_ids
from ..users.api import get_user_primary_group_id
from ..users import users_service

_FUNCTIONS_TABLE_COLS = get_columns_from_db_model(functions_table, RegisteredFunctionDB)
_FUNCTION_JOBS_TABLE_COLS = get_columns_from_db_model(
Expand Down Expand Up @@ -148,7 +148,9 @@ async def create_function( # noqa: PLR0913

registered_function = RegisteredFunctionDB.model_validate(row)

user_primary_group_id = await get_user_primary_group_id(app, user_id=user_id)
user_primary_group_id = await users_service.get_user_primary_group_id(
app, user_id=user_id
)
await set_group_permissions(
app,
connection=transaction,
Expand Down Expand Up @@ -206,7 +208,9 @@ async def create_function_job( # noqa: PLR0913

registered_function_job = RegisteredFunctionJobDB.model_validate(row)

user_primary_group_id = await get_user_primary_group_id(app, user_id=user_id)
user_primary_group_id = await users_service.get_user_primary_group_id(
app, user_id=user_id
)
await set_group_permissions(
app,
connection=transaction,
Expand Down Expand Up @@ -291,7 +295,9 @@ async def create_function_job_collection(
) # nosec
job_collection_entries.append(entry)

user_primary_group_id = await get_user_primary_group_id(app, user_id=user_id)
user_primary_group_id = await users_service.get_user_primary_group_id(
app, user_id=user_id
)
await set_group_permissions(
app,
connection=transaction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,7 @@
from ..projects.exceptions import ProjectDeleteError, ProjectNotFoundError
from ..redis import get_redis_lock_manager_client
from ..resource_manager.registry import RedisResourceRegistry
from ..users import exceptions
from ..users.api import (
delete_user_without_projects,
get_guest_user_ids_and_names,
get_user_primary_group_id,
get_user_role,
)
from ..users import exceptions, users_service
from ..users.exceptions import UserNotFoundError
from ._core_utils import get_new_project_owner_gid, replace_current_owner
from .settings import GUEST_USER_RC_LOCK_FORMAT
Expand All @@ -48,7 +42,7 @@ async def _delete_all_projects_for_user(app: web.Application, user_id: int) -> N
"""
# recover user's primary_gid
try:
project_owner_primary_gid = await get_user_primary_group_id(
project_owner_primary_gid = await users_service.get_user_primary_group_id(
app=app, user_id=user_id
)
except exceptions.UserNotFoundError:
Expand Down Expand Up @@ -149,7 +143,7 @@ async def remove_guest_user_with_all_its_resources(
"""Removes a GUEST user with all its associated projects and S3/MinIO files"""

try:
user_role: UserRole = await get_user_role(app, user_id=user_id)
user_role: UserRole = await users_service.get_user_role(app, user_id=user_id)
if user_role > UserRole.GUEST:
# NOTE: This acts as a protection barrier to avoid removing resources to more
# priviledge users
Expand All @@ -165,7 +159,7 @@ async def remove_guest_user_with_all_its_resources(
"Deleting user %s because it is a GUEST",
f"{user_id=}",
)
await delete_user_without_projects(app, user_id)
await users_service.delete_user_without_projects(app, user_id)

except (
DatabaseError,
Expand Down Expand Up @@ -205,8 +199,8 @@ async def remove_users_manually_marked_as_guests(
}

# Prevent creating this list if a guest user
guest_users: list[tuple[UserID, UserNameID]] = await get_guest_user_ids_and_names(
app
guest_users: list[tuple[UserID, UserNameID]] = (
await users_service.get_guest_user_ids_and_names(app)
)

for guest_user_id, guest_user_name in guest_users:
Expand Down Expand Up @@ -246,3 +240,8 @@ async def remove_users_manually_marked_as_guests(
app=app,
user_id=guest_user_id,
)

await remove_guest_user_with_all_its_resources(
app=app,
user_id=guest_user_id,
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
)
from ..projects.api import has_user_project_access_rights
from ..resource_manager.registry import RedisResourceRegistry
from ..users.api import get_user_role
from ..users import users_service
from ..users.exceptions import UserNotFoundError

_logger = logging.getLogger(__name__)
Expand All @@ -38,17 +38,21 @@ async def _remove_service(
save_service_state = False
else:
try:
if await get_user_role(app, user_id=service.user_id) <= UserRole.GUEST:
save_service_state = False
else:
save_service_state = await has_user_project_access_rights(
user_role: UserRole = await users_service.get_user_role(
app, user_id=service.user_id
)
except (UserNotFoundError, ValueError):
save_service_state = False
else:
save_service_state = (
user_role > UserRole.GUEST
and await has_user_project_access_rights(
app,
project_id=service.project_id,
user_id=service.user_id,
permission="write",
)
except (UserNotFoundError, ValueError):
save_service_state = False
)

with log_catch(_logger, reraise=False), log_context(
_logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
delete_project_group_without_checking_permissions,
)
from ..projects.exceptions import ProjectNotFoundError
from ..users.api import get_user, get_user_id_from_gid, get_users_in_group
from ..users import users_service
from ..users.exceptions import UserNotFoundError

_logger = logging.getLogger(__name__)
Expand All @@ -34,15 +34,17 @@ async def _fetch_new_project_owner_from_groups(
# go through user_to_groups table and fetch all uid for matching gid
for group_gid in standard_groups:
# remove the current owner from the bunch
target_group_users = await get_users_in_group(app=app, gid=int(group_gid)) - {
user_id
}
target_group_users = await users_service.get_users_in_group(
app=app, gid=int(group_gid)
) - {user_id}
_logger.info("Found group users '%s'", target_group_users)

for possible_user_id in target_group_users:
# check if the possible_user is still present in the db
try:
possible_user = await get_user(app=app, user_id=possible_user_id)
possible_user = await users_service.get_user(
app=app, user_id=possible_user_id
)
return int(possible_user["primary_gid"])
except UserNotFoundError: # noqa: PERF203
_logger.warning(
Expand Down Expand Up @@ -130,7 +132,7 @@ async def replace_current_owner(
project: dict,
) -> None:
try:
new_project_owner_id = await get_user_id_from_gid(
new_project_owner_id = await users_service.get_user_id_from_gid(
app=app, primary_gid=new_project_owner_gid
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
from aiohttp import web
from servicelib.background_task_utils import exclusive_periodic
from servicelib.logging_utils import log_context
from simcore_service_webserver.redis import get_redis_lock_manager_client_sdk

from ..api_keys import api_keys_service
from ..redis import get_redis_lock_manager_client_sdk
from ._tasks_utils import CleanupContextFunc, periodic_task_lifespan

_logger = logging.getLogger(__name__)
Expand Down
Loading
Loading