diff --git a/selfprivacy_api/__init__.py b/selfprivacy_api/__init__.py index e69de29..fed8551 100644 --- a/selfprivacy_api/__init__.py +++ b/selfprivacy_api/__init__.py @@ -0,0 +1 @@ +PLEASE_UPDATE_APP_TEXT = "Your SelfPrivacy app is out of date, please update. Some important functions are not working at the moment." diff --git a/selfprivacy_api/actions/users.py b/selfprivacy_api/actions/users.py index b15f576..e72337e 100644 --- a/selfprivacy_api/actions/users.py +++ b/selfprivacy_api/actions/users.py @@ -121,3 +121,10 @@ def get_user_by_username(username: str) -> Optional[UserDataUser]: pass return user + + +def generate_password_reset_link(username: str) -> str: + if ACTIVE_USERS_PROVIDER == JsonUserRepository: + return "Error: API using old user manager provider!" + + return ACTIVE_USERS_PROVIDER.generate_password_reset_link(username=username) diff --git a/selfprivacy_api/graphql/common_types/user.py b/selfprivacy_api/graphql/common_types/user.py index 544d091..091866c 100644 --- a/selfprivacy_api/graphql/common_types/user.py +++ b/selfprivacy_api/graphql/common_types/user.py @@ -23,12 +23,13 @@ class UserType(Enum): @strawberry.type class User: username: str - user_type: UserType + + ssh_keys: Optional[list[str]] = strawberry.field(default_factory=list) + user_type: Optional[UserType] = None displayname: Optional[str] = None - ssh_keys: list[str] = strawberry.field(default_factory=list) email: Optional[str] = None - directmemberof: Optional[list[str]] = strawberry.field(default_factory=list) - memberof: Optional[list[str]] = strawberry.field(default_factory=list) + directmemberof: Optional[list[str]] = None + memberof: Optional[list[str]] = None # userHomeFolderspace: UserHomeFolderUsage @@ -37,6 +38,7 @@ class UserMutationReturn(MutationReturnInterface): """Return type for user mutation""" user: Optional[User] = None + password_reset_link: Optional[str] = None def get_user_by_username(username: str) -> Optional[User]: @@ -45,13 +47,13 @@ def get_user_by_username(username: str) -> Optional[User]: return None return User( - user_type=UserType(user.origin.value), username=user.username, - ssh_keys=user.ssh_keys, - displayname=(user.displayname if user.displayname else user.username), - email=user.email, - directmemberof=user.directmemberof, - memberof=user.memberof, + ssh_keys=user.ssh_keys or [], + user_type=user.user_type or None, + displayname=user.displayname or None, + email=user.email or None, + directmemberof=user.directmemberof or None, + memberof=user.memberof or None, ) @@ -60,13 +62,13 @@ def get_users() -> list[User]: users = actions_get_users(exclude_root=True) return [ User( - user_type=UserType(user.origin.value), username=user.username, - ssh_keys=user.ssh_keys, - displayname=(user.displayname if user.displayname else user.username), - email=user.email, - directmemberof=user.directmemberof, - memberof=user.memberof, + ssh_keys=user.ssh_keys or [], + user_type=user.user_type or None, + displayname=user.displayname or None, + email=user.email or None, + directmemberof=user.directmemberof or None, + memberof=user.memberof or None, ) for user in users ] diff --git a/selfprivacy_api/graphql/mutations/users_mutations.py b/selfprivacy_api/graphql/mutations/users_mutations.py index 340dcfb..7c17d4b 100644 --- a/selfprivacy_api/graphql/mutations/users_mutations.py +++ b/selfprivacy_api/graphql/mutations/users_mutations.py @@ -23,6 +23,7 @@ from selfprivacy_api.actions.users import ( create_user as create_user_action, delete_user as delete_user_action, update_user as update_user_action, + generate_password_reset_link as generate_password_reset_link_action, ) from selfprivacy_api.repositories.users.exceptions import ( PasswordIsEmpty, @@ -33,7 +34,9 @@ from selfprivacy_api.repositories.users.exceptions import ( UsernameNotAlphanumeric, UsernameTooLong, UserNotFound, + SelfPrivacyAppIsOutdate, ) +from selfprivacy_api import PLEASE_UPDATE_APP_TEXT @strawberry.input @@ -44,8 +47,8 @@ class UserMutationInput: password: Optional[str] = None displayname: Optional[str] = None email: Optional[str] = None - directmemberof: Optional[list[str]] = strawberry.field(default_factory=list) - memberof: Optional[list[str]] = strawberry.field(default_factory=list) + directmemberof: Optional[list[str]] = None + memberof: Optional[list[str]] = None @strawberry.input @@ -111,7 +114,7 @@ class UsersMutations: return UserMutationReturn( success=True, - message="User created", + message=PLEASE_UPDATE_APP_TEXT if user.password else "User created", code=201, user=get_user_by_username(user.username), ) @@ -163,6 +166,12 @@ class UsersMutations: message=str(e), code=404, ) + except SelfPrivacyAppIsOutdate: + return UserMutationReturn( + success=False, + message="Error: Failed to change password.", PLEASE_UPDATE_APP_TEXT, + code=400, + ) return UserMutationReturn( success=True, @@ -195,7 +204,7 @@ class UsersMutations: message="User not found", code=404, ) - except Exception as e: + except Exception as e: # TODO why? return UserMutationReturn( success=False, message=str(e), @@ -227,7 +236,7 @@ class UsersMutations: message="User not found", code=404, ) - except Exception as e: + except Exception as e: # TODO why? return UserMutationReturn( success=False, message=str(e), @@ -240,3 +249,21 @@ class UsersMutations: code=200, user=get_user_by_username(ssh_input.username), ) + + @strawberry.mutation(permission_classes=[IsAuthenticated]) + def generate_password_reset_link(username: str) -> UserMutationReturn: + try: + password_reset_link = generate_password_reset_link_action(username=username) + except UserNotFound: + return UserMutationReturn( + success=False, + message="User not found", + code=404, + ) + + return UserMutationReturn( + success=True, + message="Link successfully created", + code=200, + password_reset_link=password_reset_link, + ) diff --git a/selfprivacy_api/models/user.py b/selfprivacy_api/models/user.py index d9b55cc..e4e8de9 100644 --- a/selfprivacy_api/models/user.py +++ b/selfprivacy_api/models/user.py @@ -15,12 +15,12 @@ class UserDataUser(BaseModel): """The user model from the userdata file""" username: str - origin: UserDataUserOrigin + ssh_keys: Optional[list[str]] + user_type: Optional[UserDataUserOrigin] displayname: Optional[ str ] # in logic graphql will return "username" if "displayname" None email: Optional[str] - ssh_keys: Optional[list[str]] directmemberof: Optional[list[str]] memberof: Optional[list[str]] diff --git a/selfprivacy_api/repositories/users/exceptions.py b/selfprivacy_api/repositories/users/exceptions.py index 9d176e1..e8c2fb7 100644 --- a/selfprivacy_api/repositories/users/exceptions.py +++ b/selfprivacy_api/repositories/users/exceptions.py @@ -28,3 +28,7 @@ class PasswordIsEmpty(Exception): class InvalidConfiguration(Exception): """The userdata is broken""" + + +class SelfPrivacyAppIsOutdate(Exception): + """SelfPrivacy app is out of date, please update. Some important functions are not working at the moment.""" diff --git a/selfprivacy_api/repositories/users/kanidm_user_repository.py b/selfprivacy_api/repositories/users/kanidm_user_repository.py index 43c4aee..cafe1b3 100644 --- a/selfprivacy_api/repositories/users/kanidm_user_repository.py +++ b/selfprivacy_api/repositories/users/kanidm_user_repository.py @@ -6,12 +6,15 @@ import re import logging import json +from selfprivacy_api.repositories.users.exceptions import SelfPrivacyAppIsOutdate from selfprivacy_api.utils import get_domain, temporary_env_var from selfprivacy_api.utils.redis_pool import RedisPool from selfprivacy_api.models.user import UserDataUser, UserDataUserOrigin from selfprivacy_api.repositories.users.abstract_user_repository import ( AbstractUserRepository, ) +from selfprivacy_api import PLEASE_UPDATE_APP_TEXT + KANIDM_URL = "https://127.0.0.1:3013" @@ -143,7 +146,7 @@ class KanidmUserRepository(AbstractUserRepository): """ if password: - pass # TODO make notif + logger.error(PLEASE_UPDATE_APP_TEXT) data = { "attrs": { @@ -175,27 +178,28 @@ class KanidmUserRepository(AbstractUserRepository): The root user will never return. """ users_data = KanidmUserRepository._send_query(endpoint="person", method="GET") + users = [] for user in users_data: - attrs = user.get("attrs", {}) + user_attrs = user.get("attrs", {}) - origin = KanidmUserRepository._check_user_origin_by_memberof( - memberof=attrs.get("memberof", []) + user_type = KanidmUserRepository._check_user_origin_by_memberof( + memberof=user_attrs.get("memberof", []) ) - if exclude_primary and origin == UserDataUserOrigin.PRIMARY: + if exclude_primary and user_type == UserDataUserOrigin.PRIMARY: continue - user_type = UserDataUser( - username=attrs.get("name", [None])[0], - displayname=attrs.get("displayname", [None])[0], - email=attrs.get("mail", [None])[0], + filled_user = UserDataUser( + username=user_attrs.get("name", [None])[0], + displayname=user_attrs.get("displayname", [None])[0], + email=user_attrs.get("mail", [None])[0], ssh_keys=[], # actions layer will full in this field - origin=origin, - directmemberof=attrs.get("directmemberof", []), - memberof=attrs.get("memberof", []), + user_type=user_type, + directmemberof=user_attrs.get("directmemberof", []), + memberof=user_attrs.get("memberof", []), ) - users.append(user_type) + users.append(filled_user) return users @staticmethod @@ -220,7 +224,7 @@ class KanidmUserRepository(AbstractUserRepository): use generate_password_reset_link() instead. """ if password: - pass # TODO make notif + raise SelfPrivacyAppIsOutdate data = { "attrs": { @@ -259,7 +263,7 @@ class KanidmUserRepository(AbstractUserRepository): displayname=attrs.get("displayname", [None])[0], email=attrs.get("mail", [None])[0], ssh_keys=[], # actions layer will full in this field - origin=KanidmUserRepository._check_user_origin_by_memberof( + user_type=KanidmUserRepository._check_user_origin_by_memberof( memberof=attrs.get("memberof", []) ), directmemberof=attrs.get("directmemberof", []), diff --git a/tests/test_graphql/test_ssh.py b/tests/test_graphql/test_ssh.py index 945f105..15e2166 100644 --- a/tests/test_graphql/test_ssh.py +++ b/tests/test_graphql/test_ssh.py @@ -89,7 +89,7 @@ def no_admin_key(generic_userdata, authorized_client): def admin_name() -> Optional[str]: users = get_users() for user in users: - if user.origin == UserDataUserOrigin.PRIMARY: + if user.user_type == UserDataUserOrigin.PRIMARY: return user.username return None diff --git a/tests/test_ssh.py b/tests/test_ssh.py index 2d0f70d..e4d59d0 100644 --- a/tests/test_ssh.py +++ b/tests/test_ssh.py @@ -73,7 +73,7 @@ def password_auth_spectrum(request): def admin_name() -> Optional[str]: users = get_users() for user in users: - if user.origin == UserDataUserOrigin.PRIMARY: + if user.user_type == UserDataUserOrigin.PRIMARY: return user.username return None