fix(organization): multiple role array not referenced properly (#1792)

This commit is contained in:
Netrifier
2025-03-14 02:14:00 +05:30
committed by GitHub
parent 95cb5e2895
commit 3af315edbc
5 changed files with 80 additions and 18 deletions

View File

@@ -597,7 +597,7 @@ auth.api.addMember({
body: {
userId: "user-id",
organizationId: "organization-id",
role: "admin"
role: "admin" // this can also be an array for multiple roles (e.g. ["admin", "sale"])
}
})
```

View File

@@ -250,6 +250,18 @@ describe("organization", async (it) => {
},
);
it("should create invitation with multiple roles", async () => {
const invite = await client.organization.inviteMember({
organizationId: organizationId,
email: "test5@test.com",
role: ["admin", "member"],
fetchOptions: {
headers,
},
});
expect(invite.data?.role).toBe("admin,member");
});
it("should allow getting a member", async () => {
const { headers } = await signInWithTestUser();
await client.organization.setActive({
@@ -519,6 +531,36 @@ describe("organization", async (it) => {
expect(member?.role).toBe("admin");
});
it("should add member on the server with multiple roles", async () => {
const newUser = await auth.api.signUpEmail({
body: {
email: "new-member-mr@email.com",
password: "password",
name: "new member mr",
},
});
const session = await auth.api.getSession({
headers: new Headers({
Authorization: `Bearer ${newUser?.token}`,
}),
});
const org = await auth.api.createOrganization({
body: {
name: "test2",
slug: "test4",
},
headers,
});
const member = await auth.api.addMember({
body: {
organizationId: org?.id,
userId: session?.user.id!,
role: ["admin", "member"],
},
});
expect(member?.role).toBe("admin,member");
});
it("should respect membershipLimit when adding members to organization", async () => {
const org = await auth.api.createOrganization({
body: {

View File

@@ -44,6 +44,10 @@ import { ORGANIZATION_ERROR_CODES } from "./error-codes";
import { defaultRoles, defaultStatements } from "./access";
import { hasPermission } from "./has-permission";
export function parseRoles(roles: string | string[]): string {
return Array.isArray(roles) ? roles.join(",") : roles;
}
export interface OrganizationOptions {
/**
* Configure whether new users are able to create new organizations.

View File

@@ -5,7 +5,7 @@ import { getOrgAdapter } from "../adapter";
import { orgMiddleware, orgSessionMiddleware } from "../call";
import { type InferRolesFromOption } from "../schema";
import { APIError } from "better-call";
import type { OrganizationOptions } from "../organization";
import { parseRoles, type OrganizationOptions } from "../organization";
import { ORGANIZATION_ERROR_CODES } from "../error-codes";
import { hasPermission } from "../has-permission";
@@ -21,9 +21,16 @@ export const createInvitation = <O extends OrganizationOptions | undefined>(
email: z.string({
description: "The email address of the user to invite",
}),
role: z.string({
description: "The role to assign to the user",
}),
role: z.union([
z.string({
description: "The role to assign to the user",
}),
z.array(
z.string({
description: "The roles to assign to the user",
}),
),
]),
organizationId: z
.string({
description: "The organization ID to invite the user to",
@@ -52,7 +59,7 @@ export const createInvitation = <O extends OrganizationOptions | undefined>(
/**
* The role to assign to the user
*/
role: InferRolesFromOption<O>;
role: InferRolesFromOption<O> | InferRolesFromOption<O>[];
/**
* The organization ID to invite
* the user to
@@ -166,7 +173,12 @@ export const createInvitation = <O extends OrganizationOptions | undefined>(
const creatorRole = ctx.context.orgOptions.creatorRole || "owner";
if (member.role !== creatorRole && ctx.body.role === creatorRole) {
const roles = parseRoles(ctx.body.role as string | string[]);
if (
member.role !== creatorRole &&
roles.split(",").includes(creatorRole)
) {
throw new APIError("FORBIDDEN", {
message:
ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE,
@@ -195,9 +207,7 @@ export const createInvitation = <O extends OrganizationOptions | undefined>(
}
const invitation = await adapter.createInvitation({
invitation: {
role: Array.isArray(ctx.body.role)
? ctx.body.role.join(",")
: (ctx.body.role as string),
role: roles,
email: ctx.body.email,
organizationId: organizationId,
...("teamId" in ctx.body

View File

@@ -5,7 +5,7 @@ import { orgMiddleware, orgSessionMiddleware } from "../call";
import type { InferRolesFromOption, Member } from "../schema";
import { APIError } from "better-call";
import { generateId } from "../../../utils";
import type { OrganizationOptions } from "../organization";
import { parseRoles, type OrganizationOptions } from "../organization";
import { getSessionFromCtx, sessionMiddleware } from "../../../api";
import { ORGANIZATION_ERROR_CODES } from "../error-codes";
import { BASE_ERROR_CODES } from "../../../error/codes";
@@ -16,14 +16,18 @@ export const addMember = <O extends OrganizationOptions>() =>
"/organization/add-member",
{
method: "POST",
body: z.record(z.string()),
body: z.object({
userId: z.string(),
role: z.union([z.string(), z.array(z.string())]),
organizationId: z.string().optional(),
}),
use: [orgMiddleware],
metadata: {
SERVER_ONLY: true,
$Infer: {
body: {} as {
userId: string;
role: InferRolesFromOption<O>;
role: InferRolesFromOption<O> | InferRolesFromOption<O>[];
organizationId?: string;
},
},
@@ -86,7 +90,7 @@ export const addMember = <O extends OrganizationOptions>() =>
id: generateId(),
organizationId: orgId,
userId: user.id,
role: ctx.body.role as string,
role: parseRoles(ctx.body.role as string | string[]),
createdAt: new Date(),
});
@@ -257,7 +261,11 @@ export const updateMemberRole = <O extends OrganizationOptions>(option: O) =>
"/organization/update-member-role",
{
method: "POST",
body: z.record(z.any()),
body: z.object({
role: z.union([z.string(), z.array(z.string())]),
memberId: z.string(),
organizationId: z.string().optional(),
}),
use: [orgMiddleware, orgSessionMiddleware],
metadata: {
$Infer: {
@@ -380,9 +388,7 @@ export const updateMemberRole = <O extends OrganizationOptions>(option: O) =>
const updatedMember = await adapter.updateMember(
ctx.body.memberId,
Array.isArray(ctx.body.role)
? ctx.body.role?.join(",")
: (ctx.body.role as string),
parseRoles(ctx.body.role as string | string[]),
);
if (!updatedMember) {
throw new APIError("BAD_REQUEST", {