feathers-casl
Version:
Add access control with CASL to your feathers application.
73 lines (61 loc) • 2.16 kB
text/typescript
import { getAvailableFields } from '../utils/index.js'
import type { Ability, AnyAbility } from '@casl/ability'
import type { Application, HookContext } from '@feathersjs/feathers'
import type { RealTimeConnection } from '@feathersjs/transport-commons'
import type { ChannelOptions, EventName, InitOptions } from '../types.js'
export const makeChannelOptions = (
app: Application,
options?: Partial<ChannelOptions>,
): ChannelOptions => {
options = options || {}
return Object.assign({}, defaultOptions, getAppOptions(app), options)
}
const defaultOptions: Omit<ChannelOptions, 'channels'> = {
activated: true,
channelOnError: ['authenticated'],
ability: (app: Application, connection: RealTimeConnection): Ability => {
return connection.ability
},
modelName: (context) => context.path,
restrictFields: true,
availableFields: (context: HookContext): string[] | undefined => {
const availableFields: string[] | ((context: HookContext) => string[]) =
context.service.options?.casl?.availableFields
return getAvailableFields(context, { availableFields })
},
useActionName: 'get',
}
export const makeDefaultOptions = (
options?: Partial<ChannelOptions>,
): ChannelOptions => {
return Object.assign({}, defaultOptions, options)
}
const getAppOptions = (
app: Application,
): ChannelOptions | Record<string, never> => {
const caslOptions: InitOptions = app?.get('casl')
return caslOptions && caslOptions.channels ? caslOptions.channels : {}
}
export const getAbility = (
app: Application,
data: Record<string, unknown>,
connection: RealTimeConnection,
context: HookContext,
options: Partial<ChannelOptions>,
): undefined | AnyAbility => {
if (options.ability) {
return typeof options.ability === 'function'
? options.ability(app, connection, data, context)
: options.ability
} else {
return connection.ability
}
}
const eventNameMap = {
create: 'created',
update: 'updated',
patch: 'patched',
remove: 'removed',
} satisfies Record<string, EventName>
export const getEventName = (method: string): EventName | undefined =>
(eventNameMap as any)[method]