Skip to content

Milestone: M2 — Platform and Client Admin | SOW Reference: FR7 | Requirement Clarity: ✅ Clear | Dev Status: 🟢 Prototyped in POC Moved from docs/requirements/tasks/user-role-enum-implementation.md — this is the original detailed implementation breakdown, unmodified below.

M2-09 User Role Enum Implementation Tasks

This document breaks down the implementation tasks for creating a comprehensive UserRole enum and updating the admin initialization script to properly assign the PLATFORM_ADMIN role to the system administrator.

Implementation Strategy

The implementation will follow these steps: 1. Create a dedicated UserRole enum in a separate file 2. Update the user entity to use this enum 3. Update the initialize_admin script to assign the PLATFORM_ADMIN role 4. Ensure proper role-based access control for organization onboarding

Current Implementation Status

The project currently has:

  1. User Role Implementation:
  2. A basic UserRole enum defined directly in the user.entity.ts file with only three roles: ADMIN, STAFFING_COMPANY, and CANDIDATE
  3. A UserRole entity that defines a many-to-many relationship between users and roles
  4. Helper methods in the User entity to check for specific roles

  5. Admin Initialization:

  6. A script (initialize_admin.ts) that creates an admin user with the ADMIN role
  7. The admin user is associated with the Curo organization

Backend Tasks

Create UserRole Enum

  • Create a new file app/backend/src/users/enums/user-role.enum.ts
  • Define a comprehensive UserRole enum with the following roles:
    • PLATFORM_ADMIN (highest level administrator with full system access)
    • SYSTEM_AUDITOR (for auditing system-wide activities)
    • ORG_ADMIN (administrator for a specific staffing company)
    • ORG_USER (standard user within a staffing company)
    • ISSUER (role with permissions to issue credentials)
    • RELYING_PARTY (role with permissions to verify credentials)
    • CREDENTIAL_MANAGER (role for managing credential lifecycle)
  • Add JSDoc comments explaining each role's purpose and permissions
  • Export the enum for use in other files

  • Update app/backend/src/users/entities/user.entity.ts

  • Import the new UserRole enum from the dedicated enum file
  • Replace the inline enum definition with the imported enum
  • Update the isAdmin() method to check for PLATFORM_ADMIN role
  • Add additional helper methods for checking other roles

Update Admin Initialization Script

  • Modify app/backend/src/scripts/initialize_admin.ts
  • Import the UserRole enum from the new enum file
  • Add functionality to insert all roles from the enum into the role table
  • Update the admin user creation to use UserRole.PLATFORM_ADMIN instead of UserRole.ADMIN
  • Create a user-role mapping for admin@curo.com with the PLATFORM_ADMIN role
  • Add comments explaining that this user has full system access
  • Ensure the admin user is properly associated with the Curo organization

Role Table Population and User-Role Mapping

  • Enhance the initialize_admin script to populate the role table:
  • Create a function to insert all roles from the UserRole enum into the role table
  • For each role in the enum:

    • Check if the role already exists in the database
    • If not, create a new Role entity with appropriate name, slug, and description
    • Set isSystem flag to true for system-wide roles
    • Save the role to the database
  • Implement user-role mapping for the admin user:

  • Retrieve the PLATFORM_ADMIN role from the database
  • Create a new UserRole entity to link the admin user to the PLATFORM_ADMIN role
  • Set the assignedBy field to the admin user's ID (self-assigned)
  • Save the user-role mapping to the database

Database Schema Updates

  • Create a migration script to update existing admin users
  • Identify users with the current ADMIN role
  • Update them to have the new PLATFORM_ADMIN role
  • Ensure no data loss during the migration

Role-Based Access Control Implementation

  • Update authorization guards to recognize the new roles
  • Modify existing guards to check for PLATFORM_ADMIN for system-wide operations
  • Add specific checks for organization onboarding permissions
  • Ensure PLATFORM_ADMIN has access to all organization management features

  • Implement role-based access for organization onboarding

  • Ensure PLATFORM_ADMIN can access all organization onboarding features
  • Restrict organization management to PLATFORM_ADMIN and ORG_ADMIN roles
  • Add proper authorization checks to all organization-related endpoints

Dependencies & Assumptions

Prerequisites

  • The user.entity.ts file must be updated to use the new enum
  • The initialize_admin.ts script must be updated to use the new role
  • Database migrations must be created to handle existing users

Cross-team Needs

  • Frontend team needs to update any role-based UI components
  • DevOps team needs to run the updated initialization script
  • QA team needs to test role-based access control

Implementation Notes

  1. Backward Compatibility:
  2. Ensure existing code that uses the UserRole enum continues to work
  3. Update any hardcoded role checks throughout the codebase

  4. Migration Considerations:

  5. The migration should be non-disruptive to existing users
  6. Consider running the migration during a maintenance window

  7. Testing Requirements:

  8. Test that the admin user can access all organization onboarding features
  9. Verify that role-based access control works correctly for all roles
  10. Ensure the initialize_admin script correctly assigns the PLATFORM_ADMIN role

