@@ -1,7 +1,5 @@
|
||||
import { ApiKey, User, Team } from "@server/models";
|
||||
import policy from "./policy";
|
||||
|
||||
const { allow } = policy;
|
||||
import { allow } from "./cancan";
|
||||
|
||||
allow(User, "createApiKey", Team, (user, team) => {
|
||||
if (!team || user.isViewer || user.teamId !== team.id) return false;
|
||||
@@ -9,6 +7,7 @@ allow(User, "createApiKey", Team, (user, team) => {
|
||||
});
|
||||
|
||||
allow(User, ["read", "update", "delete"], ApiKey, (user, apiKey) => {
|
||||
if (!apiKey) return false;
|
||||
if (user.isViewer) return false;
|
||||
return user && user.id === apiKey.userId;
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Attachment, User, Team } from "@server/models";
|
||||
import policy from "./policy";
|
||||
|
||||
const { allow } = policy;
|
||||
import { allow } from "./cancan";
|
||||
|
||||
allow(User, "createAttachment", Team, (user, team) => {
|
||||
if (!team || user.isViewer || user.teamId !== team.id) return false;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { AuthenticationProvider, User, Team } from "@server/models";
|
||||
import { AdminRequiredError } from "../errors";
|
||||
import policy from "./policy";
|
||||
|
||||
const { allow } = policy;
|
||||
import { allow } from "./cancan";
|
||||
|
||||
allow(User, "createAuthenticationProvider", Team, (actor, team) => {
|
||||
if (!team || actor.teamId !== team.id) return false;
|
||||
@@ -17,7 +15,7 @@ allow(
|
||||
AuthenticationProvider,
|
||||
|
||||
(actor, authenticationProvider) =>
|
||||
actor && actor.teamId === authenticationProvider.teamId
|
||||
actor && actor.teamId === authenticationProvider?.teamId
|
||||
);
|
||||
|
||||
allow(
|
||||
@@ -26,7 +24,7 @@ allow(
|
||||
AuthenticationProvider,
|
||||
|
||||
(actor, authenticationProvider) => {
|
||||
if (actor.teamId !== authenticationProvider.teamId) return false;
|
||||
if (actor.teamId !== authenticationProvider?.teamId) return false;
|
||||
if (actor.isAdmin) return true;
|
||||
|
||||
throw AdminRequiredError();
|
||||
|
||||
13
server/policies/cancan.ts
Normal file
13
server/policies/cancan.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import CanCan from "cancan";
|
||||
|
||||
const cancan = new CanCan();
|
||||
|
||||
export const _can = cancan.can;
|
||||
|
||||
export const _authorize = cancan.authorize;
|
||||
|
||||
export const _cannot = cancan.cannot;
|
||||
|
||||
export const _abilities = cancan.abilities;
|
||||
|
||||
export const allow = cancan.allow;
|
||||
@@ -4,6 +4,7 @@ import { flushdb } from "@server/test/support";
|
||||
import { serialize } from "./index";
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("read_write permission", () => {
|
||||
it("should allow read write permissions for team member", async () => {
|
||||
const team = await buildTeam();
|
||||
@@ -25,7 +26,7 @@ describe("read_write permission", () => {
|
||||
const user = await buildUser({
|
||||
teamId: team.id,
|
||||
});
|
||||
let collection = await buildCollection({
|
||||
const collection = await buildCollection({
|
||||
teamId: team.id,
|
||||
permission: "read_write",
|
||||
});
|
||||
@@ -36,15 +37,16 @@ describe("read_write permission", () => {
|
||||
permission: "read",
|
||||
});
|
||||
// reload to get membership
|
||||
collection = await Collection.scope({
|
||||
const reloaded = await Collection.scope({
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collection.id);
|
||||
const abilities = serialize(user, collection);
|
||||
const abilities = serialize(user, reloaded);
|
||||
expect(abilities.read).toEqual(true);
|
||||
expect(abilities.update).toEqual(true);
|
||||
expect(abilities.share).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("read permission", () => {
|
||||
it("should allow read permissions for team member", async () => {
|
||||
const team = await buildTeam();
|
||||
@@ -66,7 +68,7 @@ describe("read permission", () => {
|
||||
const user = await buildUser({
|
||||
teamId: team.id,
|
||||
});
|
||||
let collection = await buildCollection({
|
||||
const collection = await buildCollection({
|
||||
teamId: team.id,
|
||||
permission: "read",
|
||||
});
|
||||
@@ -77,15 +79,16 @@ describe("read permission", () => {
|
||||
permission: "read_write",
|
||||
});
|
||||
// reload to get membership
|
||||
collection = await Collection.scope({
|
||||
const reloaded = await Collection.scope({
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collection.id);
|
||||
const abilities = serialize(user, collection);
|
||||
const abilities = serialize(user, reloaded);
|
||||
expect(abilities.read).toEqual(true);
|
||||
expect(abilities.update).toEqual(true);
|
||||
expect(abilities.share).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("no permission", () => {
|
||||
it("should allow no permissions for team member", async () => {
|
||||
const team = await buildTeam();
|
||||
@@ -107,7 +110,7 @@ describe("no permission", () => {
|
||||
const user = await buildUser({
|
||||
teamId: team.id,
|
||||
});
|
||||
let collection = await buildCollection({
|
||||
const collection = await buildCollection({
|
||||
teamId: team.id,
|
||||
permission: null,
|
||||
});
|
||||
@@ -118,10 +121,10 @@ describe("no permission", () => {
|
||||
permission: "read_write",
|
||||
});
|
||||
// reload to get membership
|
||||
collection = await Collection.scope({
|
||||
const reloaded = await Collection.scope({
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collection.id);
|
||||
const abilities = serialize(user, collection);
|
||||
const abilities = serialize(user, reloaded);
|
||||
expect(abilities.read).toEqual(true);
|
||||
expect(abilities.update).toEqual(true);
|
||||
expect(abilities.share).toEqual(true);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import invariant from "invariant";
|
||||
import { concat, some } from "lodash";
|
||||
import { some } from "lodash";
|
||||
import { Collection, User, Team } from "@server/models";
|
||||
import { AdminRequiredError } from "../errors";
|
||||
import policy from "./policy";
|
||||
|
||||
const { allow } = policy;
|
||||
import { allow } from "./cancan";
|
||||
|
||||
allow(User, "createCollection", Team, (user, team) => {
|
||||
if (!team || user.isViewer || user.teamId !== team.id) return false;
|
||||
@@ -34,10 +32,10 @@ allow(User, "read", Collection, (user, collection) => {
|
||||
collection.memberships,
|
||||
"membership should be preloaded, did you forget withMembership scope?"
|
||||
);
|
||||
const allMemberships = concat(
|
||||
collection.memberships,
|
||||
collection.collectionGroupMemberships
|
||||
);
|
||||
const allMemberships = [
|
||||
...collection.memberships,
|
||||
...collection.collectionGroupMemberships,
|
||||
];
|
||||
return some(allMemberships, (m) =>
|
||||
["read", "read_write", "maintainer"].includes(m.permission)
|
||||
);
|
||||
@@ -56,10 +54,10 @@ allow(User, "share", Collection, (user, collection) => {
|
||||
collection.memberships,
|
||||
"membership should be preloaded, did you forget withMembership scope?"
|
||||
);
|
||||
const allMemberships = concat(
|
||||
collection.memberships,
|
||||
collection.collectionGroupMemberships
|
||||
);
|
||||
const allMemberships = [
|
||||
...collection.memberships,
|
||||
...collection.collectionGroupMemberships,
|
||||
];
|
||||
return some(allMemberships, (m) =>
|
||||
["read_write", "maintainer"].includes(m.permission)
|
||||
);
|
||||
@@ -77,10 +75,10 @@ allow(User, ["publish", "update"], Collection, (user, collection) => {
|
||||
collection.memberships,
|
||||
"membership should be preloaded, did you forget withMembership scope?"
|
||||
);
|
||||
const allMemberships = concat(
|
||||
collection.memberships,
|
||||
collection.collectionGroupMemberships
|
||||
);
|
||||
const allMemberships = [
|
||||
...collection.memberships,
|
||||
...collection.collectionGroupMemberships,
|
||||
];
|
||||
return some(allMemberships, (m) =>
|
||||
["read_write", "maintainer"].includes(m.permission)
|
||||
);
|
||||
@@ -98,10 +96,10 @@ allow(User, "delete", Collection, (user, collection) => {
|
||||
collection.memberships,
|
||||
"membership should be preloaded, did you forget withMembership scope?"
|
||||
);
|
||||
const allMemberships = concat(
|
||||
collection.memberships,
|
||||
collection.collectionGroupMemberships
|
||||
);
|
||||
const allMemberships = [
|
||||
...collection.memberships,
|
||||
...collection.collectionGroupMemberships,
|
||||
];
|
||||
return some(allMemberships, (m) =>
|
||||
["read_write", "maintainer"].includes(m.permission)
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { flushdb } from "@server/test/support";
|
||||
import { serialize } from "./index";
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("read_write collection", () => {
|
||||
it("should allow read write permissions for team member", async () => {
|
||||
const team = await buildTeam();
|
||||
@@ -33,6 +34,7 @@ describe("read_write collection", () => {
|
||||
expect(abilities.move).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("read collection", () => {
|
||||
it("should allow read only permissions permissions for team member", async () => {
|
||||
const team = await buildTeam();
|
||||
@@ -58,6 +60,7 @@ describe("read collection", () => {
|
||||
expect(abilities.move).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("private collection", () => {
|
||||
it("should allow no permissions for team member", async () => {
|
||||
const team = await buildTeam();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import invariant from "invariant";
|
||||
import { Document, Revision, User, Team } from "@server/models";
|
||||
import policy from "./policy";
|
||||
|
||||
const { allow, cannot } = policy;
|
||||
import { NavigationNode } from "~/types";
|
||||
import { allow, _cannot as cannot } from "./cancan";
|
||||
|
||||
allow(User, "createDocument", Team, (user, team) => {
|
||||
if (!team || user.isViewer || user.teamId !== team.id) return false;
|
||||
@@ -10,6 +9,8 @@ allow(User, "createDocument", Team, (user, team) => {
|
||||
});
|
||||
|
||||
allow(User, ["read", "download"], Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
|
||||
// existence of collection option is not required here to account for share tokens
|
||||
if (document.collection && cannot(user, "read", document.collection)) {
|
||||
return false;
|
||||
@@ -19,6 +20,7 @@ allow(User, ["read", "download"], Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, ["star", "unstar"], Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
if (document.archivedAt) return false;
|
||||
if (document.deletedAt) return false;
|
||||
if (document.template) return false;
|
||||
@@ -31,8 +33,13 @@ allow(User, ["star", "unstar"], Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, "share", Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
if (document.archivedAt) return false;
|
||||
if (document.deletedAt) return false;
|
||||
invariant(
|
||||
document.collection,
|
||||
"collection is missing, did you forget to include in the query scope?"
|
||||
);
|
||||
|
||||
if (cannot(user, "share", document.collection)) {
|
||||
return false;
|
||||
@@ -42,6 +49,7 @@ allow(User, "share", Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, "update", Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
if (document.archivedAt) return false;
|
||||
if (document.deletedAt) return false;
|
||||
|
||||
@@ -53,6 +61,7 @@ allow(User, "update", Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, "createChildDocument", Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
if (document.archivedAt) return false;
|
||||
if (document.deletedAt) return false;
|
||||
if (document.template) return false;
|
||||
@@ -66,6 +75,7 @@ allow(User, "createChildDocument", Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, "move", Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
if (document.archivedAt) return false;
|
||||
if (document.deletedAt) return false;
|
||||
if (!document.publishedAt) return false;
|
||||
@@ -78,6 +88,7 @@ allow(User, "move", Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, ["pin", "unpin"], Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
if (document.archivedAt) return false;
|
||||
if (document.deletedAt) return false;
|
||||
if (document.template) return false;
|
||||
@@ -91,6 +102,7 @@ allow(User, ["pin", "unpin"], Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, ["pinToHome"], Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
if (document.archivedAt) return false;
|
||||
if (document.deletedAt) return false;
|
||||
if (document.template) return false;
|
||||
@@ -100,8 +112,9 @@ allow(User, ["pinToHome"], Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, "delete", Document, (user, document) => {
|
||||
if (user.isViewer) return false;
|
||||
if (!document) return false;
|
||||
if (document.deletedAt) return false;
|
||||
if (user.isViewer) return false;
|
||||
|
||||
// allow deleting document without a collection
|
||||
if (document.collection && cannot(user, "update", document.collection)) {
|
||||
@@ -121,8 +134,9 @@ allow(User, "delete", Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, "permanentDelete", Document, (user, document) => {
|
||||
if (user.isViewer) return false;
|
||||
if (!document) return false;
|
||||
if (!document.deletedAt) return false;
|
||||
if (user.isViewer) return false;
|
||||
|
||||
// allow deleting document without a collection
|
||||
if (document.collection && cannot(user, "update", document.collection)) {
|
||||
@@ -133,8 +147,9 @@ allow(User, "permanentDelete", Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, "restore", Document, (user, document) => {
|
||||
if (user.isViewer) return false;
|
||||
if (!document) return false;
|
||||
if (!document.deletedAt) return false;
|
||||
if (user.isViewer) return false;
|
||||
|
||||
if (document.collection && cannot(user, "update", document.collection)) {
|
||||
return false;
|
||||
@@ -144,6 +159,7 @@ allow(User, "restore", Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, "archive", Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
if (!document.publishedAt) return false;
|
||||
if (document.archivedAt) return false;
|
||||
if (document.deletedAt) return false;
|
||||
@@ -156,6 +172,7 @@ allow(User, "archive", Document, (user, document) => {
|
||||
});
|
||||
|
||||
allow(User, "unarchive", Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
invariant(
|
||||
document.collection,
|
||||
"collection is missing, did you forget to include in the query scope?"
|
||||
@@ -170,10 +187,11 @@ allow(
|
||||
Document,
|
||||
"restore",
|
||||
Revision,
|
||||
(document, revision) => document.id === revision.documentId
|
||||
(document, revision) => document.id === revision?.documentId
|
||||
);
|
||||
|
||||
allow(User, "unpublish", Document, (user, document) => {
|
||||
if (!document) return false;
|
||||
invariant(
|
||||
document.collection,
|
||||
"collection is missing, did you forget to include in the query scope?"
|
||||
@@ -183,16 +201,14 @@ allow(User, "unpublish", Document, (user, document) => {
|
||||
if (cannot(user, "update", document.collection)) return false;
|
||||
const documentID = document.id;
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'documents' implicitly has an 'any' type... Remove this comment to see the full error message
|
||||
const hasChild = (documents) =>
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'doc' implicitly has an 'any' type.
|
||||
const hasChild = (documents: NavigationNode[]): boolean =>
|
||||
documents.some((doc) => {
|
||||
if (doc.id === documentID) return doc.children.length > 0;
|
||||
return hasChild(doc.children);
|
||||
});
|
||||
|
||||
return (
|
||||
!hasChild(document.collection.documentStructure) &&
|
||||
!hasChild(document.collection.documentStructure || []) &&
|
||||
user.teamId === document.teamId
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Group, User, Team } from "@server/models";
|
||||
import { AdminRequiredError } from "../errors";
|
||||
import policy from "./policy";
|
||||
|
||||
const { allow } = policy;
|
||||
import { allow } from "./cancan";
|
||||
|
||||
allow(User, "createGroup", Team, (actor, team) => {
|
||||
if (!team || actor.isViewer || actor.teamId !== team.id) return false;
|
||||
|
||||
@@ -3,12 +3,14 @@ import { flushdb } from "@server/test/support";
|
||||
import { serialize } from "./index";
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
it("should serialize policy", async () => {
|
||||
const user = await buildUser();
|
||||
const response = serialize(user, user);
|
||||
expect(response.update).toEqual(true);
|
||||
expect(response.delete).toEqual(true);
|
||||
});
|
||||
|
||||
it("should serialize domain policies on Team", async () => {
|
||||
const team = await buildTeam();
|
||||
const user = await buildUser({
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
Document,
|
||||
Group,
|
||||
} from "@server/models";
|
||||
import policy from "./policy";
|
||||
import { _abilities, _can, _cannot, _authorize } from "./cancan";
|
||||
import "./apiKey";
|
||||
import "./attachment";
|
||||
import "./authenticationProvider";
|
||||
@@ -21,19 +21,26 @@ import "./user";
|
||||
import "./team";
|
||||
import "./group";
|
||||
|
||||
const { can, abilities } = policy;
|
||||
type Policy = Record<string, boolean>;
|
||||
|
||||
// this should not be needed but is a workaround for this TypeScript issue:
|
||||
// https://github.com/microsoft/TypeScript/issues/36931
|
||||
export const authorize: typeof _authorize = _authorize;
|
||||
|
||||
export const can = _can;
|
||||
|
||||
export const cannot = _cannot;
|
||||
|
||||
export const abilities = _abilities;
|
||||
|
||||
/*
|
||||
* Given a user and a model – output an object which describes the actions the
|
||||
* user may take against the model. This serialized policy is used for testing
|
||||
* and sent in API responses to allow clients to adjust which UI is displayed.
|
||||
*/
|
||||
export function serialize(
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
model: User,
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'Attachment' refers to a value, but is being used ... Remove this comment to see the full error message
|
||||
target: Attachment | Team | Collection | Document | Group
|
||||
target: Attachment | Team | Collection | Document | User | Group | null
|
||||
): Policy {
|
||||
const output = {};
|
||||
abilities.forEach((ability) => {
|
||||
@@ -51,5 +58,3 @@ export function serialize(
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
export default policy;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Integration, User, Team } from "@server/models";
|
||||
import { AdminRequiredError } from "../errors";
|
||||
import policy from "./policy";
|
||||
|
||||
const { allow } = policy;
|
||||
import { allow } from "./cancan";
|
||||
|
||||
allow(User, "createIntegration", Team, (actor, team) => {
|
||||
if (!team || actor.isViewer || actor.teamId !== team.id) return false;
|
||||
@@ -15,7 +13,7 @@ allow(
|
||||
User,
|
||||
"read",
|
||||
Integration,
|
||||
(user, integration) => user.teamId === integration.teamId
|
||||
(user, integration) => user.teamId === integration?.teamId
|
||||
);
|
||||
|
||||
allow(User, ["update", "delete"], Integration, (user, integration) => {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { NotificationSetting, Team, User } from "@server/models";
|
||||
import policy from "./policy";
|
||||
|
||||
const { allow } = policy;
|
||||
import { allow } from "./cancan";
|
||||
|
||||
allow(User, "createNotificationSetting", Team, (user, team) => {
|
||||
if (!team || user.teamId !== team.id) return false;
|
||||
@@ -12,5 +10,5 @@ allow(
|
||||
User,
|
||||
["read", "update", "delete"],
|
||||
NotificationSetting,
|
||||
(user, setting) => user && user.id === setting.userId
|
||||
(user, setting) => user && user.id === setting?.userId
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { User, Pin } from "@server/models";
|
||||
import policy from "./policy";
|
||||
import { allow } from "./cancan";
|
||||
|
||||
const { allow } = policy;
|
||||
|
||||
allow(User, ["update", "delete"], Pin, (user, pin) => {
|
||||
if (user.teamId === pin.teamId && user.isAdmin) return true;
|
||||
return false;
|
||||
});
|
||||
allow(
|
||||
User,
|
||||
["update", "delete"],
|
||||
Pin,
|
||||
(user, pin) => user.teamId === pin?.teamId && user.isAdmin
|
||||
);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import CanCan from "cancan";
|
||||
|
||||
export default new CanCan();
|
||||
@@ -1,8 +1,9 @@
|
||||
import { SearchQuery, User } from "@server/models";
|
||||
import policy from "./policy";
|
||||
import { allow } from "./cancan";
|
||||
|
||||
const { allow } = policy;
|
||||
|
||||
allow(User, ["read", "delete"], SearchQuery, (user, searchQuery) => {
|
||||
return user && user.id === searchQuery.userId;
|
||||
});
|
||||
allow(
|
||||
User,
|
||||
["read", "delete"],
|
||||
SearchQuery,
|
||||
(user, searchQuery) => user && user.id === searchQuery?.userId
|
||||
);
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { Share, User } from "@server/models";
|
||||
import { AdminRequiredError } from "../errors";
|
||||
import policy from "./policy";
|
||||
import { allow, _cannot as cannot } from "./cancan";
|
||||
|
||||
const { allow, cannot } = policy;
|
||||
|
||||
allow(User, "read", Share, (user, share) => {
|
||||
return user.teamId === share.teamId;
|
||||
});
|
||||
allow(User, "read", Share, (user, share) => user.teamId === share?.teamId);
|
||||
|
||||
allow(User, "update", Share, (user, share) => {
|
||||
if (!share) return false;
|
||||
if (user.isViewer) return false;
|
||||
|
||||
// only the user who can share the document publicly can update the share.
|
||||
@@ -17,8 +14,9 @@ allow(User, "update", Share, (user, share) => {
|
||||
});
|
||||
|
||||
allow(User, "revoke", Share, (user, share) => {
|
||||
if (!share) return false;
|
||||
if (user.isViewer) return false;
|
||||
if (!share || user.teamId !== share.teamId) return false;
|
||||
if (user.teamId !== share.teamId) return false;
|
||||
if (user.id === share.userId) return true;
|
||||
if (user.isAdmin) return true;
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Team, User } from "@server/models";
|
||||
import policy from "./policy";
|
||||
import { allow } from "./cancan";
|
||||
|
||||
const { allow } = policy;
|
||||
|
||||
allow(User, "read", Team, (user, team) => team && user.teamId === team.id);
|
||||
allow(User, "read", Team, (user, team) => user.teamId === team?.id);
|
||||
|
||||
allow(User, "share", Team, (user, team) => {
|
||||
if (!team || user.isViewer || user.teamId !== team.id) return false;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { User, Team } from "@server/models";
|
||||
import { AdminRequiredError } from "../errors";
|
||||
import policy from "./policy";
|
||||
import { allow } from "./cancan";
|
||||
|
||||
const { allow } = policy;
|
||||
allow(
|
||||
User,
|
||||
"read",
|
||||
User,
|
||||
|
||||
(actor, user) => user && user.teamId === actor.teamId
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user