mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-10 04:19:32 +00:00
feat(organization): support multiple permissions check (#2227)
* feat: remove the artificial resource limit so that code can check Also change `permission` to `permissions` (clearer for end user). `permission` is left for backwards compatibility. * docs: add examples for multiple perms checking * refactor: check `permissions` first, then legacy one * feat: use union types for `permission` & `permissions` * fix: properly use union types * fix: remove accidental `@deprecated` comment * chore: lint * fix test * chore: add oneTimeToken plugin to client barrel exports (#2224) * docs(expo): add id token usage * feat(oauth2): override user info on provider sign-in (#2148) * feat(oauth2): override user info on provider sign-in * improve email verification handling * resolve mrge * fix(sso): update overrideUserInfo handling to use provider configuration * fix param * chore: change plugin interface middleware type (#2195) * fix: delete from session table when stopImpersonate called (#2230) * chore: fix active organization inferred type * chore: fix admin test --------- Co-authored-by: Bereket Engida <bekacru@gmail.com> Co-authored-by: Wade Fletcher <3798059+wadefletch@users.noreply.github.com> Co-authored-by: Bereket Engida <86073083+Bekacru@users.noreply.github.com> Co-authored-by: KinfeMichael Tariku <65047246+Kinfe123@users.noreply.github.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import { MysqlDialect } from "kysely";
|
|||||||
import { createPool } from "mysql2/promise";
|
import { createPool } from "mysql2/promise";
|
||||||
import { nextCookies } from "better-auth/next-js";
|
import { nextCookies } from "better-auth/next-js";
|
||||||
import { passkey } from "better-auth/plugins/passkey";
|
import { passkey } from "better-auth/plugins/passkey";
|
||||||
|
import { expo } from "@better-auth/expo";
|
||||||
import { stripe } from "@better-auth/stripe";
|
import { stripe } from "@better-auth/stripe";
|
||||||
import { Stripe } from "stripe";
|
import { Stripe } from "stripe";
|
||||||
|
|
||||||
@@ -197,5 +198,7 @@ export const auth = betterAuth({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
expo(),
|
||||||
],
|
],
|
||||||
|
trustedOrigins: ["exp://"],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -416,10 +416,18 @@ To check a user's permissions, you can use the `hasPermission` function provided
|
|||||||
|
|
||||||
```ts title="auth-client.ts"
|
```ts title="auth-client.ts"
|
||||||
const canCreateProject = await authClient.admin.hasPermission({
|
const canCreateProject = await authClient.admin.hasPermission({
|
||||||
permission: {
|
permissions: {
|
||||||
project: ["create"],
|
project: ["create"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// You can also check multiple resource permissions at the same time
|
||||||
|
const canCreateProjectAndCreateSale = await authClient.admin.hasPermission({
|
||||||
|
permissions: {
|
||||||
|
project: ["create"],
|
||||||
|
sale: ["create"]
|
||||||
|
},
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to check a user's permissions server-side, you can use the `userHasPermission` action provided by the `api` to check the user's permissions.
|
If you want to check a user's permissions server-side, you can use the `userHasPermission` action provided by the `api` to check the user's permissions.
|
||||||
@@ -429,21 +437,32 @@ import { auth } from "@/auth";
|
|||||||
auth.api.userHasPermission({
|
auth.api.userHasPermission({
|
||||||
body: {
|
body: {
|
||||||
userId: 'id', //the user id
|
userId: 'id', //the user id
|
||||||
permission: {
|
permissions: {
|
||||||
project: ["create"], // This must match the structure in your access control
|
project: ["create"], // This must match the structure in your access control
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
//you can also just pass the role directly
|
// You can also just pass the role directly
|
||||||
auth.api.userHasPermission({
|
auth.api.userHasPermission({
|
||||||
body: {
|
body: {
|
||||||
role: "admin",
|
role: "admin",
|
||||||
permission: {
|
permissions: {
|
||||||
project: ["create"], // This must match the structure in your access control
|
project: ["create"], // This must match the structure in your access control
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// You can also check multiple resource permissions at the same time
|
||||||
|
auth.api.userHasPermission({
|
||||||
|
body: {
|
||||||
|
role: "admin",
|
||||||
|
permissions: {
|
||||||
|
project: ["create"], // This must match the structure in your access control
|
||||||
|
sale: ["create"]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -453,11 +472,20 @@ Once you have defined the roles and permissions to avoid checking the permission
|
|||||||
|
|
||||||
```ts title="auth-client.ts"
|
```ts title="auth-client.ts"
|
||||||
const canCreateProject = client.admin.checkRolePermission({
|
const canCreateProject = client.admin.checkRolePermission({
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["delete"],
|
user: ["delete"],
|
||||||
},
|
},
|
||||||
role: "admin",
|
role: "admin",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// You can also check multiple resource permissions at the same time
|
||||||
|
const canCreateProjectAndRevokeSession = client.admin.checkRolePermission({
|
||||||
|
permissions: {
|
||||||
|
user: ["delete"],
|
||||||
|
session: ["revoke"]
|
||||||
|
},
|
||||||
|
role: "admin",
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Schema
|
## Schema
|
||||||
@@ -584,4 +612,3 @@ admin({
|
|||||||
bannedUserMessage: "Custom banned user message",
|
bannedUserMessage: "Custom banned user message",
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -803,21 +803,40 @@ import { auth } from "@/auth";
|
|||||||
auth.api.hasPermission({
|
auth.api.hasPermission({
|
||||||
headers: await headers(),
|
headers: await headers(),
|
||||||
body: {
|
body: {
|
||||||
permission: {
|
permissions: {
|
||||||
project: ["create"] // This must match the structure in your access control
|
project: ["create"] // This must match the structure in your access control
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// You can also check multiple resource permissions at the same time
|
||||||
|
auth.api.hasPermission({
|
||||||
|
headers: await headers(),
|
||||||
|
body: {
|
||||||
|
permissions: {
|
||||||
|
project: ["create"], // This must match the structure in your access control
|
||||||
|
sale: ["create"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to check the permission of the user on the client from the server you can use the `hasPermission` function provided by the client.
|
If you want to check the permission of the user on the client from the server you can use the `hasPermission` function provided by the client.
|
||||||
|
|
||||||
```ts title="auth-client.ts"
|
```ts title="auth-client.ts"
|
||||||
const canCreateProject = await authClient.organization.hasPermission({
|
const canCreateProject = await authClient.organization.hasPermission({
|
||||||
permission: {
|
permissions: {
|
||||||
project: ["create"]
|
project: ["create"]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// You can also check multiple resource permissions at the same time
|
||||||
|
const canCreateProjectAndCreateSale = await authClient.organization.hasPermission({
|
||||||
|
permissions: {
|
||||||
|
project: ["create"],
|
||||||
|
sale: ["create"]
|
||||||
|
}
|
||||||
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
**Check Role Permission**:
|
**Check Role Permission**:
|
||||||
@@ -826,11 +845,20 @@ Once you have defined the roles and permissions to avoid checking the permission
|
|||||||
|
|
||||||
```ts title="auth-client.ts"
|
```ts title="auth-client.ts"
|
||||||
const canCreateProject = client.organization.checkRolePermission({
|
const canCreateProject = client.organization.checkRolePermission({
|
||||||
permission: {
|
permissions: {
|
||||||
organization: ["delete"],
|
organization: ["delete"],
|
||||||
},
|
},
|
||||||
role: "admin",
|
role: "admin",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// You can also check multiple resource permissions at the same time
|
||||||
|
const canCreateProjectAndCreateSale = client.organization.checkRolePermission({
|
||||||
|
permissions: {
|
||||||
|
organization: ["delete"],
|
||||||
|
member: ["delete"]
|
||||||
|
},
|
||||||
|
role: "admin",
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Teams
|
## Teams
|
||||||
|
|||||||
@@ -624,7 +624,7 @@ describe("Admin plugin", async () => {
|
|||||||
|
|
||||||
describe("access control", async (it) => {
|
describe("access control", async (it) => {
|
||||||
const ac = createAccessControl({
|
const ac = createAccessControl({
|
||||||
user: ["create", "read", "update", "delete", "list"],
|
user: ["create", "read", "update", "delete", "list", "bulk-delete"],
|
||||||
order: ["create", "read", "update", "delete", "update-many"],
|
order: ["create", "read", "update", "delete", "update-many"],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -699,61 +699,136 @@ describe("access control", async (it) => {
|
|||||||
it("should validate on the client", async () => {
|
it("should validate on the client", async () => {
|
||||||
const canCreateOrder = client.admin.checkRolePermission({
|
const canCreateOrder = client.admin.checkRolePermission({
|
||||||
role: "admin",
|
role: "admin",
|
||||||
permission: {
|
permissions: {
|
||||||
order: ["create"],
|
order: ["create"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(canCreateOrder).toBe(true);
|
expect(canCreateOrder).toBe(true);
|
||||||
|
|
||||||
|
// To be removed when `permission` will be removed entirely
|
||||||
|
const canCreateOrderLegacy = client.admin.checkRolePermission({
|
||||||
|
role: "admin",
|
||||||
|
permission: {
|
||||||
|
order: ["create"],
|
||||||
|
user: ["read"],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(canCreateOrderLegacy).toBe(true);
|
||||||
|
|
||||||
|
const canCreateOrderAndReadUser = client.admin.checkRolePermission({
|
||||||
|
role: "admin",
|
||||||
|
permissions: {
|
||||||
|
order: ["create"],
|
||||||
|
user: ["read"],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(canCreateOrderAndReadUser).toBe(true);
|
||||||
|
|
||||||
const canCreateUser = client.admin.checkRolePermission({
|
const canCreateUser = client.admin.checkRolePermission({
|
||||||
role: "user",
|
role: "user",
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["create"],
|
user: ["create"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(canCreateUser).toBe(false);
|
expect(canCreateUser).toBe(false);
|
||||||
|
|
||||||
|
const canCreateOrderAndCreateUser = client.admin.checkRolePermission({
|
||||||
|
role: "user",
|
||||||
|
permissions: {
|
||||||
|
order: ["create"],
|
||||||
|
user: ["create"],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(canCreateOrderAndCreateUser).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should validate using userId", async () => {
|
it("should validate using userId", async () => {
|
||||||
const canCreateUser = await auth.api.userHasPermission({
|
const canCreateUser = await auth.api.userHasPermission({
|
||||||
body: {
|
body: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["create"],
|
user: ["create"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(canCreateUser.success).toBe(true);
|
expect(canCreateUser.success).toBe(true);
|
||||||
|
|
||||||
|
const canCreateUserAndCreateOrder = await auth.api.userHasPermission({
|
||||||
|
body: {
|
||||||
|
userId: user.id,
|
||||||
|
permissions: {
|
||||||
|
user: ["create"],
|
||||||
|
order: ["create"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(canCreateUserAndCreateOrder.success).toBe(true);
|
||||||
|
|
||||||
const canUpdateManyOrder = await auth.api.userHasPermission({
|
const canUpdateManyOrder = await auth.api.userHasPermission({
|
||||||
body: {
|
body: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
permission: {
|
permissions: {
|
||||||
order: ["update-many"],
|
order: ["update-many"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(canUpdateManyOrder.success).toBe(false);
|
expect(canUpdateManyOrder.success).toBe(false);
|
||||||
|
|
||||||
|
const canUpdateManyOrderAndBulkDeleteUser =
|
||||||
|
await auth.api.userHasPermission({
|
||||||
|
body: {
|
||||||
|
userId: user.id,
|
||||||
|
permissions: {
|
||||||
|
user: ["bulk-delete"],
|
||||||
|
order: ["update-many"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(canUpdateManyOrderAndBulkDeleteUser.success).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should validate using role", async () => {
|
it("should validate using role", async () => {
|
||||||
const canCreateUser = await auth.api.userHasPermission({
|
const canCreateUser = await auth.api.userHasPermission({
|
||||||
body: {
|
body: {
|
||||||
role: "admin",
|
role: "admin",
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["create"],
|
user: ["create"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(canCreateUser.success).toBe(true);
|
expect(canCreateUser.success).toBe(true);
|
||||||
|
|
||||||
|
const canCreateUserAndCreateOrder = await auth.api.userHasPermission({
|
||||||
|
body: {
|
||||||
|
role: "admin",
|
||||||
|
permissions: {
|
||||||
|
user: ["create"],
|
||||||
|
order: ["create"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(canCreateUserAndCreateOrder.success).toBe(true);
|
||||||
|
|
||||||
const canUpdateOrder = await auth.api.userHasPermission({
|
const canUpdateOrder = await auth.api.userHasPermission({
|
||||||
body: {
|
body: {
|
||||||
role: "user",
|
role: "user",
|
||||||
permission: {
|
permissions: {
|
||||||
order: ["update"],
|
order: ["update"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(canUpdateOrder.success).toBe(false);
|
expect(canUpdateOrder.success).toBe(false);
|
||||||
|
|
||||||
|
const canUpdateOrderAndUpdateUser = await auth.api.userHasPermission({
|
||||||
|
body: {
|
||||||
|
role: "user",
|
||||||
|
permissions: {
|
||||||
|
order: ["update"],
|
||||||
|
user: ["update"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(canUpdateOrderAndUpdateUser.success).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shouldn't allow to list users", async () => {
|
it("shouldn't allow to list users", async () => {
|
||||||
|
|||||||
@@ -119,6 +119,26 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
? S
|
? S
|
||||||
: DefaultStatements;
|
: DefaultStatements;
|
||||||
|
|
||||||
|
type PermissionType = {
|
||||||
|
[key in keyof Statements]?: Array<
|
||||||
|
Statements[key] extends readonly unknown[]
|
||||||
|
? Statements[key][number]
|
||||||
|
: never
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
type PermissionExclusive =
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* @deprecated Use `permissions` instead
|
||||||
|
*/
|
||||||
|
permission: PermissionType;
|
||||||
|
permissions?: never;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
permissions: PermissionType;
|
||||||
|
permission?: never;
|
||||||
|
};
|
||||||
|
|
||||||
const adminMiddleware = createAuthMiddleware(async (ctx) => {
|
const adminMiddleware = createAuthMiddleware(async (ctx) => {
|
||||||
const session = await getSessionFromCtx(ctx);
|
const session = await getSessionFromCtx(ctx);
|
||||||
if (!session) {
|
if (!session) {
|
||||||
@@ -265,7 +285,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: ctx.context.session.user.role,
|
role: ctx.context.session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["set-role"],
|
user: ["set-role"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -370,7 +390,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: session.user.id,
|
userId: session.user.id,
|
||||||
role: session.user.role,
|
role: session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["create"],
|
user: ["create"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -528,7 +548,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: session.user.role,
|
role: session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["list"],
|
user: ["list"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -629,7 +649,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: session.user.role,
|
role: session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
session: ["list"],
|
session: ["list"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -689,7 +709,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: session.user.role,
|
role: session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["ban"],
|
user: ["ban"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -769,7 +789,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: session.user.role,
|
role: session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["ban"],
|
user: ["ban"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -848,7 +868,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: ctx.context.session.user.role,
|
role: ctx.context.session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["impersonate"],
|
user: ["impersonate"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1001,7 +1021,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: session.user.role,
|
role: session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
session: ["revoke"],
|
session: ["revoke"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1061,7 +1081,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: session.user.role,
|
role: session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
session: ["revoke"],
|
session: ["revoke"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1120,7 +1140,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: session.user.role,
|
role: session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["delete"],
|
user: ["delete"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1178,7 +1198,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: ctx.context.session.user.id,
|
userId: ctx.context.session.user.id,
|
||||||
role: ctx.context.session.user.role,
|
role: ctx.context.session.user.role,
|
||||||
options: opts,
|
options: opts,
|
||||||
permission: {
|
permissions: {
|
||||||
user: ["set-password"],
|
user: ["set-password"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1204,11 +1224,23 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
"/admin/has-permission",
|
"/admin/has-permission",
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: z.object({
|
body: z
|
||||||
permission: z.record(z.string(), z.array(z.string())),
|
.object({
|
||||||
userId: z.coerce.string().optional(),
|
userId: z.coerce.string().optional(),
|
||||||
role: z.string().optional(),
|
role: z.string().optional(),
|
||||||
|
})
|
||||||
|
.and(
|
||||||
|
z.union([
|
||||||
|
z.object({
|
||||||
|
permission: z.record(z.string(), z.array(z.string())),
|
||||||
|
permissions: z.undefined(),
|
||||||
}),
|
}),
|
||||||
|
z.object({
|
||||||
|
permission: z.undefined(),
|
||||||
|
permissions: z.record(z.string(), z.array(z.string())),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
),
|
||||||
metadata: {
|
metadata: {
|
||||||
openapi: {
|
openapi: {
|
||||||
description: "Check if the user has permission",
|
description: "Check if the user has permission",
|
||||||
@@ -1221,9 +1253,14 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
permission: {
|
permission: {
|
||||||
type: "object",
|
type: "object",
|
||||||
description: "The permission to check",
|
description: "The permission to check",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
permissions: {
|
||||||
|
type: "object",
|
||||||
|
description: "The permission to check",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
required: ["permission"],
|
required: ["permissions"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1251,11 +1288,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
$Infer: {
|
$Infer: {
|
||||||
body: {} as {
|
body: {} as PermissionExclusive & {
|
||||||
permission: {
|
|
||||||
//@ts-expect-error
|
|
||||||
[key in keyof Statements]?: Array<Statements[key][number]>;
|
|
||||||
};
|
|
||||||
userId?: string;
|
userId?: string;
|
||||||
role?: InferAdminRolesFromOption<O>;
|
role?: InferAdminRolesFromOption<O>;
|
||||||
},
|
},
|
||||||
@@ -1263,13 +1296,10 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
if (
|
if (!ctx.body?.permission && !ctx.body?.permissions) {
|
||||||
!ctx.body.permission ||
|
|
||||||
Object.keys(ctx.body.permission).length > 1
|
|
||||||
) {
|
|
||||||
throw new APIError("BAD_REQUEST", {
|
throw new APIError("BAD_REQUEST", {
|
||||||
message:
|
message:
|
||||||
"invalid permission check. you can only check one resource permission at a time.",
|
"invalid permission check. no permission(s) were passed.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const session = await getSessionFromCtx(ctx);
|
const session = await getSessionFromCtx(ctx);
|
||||||
@@ -1297,7 +1327,7 @@ export const admin = <O extends AdminOptions>(options?: O) => {
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
options: options as AdminOptions,
|
options: options as AdminOptions,
|
||||||
permission: ctx.body.permission as any,
|
permissions: (ctx.body.permissions ?? ctx.body.permission) as any,
|
||||||
});
|
});
|
||||||
return ctx.json({
|
return ctx.json({
|
||||||
error: null,
|
error: null,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { BetterAuthError } from "../../error";
|
|
||||||
import type { BetterAuthClientPlugin } from "../../types";
|
import type { BetterAuthClientPlugin } from "../../types";
|
||||||
import { type AccessControl, type Role } from "../access";
|
import { type AccessControl, type Role } from "../access";
|
||||||
import { adminAc, defaultStatements, userAc } from "./access";
|
import { adminAc, defaultStatements, userAc } from "./access";
|
||||||
@@ -17,6 +16,26 @@ export const adminClient = <O extends AdminClientOptions>(options?: O) => {
|
|||||||
type Statements = O["ac"] extends AccessControl<infer S>
|
type Statements = O["ac"] extends AccessControl<infer S>
|
||||||
? S
|
? S
|
||||||
: DefaultStatements;
|
: DefaultStatements;
|
||||||
|
type PermissionType = {
|
||||||
|
[key in keyof Statements]?: Array<
|
||||||
|
Statements[key] extends readonly unknown[]
|
||||||
|
? Statements[key][number]
|
||||||
|
: never
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
type PermissionExclusive =
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* @deprecated Use `permissions` instead
|
||||||
|
*/
|
||||||
|
permission: PermissionType;
|
||||||
|
permissions?: never;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
permissions: PermissionType;
|
||||||
|
permission?: never;
|
||||||
|
};
|
||||||
|
|
||||||
const roles = {
|
const roles = {
|
||||||
admin: adminAc,
|
admin: adminAc,
|
||||||
user: userAc,
|
user: userAc,
|
||||||
@@ -44,25 +63,18 @@ export const adminClient = <O extends AdminClientOptions>(options?: O) => {
|
|||||||
R extends O extends { roles: any }
|
R extends O extends { roles: any }
|
||||||
? keyof O["roles"]
|
? keyof O["roles"]
|
||||||
: "admin" | "user",
|
: "admin" | "user",
|
||||||
>(data: {
|
>(
|
||||||
|
data: PermissionExclusive & {
|
||||||
role: R;
|
role: R;
|
||||||
permission: {
|
},
|
||||||
//@ts-expect-error fix this later
|
) => {
|
||||||
[key in keyof Statements]?: Statements[key][number][];
|
|
||||||
};
|
|
||||||
}) => {
|
|
||||||
if (Object.keys(data.permission).length > 1) {
|
|
||||||
throw new BetterAuthError(
|
|
||||||
"you can only check one resource permission at a time.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const isAuthorized = hasPermission({
|
const isAuthorized = hasPermission({
|
||||||
role: data.role as string,
|
role: data.role as string,
|
||||||
options: {
|
options: {
|
||||||
ac: options?.ac,
|
ac: options?.ac,
|
||||||
roles: roles,
|
roles: roles,
|
||||||
},
|
},
|
||||||
permission: data.permission as any,
|
permissions: (data.permissions ?? data.permission) as any,
|
||||||
});
|
});
|
||||||
return isAuthorized;
|
return isAuthorized;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,22 +1,37 @@
|
|||||||
import { defaultRoles } from "./access";
|
import { defaultRoles } from "./access";
|
||||||
import type { AdminOptions } from "./admin";
|
import type { AdminOptions } from "./admin";
|
||||||
|
|
||||||
export const hasPermission = (input: {
|
type PermissionExclusive =
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* @deprecated Use `permissions` instead
|
||||||
|
*/
|
||||||
|
permission: { [key: string]: string[] };
|
||||||
|
permissions?: never;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
permissions: { [key: string]: string[] };
|
||||||
|
permission?: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hasPermission = (
|
||||||
|
input: {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
role?: string;
|
role?: string;
|
||||||
options?: AdminOptions;
|
options?: AdminOptions;
|
||||||
permission: {
|
} & PermissionExclusive,
|
||||||
[key: string]: string[];
|
) => {
|
||||||
};
|
|
||||||
}) => {
|
|
||||||
if (input.userId && input.options?.adminUserIds?.includes(input.userId)) {
|
if (input.userId && input.options?.adminUserIds?.includes(input.userId)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!input.permissions && !input.permission) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const roles = (input.role || input.options?.defaultRole || "user").split(",");
|
const roles = (input.role || input.options?.defaultRole || "user").split(",");
|
||||||
const acRoles = input.options?.roles || defaultRoles;
|
const acRoles = input.options?.roles || defaultRoles;
|
||||||
for (const role of roles) {
|
for (const role of roles) {
|
||||||
const _role = acRoles[role as keyof typeof acRoles];
|
const _role = acRoles[role as keyof typeof acRoles];
|
||||||
const result = _role?.authorize(input.permission);
|
const result = _role?.authorize(input.permission ?? input.permissions);
|
||||||
if (result?.success) {
|
if (result?.success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { type AccessControl, type Role } from "../access";
|
|||||||
import type { BetterAuthClientPlugin } from "../../client/types";
|
import type { BetterAuthClientPlugin } from "../../client/types";
|
||||||
import type { organization } from "./organization";
|
import type { organization } from "./organization";
|
||||||
import { useAuthQuery } from "../../client";
|
import { useAuthQuery } from "../../client";
|
||||||
import { BetterAuthError } from "../../error";
|
|
||||||
import { defaultStatements, adminAc, memberAc, ownerAc } from "./access";
|
import { defaultStatements, adminAc, memberAc, ownerAc } from "./access";
|
||||||
import { hasPermission } from "./has-permission";
|
import { hasPermission } from "./has-permission";
|
||||||
|
|
||||||
@@ -37,6 +36,26 @@ export const organizationClient = <O extends OrganizationClientOptions>(
|
|||||||
type Statements = O["ac"] extends AccessControl<infer S>
|
type Statements = O["ac"] extends AccessControl<infer S>
|
||||||
? S
|
? S
|
||||||
: DefaultStatements;
|
: DefaultStatements;
|
||||||
|
type PermissionType = {
|
||||||
|
[key in keyof Statements]?: Array<
|
||||||
|
Statements[key] extends readonly unknown[]
|
||||||
|
? Statements[key][number]
|
||||||
|
: never
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
type PermissionExclusive =
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* @deprecated Use `permissions` instead
|
||||||
|
*/
|
||||||
|
permission: PermissionType;
|
||||||
|
permissions?: never;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
permissions: PermissionType;
|
||||||
|
permission?: never;
|
||||||
|
};
|
||||||
|
|
||||||
const roles = {
|
const roles = {
|
||||||
admin: adminAc,
|
admin: adminAc,
|
||||||
member: memberAc,
|
member: memberAc,
|
||||||
@@ -86,25 +105,18 @@ export const organizationClient = <O extends OrganizationClientOptions>(
|
|||||||
R extends O extends { roles: any }
|
R extends O extends { roles: any }
|
||||||
? keyof O["roles"]
|
? keyof O["roles"]
|
||||||
: "admin" | "member" | "owner",
|
: "admin" | "member" | "owner",
|
||||||
>(data: {
|
>(
|
||||||
|
data: PermissionExclusive & {
|
||||||
role: R;
|
role: R;
|
||||||
permission: {
|
},
|
||||||
//@ts-expect-error fix this later
|
) => {
|
||||||
[key in keyof Statements]?: Statements[key][number][];
|
|
||||||
};
|
|
||||||
}) => {
|
|
||||||
if (Object.keys(data.permission).length > 1) {
|
|
||||||
throw new BetterAuthError(
|
|
||||||
"you can only check one resource permission at a time.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const isAuthorized = hasPermission({
|
const isAuthorized = hasPermission({
|
||||||
role: data.role as string,
|
role: data.role as string,
|
||||||
options: {
|
options: {
|
||||||
ac: options?.ac,
|
ac: options?.ac,
|
||||||
roles: roles,
|
roles: roles,
|
||||||
},
|
},
|
||||||
permission: data.permission as any,
|
permissions: (data.permissions ?? data.permission) as any,
|
||||||
});
|
});
|
||||||
return isAuthorized;
|
return isAuthorized;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,18 +1,33 @@
|
|||||||
import { defaultRoles } from "./access";
|
import { defaultRoles } from "./access";
|
||||||
import type { OrganizationOptions } from "./organization";
|
import type { OrganizationOptions } from "./organization";
|
||||||
|
|
||||||
export const hasPermission = (input: {
|
type PermissionExclusive =
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* @deprecated Use `permissions` instead
|
||||||
|
*/
|
||||||
|
permission: { [key: string]: string[] };
|
||||||
|
permissions?: never;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
permissions: { [key: string]: string[] };
|
||||||
|
permission?: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hasPermission = (
|
||||||
|
input: {
|
||||||
role: string;
|
role: string;
|
||||||
options: OrganizationOptions;
|
options: OrganizationOptions;
|
||||||
permission: {
|
} & PermissionExclusive,
|
||||||
[key: string]: string[];
|
) => {
|
||||||
};
|
if (!input.permissions && !input.permission) {
|
||||||
}) => {
|
return false;
|
||||||
|
}
|
||||||
const roles = input.role.split(",");
|
const roles = input.role.split(",");
|
||||||
const acRoles = input.options.roles || defaultRoles;
|
const acRoles = input.options.roles || defaultRoles;
|
||||||
for (const role of roles) {
|
for (const role of roles) {
|
||||||
const _role = acRoles[role as keyof typeof acRoles];
|
const _role = acRoles[role as keyof typeof acRoles];
|
||||||
const result = _role?.authorize(input.permission);
|
const result = _role?.authorize(input.permissions ?? input.permission);
|
||||||
if (result?.success) {
|
if (result?.success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { createAuthClient } from "../../client";
|
|||||||
import { organizationClient } from "./client";
|
import { organizationClient } from "./client";
|
||||||
import { createAccessControl } from "../access";
|
import { createAccessControl } from "../access";
|
||||||
import { ORGANIZATION_ERROR_CODES } from "./error-codes";
|
import { ORGANIZATION_ERROR_CODES } from "./error-codes";
|
||||||
import { BetterAuthError } from "../../error";
|
|
||||||
import { APIError } from "better-call";
|
import { APIError } from "better-call";
|
||||||
|
|
||||||
describe("organization", async (it) => {
|
describe("organization", async (it) => {
|
||||||
@@ -500,7 +499,7 @@ describe("organization", async (it) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const hasPermission = await client.organization.hasPermission({
|
const hasPermission = await client.organization.hasPermission({
|
||||||
permission: {
|
permissions: {
|
||||||
member: ["update"],
|
member: ["update"],
|
||||||
},
|
},
|
||||||
fetchOptions: {
|
fetchOptions: {
|
||||||
@@ -508,6 +507,17 @@ describe("organization", async (it) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(hasPermission.data?.success).toBe(true);
|
expect(hasPermission.data?.success).toBe(true);
|
||||||
|
|
||||||
|
const hasMultiplePermissions = await client.organization.hasPermission({
|
||||||
|
permissions: {
|
||||||
|
member: ["update"],
|
||||||
|
invitation: ["create"],
|
||||||
|
},
|
||||||
|
fetchOptions: {
|
||||||
|
headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(hasMultiplePermissions.data?.success).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should allow deleting organization", async () => {
|
it("should allow deleting organization", async () => {
|
||||||
@@ -795,15 +805,25 @@ describe("access control", async (it) => {
|
|||||||
it("should return success", async () => {
|
it("should return success", async () => {
|
||||||
const canCreateProject = checkRolePermission({
|
const canCreateProject = checkRolePermission({
|
||||||
role: "admin",
|
role: "admin",
|
||||||
permission: {
|
permissions: {
|
||||||
project: ["create"],
|
project: ["create"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(canCreateProject).toBe(true);
|
expect(canCreateProject).toBe(true);
|
||||||
const canCreateProjectServer = await hasPermission({
|
|
||||||
|
// To be removed when `permission` will be removed entirely
|
||||||
|
const canCreateProjectLegacy = checkRolePermission({
|
||||||
|
role: "admin",
|
||||||
permission: {
|
permission: {
|
||||||
project: ["create"],
|
project: ["create"],
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
expect(canCreateProjectLegacy).toBe(true);
|
||||||
|
|
||||||
|
const canCreateProjectServer = await hasPermission({
|
||||||
|
permissions: {
|
||||||
|
project: ["create"],
|
||||||
|
},
|
||||||
fetchOptions: {
|
fetchOptions: {
|
||||||
headers,
|
headers,
|
||||||
},
|
},
|
||||||
@@ -814,7 +834,7 @@ describe("access control", async (it) => {
|
|||||||
it("should return not success", async () => {
|
it("should return not success", async () => {
|
||||||
const canCreateProject = checkRolePermission({
|
const canCreateProject = checkRolePermission({
|
||||||
role: "admin",
|
role: "admin",
|
||||||
permission: {
|
permissions: {
|
||||||
project: ["delete"],
|
project: ["delete"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -822,21 +842,14 @@ describe("access control", async (it) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return not success", async () => {
|
it("should return not success", async () => {
|
||||||
let error: BetterAuthError | null = null;
|
const res = checkRolePermission({
|
||||||
try {
|
|
||||||
checkRolePermission({
|
|
||||||
role: "admin",
|
role: "admin",
|
||||||
permission: {
|
permissions: {
|
||||||
project: ["read"],
|
project: ["read"],
|
||||||
sales: ["delete"],
|
sales: ["delete"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
expect(res).toBe(false);
|
||||||
if (e instanceof BetterAuthError) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect(error).toBeInstanceOf(BetterAuthError);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -443,6 +443,26 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
|
|||||||
type Statements = O["ac"] extends AccessControl<infer S>
|
type Statements = O["ac"] extends AccessControl<infer S>
|
||||||
? S
|
? S
|
||||||
: DefaultStatements;
|
: DefaultStatements;
|
||||||
|
type PermissionType = {
|
||||||
|
[key in keyof Statements]?: Array<
|
||||||
|
Statements[key] extends readonly unknown[]
|
||||||
|
? Statements[key][number]
|
||||||
|
: never
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
type PermissionExclusive =
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* @deprecated Use `permissions` instead
|
||||||
|
*/
|
||||||
|
permission: PermissionType;
|
||||||
|
permissions?: never;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
permissions: PermissionType;
|
||||||
|
permission?: never;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: "organization",
|
id: "organization",
|
||||||
endpoints: {
|
endpoints: {
|
||||||
@@ -454,18 +474,26 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
|
|||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
requireHeaders: true,
|
requireHeaders: true,
|
||||||
body: z.object({
|
body: z
|
||||||
|
.object({
|
||||||
organizationId: z.string().optional(),
|
organizationId: z.string().optional(),
|
||||||
|
})
|
||||||
|
.and(
|
||||||
|
z.union([
|
||||||
|
z.object({
|
||||||
permission: z.record(z.string(), z.array(z.string())),
|
permission: z.record(z.string(), z.array(z.string())),
|
||||||
|
permissions: z.undefined(),
|
||||||
}),
|
}),
|
||||||
|
z.object({
|
||||||
|
permission: z.undefined(),
|
||||||
|
permissions: z.record(z.string(), z.array(z.string())),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
),
|
||||||
use: [orgSessionMiddleware],
|
use: [orgSessionMiddleware],
|
||||||
metadata: {
|
metadata: {
|
||||||
$Infer: {
|
$Infer: {
|
||||||
body: {} as {
|
body: {} as PermissionExclusive & {
|
||||||
permission: {
|
|
||||||
//@ts-expect-error
|
|
||||||
[key in keyof Statements]?: Array<Statements[key][number]>;
|
|
||||||
};
|
|
||||||
organizationId?: string;
|
organizationId?: string;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -480,9 +508,14 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
|
|||||||
permission: {
|
permission: {
|
||||||
type: "object",
|
type: "object",
|
||||||
description: "The permission to check",
|
description: "The permission to check",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
permissions: {
|
||||||
|
type: "object",
|
||||||
|
description: "The permission to check",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
required: ["permission"],
|
required: ["permissions"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -534,7 +567,7 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
|
|||||||
const result = hasPermission({
|
const result = hasPermission({
|
||||||
role: member.role,
|
role: member.role,
|
||||||
options: options as OrganizationOptions,
|
options: options as OrganizationOptions,
|
||||||
permission: ctx.body.permission as any,
|
permissions: (ctx.body.permissions ?? ctx.body.permission) as any,
|
||||||
});
|
});
|
||||||
return ctx.json({
|
return ctx.json({
|
||||||
error: null,
|
error: null,
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export const createInvitation = <O extends OrganizationOptions | undefined>(
|
|||||||
const canInvite = hasPermission({
|
const canInvite = hasPermission({
|
||||||
role: member.role,
|
role: member.role,
|
||||||
options: ctx.context.orgOptions,
|
options: ctx.context.orgOptions,
|
||||||
permission: {
|
permissions: {
|
||||||
invitation: ["create"],
|
invitation: ["create"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -489,7 +489,7 @@ export const cancelInvitation = createAuthEndpoint(
|
|||||||
const canCancel = hasPermission({
|
const canCancel = hasPermission({
|
||||||
role: member.role,
|
role: member.role,
|
||||||
options: ctx.context.orgOptions,
|
options: ctx.context.orgOptions,
|
||||||
permission: {
|
permissions: {
|
||||||
invitation: ["cancel"],
|
invitation: ["cancel"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ export const removeMember = createAuthEndpoint(
|
|||||||
const canDeleteMember = hasPermission({
|
const canDeleteMember = hasPermission({
|
||||||
role: member.role,
|
role: member.role,
|
||||||
options: ctx.context.orgOptions,
|
options: ctx.context.orgOptions,
|
||||||
permission: {
|
permissions: {
|
||||||
member: ["delete"],
|
member: ["delete"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -399,7 +399,7 @@ export const updateMemberRole = <O extends OrganizationOptions>(option: O) =>
|
|||||||
const canUpdateMember = hasPermission({
|
const canUpdateMember = hasPermission({
|
||||||
role: member.role,
|
role: member.role,
|
||||||
options: ctx.context.orgOptions,
|
options: ctx.context.orgOptions,
|
||||||
permission: {
|
permissions: {
|
||||||
member: ["update"],
|
member: ["update"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ export const updateOrganization = createAuthEndpoint(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const canUpdateOrg = hasPermission({
|
const canUpdateOrg = hasPermission({
|
||||||
permission: {
|
permissions: {
|
||||||
organization: ["update"],
|
organization: ["update"],
|
||||||
},
|
},
|
||||||
role: member.role,
|
role: member.role,
|
||||||
@@ -403,7 +403,7 @@ export const deleteOrganization = createAuthEndpoint(
|
|||||||
}
|
}
|
||||||
const canDeleteOrg = hasPermission({
|
const canDeleteOrg = hasPermission({
|
||||||
role: member.role,
|
role: member.role,
|
||||||
permission: {
|
permissions: {
|
||||||
organization: ["delete"],
|
organization: ["delete"],
|
||||||
},
|
},
|
||||||
options: ctx.context.orgOptions,
|
options: ctx.context.orgOptions,
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export const createTeam = <O extends OrganizationOptions | undefined>(
|
|||||||
const canCreate = hasPermission({
|
const canCreate = hasPermission({
|
||||||
role: member.role,
|
role: member.role,
|
||||||
options: ctx.context.orgOptions,
|
options: ctx.context.orgOptions,
|
||||||
permission: {
|
permissions: {
|
||||||
team: ["create"],
|
team: ["create"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -208,7 +208,7 @@ export const removeTeam = createAuthEndpoint(
|
|||||||
const canRemove = hasPermission({
|
const canRemove = hasPermission({
|
||||||
role: member.role,
|
role: member.role,
|
||||||
options: ctx.context.orgOptions,
|
options: ctx.context.orgOptions,
|
||||||
permission: {
|
permissions: {
|
||||||
team: ["delete"],
|
team: ["delete"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -329,7 +329,7 @@ export const updateTeam = createAuthEndpoint(
|
|||||||
const canUpdate = hasPermission({
|
const canUpdate = hasPermission({
|
||||||
role: member.role,
|
role: member.role,
|
||||||
options: ctx.context.orgOptions,
|
options: ctx.context.orgOptions,
|
||||||
permission: {
|
permissions: {
|
||||||
team: ["update"],
|
team: ["update"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user