Fixed bugs in memberships management

This commit is contained in:
giancarlo
2024-04-20 18:12:04 +08:00
parent efd27aa7de
commit bf0d2e1c87
10 changed files with 2114 additions and 2255 deletions

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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 };
}

View File

@@ -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,