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 1153x                                                   10x 41x   41x 41x   41x 5x                       36x 2x   34x       36x   36x 6x 6x                           30x                                     3x 3x   3x       3x   3x   3x 2x 2x 2x 2x     1x     3x        
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 tenantId = event.requestContext?.authorizer?.claims?.['custom:tenantId']
      const userId = event.requestContext?.authorizer?.claims?.sub
 
      if (!userId || !tenantId) {
        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, tenantId, 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 tenantId = event.requestContext?.authorizer?.claims?.['custom:tenantId']
  const userId = event.requestContext?.authorizer?.claims?.sub
 
  Iif (!userId || !tenantId) {
    throw new Error('Missing authentication context')
  }
 
  const requiredPermission = typeof permission === 'function' ? permission(event) : permission
 
  const result = await permissionChecker.hasPermission(userId, tenantId, requiredPermission)
 
  if (!result.hasPermission) {
    const error = new Error('Insufficient permissions')
    error.statusCode = 403
    error.requiredPermission = requiredPermission
    throw error
  }
 
  return true
}
 
module.exports = {
  requirePermission,
  checkPermission
}