Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement updateRole #10009

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Implement updateRole #10009

wants to merge 2 commits into from

Conversation

ijreilly
Copy link
Collaborator

@ijreilly ijreilly commented Feb 4, 2025

In this PR, we are implementing the updateRole endpoint with the following rules

  1. A user can only update a member's role if they have the permission (= the admin role)
  2. Admin role can't be unassigned if there are no other admin in the workspace
  3. (For now) as members can only have one role for now, when they are assigned a new role, they are first unassigned the other role (if any)
  4. (For now) removing a member's admin role = leaving the member with no role = calling updateRole with a null roleId

}

const userWorkspace = userWorkspaces?.find(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to do - use a map

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR implements a comprehensive role management system with permission checks for workspace members, focusing on role assignments and admin role protection.

  • Added updateWorkspaceMemberRole mutation in /packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts with admin permission validation
  • Implemented safeguards in /packages/twenty-server/src/engine/metadata-modules/userRole/userRole.service.ts to prevent unassigning the last admin role
  • Enhanced WorkspaceMember DTO in /packages/twenty-server/src/engine/core-modules/user/dtos/workspace-member.dto.ts with roles array and userWorkspaceId fields
  • Added new permission exception codes in /packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts for role-specific error handling
  • Modified workspaceMembers resolver in /packages/twenty-server/src/engine/core-modules/user/user.resolver.ts to include role information

7 file(s) reviewed, 9 comment(s)
Edit PR Review Bot Settings | Greptile

const avatarUrlToken = await this.fileService.encodeFileToken({
workspaceMemberId: workspaceMember.id,
workspaceMemberEntityId: workspaceMemberEntity.id,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: workspaceMemberEntityId is used here but workspaceMemberId is used on line 150. This inconsistency could cause issues with file token generation.

Suggested change
workspaceMemberEntityId: workspaceMemberEntity.id,
workspaceMemberId: workspaceMemberEntity.id,

Comment on lines 199 to 201
const role = await this.userRoleService
.getRolesForUserWorkspace(userWorkspace.id)
.then(([roleEntity]) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: getRolesForUserWorkspace returns an array but only the first role is used. This should be documented or handled explicitly if multiple roles are not supported.

Comment on lines +195 to +197
if (!userWorkspace) {
throw new Error('User workspace not found');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: This error should include more context like the userWorkspace.id to help with debugging

Comment on lines +77 to +82
if (!isDefined(roleId)) {
return this.userRoleService.unassignRolesFromWorkspaceMember({
workspaceMemberId,
workspaceId: workspace.id,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing validation to prevent unassigning the last admin role from a workspace. This could leave the workspace without any admin users.

Comment on lines +84 to +88
return this.userRoleService.assignRoleToWorkspaceMember({
workspaceId: workspace.id,
workspaceMemberId,
roleId,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: No error handling around the assignRoleToWorkspaceMember call. Should wrap in try/catch to handle potential database errors or role assignment failures gracefully.

Comment on lines +67 to +68
@Args('roleId', { type: () => String, nullable: true })
roleId: string | null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider validating that roleId exists in the workspace before attempting assignment. Currently no validation that the role is valid.

): Promise<void> {
const roles = await this.getRolesForUserWorkspace(userWorkspaceId);

const adminRole = roles.find((role: RoleDTO) => role.isEditable === false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: isEditable === false is not a reliable way to identify admin roles. Consider adding an explicit isAdmin flag or role type enum

Comment on lines +176 to +184
if (!isEmpty(userWorkspaceRoles)) {
await Promise.all(
userWorkspaceRoles.map((userWorkspaceRole) =>
this.userWorkspaceRoleRepository.delete({
id: userWorkspaceRole.id,
}),
),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Transaction needed here to ensure atomic role unassignment when multiple roles exist

Comment on lines +24 to +25
const [roleOfUserWorkspace] =
await this.userRoleService.getRolesForUserWorkspace(userWorkspaceId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Array destructuring assumes first role is the most relevant. Consider what happens if user has multiple roles with different permissions - could lead to incorrect permission checks.

Suggested change
const [roleOfUserWorkspace] =
await this.userRoleService.getRolesForUserWorkspace(userWorkspaceId);
const userWorkspaceRoles =
await this.userRoleService.getRolesForUserWorkspace(userWorkspaceId);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant