Files
outline/server/policies/document.ts
Apoorv Mishra a89d30c735 Allow drafts to be created without requiring a collection (#4175)
* feat(server): allow document to be created without collectionId

* fix(server): policies for a draft doc without collection

* fix(app): hide share button for drafts

* feat(server): permissions around publishing a draft

* fix(server): return drafts without collection

* fix(server): handle draft deletion

* fix(server): show drafts in deleted docs

* fix(server): allow drafts without collection to be restored

* feat(server): return drafts in search results

* fix: use buildDraftDocument for drafts

* fix: remove isDraftWithoutCollection

* fix: do not return drafts for team

* fix: put invariants

* fix: query clause

* fix: check only for undefined

* fix: restore includeDrafts clause as it was before
2022-10-25 18:01:57 +05:30

342 lines
7.0 KiB
TypeScript

import invariant from "invariant";
import { Document, Revision, User, Team } from "@server/models";
import { allow, _cannot as cannot } from "./cancan";
allow(User, "createDocument", Team, (user, team) => {
if (!team || user.isViewer || user.teamId !== team.id) {
return false;
}
return true;
});
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;
}
return user.teamId === document.teamId;
});
allow(User, "star", Document, (user, document) => {
if (!document) {
return false;
}
if (document.archivedAt) {
return false;
}
if (document.deletedAt) {
return false;
}
if (document.template) {
return false;
}
if (document.collectionId) {
invariant(
document.collection,
"collection is missing, did you forget to include in the query scope?"
);
if (cannot(user, "read", document.collection)) {
return false;
}
}
return user.teamId === document.teamId;
});
allow(User, "unstar", Document, (user, document) => {
if (!document) {
return false;
}
if (document.template) {
return false;
}
if (document.collectionId) {
invariant(
document.collection,
"collection is missing, did you forget to include in the query scope?"
);
if (cannot(user, "read", document.collection)) {
return false;
}
}
return user.teamId === document.teamId;
});
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;
}
return user.teamId === document.teamId;
});
allow(User, "update", Document, (user, document) => {
if (!document) {
return false;
}
if (document.archivedAt) {
return false;
}
if (document.deletedAt) {
return false;
}
if (document.collectionId) {
invariant(
document.collection,
"collection is missing, did you forget to include in the query scope?"
);
if (cannot(user, "update", document.collection)) {
return false;
}
}
return user.teamId === document.teamId;
});
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;
}
if (!document.publishedAt) {
return false;
}
invariant(
document.collection,
"collection is missing, did you forget to include in the query scope?"
);
if (cannot(user, "update", document.collection)) {
return false;
}
return user.teamId === document.teamId;
});
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;
}
invariant(
document.collection,
"collection is missing, did you forget to include in the query scope?"
);
if (cannot(user, "update", document.collection)) {
return false;
}
return user.teamId === document.teamId;
});
allow(
User,
["pin", "unpin", "subscribe", "unsubscribe"],
Document,
(user, document) => {
if (!document) {
return false;
}
if (document.archivedAt) {
return false;
}
if (document.deletedAt) {
return false;
}
if (document.template) {
return false;
}
if (!document.publishedAt) {
return false;
}
invariant(
document.collection,
"collection is missing, did you forget to include in the query scope?"
);
if (cannot(user, "update", document.collection)) {
return false;
}
return user.teamId === document.teamId;
}
);
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;
}
if (!document.publishedAt) {
return false;
}
return user.teamId === document.teamId && user.isAdmin;
});
allow(User, "delete", Document, (user, document) => {
if (!document) {
return false;
}
if (document.deletedAt) {
return false;
}
// allow deleting document without a collection
if (document.collection && cannot(user, "update", document.collection)) {
return false;
}
// unpublished drafts can always be deleted
if (
!document.deletedAt &&
!document.publishedAt &&
user.teamId === document.teamId
) {
return true;
}
return user.teamId === document.teamId;
});
allow(User, "permanentDelete", Document, (user, document) => {
if (!document) {
return false;
}
if (!document.deletedAt) {
return false;
}
// allow deleting document without a collection
if (document.collection && cannot(user, "update", document.collection)) {
return false;
}
return user.teamId === document.teamId;
});
allow(User, "restore", Document, (user, document) => {
if (!document) {
return false;
}
if (!document.deletedAt) {
return false;
}
if (document.collection && cannot(user, "update", document.collection)) {
return false;
}
return user.teamId === document.teamId;
});
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;
}
invariant(
document.collection,
"collection is missing, did you forget to include in the query scope?"
);
if (cannot(user, "update", document.collection)) {
return false;
}
return user.teamId === document.teamId;
});
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?"
);
if (cannot(user, "update", document.collection)) {
return false;
}
if (!document.archivedAt) {
return false;
}
if (document.deletedAt) {
return false;
}
return user.teamId === document.teamId;
});
allow(
Document,
"restore",
Revision,
(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?"
);
if (!document.publishedAt || !!document.deletedAt || !!document.archivedAt) {
return false;
}
if (cannot(user, "update", document.collection)) {
return false;
}
return user.teamId === document.teamId;
});