All files / utils requirePermission.js

90% Statements 27/30
87.5% Branches 14/16
100% Functions 3/3
90% Lines 27/30

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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 1156x                                                   13x 70x   70x 70x   70x 5x                       65x 2x   63x       65x   65x 8x 8x                           57x                                     3x 3x   3x       3x   3x   3x 2x 2x 2x 2x     1x     6x        
const { permissionChecker } = require('./PermissionChecker')
 
/**
 * Middleware to check user permissions before executing Lambda handler
 *
 * @param {Function} handler - The actual Lambda handler function
 * @param {Object} options - Configuration options
 * @param {Function|string} options.permission - Permission string or function that returns permission string
 * @param {Function} [options.resource] - Optional function to extract resource from event (for dynamic permissions)
 * @returns {Function} Wrapped Lambda handler with permission check
 *
 * @example
 * // Static permission
 * exports.handler = requirePermission(async (event) => {
 *   // handler logic
 * }, { permission: 'entity:create' });
 *
 * @example
 * // Dynamic permission based on path parameter
 * exports.handler = requirePermission(async (event) => {
 *   // handler logic
 * }, {
 *   permission: (event) => `entity-${event.pathParameters.id}:read`
 * });
 */
function requirePermission(handler, options) {
  return async (event) => {
    try {
      // Extract user info from Cognito authorizer
      const flowId = event.requestContext?.authorizer?.claims?.['custom:flowId']
      const userId = event.requestContext?.authorizer?.claims?.sub
 
      if (!userId || !flowId) {
        return {
          statusCode: 401,
          headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
          },
          body: JSON.stringify({ error: 'Unauthorized - Missing authentication' })
        }
      }
 
      // Determine required permission
      let requiredPermission
      if (typeof options.permission === 'function') {
        requiredPermission = options.permission(event)
      } else {
        requiredPermission = options.permission
      }
 
      // Check permission
      const result = await permissionChecker.hasPermission(userId, flowId, requiredPermission)
 
      if (!result.hasPermission) {
        console.log(`Permission denied: User ${userId} lacks ${requiredPermission}`, result.reason)
        return {
          statusCode: 403,
          headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
          },
          body: JSON.stringify({
            error: 'Forbidden - Insufficient permissions',
            required: requiredPermission
          })
        }
      }
 
      // Permission granted, execute handler
      return await handler(event)
    } catch (error) {
      console.error('Error in requirePermission middleware:', error)
      return {
        statusCode: 500,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({ error: 'Internal server error' })
      }
    }
  }
}
 
/**
 * Helper to check permission without wrapping (for use inside handlers)
 */
async function checkPermission(event, permission) {
  const flowId = event.requestContext?.authorizer?.claims?.['custom:flowId']
  const userId = event.requestContext?.authorizer?.claims?.sub
 
  Iif (!userId || !flowId) {
    throw new Error('Missing authentication context')
  }
 
  const requiredPermission = typeof permission === 'function' ? permission(event) : permission
 
  const result = await permissionChecker.hasPermission(userId, flowId, requiredPermission)
 
  if (!result.hasPermission) {
    const error = new Error('Insufficient permissions')
    error.statusCode = 403
    error.requiredPermission = requiredPermission
    throw error
  }
 
  return true
}
 
module.exports = {
  requirePermission,
  checkPermission
}