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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "feathers-casl",
"version": "2.2.2",
"version": "2.2.3",
"description": "Add access control with CASL to your feathers application.",
"author": "fratzinger",
"homepage": "https://feathers-casl.netlify.app/",
Expand Down
8 changes: 8 additions & 0 deletions src/hooks/authorize/authorize.hook.after.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ export const authorizeAfter = async <H extends HookContext = HookContext>(
result = pickFieldsForItem(items[0])
if (method === 'get' && _isEmpty(result)) {
if (options.actionOnForbidden) options.actionOnForbidden()
if (options.debug) {
console.error(
'Feathers-CASL: authorizeAfter hook - all fields are restricted for this action',
method,
modelName,
items[0],
)
}
throw new Forbidden(`You're not allowed to ${method} ${modelName}`)
}
}
Expand Down
50 changes: 48 additions & 2 deletions src/hooks/authorize/authorize.hook.before.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,30 @@ const handleSingle = async <H extends HookContext = HookContext>(

const getMethod = service._get ? '_get' : 'get'

const item = await service[getMethod](id, paramsGet)
const item = await service[getMethod](id, paramsGet).catch((err: any) => {
if (options.debug) {
console.error(
'Feathers-CASL: authorizeBefore hook - error fetching item for single-item authorization check',
getMethod,
method,
id,
ability.relevantRuleFor(method, modelName),
paramsGet.query,
'query from casl',
mergeQueryFromAbility(
context.app,
ability,
method,
modelName,
{},
context.service,
options,
),
)
}

throw err
})

const restrictingFields = hasRestrictingFields(
ability,
Expand All @@ -180,6 +203,14 @@ const handleSingle = async <H extends HookContext = HookContext>(
if (options.actionOnForbidden) {
options.actionOnForbidden()
}
if (options.debug) {
console.error(
'Feathers-CASL: authorizeBefore hook - all fields are restricted for this action',
method,
modelName,
id,
)
}
throw new Forbidden("You're not allowed to make this request")
}

Expand All @@ -198,6 +229,14 @@ const handleSingle = async <H extends HookContext = HookContext>(
if (options.actionOnForbidden) {
options.actionOnForbidden()
}
if (options.debug) {
console.error(
'Feathers-CASL: authorizeBefore hook - no fields are allowed to be changed for this action',
method,
modelName,
id,
)
}
throw new Forbidden("You're not allowed to make this request")
}

Expand All @@ -222,7 +261,7 @@ const checkData = <H extends HookContext = HookContext>(
data: Record<string, unknown>,
options: Pick<
AuthorizeHookOptions,
'actionOnForbidden' | 'usePatchData' | 'useUpdateData' | 'method'
'actionOnForbidden' | 'usePatchData' | 'useUpdateData' | 'method' | 'debug'
>,
): void => {
const method = getMethodName(context, options)
Expand Down Expand Up @@ -261,6 +300,13 @@ const handleMulti = async <H extends HookContext = HookContext>(
if (options.actionOnForbidden) {
options.actionOnForbidden()
}
if (options.debug) {
console.error(
'Feathers-CASL: authorizeBefore hook - all fields are restricted for multi-patch action',
method,
modelName,
)
}
throw new Forbidden("You're not allowed to make this request")
}
if (fields && fields.length > 0) {
Expand Down
11 changes: 10 additions & 1 deletion src/hooks/authorize/authorize.hook.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type {
ThrowUnlessCanOptions,
} from '../../types.js'
import type { Promisable } from 'type-fest'
import { getMethodName } from '../../utils/getMethodName'
import { getMethodName } from '../../utils/getMethodName.js'

declare module '@feathersjs/feathers' {
interface Params {
Expand Down Expand Up @@ -146,6 +146,15 @@ export const throwUnlessCan = <T extends ForcedSubject<string>>(
): boolean => {
if (ability.cannot(method, resource)) {
if (options.actionOnForbidden) options.actionOnForbidden()
if (options.debug) {
console.error(
'Feathers-CASL: throwUnlessCan - permission denied',
method,
modelName,
resource,
ability.relevantRuleFor(method, resource),
)
}
if (!options.skipThrow) {
throw new Forbidden(`You are not allowed to ${method} ${modelName}`)
}
Expand Down
1 change: 1 addition & 0 deletions src/hooks/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const defaultOptions: HookBaseOptions = {
return context.path
},
notSkippable: false,
debug: false,
}

export const makeDefaultBaseOptions = (): HookBaseOptions => {
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface HookBaseOptions<H extends HookContext = HookContext> {
modelName: GetModelName
notSkippable: boolean
method?: string | ((context: H) => string)
debug?: boolean
}

export interface CheckBasicPermissionHookOptions<
Expand Down Expand Up @@ -128,7 +129,7 @@ export interface GetMinimalFieldsOptions {
export type Path = string | Array<string | number>

export interface ThrowUnlessCanOptions
extends Pick<HookBaseOptions, 'actionOnForbidden'> {
extends Pick<HookBaseOptions, 'actionOnForbidden' | 'debug'> {
skipThrow: boolean
}

Expand Down
Loading