mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-26 08:56:40 -05:00
fix(organization): multiple role array not referenced properly (#1792)
This commit is contained in:
@@ -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"])
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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", {
|
||||
|
||||
Reference in New Issue
Block a user