All files / src/hooks beforeOperation.ts

100% Statements 19/19
100% Branches 15/15
100% Functions 2/2
100% Lines 18/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 501x           1x   21x   48x 1x         47x       47x 3x     44x     44x 1x       43x 2x       41x     41x   41x 8x     33x      
import { checkPermission } from '../utils/checkPermission'
import type { GatekeeperOptions } from '../types'
 
/**
 * Global beforeOperation hook to check permissions
 */
export const createBeforeOperationHook = (options: GatekeeperOptions = {}) => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return async ({ args, operation }: any) => {
    // Skip permission check for excluded collections
    if (options.excludeCollections?.includes(args.collection)) {
      return args
    }
 
    // Skip permission checks if configured
    const shouldSkip =
      typeof options.skipPermissionChecks === 'function'
        ? options.skipPermissionChecks()
        : options.skipPermissionChecks
 
    if (shouldSkip) {
      return args
    }
 
    const { req, collection } = args
 
    // Skip if no user (public access will be handled by collection access control)
    if (!req?.user) {
      return args
    }
 
    // Skip if user doesn't have role (shouldn't happen for backend users)
    if (!('role' in req.user) || !req.user.role) {
      return args
    }
 
    // Build permission string
    const permission = `${collection}.${operation}`
 
    // Check permission (pass user ID for first user check)
    const hasAccess = await checkPermission(req.payload, req.user.role, permission, req.user.id)
 
    if (!hasAccess) {
      throw new Error(`Permission denied: You don't have ${operation} access to ${collection}`)
    }
 
    return args
  }
}