Allow creating published share record, closes #5902

This commit is contained in:
Tom Moor
2023-09-30 12:38:16 -04:00
parent 2868ab2d00
commit e2a6d828a9
3 changed files with 132 additions and 58 deletions

View File

@@ -68,6 +68,14 @@ export const SharesCreateSchema = BaseSchema.extend({
.refine((val) => isUUID(val) || SLUG_URL_REGEX.test(val), {
message: "must be uuid or url slug",
}),
published: z.boolean().default(false),
urlId: z
.string()
.regex(SHARE_URL_SLUG_REGEX, {
message: "must contain only alphanumeric and dashes",
})
.optional(),
includeChildDocuments: z.boolean().default(false),
}),
});

View File

@@ -226,7 +226,66 @@ describe("#shares.create", () => {
expect(body.data.documentTitle).toBe(document.title);
});
it("should allow creating a share record with read-only permissions but no publishing", async () => {
it("should allow creating a published share record for document", async () => {
const user = await buildUser();
const document = await buildDocument({
userId: user.id,
teamId: user.teamId,
});
const res = await server.post("/api/shares.create", {
body: {
token: user.getJwtToken(),
documentId: document.id,
includeChildDocuments: true,
published: true,
urlId: "test",
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.data.published).toBe(true);
expect(body.data.includeChildDocuments).toBe(true);
expect(body.data.urlId).toBe("test");
expect(body.data.documentTitle).toBe(document.title);
});
it("should fail creating a share record with read-only permissions and publishing", async () => {
const team = await buildTeam();
const user = await buildUser({ teamId: team.id });
const collection = await buildCollection({
userId: user.id,
teamId: team.id,
});
const document = await buildDocument({
userId: user.id,
collectionId: collection.id,
teamId: team.id,
});
collection.permission = null;
await collection.save();
await UserPermission.update(
{
userId: user.id,
permission: CollectionPermission.Read,
},
{
where: {
createdById: user.id,
collectionId: collection.id,
},
}
);
const res = await server.post("/api/shares.create", {
body: {
token: user.getJwtToken(),
documentId: document.id,
published: true,
},
});
expect(res.status).toEqual(403);
});
it("should allow creating a share record with read-only permissions but not publishing", async () => {
const team = await buildTeam();
const user = await buildUser({ teamId: team.id });
const collection = await buildCollection({

View File

@@ -161,6 +161,69 @@ router.post(
}
);
router.post(
"shares.create",
auth(),
validate(T.SharesCreateSchema),
async (ctx: APIContext<T.SharesCreateReq>) => {
const { documentId, published, urlId, includeChildDocuments } =
ctx.input.body;
const { user } = ctx.state.auth;
const document = await Document.findByPk(documentId, {
userId: user.id,
});
// user could be creating the share link to share with team members
authorize(user, "read", document);
if (published) {
authorize(user, "share", user.team);
authorize(user, "share", document);
}
const [share, isCreated] = await Share.findOrCreate({
where: {
documentId,
teamId: user.teamId,
revokedAt: null,
},
defaults: {
userId: user.id,
published,
includeChildDocuments,
urlId,
},
});
if (isCreated) {
await Event.create({
name: "shares.create",
documentId,
collectionId: document.collectionId,
modelId: share.id,
teamId: user.teamId,
actorId: user.id,
data: {
name: document.title,
published,
includeChildDocuments,
urlId,
},
ip: ctx.request.ip,
});
}
share.team = user.team;
share.user = user;
share.document = document;
ctx.body = {
data: presentShare(share),
policies: presentPolicies(user, [share]),
};
}
);
router.post(
"shares.update",
auth(),
@@ -169,8 +232,7 @@ router.post(
const { id, includeChildDocuments, published, urlId } = ctx.input.body;
const { user } = ctx.state.auth;
const team = await Team.findByPk(user.teamId);
authorize(user, "share", team);
authorize(user, "share", user.team);
// fetch the share with document and collection.
const share = await Share.scope({
@@ -218,61 +280,6 @@ router.post(
}
);
router.post(
"shares.create",
auth(),
validate(T.SharesCreateSchema),
async (ctx: APIContext<T.SharesCreateReq>) => {
const { documentId } = ctx.input.body;
const { user } = ctx.state.auth;
const document = await Document.findByPk(documentId, {
userId: user.id,
});
// user could be creating the share link to share with team members
authorize(user, "read", document);
const team = await Team.findByPk(user.teamId);
const [share, isCreated] = await Share.findOrCreate({
where: {
documentId,
teamId: user.teamId,
revokedAt: null,
},
defaults: {
userId: user.id,
},
});
if (isCreated) {
await Event.create({
name: "shares.create",
documentId,
collectionId: document.collectionId,
modelId: share.id,
teamId: user.teamId,
actorId: user.id,
data: {
name: document.title,
},
ip: ctx.request.ip,
});
}
if (team) {
share.team = team;
}
share.user = user;
share.document = document;
ctx.body = {
data: presentShare(share),
policies: presentPolicies(user, [share]),
};
}
);
router.post(
"shares.revoke",
auth(),