Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions docs/hooks.md

Large diffs are not rendered by default.

30 changes: 18 additions & 12 deletions src/hooks/authorize/authorize.hook.before.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ export const authorizeBefore = async <H extends HookContext = HookContext>(
actionOnForbidden: options.actionOnForbidden,
checkCreateForData: true,
});
} else if (context.data) {
checkData(context, ability, modelName, { ...context.data }, options);
}

return context;
Expand Down Expand Up @@ -165,7 +167,9 @@ const handleSingle = async <H extends HookContext = HookContext>(

const getMethod = service._get ? "_get" : "get";

const item = await service[getMethod](id, paramsGet);
const item = service[getMethod]
? await service[getMethod](id, paramsGet)
: {};

const restrictingFields = hasRestrictingFields(
ability,
Expand All @@ -187,7 +191,13 @@ const handleSingle = async <H extends HookContext = HookContext>(
? context.data
: _pick(context.data, restrictingFields as string[]);

checkData(context, ability, modelName, data, options);
checkData(
context,
ability,
modelName,
_isEmpty(data) ? data : { ...data, [options.idField]: id },
options
);

if (!restrictingFields) {
return context;
Expand Down Expand Up @@ -222,24 +232,20 @@ const checkData = <H extends HookContext = HookContext>(
data: Record<string, unknown>,
options: Pick<
AuthorizeHookOptions,
"actionOnForbidden" | "usePatchData" | "useUpdateData" | "method"
"actionOnForbidden" | "idField" | "checkRequestData" | "method"
>
): void => {
const method = getMethodName(context, options);

if (
(method === "patch" && !options.usePatchData) ||
(method === "update" && !options.useUpdateData)
!options.checkRequestData ||
["get", "remove", "find"].includes(method) ||
!ability.possibleRulesFor(method, modelName).length
) {
return;
}
throwUnlessCan(
ability,
`${method}-data`,
subject(modelName, data),
modelName,
options
);

throwUnlessCan(ability, method, subject(modelName, data), modelName, options);
};

const handleMulti = async <H extends HookContext = HookContext>(
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/authorize/authorize.hook.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ const defaultOptions: AuthorizeHookOptionsExclusive<HookContext> = {
context.service.options?.casl?.availableFields;
return getAvailableFields(context, { availableFields });
},
usePatchData: false,
useUpdateData: false,
checkRequestData: false,
checkRequestDataSameRules: false,
idField: "_id",
};

export const makeDefaultOptions = (
Expand Down
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export interface AuthorizeHookOptions<H extends HookContext = HookContext>
extends HookBaseOptions<H>,
AuthorizeChannelCommonsOptions<H> {
adapter: Adapter;
useUpdateData: boolean;
usePatchData: boolean;
checkRequestData: boolean;
idField: string;
}

export type AuthorizeHookOptionsExclusive<H extends HookContext = HookContext> =
Expand Down
4 changes: 2 additions & 2 deletions test/hooks/authorize/adapters/makeTests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default async function (
name,
makeService,
clean,
Object.assign({ useUpdateData: true }, authorizeHookOptions),
Object.assign({ checkRequestData: true }, authorizeHookOptions),
makeTestsOptions
);
makePatchTests(
Expand All @@ -80,7 +80,7 @@ export default async function (
name,
makeService,
clean,
Object.assign({ usePatchData: true }, authorizeHookOptions),
Object.assign({ checkRequestData: true }, authorizeHookOptions),
makeTestsOptions
);
makePatchMultiTests(
Expand Down
46 changes: 6 additions & 40 deletions test/hooks/authorize/adapters/makeTests/patch-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,34 +101,6 @@ export default (
}
});

it("fails with no 'patch-data' rule", async function () {
const readMethod = ["read", "get"];

for (const read of readMethod) {
await clean(app, service);
const item = await service.create({ test: true, userId: 1 });
let rejected = false;
try {
await service.patch(
item[id],
{ test: false },
{
ability: defineAbility(
(can) => {
can("patch", "tests");
can(read, "tests");
},
{ resolveAction }
),
}
);
} catch (err) {
rejected = true;
}
assert.ok(rejected, "rejected");
}
});

it("basic cannot 'patch-data'", async function () {
const readMethod = ["read", "get"];

Expand All @@ -143,9 +115,7 @@ export default (
{
ability: defineAbility(
(can, cannot) => {
can("patch", "tests");
can("patch-data", "tests");
cannot("patch-data", "tests", { test: false });
cannot("patch", "tests", { test: false });
can(read, "tests");
},
{ resolveAction }
Expand All @@ -159,7 +129,7 @@ export default (
}
});

it("basic can 'patch-data' with fail", async function () {
it("basic can 'patch' with fail", async function () {
const readMethod = ["read", "get"];

for (const read of readMethod) {
Expand All @@ -172,9 +142,7 @@ export default (
{
ability: defineAbility(
(can) => {
can("patch", "tests");
can("patch-data", "tests");
can("patch-data", "tests", { test: true });
can("patch", "tests", { test: true });
can(read, "tests");
},
{ resolveAction }
Expand All @@ -188,21 +156,19 @@ export default (
}
});

it("basic can 'patch-data'", async function () {
it("basic can 'patch'", async function () {
const readMethod = ["read", "get"];

for (const read of readMethod) {
await clean(app, service);
const item = await service.create({ test: true, userId: 1 });
const patchedItem = await service.patch(
item[id],
{ test: false },
{ test: false, userId: 1 },
{
ability: defineAbility(
(can) => {
can("patch", "tests");
can("patch-data", "tests");
can("patch-data", "tests", { test: false });
can("patch", "tests", { userId: 1 });
can(read, "tests");
},
{ resolveAction }
Expand Down
46 changes: 6 additions & 40 deletions test/hooks/authorize/adapters/makeTests/update-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,35 +101,7 @@ export default (
}
});

it("fails with no 'update-data' rule", async function () {
const readMethod = ["read", "get"];

for (const read of readMethod) {
await clean(app, service);
const item = await service.create({ test: true, userId: 1 });
let rejected = false;
try {
await service.update(
item[id],
{ [id]: item[id], test: false, userId: 1 },
{
ability: defineAbility(
(can) => {
can("update", "tests");
can(read, "tests");
},
{ resolveAction }
),
}
);
} catch (err) {
rejected = true;
}
assert.ok(rejected, "rejected");
}
});

it("basic cannot 'update-data'", async function () {
it("basic cannot 'update'", async function () {
const readMethod = ["read", "get"];

for (const read of readMethod) {
Expand All @@ -143,9 +115,7 @@ export default (
{
ability: defineAbility(
(can, cannot) => {
can("update", "tests");
can("update-data", "tests");
cannot("update-data", "tests", { test: false });
cannot("update", "tests", { test: false });
can(read, "tests");
},
{ resolveAction }
Expand All @@ -159,7 +129,7 @@ export default (
}
});

it("basic can 'update-data' with fail", async function () {
it("basic can 'update' with fail", async function () {
const readMethod = ["read", "get"];

for (const read of readMethod) {
Expand All @@ -172,9 +142,7 @@ export default (
{
ability: defineAbility(
(can) => {
can("update", "tests");
can("update-data", "tests");
can("update-data", "tests", { test: true });
can("update", "tests", { test: true });
can(read, "tests");
},
{ resolveAction }
Expand All @@ -188,7 +156,7 @@ export default (
}
});

it("basic can 'update-data'", async function () {
it("basic can 'update'", async function () {
const readMethod = ["read", "get"];

for (const read of readMethod) {
Expand All @@ -200,9 +168,7 @@ export default (
{
ability: defineAbility(
(can) => {
can("update", "tests");
can("update-data", "tests");
can("update-data", "tests", { test: false });
can("update", "tests", { userId: 1 });
can(read, "tests");
},
{ resolveAction }
Expand Down