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:
- User Role Implementation:
- A basic UserRole enum defined directly in the user.entity.ts file with only three roles: ADMIN, STAFFING_COMPANY, and CANDIDATE
- A UserRole entity that defines a many-to-many relationship between users and roles
-
Helper methods in the User entity to check for specific roles
-
Admin Initialization:
- A script (initialize_admin.ts) that creates an admin user with the ADMIN role
- 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¶
- Backward Compatibility:
- Ensure existing code that uses the UserRole enum continues to work
-
Update any hardcoded role checks throughout the codebase
-
Migration Considerations:
- The migration should be non-disruptive to existing users
-
Consider running the migration during a maintenance window
-
Testing Requirements:
- Test that the admin user can access all organization onboarding features
- Verify that role-based access control works correctly for all roles
- 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 ...
}