Rename user archive action to deactivate
"Archive" was misleading for users: the action sets a profile to DEACTIVATED while keeping the person in the organization. Rename it to "deactivate" across the API, CLI, MCP, n8n, and console UI. Consolidate the two overlapping operations into a single deactivateUser backed by the fuller, guarded logic (SCIM guard, last-active-owner guard, invitation expiry, signature cancellation, membership update, webhook) and authorized via iam:membership-profile:deactivate. Remove the archiveUser surface and the thin state-only deactivate path. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -1361,10 +1361,10 @@
|
|||||||
"peoplePage": { "title": "People", "actions": { "add": "Add Person" } },
|
"peoplePage": { "title": "People", "actions": { "add": "Add Person" } },
|
||||||
"personPage": {
|
"personPage": {
|
||||||
"breadcrumb": { "people": "People" },
|
"breadcrumb": { "people": "People" },
|
||||||
"messages": { "archived": "Person archived successfully", "removed": "Person removed successfully" },
|
"messages": { "deactivated": "Person deactivated successfully", "removed": "Person removed successfully" },
|
||||||
"errors": { "archive": "Failed to archive person", "remove": "Failed to remove person" },
|
"errors": { "deactivate": "Failed to deactivate person", "remove": "Failed to remove person" },
|
||||||
"confirmations": { "archive": "Are you sure you want to archive {{name}}?", "remove": "Are you sure you want to remove {{name}}?" },
|
"confirmations": { "deactivate": "Are you sure you want to deactivate {{name}}?", "remove": "Are you sure you want to remove {{name}}?" },
|
||||||
"actions": { "archive": "Archive", "remove": "Remove" }
|
"actions": { "deactivate": "Deactivate", "remove": "Remove" }
|
||||||
},
|
},
|
||||||
"addPersonDialog": { "title": "Add Person" },
|
"addPersonDialog": { "title": "Add Person" },
|
||||||
"peopleList": {
|
"peopleList": {
|
||||||
@@ -1374,10 +1374,10 @@
|
|||||||
"filters": { "allStatuses": "All statuses", "pending": "Pending", "active": "Active", "deactivated": "Deactivated", "allRoles": "All roles", "allTypes": "All types" }
|
"filters": { "allStatuses": "All statuses", "pending": "Pending", "active": "Active", "deactivated": "Deactivated", "allRoles": "All roles", "allTypes": "All types" }
|
||||||
},
|
},
|
||||||
"peopleListItem": {
|
"peopleListItem": {
|
||||||
"messages": { "invitationSent": "Invitation sent successfully", "roleUpdated": "Role updated successfully", "archived": "Person archived successfully", "removed": "Person removed successfully" },
|
"messages": { "invitationSent": "Invitation sent successfully", "roleUpdated": "Role updated successfully", "deactivated": "Person deactivated successfully", "removed": "Person removed successfully" },
|
||||||
"errors": { "sendInvitation": "Failed to send invitation", "updateRole": "Failed to update role", "archive": "Failed to archive person", "remove": "Failed to remove person" },
|
"errors": { "sendInvitation": "Failed to send invitation", "updateRole": "Failed to update role", "deactivate": "Failed to deactivate person", "remove": "Failed to remove person" },
|
||||||
"confirmations": { "sendActivationEmail": "Send the activation email to {{name}}?", "archive": "Are you sure you want to archive {{name}}?", "remove": "Are you sure you want to remove {{name}}?" },
|
"confirmations": { "sendActivationEmail": "Send the activation email to {{name}}?", "deactivate": "Are you sure you want to deactivate {{name}}?", "remove": "Are you sure you want to remove {{name}}?" },
|
||||||
"actions": { "send": "Send", "sendActivationMail": "Send activation mail", "resendActivationMail": "Resend activation mail", "archivePerson": "Archive person", "removePerson": "Remove person" }
|
"actions": { "send": "Send", "sendActivationMail": "Send activation mail", "resendActivationMail": "Resend activation mail", "deactivatePerson": "Deactivate person", "removePerson": "Remove person" }
|
||||||
},
|
},
|
||||||
"personForm": {
|
"personForm": {
|
||||||
"messages": { "created": "Person created successfully.", "updated": "Person updated successfully." },
|
"messages": { "created": "Person created successfully.", "updated": "Person updated successfully." },
|
||||||
|
|||||||
@@ -2427,19 +2427,19 @@
|
|||||||
"people": "Personnes"
|
"people": "Personnes"
|
||||||
},
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
"archived": "Personne archivée avec succès",
|
"deactivated": "Personne désactivée avec succès",
|
||||||
"removed": "Personne supprimée avec succès"
|
"removed": "Personne supprimée avec succès"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"archive": "Échec de l’archivage de la personne",
|
"deactivate": "Échec de la désactivation de la personne",
|
||||||
"remove": "Échec de la suppression de la personne"
|
"remove": "Échec de la suppression de la personne"
|
||||||
},
|
},
|
||||||
"confirmations": {
|
"confirmations": {
|
||||||
"archive": "Voulez-vous vraiment archiver {{name}} ?",
|
"deactivate": "Voulez-vous vraiment désactiver {{name}} ?",
|
||||||
"remove": "Voulez-vous vraiment supprimer {{name}} ?"
|
"remove": "Voulez-vous vraiment supprimer {{name}} ?"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"archive": "Archiver",
|
"deactivate": "Désactiver",
|
||||||
"remove": "Supprimer"
|
"remove": "Supprimer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2469,25 +2469,25 @@
|
|||||||
"messages": {
|
"messages": {
|
||||||
"invitationSent": "Invitation envoyée avec succès",
|
"invitationSent": "Invitation envoyée avec succès",
|
||||||
"roleUpdated": "Rôle mis à jour avec succès",
|
"roleUpdated": "Rôle mis à jour avec succès",
|
||||||
"archived": "Personne archivée avec succès",
|
"deactivated": "Personne désactivée avec succès",
|
||||||
"removed": "Personne supprimée avec succès"
|
"removed": "Personne supprimée avec succès"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"sendInvitation": "Échec de l’envoi de l’invitation",
|
"sendInvitation": "Échec de l’envoi de l’invitation",
|
||||||
"updateRole": "Échec de la mise à jour du rôle",
|
"updateRole": "Échec de la mise à jour du rôle",
|
||||||
"archive": "Échec de l’archivage de la personne",
|
"deactivate": "Échec de la désactivation de la personne",
|
||||||
"remove": "Échec de la suppression de la personne"
|
"remove": "Échec de la suppression de la personne"
|
||||||
},
|
},
|
||||||
"confirmations": {
|
"confirmations": {
|
||||||
"sendActivationEmail": "Envoyer l’e-mail d’activation à {{name}} ?",
|
"sendActivationEmail": "Envoyer l’e-mail d’activation à {{name}} ?",
|
||||||
"archive": "Voulez-vous vraiment archiver {{name}} ?",
|
"deactivate": "Voulez-vous vraiment désactiver {{name}} ?",
|
||||||
"remove": "Voulez-vous vraiment supprimer {{name}} ?"
|
"remove": "Voulez-vous vraiment supprimer {{name}} ?"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"send": "Envoyer",
|
"send": "Envoyer",
|
||||||
"sendActivationMail": "Envoyer l’e-mail d’activation",
|
"sendActivationMail": "Envoyer l’e-mail d’activation",
|
||||||
"resendActivationMail": "Renvoyer l’e-mail d’activation",
|
"resendActivationMail": "Renvoyer l’e-mail d’activation",
|
||||||
"archivePerson": "Archiver la personne",
|
"deactivatePerson": "Désactiver la personne",
|
||||||
"removePerson": "Supprimer la personne"
|
"removePerson": "Supprimer la personne"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const personPageQuery = graphql`
|
|||||||
emailAddress
|
emailAddress
|
||||||
source
|
source
|
||||||
state
|
state
|
||||||
canDelete: permission(action: "iam:membership-profile:delete")
|
canDeactivate: permission(action: "iam:membership-profile:deactivate")
|
||||||
canRemoveMember: permission(action: "iam:membership:delete")
|
canRemoveMember: permission(action: "iam:membership:delete")
|
||||||
...PersonFormFragment
|
...PersonFormFragment
|
||||||
}
|
}
|
||||||
@@ -58,12 +58,12 @@ const removeUserMutation = graphql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const archiveUserMutation = graphql`
|
const deactivateUserMutation = graphql`
|
||||||
mutation PersonPage_archiveMutation(
|
mutation PersonPage_deactivateMutation(
|
||||||
$input: ArchiveUserInput!
|
$input: DeactivateUserInput!
|
||||||
) {
|
) {
|
||||||
archiveUser(input: $input) {
|
deactivateUser(input: $input) {
|
||||||
archivedProfileId
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -81,11 +81,11 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
|||||||
throw new Error("invalid type for node");
|
throw new Error("invalid type for node");
|
||||||
}
|
}
|
||||||
|
|
||||||
const [archiveUser, isArchiving] = useMutationWithToasts(
|
const [deactivateUser, isDeactivating] = useMutationWithToasts(
|
||||||
archiveUserMutation,
|
deactivateUserMutation,
|
||||||
{
|
{
|
||||||
successMessage: t("personPage.messages.archived"),
|
successMessage: t("personPage.messages.deactivated"),
|
||||||
errorMessage: t("personPage.errors.archive"),
|
errorMessage: t("personPage.errors.deactivate"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const [removeUser, isRemoving] = useMutationWithToasts(
|
const [removeUser, isRemoving] = useMutationWithToasts(
|
||||||
@@ -95,12 +95,12 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
|||||||
errorMessage: t("personPage.errors.remove"),
|
errorMessage: t("personPage.errors.remove"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const isMutating = isArchiving || isRemoving;
|
const isMutating = isDeactivating || isRemoving;
|
||||||
|
|
||||||
const handleArchive = () => {
|
const handleDeactivate = () => {
|
||||||
confirm(
|
confirm(
|
||||||
() => {
|
() => {
|
||||||
return archiveUser({
|
return deactivateUser({
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
profileId: person.id,
|
profileId: person.id,
|
||||||
@@ -113,7 +113,7 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message: t("personPage.confirmations.archive", { name: person.fullName }),
|
message: t("personPage.confirmations.deactivate", { name: person.fullName }),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -139,7 +139,7 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const canArchive = person.canDelete && person.source !== "SCIM" && person.state !== "DEACTIVATED";
|
const canDeactivate = person.canDeactivate && person.source !== "SCIM" && person.state !== "DEACTIVATED";
|
||||||
const canRemove = person.canRemoveMember && person.source !== "SCIM";
|
const canRemove = person.canRemoveMember && person.source !== "SCIM";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -166,15 +166,15 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
|||||||
<div className="text-lg text-txt-secondary">{person.emailAddress}</div>
|
<div className="text-lg text-txt-secondary">{person.emailAddress}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(canArchive || canRemove) && (
|
{(canDeactivate || canRemove) && (
|
||||||
<ActionDropdown variant="secondary">
|
<ActionDropdown variant="secondary">
|
||||||
{canArchive && (
|
{canDeactivate && (
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
icon={IconArchive}
|
icon={IconArchive}
|
||||||
onClick={handleArchive}
|
onClick={handleDeactivate}
|
||||||
disabled={isMutating}
|
disabled={isMutating}
|
||||||
>
|
>
|
||||||
{t("personPage.actions.archive")}
|
{t("personPage.actions.deactivate")}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
)}
|
)}
|
||||||
{canRemove && (
|
{canRemove && (
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const fragment = graphql`
|
|||||||
createdAt
|
createdAt
|
||||||
canUpdate: permission(action: "iam:membership-profile:update")
|
canUpdate: permission(action: "iam:membership-profile:update")
|
||||||
canInvite: permission(action: "iam:invitation:create")
|
canInvite: permission(action: "iam:invitation:create")
|
||||||
canDelete: permission(action: "iam:membership-profile:delete")
|
canDeactivate: permission(action: "iam:membership-profile:deactivate")
|
||||||
canRemoveMember: permission(action: "iam:membership:delete")
|
canRemoveMember: permission(action: "iam:membership:delete")
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -116,10 +116,10 @@ const removeUserMutation = graphql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const archiveUserMutation = graphql`
|
const deactivateUserMutation = graphql`
|
||||||
mutation PeopleListItem_archiveMutation($input: ArchiveUserInput!) {
|
mutation PeopleListItem_deactivateMutation($input: DeactivateUserInput!) {
|
||||||
archiveUser(input: $input) {
|
deactivateUser(input: $input) {
|
||||||
archivedProfileId
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -149,7 +149,7 @@ export function PeopleListItem(props: {
|
|||||||
const isInactive = !isActive;
|
const isInactive = !isActive;
|
||||||
|
|
||||||
const canSendActivationMail = !isActive && profile.source !== "SCIM" && profile.canInvite;
|
const canSendActivationMail = !isActive && profile.source !== "SCIM" && profile.canInvite;
|
||||||
const canArchive = profile.canDelete && profile.source !== "SCIM" && profile.state !== "DEACTIVATED";
|
const canDeactivate = profile.canDeactivate && profile.source !== "SCIM" && profile.state !== "DEACTIVATED";
|
||||||
const canRemove = profile.canRemoveMember && profile.source !== "SCIM";
|
const canRemove = profile.canRemoveMember && profile.source !== "SCIM";
|
||||||
|
|
||||||
const [inviteUser]
|
const [inviteUser]
|
||||||
@@ -164,11 +164,11 @@ export function PeopleListItem(props: {
|
|||||||
errorMessage: t("peopleListItem.errors.updateRole"),
|
errorMessage: t("peopleListItem.errors.updateRole"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const [archiveUser, isArchiving] = useMutationWithToasts(
|
const [deactivateUser, isDeactivating] = useMutationWithToasts(
|
||||||
archiveUserMutation,
|
deactivateUserMutation,
|
||||||
{
|
{
|
||||||
successMessage: t("peopleListItem.messages.archived"),
|
successMessage: t("peopleListItem.messages.deactivated"),
|
||||||
errorMessage: t("peopleListItem.errors.archive"),
|
errorMessage: t("peopleListItem.errors.deactivate"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const [removeUser, isRemoving] = useMutationWithToasts(
|
const [removeUser, isRemoving] = useMutationWithToasts(
|
||||||
@@ -178,7 +178,7 @@ export function PeopleListItem(props: {
|
|||||||
errorMessage: t("peopleListItem.errors.remove"),
|
errorMessage: t("peopleListItem.errors.remove"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const isMutating = isArchiving || isRemoving;
|
const isMutating = isDeactivating || isRemoving;
|
||||||
|
|
||||||
const handleInvite = () => {
|
const handleInvite = () => {
|
||||||
confirm(
|
confirm(
|
||||||
@@ -211,10 +211,10 @@ export function PeopleListItem(props: {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const handleArchive = () => {
|
const handleDeactivate = () => {
|
||||||
confirm(
|
confirm(
|
||||||
() => {
|
() => {
|
||||||
return archiveUser({
|
return deactivateUser({
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
profileId: profile.id,
|
profileId: profile.id,
|
||||||
@@ -227,7 +227,7 @@ export function PeopleListItem(props: {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message: t("peopleListItem.confirmations.archive", { name: profile.fullName }),
|
message: t("peopleListItem.confirmations.deactivate", { name: profile.fullName }),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -307,7 +307,7 @@ export function PeopleListItem(props: {
|
|||||||
{dateFormat(i18n.language, profile.createdAt)}
|
{dateFormat(i18n.language, profile.createdAt)}
|
||||||
</Td>
|
</Td>
|
||||||
<Td noLink width={160} className="text-end">
|
<Td noLink width={160} className="text-end">
|
||||||
{(canSendActivationMail || canArchive || canRemove) && (
|
{(canSendActivationMail || canDeactivate || canRemove) && (
|
||||||
<ActionDropdown>
|
<ActionDropdown>
|
||||||
{canSendActivationMail && (
|
{canSendActivationMail && (
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
@@ -317,12 +317,12 @@ export function PeopleListItem(props: {
|
|||||||
{lastInvitation ? t("peopleListItem.actions.resendActivationMail") : t("peopleListItem.actions.sendActivationMail")}
|
{lastInvitation ? t("peopleListItem.actions.resendActivationMail") : t("peopleListItem.actions.sendActivationMail")}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
)}
|
)}
|
||||||
{canArchive && (
|
{canDeactivate && (
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
onClick={handleArchive}
|
onClick={handleDeactivate}
|
||||||
icon={IconArchive}
|
icon={IconArchive}
|
||||||
>
|
>
|
||||||
{t("peopleListItem.actions.archivePerson")}
|
{t("peopleListItem.actions.deactivatePerson")}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
)}
|
)}
|
||||||
{canRemove && (
|
{canRemove && (
|
||||||
|
|||||||
@@ -605,13 +605,13 @@ func TestUser_RemoveUser_ProfileInUse(t *testing.T) {
|
|||||||
assert.Contains(t, gqlErrors[0].Message, "referenced by other resources")
|
assert.Contains(t, gqlErrors[0].Message, "referenced by other resources")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUser_ArchiveUser(t *testing.T) {
|
func TestUser_DeactivateUser(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||||
|
|
||||||
// Create a user to archive.
|
// Create a user to deactivate.
|
||||||
userToArchive := testutil.NewClientInOrg(t, testutil.RoleViewer, owner)
|
userToDeactivate := testutil.NewClientInOrg(t, testutil.RoleViewer, owner)
|
||||||
_ = userToArchive
|
_ = userToDeactivate
|
||||||
|
|
||||||
query := `
|
query := `
|
||||||
query($id: ID!) {
|
query($id: ID!) {
|
||||||
@@ -666,17 +666,17 @@ func TestUser_ArchiveUser(t *testing.T) {
|
|||||||
require.NotEmpty(t, userID, "Should find viewer member")
|
require.NotEmpty(t, userID, "Should find viewer member")
|
||||||
|
|
||||||
mutation := `
|
mutation := `
|
||||||
mutation($input: ArchiveUserInput!) {
|
mutation($input: DeactivateUserInput!) {
|
||||||
archiveUser(input: $input) {
|
deactivateUser(input: $input) {
|
||||||
archivedProfileId
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
var mutationResult struct {
|
var mutationResult struct {
|
||||||
ArchiveUser struct {
|
DeactivateUser struct {
|
||||||
ArchivedProfileID string `json:"archivedProfileId"`
|
Success bool `json:"success"`
|
||||||
} `json:"archiveUser"`
|
} `json:"deactivateUser"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err = owner.ExecuteConnect(mutation, map[string]any{
|
err = owner.ExecuteConnect(mutation, map[string]any{
|
||||||
@@ -687,24 +687,24 @@ func TestUser_ArchiveUser(t *testing.T) {
|
|||||||
}, &mutationResult)
|
}, &mutationResult)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, userID, mutationResult.ArchiveUser.ArchivedProfileID)
|
assert.True(t, mutationResult.DeactivateUser.Success)
|
||||||
|
|
||||||
err = owner.ExecuteConnect(query, map[string]any{
|
err = owner.ExecuteConnect(query, map[string]any{
|
||||||
"id": owner.GetOrganizationID().String(),
|
"id": owner.GetOrganizationID().String(),
|
||||||
}, &result)
|
}, &result)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var archivedUserState string
|
var deactivatedUserState string
|
||||||
|
|
||||||
for _, edge := range result.Node.Profiles.Edges {
|
for _, edge := range result.Node.Profiles.Edges {
|
||||||
if edge.Node.ID == userID {
|
if edge.Node.ID == userID {
|
||||||
archivedUserState = edge.Node.State
|
deactivatedUserState = edge.Node.State
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NotEmpty(t, archivedUserState, "Should still find archived user")
|
require.NotEmpty(t, deactivatedUserState, "Should still find deactivated user")
|
||||||
assert.Equal(t, "DEACTIVATED", archivedUserState)
|
assert.Equal(t, "DEACTIVATED", deactivatedUserState)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUser_DeactivateUserCancelsSignatureRequests(t *testing.T) {
|
func TestUser_DeactivateUserCancelsSignatureRequests(t *testing.T) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const description: INodeProperties[] = [
|
|||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: ['user'],
|
resource: ['user'],
|
||||||
operation: ['archiveUser'],
|
operation: ['deactivateUser'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
@@ -43,11 +43,11 @@ export const description: INodeProperties[] = [
|
|||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: ['user'],
|
resource: ['user'],
|
||||||
operation: ['archiveUser'],
|
operation: ['deactivateUser'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The ID of the user (profile) to archive in the organization',
|
description: 'The ID of the user (profile) to deactivate in the organization',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -60,9 +60,9 @@ export async function execute(
|
|||||||
const userId = this.getNodeParameter('userId', itemIndex) as string;
|
const userId = this.getNodeParameter('userId', itemIndex) as string;
|
||||||
|
|
||||||
const query = `
|
const query = `
|
||||||
mutation ArchiveUser($input: ArchiveUserInput!) {
|
mutation DeactivateUser($input: DeactivateUserInput!) {
|
||||||
archiveUser(input: $input) {
|
deactivateUser(input: $input) {
|
||||||
archivedProfileId
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
import type { INodeProperties } from 'n8n-workflow';
|
import type { INodeProperties } from 'n8n-workflow';
|
||||||
import * as archiveUserOp from './archiveUser.operation';
|
import * as deactivateUserOp from './deactivateUser.operation';
|
||||||
import * as listUsersOp from './listUsers.operation';
|
import * as listUsersOp from './listUsers.operation';
|
||||||
import * as getUserOp from './getUser.operation';
|
import * as getUserOp from './getUser.operation';
|
||||||
import * as createUserOp from './createUser.operation';
|
import * as createUserOp from './createUser.operation';
|
||||||
@@ -40,18 +40,18 @@ export const description: INodeProperties[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
{
|
|
||||||
name: 'Archive',
|
|
||||||
value: 'archiveUser',
|
|
||||||
description: 'Archive a user in the organization',
|
|
||||||
action: 'Archive a user',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'Create',
|
name: 'Create',
|
||||||
value: 'createUser',
|
value: 'createUser',
|
||||||
description: 'Create a new user in the organization',
|
description: 'Create a new user in the organization',
|
||||||
action: 'Create a user',
|
action: 'Create a user',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Deactivate',
|
||||||
|
value: 'deactivateUser',
|
||||||
|
description: 'Deactivate a user in the organization',
|
||||||
|
action: 'Deactivate a user',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Get',
|
name: 'Get',
|
||||||
value: 'getUser',
|
value: 'getUser',
|
||||||
@@ -91,7 +91,7 @@ export const description: INodeProperties[] = [
|
|||||||
],
|
],
|
||||||
default: 'listUsers',
|
default: 'listUsers',
|
||||||
},
|
},
|
||||||
...archiveUserOp.description,
|
...deactivateUserOp.description,
|
||||||
...listUsersOp.description,
|
...listUsersOp.description,
|
||||||
...getUserOp.description,
|
...getUserOp.description,
|
||||||
...createUserOp.description,
|
...createUserOp.description,
|
||||||
@@ -102,7 +102,7 @@ export const description: INodeProperties[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export {
|
export {
|
||||||
archiveUserOp as archiveUser,
|
deactivateUserOp as deactivateUser,
|
||||||
listUsersOp as listUsers,
|
listUsersOp as listUsers,
|
||||||
getUserOp as getUser,
|
getUserOp as getUser,
|
||||||
createUserOp as createUser,
|
createUserOp as createUser,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
package archive
|
package deactivate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -29,34 +29,34 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const archiveMutation = `
|
const deactivateMutation = `
|
||||||
mutation($input: ArchiveUserInput!) {
|
mutation($input: DeactivateUserInput!) {
|
||||||
archiveUser(input: $input) {
|
deactivateUser(input: $input) {
|
||||||
archivedProfileId
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
func NewCmdArchive(f *cmdutil.Factory) *cobra.Command {
|
func NewCmdDeactivate(f *cmdutil.Factory) *cobra.Command {
|
||||||
var (
|
var (
|
||||||
flagOrg string
|
flagOrg string
|
||||||
flagYes bool
|
flagYes bool
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "archive <id>",
|
Use: "deactivate <id>",
|
||||||
Short: "Archive a user",
|
Short: "Deactivate a user",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if !flagYes {
|
if !flagYes {
|
||||||
if !f.IOStreams.IsInteractive() {
|
if !f.IOStreams.IsInteractive() {
|
||||||
return fmt.Errorf("cannot archive user: confirmation required, use --yes to confirm")
|
return fmt.Errorf("cannot deactivate user: confirmation required, use --yes to confirm")
|
||||||
}
|
}
|
||||||
|
|
||||||
var confirmed bool
|
var confirmed bool
|
||||||
|
|
||||||
err := huh.NewConfirm().
|
err := huh.NewConfirm().
|
||||||
Title(fmt.Sprintf("Archive user %s?", args[0])).
|
Title(fmt.Sprintf("Deactivate user %s?", args[0])).
|
||||||
Value(&confirmed).
|
Value(&confirmed).
|
||||||
Run()
|
Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -95,7 +95,7 @@ func NewCmdArchive(f *cmdutil.Factory) *cobra.Command {
|
|||||||
)
|
)
|
||||||
|
|
||||||
_, err = client.Do(
|
_, err = client.Do(
|
||||||
archiveMutation,
|
deactivateMutation,
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"organizationId": flagOrg,
|
"organizationId": flagOrg,
|
||||||
@@ -107,7 +107,7 @@ func NewCmdArchive(f *cmdutil.Factory) *cobra.Command {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(f.IOStreams.Out, "Archived user %s\n", args[0])
|
_, _ = fmt.Fprintf(f.IOStreams.Out, "Deactivated user %s\n", args[0])
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@@ -23,7 +23,7 @@ package user
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
||||||
"go.probo.inc/probo/pkg/cmd/user/archive"
|
"go.probo.inc/probo/pkg/cmd/user/deactivate"
|
||||||
"go.probo.inc/probo/pkg/cmd/user/list"
|
"go.probo.inc/probo/pkg/cmd/user/list"
|
||||||
"go.probo.inc/probo/pkg/cmd/user/remove"
|
"go.probo.inc/probo/pkg/cmd/user/remove"
|
||||||
"go.probo.inc/probo/pkg/cmd/user/view"
|
"go.probo.inc/probo/pkg/cmd/user/view"
|
||||||
@@ -37,7 +37,7 @@ func NewCmdUser(f *cmdutil.Factory) *cobra.Command {
|
|||||||
|
|
||||||
cmd.AddCommand(list.NewCmdList(f))
|
cmd.AddCommand(list.NewCmdList(f))
|
||||||
cmd.AddCommand(view.NewCmdView(f))
|
cmd.AddCommand(view.NewCmdView(f))
|
||||||
cmd.AddCommand(archive.NewCmdArchive(f))
|
cmd.AddCommand(deactivate.NewCmdDeactivate(f))
|
||||||
cmd.AddCommand(remove.NewCmdRemove(f))
|
cmd.AddCommand(remove.NewCmdRemove(f))
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ func (s *OrganizationService) RemoveUser(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OrganizationService) ArchiveUser(
|
func (s *OrganizationService) DeactivateUser(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
scope coredata.Scoper,
|
scope coredata.Scoper,
|
||||||
organizationID gid.GID,
|
organizationID gid.GID,
|
||||||
@@ -1183,55 +1183,6 @@ func (s *OrganizationService) UpdateUser(ctx context.Context, req *UpdateUserReq
|
|||||||
return profile, nil
|
return profile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OrganizationService) UpdateUserState(
|
|
||||||
ctx context.Context,
|
|
||||||
userID gid.GID,
|
|
||||||
state coredata.ProfileState,
|
|
||||||
) (*coredata.MembershipProfile, error) {
|
|
||||||
var (
|
|
||||||
scope = coredata.NewScopeFromObjectID(userID)
|
|
||||||
profile = &coredata.MembershipProfile{}
|
|
||||||
)
|
|
||||||
|
|
||||||
err := s.pg.WithTx(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, tx pg.Tx) error {
|
|
||||||
if err := profile.LoadByID(ctx, tx, scope, userID); err != nil {
|
|
||||||
return fmt.Errorf("cannot load profile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
switch state {
|
|
||||||
case coredata.ProfileStatePending:
|
|
||||||
profile.MarkPending(now)
|
|
||||||
case coredata.ProfileStateActive:
|
|
||||||
profile.MarkActive(now)
|
|
||||||
case coredata.ProfileStateDeactivated:
|
|
||||||
profile.MarkDeactivated(now)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := profile.Update(ctx, tx, scope); err != nil {
|
|
||||||
return fmt.Errorf("cannot update profile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if state == coredata.ProfileStateDeactivated {
|
|
||||||
signatures := &coredata.DocumentVersionSignatures{}
|
|
||||||
if err := signatures.DeleteRequestedBySignatory(ctx, tx, scope, profile.ID); err != nil {
|
|
||||||
return fmt.Errorf("cannot delete requested signatures: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return profile, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OrganizationService) GetProfile(ctx context.Context, profileID gid.GID) (*coredata.MembershipProfile, error) {
|
func (s *OrganizationService) GetProfile(ctx context.Context, profileID gid.GID) (*coredata.MembershipProfile, error) {
|
||||||
profile := &coredata.MembershipProfile{}
|
profile := &coredata.MembershipProfile{}
|
||||||
|
|
||||||
|
|||||||
@@ -104,9 +104,8 @@ extend type Mutation {
|
|||||||
createUser(input: CreateUserInput!): CreateUserPayload
|
createUser(input: CreateUserInput!): CreateUserPayload
|
||||||
@authentication(required: PRESENT)
|
@authentication(required: PRESENT)
|
||||||
deactivateUser(input: DeactivateUserInput!): DeactivateUserPayload
|
deactivateUser(input: DeactivateUserInput!): DeactivateUserPayload
|
||||||
updateUser(input: UpdateUserInput!): UpdateUserPayload!
|
|
||||||
archiveUser(input: ArchiveUserInput!): ArchiveUserPayload
|
|
||||||
@authentication(required: PRESENT)
|
@authentication(required: PRESENT)
|
||||||
|
updateUser(input: UpdateUserInput!): UpdateUserPayload!
|
||||||
removeUser(input: RemoveUserInput!): RemoveUserPayload
|
removeUser(input: RemoveUserInput!): RemoveUserPayload
|
||||||
@authentication(required: PRESENT)
|
@authentication(required: PRESENT)
|
||||||
}
|
}
|
||||||
@@ -148,11 +147,6 @@ input RemoveUserInput {
|
|||||||
profileId: ID!
|
profileId: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
input ArchiveUserInput {
|
|
||||||
organizationId: ID!
|
|
||||||
profileId: ID!
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateUserPayload {
|
type CreateUserPayload {
|
||||||
profileEdge: ProfileEdge!
|
profileEdge: ProfileEdge!
|
||||||
}
|
}
|
||||||
@@ -168,7 +162,3 @@ type UpdateUserPayload {
|
|||||||
type RemoveUserPayload {
|
type RemoveUserPayload {
|
||||||
deletedProfileId: ID!
|
deletedProfileId: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArchiveUserPayload {
|
|
||||||
archivedProfileId: ID!
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -64,17 +64,23 @@ func (r *mutationResolver) CreateUser(ctx context.Context, input types.CreateUse
|
|||||||
|
|
||||||
// DeactivateUser is the resolver for the deactivateUser field.
|
// DeactivateUser is the resolver for the deactivateUser field.
|
||||||
func (r *mutationResolver) DeactivateUser(ctx context.Context, input types.DeactivateUserInput) (*types.DeactivateUserPayload, error) {
|
func (r *mutationResolver) DeactivateUser(ctx context.Context, input types.DeactivateUserInput) (*types.DeactivateUserPayload, error) {
|
||||||
if _, err := r.authorize(ctx, input.ProfileID, iam.ActionMembershipProfileDeactivate); err != nil {
|
scope, err := r.authorize(ctx, input.ProfileID, iam.ActionMembershipProfileDeactivate)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := r.iam.OrganizationService.UpdateUserState(
|
err = r.iam.OrganizationService.DeactivateUser(ctx, scope, input.OrganizationID, input.ProfileID)
|
||||||
ctx,
|
|
||||||
input.ProfileID,
|
|
||||||
coredata.ProfileStateDeactivated,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.ErrorCtx(ctx, "cannot deactivate profile", log.Error(err))
|
if _, ok := errors.AsType[*iam.ErrUserManagedBySCIM](err); ok {
|
||||||
|
return nil, gqlutils.Conflictf(ctx, "user is managed by SCIM and cannot be deactivated")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := errors.AsType[*iam.ErrLastActiveOwner](err); ok {
|
||||||
|
return nil, gqlutils.Conflictf(ctx, "cannot deactivate last active owner")
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot deactivate user", log.Error(err))
|
||||||
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,31 +117,6 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, input types.UpdateUse
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArchiveUser is the resolver for the archiveUser field.
|
|
||||||
func (r *mutationResolver) ArchiveUser(ctx context.Context, input types.ArchiveUserInput) (*types.ArchiveUserPayload, error) {
|
|
||||||
scope, err := r.authorize(ctx, input.ProfileID, iam.ActionMembershipProfileDelete)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.iam.OrganizationService.ArchiveUser(ctx, scope, input.OrganizationID, input.ProfileID)
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := errors.AsType[*iam.ErrUserManagedBySCIM](err); ok {
|
|
||||||
return nil, gqlutils.Conflictf(ctx, "user is managed by SCIM and cannot be archived")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := errors.AsType[*iam.ErrLastActiveOwner](err); ok {
|
|
||||||
return nil, gqlutils.Conflictf(ctx, "cannot archive last active owner")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot archive user from organization", log.Error(err))
|
|
||||||
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.ArchiveUserPayload{ArchivedProfileID: input.ProfileID}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveUser is the resolver for the removeUser field.
|
// RemoveUser is the resolver for the removeUser field.
|
||||||
func (r *mutationResolver) RemoveUser(ctx context.Context, input types.RemoveUserInput) (*types.RemoveUserPayload, error) {
|
func (r *mutationResolver) RemoveUser(ctx context.Context, input types.RemoveUserInput) (*types.RemoveUserPayload, error) {
|
||||||
scope, err := r.authorize(ctx, input.ProfileID, iam.ActionMembershipDelete)
|
scope, err := r.authorize(ctx, input.ProfileID, iam.ActionMembershipDelete)
|
||||||
|
|||||||
@@ -2994,26 +2994,26 @@ func (r *Resolver) RemoveUserTool(ctx context.Context, req *mcp.CallToolRequest,
|
|||||||
return nil, types.RemoveUserOutput{DeletedUserID: input.ProfileID}, nil
|
return nil, types.RemoveUserOutput{DeletedUserID: input.ProfileID}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) ArchiveUserTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ArchiveUserInput) (*mcp.CallToolResult, types.ArchiveUserOutput, error) {
|
func (r *Resolver) DeactivateUserTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeactivateUserInput) (*mcp.CallToolResult, types.DeactivateUserOutput, error) {
|
||||||
scope, err := r.Authorize(ctx, input.ProfileID, iam.ActionMembershipProfileDelete)
|
scope, err := r.Authorize(ctx, input.ProfileID, iam.ActionMembershipProfileDeactivate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.ArchiveUserOutput{}, err
|
return nil, types.DeactivateUserOutput{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.iamSvc.OrganizationService.ArchiveUser(ctx, scope, input.OrganizationID, input.ProfileID)
|
err = r.iamSvc.OrganizationService.DeactivateUser(ctx, scope, input.OrganizationID, input.ProfileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := errors.AsType[*iam.ErrUserManagedBySCIM](err); ok {
|
if _, ok := errors.AsType[*iam.ErrUserManagedBySCIM](err); ok {
|
||||||
return nil, types.ArchiveUserOutput{}, fmt.Errorf("user is managed by SCIM and cannot be archived: %w", err)
|
return nil, types.DeactivateUserOutput{}, fmt.Errorf("user is managed by SCIM and cannot be deactivated: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := errors.AsType[*iam.ErrLastActiveOwner](err); ok {
|
if _, ok := errors.AsType[*iam.ErrLastActiveOwner](err); ok {
|
||||||
return nil, types.ArchiveUserOutput{}, fmt.Errorf("cannot archive last active owner: %w", err)
|
return nil, types.DeactivateUserOutput{}, fmt.Errorf("cannot deactivate last active owner: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, types.ArchiveUserOutput{}, fmt.Errorf("archive user: %w", err)
|
return nil, types.DeactivateUserOutput{}, fmt.Errorf("deactivate user: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, types.ArchiveUserOutput{ArchivedUserID: input.ProfileID}, nil
|
return nil, types.DeactivateUserOutput{DeactivatedUserID: input.ProfileID}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) DeleteDataProtectionImpactAssessmentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteDataProtectionImpactAssessmentInput) (*mcp.CallToolResult, types.DeleteDataProtectionImpactAssessmentOutput, error) {
|
func (r *Resolver) DeleteDataProtectionImpactAssessmentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteDataProtectionImpactAssessmentInput) (*mcp.CallToolResult, types.DeleteDataProtectionImpactAssessmentOutput, error) {
|
||||||
|
|||||||
@@ -1698,7 +1698,7 @@ components:
|
|||||||
$ref: "#/components/schemas/GID"
|
$ref: "#/components/schemas/GID"
|
||||||
description: Deleted user (profile) ID
|
description: Deleted user (profile) ID
|
||||||
|
|
||||||
ArchiveUserInput:
|
DeactivateUserInput:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- organization_id
|
- organization_id
|
||||||
@@ -1709,16 +1709,16 @@ components:
|
|||||||
description: Organization ID
|
description: Organization ID
|
||||||
profile_id:
|
profile_id:
|
||||||
$ref: "#/components/schemas/GID"
|
$ref: "#/components/schemas/GID"
|
||||||
description: User (profile) ID to archive
|
description: User (profile) ID to deactivate
|
||||||
|
|
||||||
ArchiveUserOutput:
|
DeactivateUserOutput:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- archived_user_id
|
- deactivated_user_id
|
||||||
properties:
|
properties:
|
||||||
archived_user_id:
|
deactivated_user_id:
|
||||||
$ref: "#/components/schemas/GID"
|
$ref: "#/components/schemas/GID"
|
||||||
description: Archived user (profile) ID
|
description: Deactivated user (profile) ID
|
||||||
|
|
||||||
GetProfileInput:
|
GetProfileInput:
|
||||||
type: object
|
type: object
|
||||||
@@ -12679,14 +12679,14 @@ tools:
|
|||||||
$ref: "#/components/schemas/RemoveUserInput"
|
$ref: "#/components/schemas/RemoveUserInput"
|
||||||
outputSchema:
|
outputSchema:
|
||||||
$ref: "#/components/schemas/RemoveUserOutput"
|
$ref: "#/components/schemas/RemoveUserOutput"
|
||||||
- name: archiveUser
|
- name: deactivateUser
|
||||||
description: Archive a user in the organization
|
description: Deactivate a user in the organization
|
||||||
hints:
|
hints:
|
||||||
readonly: false
|
readonly: false
|
||||||
inputSchema:
|
inputSchema:
|
||||||
$ref: "#/components/schemas/ArchiveUserInput"
|
$ref: "#/components/schemas/DeactivateUserInput"
|
||||||
outputSchema:
|
outputSchema:
|
||||||
$ref: "#/components/schemas/ArchiveUserOutput"
|
$ref: "#/components/schemas/DeactivateUserOutput"
|
||||||
- name: addThirdParty
|
- name: addThirdParty
|
||||||
description: Add a new thirdParty to the organization
|
description: Add a new thirdParty to the organization
|
||||||
hints:
|
hints:
|
||||||
|
|||||||
Reference in New Issue
Block a user