diff --git a/api/python/quilt3-admin/queries.graphql b/api/python/quilt3-admin/queries.graphql index beb9d608b9d..7ce6ff1af6d 100644 --- a/api/python/quilt3-admin/queries.graphql +++ b/api/python/quilt3-admin/queries.graphql @@ -29,6 +29,11 @@ fragment UserSelection on User { ...RoleSelection } } +fragment UserMutationSelection on UserResult { + ...UserSelection + ...InvalidInputSelection + ...OperationErrorSelection +} fragment InvalidInputSelection on InvalidInput { errors { path @@ -43,6 +48,12 @@ fragment OperationErrorSelection on OperationError { context } +query getRoles { + roles { + ...RoleSelection + } +} + query getUser($name: String!) { admin { user { @@ -67,9 +78,7 @@ mutation createUser($input: UserInput!) { admin { user { create(input: $input) { - ...UserSelection - ...InvalidInputSelection - ...OperationErrorSelection + ...UserMutationSelection } } } @@ -88,9 +97,52 @@ mutation deleteUser($name: String!) { } } -query getRoles { - roles { - ...RoleSelection +mutation setUserEmail($email: String!, $name: String!) { + admin { + user { + mutate(name: $name) { + setEmail(email: $email) { + ...UserMutationSelection + } + } + } + } +} + +mutation setUserAdmin($name: String!, $admin: Boolean!) { + admin { + user { + mutate(name: $name) { + setAdmin(admin: $admin) { + ...UserMutationSelection + } + } + } + } +} + +mutation setUserActive($active: Boolean!, $name: String!) { + admin { + user { + mutate(name: $name) { + setActive(active: $active) { + ...UserMutationSelection + } + } + } + } +} + +mutation resetUserPassword($name: String!) { + admin { + user { + mutate(name: $name) { + resetPassword { + ...InvalidInputSelection + ...OperationErrorSelection + } + } + } } } diff --git a/api/python/quilt3/admin/__init__.py b/api/python/quilt3/admin/__init__.py index da71fe8a49a..011475c9bea 100644 --- a/api/python/quilt3/admin/__init__.py +++ b/api/python/quilt3/admin/__init__.py @@ -57,16 +57,27 @@ def __init__(self): super().__init__(None) -def _handle_errors(result: _graphql_client.BaseModel) -> Any: +def _handle_errors(result: _graphql_client.BaseModel) -> _graphql_client.BaseModel: if isinstance(result, (_graphql_client.InvalidInputSelection, _graphql_client.OperationErrorSelection)): raise Quilt3AdminError(result) return result +def _handle_user_mutation(result: _graphql_client.BaseModel) -> User: + return User(**_handle_errors(result).model_dump()) + + def _get_client(): return _graphql_client.Client() +def get_roles() -> List[Role]: + """ + Get a list of all roles in the registry. + """ + return [role_adapter.validate_python(r.model_dump()) for r in _get_client().get_roles()] + + def get_user(name: str) -> Optional[User]: """ Get a specific user from the registry. Return `None` if the user does not exist. @@ -118,11 +129,59 @@ def delete_user(name: str) -> None: _handle_errors(result.delete) -def get_roles() -> List[Role]: +def set_user_email(name: str, email: str) -> User: """ - Get a list of all roles in the registry. + Set the email for a user. + + Args: + name: Username of user to update. + email: Email to set for the user. """ - return [role_adapter.validate_python(r.model_dump()) for r in _get_client().get_roles()] + result = _get_client().set_user_email(name=name, email=email) + if result is None: + raise UserNotFoundError + return _handle_user_mutation(result.set_email) + + +def set_user_admin(name: str, admin: bool) -> User: + """ + Set the admin status for a user. + + Args: + name: Username of user to update. + admin: Admin status to set for the user. + """ + result = _get_client().set_user_admin(name=name, admin=admin) + if result is None: + raise UserNotFoundError + return _handle_user_mutation(result.set_admin) + + +def set_user_active(name: str, active: bool) -> User: + """ + Set the active status for a user. + + Args: + name: Username of user to update. + active: Active status to set for the user. + """ + result = _get_client().set_user_active(name=name, active=active) + if result is None: + raise UserNotFoundError + return _handle_user_mutation(result.set_active) + + +def reset_user_password(name: str) -> None: + """ + Reset the password for a user. + + Args: + name: Username of user to update. + """ + result = _get_client().reset_user_password(name=name) + if result is None: + raise UserNotFoundError + _handle_errors(result.reset_password) def set_role( diff --git a/api/python/quilt3/admin/_graphql_client/__init__.py b/api/python/quilt3/admin/_graphql_client/__init__.py index 3fce151f497..15a154c05ab 100644 --- a/api/python/quilt3/admin/_graphql_client/__init__.py +++ b/api/python/quilt3/admin/_graphql_client/__init__.py @@ -54,6 +54,15 @@ RemoveRolesAdminUserMutateRemoveRolesOperationError, RemoveRolesAdminUserMutateRemoveRolesUser, ) +from .reset_user_password import ( + ResetUserPassword, + ResetUserPasswordAdmin, + ResetUserPasswordAdminUser, + ResetUserPasswordAdminUserMutate, + ResetUserPasswordAdminUserMutateResetPasswordInvalidInput, + ResetUserPasswordAdminUserMutateResetPasswordOk, + ResetUserPasswordAdminUserMutateResetPasswordOperationError, +) from .set_role import ( SetRole, SetRoleAdmin, @@ -63,6 +72,33 @@ SetRoleAdminUserMutateSetRoleOperationError, SetRoleAdminUserMutateSetRoleUser, ) +from .set_user_active import ( + SetUserActive, + SetUserActiveAdmin, + SetUserActiveAdminUser, + SetUserActiveAdminUserMutate, + SetUserActiveAdminUserMutateSetActiveInvalidInput, + SetUserActiveAdminUserMutateSetActiveOperationError, + SetUserActiveAdminUserMutateSetActiveUser, +) +from .set_user_admin import ( + SetUserAdmin, + SetUserAdminAdmin, + SetUserAdminAdminUser, + SetUserAdminAdminUserMutate, + SetUserAdminAdminUserMutateSetAdminInvalidInput, + SetUserAdminAdminUserMutateSetAdminOperationError, + SetUserAdminAdminUserMutateSetAdminUser, +) +from .set_user_email import ( + SetUserEmail, + SetUserEmailAdmin, + SetUserEmailAdminUser, + SetUserEmailAdminUserMutate, + SetUserEmailAdminUserMutateSetEmailInvalidInput, + SetUserEmailAdminUserMutateSetEmailOperationError, + SetUserEmailAdminUserMutateSetEmailUser, +) __all__ = [ "AddRoles", @@ -110,6 +146,13 @@ "RemoveRolesAdminUserMutateRemoveRolesInvalidInput", "RemoveRolesAdminUserMutateRemoveRolesOperationError", "RemoveRolesAdminUserMutateRemoveRolesUser", + "ResetUserPassword", + "ResetUserPasswordAdmin", + "ResetUserPasswordAdminUser", + "ResetUserPasswordAdminUserMutate", + "ResetUserPasswordAdminUserMutateResetPasswordInvalidInput", + "ResetUserPasswordAdminUserMutateResetPasswordOk", + "ResetUserPasswordAdminUserMutateResetPasswordOperationError", "SetRole", "SetRoleAdmin", "SetRoleAdminUser", @@ -117,6 +160,27 @@ "SetRoleAdminUserMutateSetRoleInvalidInput", "SetRoleAdminUserMutateSetRoleOperationError", "SetRoleAdminUserMutateSetRoleUser", + "SetUserActive", + "SetUserActiveAdmin", + "SetUserActiveAdminUser", + "SetUserActiveAdminUserMutate", + "SetUserActiveAdminUserMutateSetActiveInvalidInput", + "SetUserActiveAdminUserMutateSetActiveOperationError", + "SetUserActiveAdminUserMutateSetActiveUser", + "SetUserAdmin", + "SetUserAdminAdmin", + "SetUserAdminAdminUser", + "SetUserAdminAdminUserMutate", + "SetUserAdminAdminUserMutateSetAdminInvalidInput", + "SetUserAdminAdminUserMutateSetAdminOperationError", + "SetUserAdminAdminUserMutateSetAdminUser", + "SetUserEmail", + "SetUserEmailAdmin", + "SetUserEmailAdminUser", + "SetUserEmailAdminUserMutate", + "SetUserEmailAdminUserMutateSetEmailInvalidInput", + "SetUserEmailAdminUserMutateSetEmailOperationError", + "SetUserEmailAdminUserMutateSetEmailUser", "UnmanagedRoleSelection", "Upload", "UserInput", diff --git a/api/python/quilt3/admin/_graphql_client/client.py b/api/python/quilt3/admin/_graphql_client/client.py index 1e7e3787bb7..648257d2637 100644 --- a/api/python/quilt3/admin/_graphql_client/client.py +++ b/api/python/quilt3/admin/_graphql_client/client.py @@ -18,7 +18,11 @@ from .get_users import GetUsers, GetUsersAdminUserList from .input_types import UserInput from .remove_roles import RemoveRoles, RemoveRolesAdminUserMutate +from .reset_user_password import ResetUserPassword, ResetUserPasswordAdminUserMutate from .set_role import SetRole, SetRoleAdminUserMutate +from .set_user_active import SetUserActive, SetUserActiveAdminUserMutate +from .set_user_admin import SetUserAdmin, SetUserAdminAdminUserMutate +from .set_user_email import SetUserEmail, SetUserEmailAdminUserMutate def gql(q: str) -> str: @@ -26,6 +30,43 @@ def gql(q: str) -> str: class Client(BaseClient): + def get_roles( + self, **kwargs: Any + ) -> List[Union[GetRolesRolesUnmanagedRole, GetRolesRolesManagedRole]]: + query = gql( + """ + query getRoles { + roles { + ...RoleSelection + } + } + + fragment ManagedRoleSelection on ManagedRole { + id + name + arn + } + + fragment RoleSelection on Role { + __typename + ...UnmanagedRoleSelection + ...ManagedRoleSelection + } + + fragment UnmanagedRoleSelection on UnmanagedRole { + id + name + arn + } + """ + ) + variables: Dict[str, object] = {} + response = self.execute( + query=query, operation_name="getRoles", variables=variables, **kwargs + ) + data = self.get_data(response) + return GetRoles.model_validate(data).roles + def get_user(self, name: str, **kwargs: Any) -> Optional[GetUserAdminUserGet]: query = gql( """ @@ -150,9 +191,7 @@ def create_user(self, input: UserInput, **kwargs: Any) -> Union[ user { create(input: $input) { __typename - ...UserSelection - ...InvalidInputSelection - ...OperationErrorSelection + ...UserMutationSelection } } } @@ -191,6 +230,12 @@ def create_user(self, input: UserInput, **kwargs: Any) -> Union[ arn } + fragment UserMutationSelection on UserResult { + ...UserSelection + ...InvalidInputSelection + ...OperationErrorSelection + } + fragment UserSelection on User { name email @@ -258,16 +303,196 @@ def delete_user( data = self.get_data(response) return DeleteUser.model_validate(data).admin.user.mutate - def get_roles( - self, **kwargs: Any - ) -> List[Union[GetRolesRolesUnmanagedRole, GetRolesRolesManagedRole]]: + def set_user_email( + self, email: str, name: str, **kwargs: Any + ) -> Optional[SetUserEmailAdminUserMutate]: query = gql( """ - query getRoles { - roles { + mutation setUserEmail($email: String!, $name: String!) { + admin { + user { + mutate(name: $name) { + setEmail(email: $email) { + __typename + ...UserMutationSelection + } + } + } + } + } + + fragment InvalidInputSelection on InvalidInput { + errors { + path + message + name + context + } + } + + fragment ManagedRoleSelection on ManagedRole { + id + name + arn + } + + fragment OperationErrorSelection on OperationError { + message + name + context + } + + fragment RoleSelection on Role { + __typename + ...UnmanagedRoleSelection + ...ManagedRoleSelection + } + + fragment UnmanagedRoleSelection on UnmanagedRole { + id + name + arn + } + + fragment UserMutationSelection on UserResult { + ...UserSelection + ...InvalidInputSelection + ...OperationErrorSelection + } + + fragment UserSelection on User { + name + email + dateJoined + lastLogin + isActive + isAdmin + isSsoOnly + isService + role { + ...RoleSelection + } + extraRoles { + ...RoleSelection + } + } + """ + ) + variables: Dict[str, object] = {"email": email, "name": name} + response = self.execute( + query=query, operation_name="setUserEmail", variables=variables, **kwargs + ) + data = self.get_data(response) + return SetUserEmail.model_validate(data).admin.user.mutate + + def set_user_admin( + self, name: str, admin: bool, **kwargs: Any + ) -> Optional[SetUserAdminAdminUserMutate]: + query = gql( + """ + mutation setUserAdmin($name: String!, $admin: Boolean!) { + admin { + user { + mutate(name: $name) { + setAdmin(admin: $admin) { + __typename + ...UserMutationSelection + } + } + } + } + } + + fragment InvalidInputSelection on InvalidInput { + errors { + path + message + name + context + } + } + + fragment ManagedRoleSelection on ManagedRole { + id + name + arn + } + + fragment OperationErrorSelection on OperationError { + message + name + context + } + + fragment RoleSelection on Role { + __typename + ...UnmanagedRoleSelection + ...ManagedRoleSelection + } + + fragment UnmanagedRoleSelection on UnmanagedRole { + id + name + arn + } + + fragment UserMutationSelection on UserResult { + ...UserSelection + ...InvalidInputSelection + ...OperationErrorSelection + } + + fragment UserSelection on User { + name + email + dateJoined + lastLogin + isActive + isAdmin + isSsoOnly + isService + role { + ...RoleSelection + } + extraRoles { ...RoleSelection } } + """ + ) + variables: Dict[str, object] = {"name": name, "admin": admin} + response = self.execute( + query=query, operation_name="setUserAdmin", variables=variables, **kwargs + ) + data = self.get_data(response) + return SetUserAdmin.model_validate(data).admin.user.mutate + + def set_user_active( + self, active: bool, name: str, **kwargs: Any + ) -> Optional[SetUserActiveAdminUserMutate]: + query = gql( + """ + mutation setUserActive($active: Boolean!, $name: String!) { + admin { + user { + mutate(name: $name) { + setActive(active: $active) { + __typename + ...UserMutationSelection + } + } + } + } + } + + fragment InvalidInputSelection on InvalidInput { + errors { + path + message + name + context + } + } fragment ManagedRoleSelection on ManagedRole { id @@ -275,6 +500,12 @@ def get_roles( arn } + fragment OperationErrorSelection on OperationError { + message + name + context + } + fragment RoleSelection on Role { __typename ...UnmanagedRoleSelection @@ -286,14 +517,82 @@ def get_roles( name arn } + + fragment UserMutationSelection on UserResult { + ...UserSelection + ...InvalidInputSelection + ...OperationErrorSelection + } + + fragment UserSelection on User { + name + email + dateJoined + lastLogin + isActive + isAdmin + isSsoOnly + isService + role { + ...RoleSelection + } + extraRoles { + ...RoleSelection + } + } """ ) - variables: Dict[str, object] = {} + variables: Dict[str, object] = {"active": active, "name": name} response = self.execute( - query=query, operation_name="getRoles", variables=variables, **kwargs + query=query, operation_name="setUserActive", variables=variables, **kwargs ) data = self.get_data(response) - return GetRoles.model_validate(data).roles + return SetUserActive.model_validate(data).admin.user.mutate + + def reset_user_password( + self, name: str, **kwargs: Any + ) -> Optional[ResetUserPasswordAdminUserMutate]: + query = gql( + """ + mutation resetUserPassword($name: String!) { + admin { + user { + mutate(name: $name) { + resetPassword { + __typename + ...InvalidInputSelection + ...OperationErrorSelection + } + } + } + } + } + + fragment InvalidInputSelection on InvalidInput { + errors { + path + message + name + context + } + } + + fragment OperationErrorSelection on OperationError { + message + name + context + } + """ + ) + variables: Dict[str, object] = {"name": name} + response = self.execute( + query=query, + operation_name="resetUserPassword", + variables=variables, + **kwargs + ) + data = self.get_data(response) + return ResetUserPassword.model_validate(data).admin.user.mutate def set_role( self, diff --git a/docs/api-reference/Admin.md b/docs/api-reference/Admin.md index 15dde225d04..232433d9c0e 100644 --- a/docs/api-reference/Admin.md +++ b/docs/api-reference/Admin.md @@ -13,6 +13,11 @@ APIs for Quilt administrators. 'Registry' refers to Quilt stack backend services ## User(name: str, email: str, date\_joined: datetime.datetime, last\_login: datetime.datetime, is\_active: bool, is\_admin: bool, is\_sso\_only: bool, is\_service: bool, role: Optional[Annotated[Union[quilt3.admin.ManagedRole, quilt3.admin.UnmanagedRole], FieldInfo(annotation=NoneType, required=True, discriminator='typename\_\_')]], extra\_roles: List[Annotated[Union[quilt3.admin.ManagedRole, quilt3.admin.UnmanagedRole], FieldInfo(annotation=NoneType, required=True, discriminator='typename\_\_')]]) -> None {#User} +## get\_roles() -> List[Union[quilt3.admin.ManagedRole, quilt3.admin.UnmanagedRole]] {#get\_roles} + +Get a list of all roles in the registry. + + ## get\_user(name: str) -> Optional[quilt3.admin.User] {#get\_user} Get a specific user from the registry. Return `None` if the user does not exist. @@ -48,9 +53,43 @@ __Arguments__ * __name__: Username of user to delete. -## get\_roles() -> List[Union[quilt3.admin.ManagedRole, quilt3.admin.UnmanagedRole]] {#get\_roles} +## set\_user\_email(name: str, email: str) -> quilt3.admin.User {#set\_user\_email} -Get a list of all roles in the registry. +Set the email for a user. + +__Arguments__ + +* __name__: Username of user to update. +* __email__: Email to set for the user. + + +## set\_user\_admin(name: str, admin: bool) -> quilt3.admin.User {#set\_user\_admin} + +Set the admin status for a user. + +__Arguments__ + +* __name__: Username of user to update. +* __admin__: Admin status to set for the user. + + +## set\_user\_active(name: str, active: bool) -> quilt3.admin.User {#set\_user\_active} + +Set the active status for a user. + +__Arguments__ + +* __name__: Username of user to update. +* __active__: Active status to set for the user. + + +## reset\_user\_password(name: str) -> None {#reset\_user\_password} + +Reset the password for a user. + +__Arguments__ + +* __name__: Username of user to update. ## set\_role(name: str, role: str, extra\_roles: Optional[List[str]] = None, \*, append: bool = False) -> quilt3.admin.User {#set\_role}