Fixed bugs in memberships management
This commit is contained in:
@@ -56,16 +56,23 @@ export function AccountMembersTable({
|
||||
const { t } = useTranslation('teams');
|
||||
|
||||
const permissions = {
|
||||
canUpdateRole: (targetRole: number) =>
|
||||
canManageRoles && targetRole < userRoleHierarchy,
|
||||
canRemoveFromAccount: (targetRole: number) =>
|
||||
canManageRoles && targetRole < userRoleHierarchy,
|
||||
canUpdateRole: (targetRole: number) => {
|
||||
return (
|
||||
isPrimaryOwner || (canManageRoles && userRoleHierarchy < targetRole)
|
||||
);
|
||||
},
|
||||
canRemoveFromAccount: (targetRole: number) => {
|
||||
return (
|
||||
isPrimaryOwner || (canManageRoles && userRoleHierarchy < targetRole)
|
||||
);
|
||||
},
|
||||
canTransferOwnership: isPrimaryOwner,
|
||||
};
|
||||
|
||||
const columns = useGetColumns(permissions, {
|
||||
currentUserId,
|
||||
currentAccountId,
|
||||
currentRoleHierarchy: userRoleHierarchy,
|
||||
});
|
||||
|
||||
const filteredMembers = members.filter((member) => {
|
||||
@@ -96,6 +103,7 @@ function useGetColumns(
|
||||
params: {
|
||||
currentUserId: string;
|
||||
currentAccountId: string;
|
||||
currentRoleHierarchy: number;
|
||||
},
|
||||
): ColumnDef<Members[0]>[] {
|
||||
const { t } = useTranslation('teams');
|
||||
@@ -173,6 +181,7 @@ function useGetColumns(
|
||||
member={row.original}
|
||||
currentUserId={params.currentUserId}
|
||||
accountId={params.currentAccountId}
|
||||
currentRoleHierarchy={params.currentRoleHierarchy}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@@ -185,12 +194,13 @@ function ActionsDropdown({
|
||||
permissions,
|
||||
member,
|
||||
currentUserId,
|
||||
accountId,
|
||||
currentRoleHierarchy,
|
||||
}: {
|
||||
permissions: Permissions;
|
||||
member: Members[0];
|
||||
currentUserId: string;
|
||||
accountId: string;
|
||||
currentRoleHierarchy: number;
|
||||
}) {
|
||||
const [isRemoving, setIsRemoving] = useState(false);
|
||||
const [isTransferring, setIsTransferring] = useState(false);
|
||||
@@ -262,10 +272,10 @@ function ActionsDropdown({
|
||||
<UpdateMemberRoleDialog
|
||||
isOpen
|
||||
setIsOpen={setIsUpdatingRole}
|
||||
accountId={member.id}
|
||||
userId={member.user_id}
|
||||
userRole={member.role}
|
||||
userRoleHierarchy={memberRoleHierarchy}
|
||||
teamAccountId={member.account_id}
|
||||
userRoleHierarchy={currentRoleHierarchy}
|
||||
/>
|
||||
</If>
|
||||
|
||||
@@ -274,7 +284,7 @@ function ActionsDropdown({
|
||||
isOpen
|
||||
setIsOpen={setIsTransferring}
|
||||
targetDisplayName={member.name ?? member.email}
|
||||
accountId={accountId}
|
||||
accountId={member.account_id}
|
||||
userId={member.user_id}
|
||||
/>
|
||||
</If>
|
||||
|
||||
@@ -36,14 +36,14 @@ export const UpdateMemberRoleDialog: React.FC<{
|
||||
isOpen: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
userId: string;
|
||||
accountId: string;
|
||||
teamAccountId: string;
|
||||
userRole: Role;
|
||||
userRoleHierarchy: number;
|
||||
}> = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
userId,
|
||||
accountId,
|
||||
teamAccountId,
|
||||
userRole,
|
||||
userRoleHierarchy,
|
||||
}) => {
|
||||
@@ -61,14 +61,14 @@ export const UpdateMemberRoleDialog: React.FC<{
|
||||
</DialogHeader>
|
||||
|
||||
<RolesDataProvider
|
||||
accountId={accountId}
|
||||
accountId={teamAccountId}
|
||||
maxRoleHierarchy={userRoleHierarchy}
|
||||
>
|
||||
{(data) => (
|
||||
<UpdateMemberForm
|
||||
setIsOpen={setIsOpen}
|
||||
userId={userId}
|
||||
accountId={accountId}
|
||||
teamAccountId={teamAccountId}
|
||||
userRole={userRole}
|
||||
roles={data}
|
||||
/>
|
||||
@@ -82,13 +82,13 @@ export const UpdateMemberRoleDialog: React.FC<{
|
||||
function UpdateMemberForm({
|
||||
userId,
|
||||
userRole,
|
||||
accountId,
|
||||
teamAccountId,
|
||||
setIsOpen,
|
||||
roles,
|
||||
}: React.PropsWithChildren<{
|
||||
userId: string;
|
||||
userRole: Role;
|
||||
accountId: string;
|
||||
teamAccountId: string;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
roles: Role[];
|
||||
}>) {
|
||||
@@ -99,7 +99,11 @@ function UpdateMemberForm({
|
||||
const onSubmit = ({ role }: { role: Role }) => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
await updateMemberRoleAction({ accountId, userId, role });
|
||||
await updateMemberRoleAction({
|
||||
accountId: teamAccountId,
|
||||
userId,
|
||||
role,
|
||||
});
|
||||
|
||||
setIsOpen(false);
|
||||
} catch (e) {
|
||||
|
||||
@@ -33,6 +33,9 @@ export async function removeMemberFromAccountAction(
|
||||
userId,
|
||||
});
|
||||
|
||||
// revalidate all pages that depend on the account
|
||||
revalidatePath('/home/[account]', 'layout');
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@@ -44,12 +47,20 @@ export async function updateMemberRoleAction(
|
||||
await assertSession(client);
|
||||
|
||||
const service = new AccountMembersService(client);
|
||||
const adminClient = getSupabaseServerActionClient({ admin: true });
|
||||
|
||||
await service.updateMemberRole({
|
||||
accountId: params.accountId,
|
||||
userId: params.userId,
|
||||
role: params.role,
|
||||
});
|
||||
// update the role of the member
|
||||
await service.updateMemberRole(
|
||||
{
|
||||
accountId: params.accountId,
|
||||
userId: params.userId,
|
||||
role: params.role,
|
||||
},
|
||||
adminClient,
|
||||
);
|
||||
|
||||
// revalidate all pages that depend on the account
|
||||
revalidatePath('/home/[account]', 'layout');
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@@ -59,7 +59,10 @@ export class AccountMembersService {
|
||||
return data;
|
||||
}
|
||||
|
||||
async updateMemberRole(params: z.infer<typeof UpdateMemberRoleSchema>) {
|
||||
async updateMemberRole(
|
||||
params: z.infer<typeof UpdateMemberRoleSchema>,
|
||||
adminClient: SupabaseClient<Database>,
|
||||
) {
|
||||
const logger = await getLogger();
|
||||
|
||||
const ctx = {
|
||||
@@ -67,9 +70,33 @@ export class AccountMembersService {
|
||||
...params,
|
||||
};
|
||||
|
||||
logger.info(ctx, `Updating member role...`);
|
||||
logger.info(ctx, `Validating permissions to update member role...`);
|
||||
|
||||
const { data, error } = await this.client
|
||||
const { data: canActionAccountMember, error: accountError } =
|
||||
await this.client.rpc('can_action_account_member', {
|
||||
user_id: params.userId,
|
||||
target_team_account_id: params.accountId,
|
||||
});
|
||||
|
||||
if (accountError ?? !canActionAccountMember) {
|
||||
logger.error(
|
||||
{
|
||||
...ctx,
|
||||
accountError,
|
||||
},
|
||||
`Failed to validate permissions to update member role`,
|
||||
);
|
||||
|
||||
throw new Error(`Failed to validate permissions to update member role`);
|
||||
}
|
||||
|
||||
logger.info(ctx, `Permissions validated. Updating member role...`);
|
||||
|
||||
// we use the Admin client to update the role
|
||||
// since we do not set any RLS policies on the accounts_memberships table
|
||||
// for updating accounts_memberships. Instead, we use the can_action_account_member
|
||||
// RPC to validate permissions to update the role
|
||||
const { data, error } = await adminClient
|
||||
.from('accounts_memberships')
|
||||
.update({
|
||||
account_role: params.role,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user