Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 9 additions & 36 deletions api/users/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
from ayon_server.api.responses import EmptyResponse
from ayon_server.auth.session import Session
from ayon_server.auth.utils import validate_password
from ayon_server.config import ayonconfig
from ayon_server.entities import UserEntity
from ayon_server.events import EventStream
from ayon_server.exceptions import (
BadRequestException,
ConflictException,
Expand Down Expand Up @@ -149,19 +147,7 @@ async def create_user(
raise BadRequestException("Only service users can have API keys")
nuser.set_api_key(put_data.api_key)

event: dict[str, Any] = {
"topic": "entity.user.created",
"description": f"User {user_name} created",
"summary": {"entityName": user.name},
}

await nuser.save()
await EventStream.dispatch(
sender=sender,
sender_type=sender_type,
user=user.name,
**event,
)
await nuser.save(user=user, sendor=sender, sender_type=sender_type)
return EmptyResponse()


Expand All @@ -176,24 +162,7 @@ async def delete_user(
raise ForbiddenException

target_user = await UserEntity.load(user_name)

event: dict[str, Any] = {
"description": f"User {user_name} deleted",
"summary": {"entityName": user_name},
}
if ayonconfig.audit_trail:
event["payload"] = {
"entityData": target_user.dict_simple(),
}

await target_user.delete()
await EventStream.dispatch(
"entity.user.deleted",
sender=sender,
sender_type=sender_type,
user=user.name,
**event,
)
await target_user.delete(user=user.name, sendor=sender, sender_type=sender_type)
return EmptyResponse()


Expand Down Expand Up @@ -286,6 +255,8 @@ async def change_password(
patch_data: ChangePasswordRequestModel,
user: CurrentUser,
user_name: UserName,
sender: Sender,
sender_type: SenderType,
) -> EmptyResponse:
patch_data_dict = patch_data.dict(exclude_unset=True)

Expand All @@ -301,7 +272,7 @@ async def change_password(
complexity_check=not user.is_admin,
)

await target_user.save()
await target_user.save(user=user.name, sender=sender, sender_type=sender_type)
return EmptyResponse()

elif "api_key" in patch_data_dict:
Expand All @@ -313,7 +284,7 @@ async def change_password(
raise BadRequestException(f"{user_name} is not a service account")
target_user.set_api_key(patch_data.api_key)

await target_user.save()
await target_user.save(user=user.name, sender=sender, sender_type=sender_type)
return EmptyResponse()

raise BadRequestException("No password or API key provided")
Expand Down Expand Up @@ -460,6 +431,8 @@ async def assign_access_groups(
patch_data: AssignAccessGroupsRequestModel,
user: CurrentUser,
user_name: UserName,
sender: Sender,
sender_type: SenderType,
) -> EmptyResponse:
if not user.is_manager:
raise ForbiddenException("You are not permitted to assign access groups")
Expand All @@ -477,7 +450,7 @@ async def assign_access_groups(
ag_set[project_name] = access_groups

target_user.data["accessGroups"] = ag_set
await target_user.save()
await target_user.save(user=user.name, sender=sender, sender_type=sender_type)
return EmptyResponse()


Expand Down
49 changes: 48 additions & 1 deletion ayon_server/entities/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from ayon_server.entities.core import TopLevelEntity, attribute_library
from ayon_server.entities.models import ModelSet
from ayon_server.entities.project import ProjectEntity
from ayon_server.events import EventStream
from ayon_server.exceptions import (
ConstraintViolationException,
ForbiddenException,
Expand Down Expand Up @@ -62,6 +63,7 @@ class UserEntity(TopLevelEntity):
path_access_cache: dict[str, dict[AccessType, list[str]]] | None = None
save_hooks: list[Callable[["UserEntity"], Awaitable[None]]] = []
_teams: set[str] | None = None
_events: list[dict[str, Any]]

#
# Load
Expand All @@ -77,6 +79,7 @@ def __init__(
self.was_active = self.active and self.exists
self.was_service = self.is_service and self.exists
self.original_email = self.attrib.email
self._events = []

@classmethod
async def load(
Expand Down Expand Up @@ -142,7 +145,6 @@ async def save(
self.data.pop("userPool", None)

if self.attrib.email and (self.attrib.email != self.original_email):
logger.info(f"Email changed for user {self.name}")
# Email changed, we need to check if it's unique
# We cannot use DB index here.
res = await Postgres.fetch(
Expand Down Expand Up @@ -187,6 +189,15 @@ async def save(
**data,
)
)

for event in self._events:
await EventStream.dispatch(
user=kwargs.get("user", None),
sender=kwargs.get("sender", None),
sender_type=kwargs.get("sender_type", None),
**event,
)

else:
await Postgres.execute(
*SQLTool.insert(
Expand All @@ -197,6 +208,16 @@ async def save(
await Redis.delete("user.avatar", self.name)
self.exists = True

await EventStream.dispatch(
topic="entity.user.created",
payload={"entityData": self.dict_simple()},
summary={"entityName": self.name},
description=f"User {self.name} created",
user=kwargs.get("user", None),
sender=kwargs.get("sender", None),
sender_type=kwargs.get("sender_type", None),
)

if run_hooks:
for hook in self.save_hooks:
await hook(self)
Expand Down Expand Up @@ -240,6 +261,16 @@ async def delete(self, *args, **kwargs) -> bool:
except Postgres.UndefinedTableError:
continue

await EventStream.dispatch(
topic="entity.user.deleted",
payload={"entityData": self.dict_simple()},
summary={"entityName": self.name},
description=f"User {self.name} deleted",
user=kwargs.get("user", None),
sender=kwargs.get("sender", None),
sender_type=kwargs.get("sender_type", None),
)

return res[0]["count"]

#
Expand Down Expand Up @@ -379,6 +410,14 @@ def set_password(
raise LowPasswordComplexityException
hashed_password = create_password(password)
self.data["password"] = hashed_password
self._events.append(
{
"topic": "entity.user.password_changed",
"payload": {},
"summary": {"entityName": self.name},
"description": f"Password changed for user {self.name}",
}
)

def set_api_key(self, api_key: str | None) -> None:
"""Set user api key."""
Expand All @@ -393,6 +432,14 @@ def set_api_key(self, api_key: str | None) -> None:

self.data["apiKey"] = hash_password(api_key)
self.data["apiKeyPreview"] = api_key_preview
self._events.append(
{
"topic": "entity.user.api_key_changed",
"payload": {},
"summary": {"entityName": self.name},
"description": f"API key changed for user {self.name}",
}
)

async def send_mail(
self,
Expand Down