feathers-casl
Version:
Add access control with CASL to your feathers application.
71 lines (60 loc) • 1.84 kB
text/typescript
import { subject } from '@casl/ability'
import { getDataIsArray } from 'feathers-utils'
import { throwUnlessCan } from './authorize/authorize.hook.utils.js'
import type { AnyAbility } from '@casl/ability'
import type { HookContext } from '@feathersjs/feathers'
import type {
CheckBasicPermissionHookOptions,
HookBaseOptions,
ThrowUnlessCanOptions,
} from '../types.js'
import { getMethodName } from '../utils/getMethodName.js'
const defaultOptions = {
ability: undefined,
actionOnForbidden: undefined,
checkMultiActions: false,
checkAbilityForInternal: false,
modelName: (context: Pick<HookContext, 'path'>): string => {
return context.path
},
notSkippable: false,
debug: false,
} satisfies Partial<HookBaseOptions>
export const makeDefaultBaseOptions = (): HookBaseOptions => {
return Object.assign({}, defaultOptions) as unknown as HookBaseOptions
}
export const checkCreatePerItem = (
context: HookContext,
ability: AnyAbility,
modelName: string,
options: Partial<
Pick<ThrowUnlessCanOptions, 'actionOnForbidden' | 'skipThrow'>
> &
Partial<
Pick<CheckBasicPermissionHookOptions, 'checkCreateForData' | 'method'>
>,
): HookContext => {
const method = getMethodName(context, options)
if (method !== 'create' || !options.checkCreateForData) {
return context
}
const checkCreateForData =
typeof options.checkCreateForData === 'function'
? options.checkCreateForData(context)
: true
if (!checkCreateForData) {
return context
}
// we have all information we need (maybe we need populated data?)
const { data: items } = getDataIsArray(context)
for (let i = 0, n = items.length; i < n; i++) {
throwUnlessCan(
ability,
method,
subject(modelName, items[i]),
modelName,
options,
)
}
return context
}