All files / roles create.js

96.77% Statements 30/31
91.3% Branches 21/23
100% Functions 1/1
96.77% Lines 30/31

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 115 116 117 118 119 120 121 122 123 1241x 1x 1x 1x 1x 1x   1x 1x     20x 20x 20x 20x   20x                       20x 4x                     16x             15x 2x                     13x 13x   11x 11x   11x                           11x             10x                 4x     4x 2x                   2x                     1x      
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb')
const { DynamoDBDocumentClient, PutCommand, GetCommand } = require('@aws-sdk/lib-dynamodb')
const { randomUUID } = require('crypto')
const { requirePermission } = require('../utils/requirePermission')
const { validateRolePermissions } = require('../utils/validateRolePermissions')
const { permissionChecker } = require('../utils/PermissionChecker')
 
const client = new DynamoDBClient({})
const docClient = DynamoDBDocumentClient.from(client)
 
async function createRoleHandler(event) {
  try {
    const tenantId = event.pathParameters?.tenantId
    const body = JSON.parse(event.body || '{}')
    const userId = event.requestContext?.authorizer?.claims?.sub
 
    Iif (!userId || !tenantId) {
      return {
        statusCode: 401,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({ error: 'Unauthorized' })
      }
    }
 
    // Validate required fields
    if (!body.name || !body.permissions || !Array.isArray(body.permissions)) {
      return {
        statusCode: 400,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({ error: 'Missing required fields: name, permissions' })
      }
    }
 
    // Check if user is member of this tenant
    const membershipCheck = await docClient.send(
      new GetCommand({
        TableName: process.env.MEMBERSHIPS_TABLE_NAME,
        Key: { userId, tenantId }
      })
    )
 
    if (!membershipCheck.Item || membershipCheck.Item.status !== 'active') {
      return {
        statusCode: 403,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({ error: 'Access denied - not a member of this flow' })
      }
    }
 
    // Validate that the creator has all the permissions they're trying to grant
    const creatorPermissions = await permissionChecker.getUserPermissions(userId, tenantId)
    await validateRolePermissions(body.permissions, creatorPermissions)
 
    const roleId = body.roleId || randomUUID()
    const now = new Date().toISOString()
 
    const role = {
      PK: `TENANT#${tenantId}`,
      SK: `ROLE#${roleId}`,
      tenantId: tenantId,
      roleId: roleId,
      name: body.name,
      description: body.description || '',
      permissions: body.permissions,
      isSystemRole: false,
      createdBy: userId,
      createdAt: now,
      updatedAt: now
    }
 
    await docClient.send(
      new PutCommand({
        TableName: process.env.ROLES_TABLE_NAME,
        Item: role
      })
    )
 
    return {
      statusCode: 201,
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
      },
      body: JSON.stringify(role)
    }
  } catch (error) {
    console.error('Error creating role:', error)
 
    // Handle permission validation errors specifically
    if (error.message && error.message.includes('permission to grant')) {
      return {
        statusCode: 403,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({ error: error.message })
      }
    }
 
    return {
      statusCode: 500,
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
      },
      body: JSON.stringify({ error: 'Internal server error' })
    }
  }
}
 
exports.handler = requirePermission(createRoleHandler, {
  permission: 'roles:create'
})