Implementation Examples

UserRole Enum Example

// app/backend/src/users/enums/user-role.enum.ts

/**
 * Enum representing the different roles a user can have in the system
 */
export enum UserRole {
  /**
   * Highest level administrator with access to all platform functions and organizations
   */
  PLATFORM_ADMIN = 'platform_admin',

  /**
   * Role for auditing system-wide activities and compliance
   */
  SYSTEM_AUDITOR = 'system_auditor',

  /**
   * Administrator for a specific staffing company with full access to company settings
   */
  ORG_ADMIN = 'org_admin',

  /**
   * Standard user within a staffing company with limited administrative capabilities
   */
  ORG_USER = 'org_user',

  /**
   * Role with permissions to issue credentials to candidates
   */
  ISSUER = 'issuer',

  /**
   * Role with permissions to request and verify credential disclosures
   */
  RELYING_PARTY = 'relying_party',

  /**
   * Role focused on managing the credential lifecycle
   */
  CREDENTIAL_MANAGER = 'credential_manager',
}

Initialize Admin Script Update Example

// Excerpt from app/backend/src/scripts/initialize_admin.ts

import { DataSource } from 'typeorm';
import { typeOrmConfig } from '../database/typeorm.config';
import { Organization, KybStatus } from '../admin/entities/organization.entity';
import { User, UserStatus } from '../users/entities/user.entity';
import { UserRole } from '../users/enums/user-role.enum';
import { Role } from '../admin/entities/role.entity';
import { UserRole as UserRoleEntity } from '../users/entities/user-role.entity';

// Function to initialize roles in the database
async function initializeRoles(dataSource: DataSource): Promise<Map<string, Role>> {
  console.log('Initializing roles...');
  const roleRepository = dataSource.getRepository(Role);
  const roleMap = new Map<string, Role>();

  // Define roles with descriptions
  const roleDefinitions = [
    { slug: UserRole.PLATFORM_ADMIN, name: 'Platform Administrator', description: 'Full access to all platform functions', isSystem: true },
    { slug: UserRole.SYSTEM_AUDITOR, name: 'System Auditor', description: 'Access to audit logs and compliance reports', isSystem: true },
    { slug: UserRole.ORG_ADMIN, name: 'Organization Administrator', description: 'Full access to organization settings', isSystem: false },
    { slug: UserRole.ORG_USER, name: 'Organization User', description: 'Limited access to organization functions', isSystem: false },
    { slug: UserRole.ISSUER, name: 'Credential Issuer', description: 'Can issue credentials to candidates', isSystem: false },
    { slug: UserRole.RELYING_PARTY, name: 'Relying Party', description: 'Can request and verify credentials', isSystem: false },
    { slug: UserRole.CREDENTIAL_MANAGER, name: 'Credential Manager', description: 'Manages credential lifecycle', isSystem: false },
  ];

  // Create or update each role
  for (const roleDef of roleDefinitions) {
    let role = await roleRepository.findOne({ where: { slug: roleDef.slug } });

    if (!role) {
      console.log(`Creating role: ${roleDef.name}`);
      role = new Role();
      role.slug = roleDef.slug;
      role.name = roleDef.name;
      role.description = roleDef.description;
      role.isSystem = roleDef.isSystem;
      await roleRepository.save(role);
    } else {
      console.log(`Role ${roleDef.name} already exists`);
    }

    roleMap.set(roleDef.slug, role);
  }

  return roleMap;
}

// Function to assign role to user
async function assignRoleToUser(
  dataSource: DataSource,
  userId: string,
  roleId: string,
  assignedById: string
): Promise<void> {
  console.log(`Assigning role ${roleId} to user ${userId}`);
  const userRoleRepository = dataSource.getRepository(UserRoleEntity);

  // Check if the user already has this role
  const existingUserRole = await userRoleRepository.findOne({
    where: { userId, roleId }
  });

  if (!existingUserRole) {
    const userRole = new UserRoleEntity();
    userRole.userId = userId;
    userRole.roleId = roleId;
    userRole.assignedBy = assignedById;
    await userRoleRepository.save(userRole);
    console.log('Role assigned successfully');
  } else {
    console.log('User already has this role');
  }
}

// In the main initializeAdmin function:
async function initializeAdmin() {
  // ... existing code ...

  // Initialize roles
  const roleMap = await initializeRoles(dataSource);

  // ... existing code for creating organization and admin user ...

  // Assign PLATFORM_ADMIN role to the admin user
  const platformAdminRole = roleMap.get(UserRole.PLATFORM_ADMIN);
  if (platformAdminRole && adminUser) {
    await assignRoleToUser(dataSource, adminUser.id, platformAdminRole.id, adminUser.id);
  }

  // ... rest of the function ...
}