- Renamed auth-service to iam-service across various files for consistency. - Updated Dockerfiles, deployment configurations, and documentation to reflect the service name change. - Enhanced testing commands in documentation to point to the new iam-service. - Removed outdated auth-service files and configurations to streamline the project structure. - Improved bilingual documentation for clarity on the new service structure and usage.
220 lines
5.4 KiB
TypeScript
220 lines
5.4 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import { rbacService } from './rbac.service';
|
|
import { policyEngine } from './policy.engine';
|
|
import { z } from 'zod';
|
|
import { requirePermission } from '../../middlewares/rbac.middleware';
|
|
|
|
const AssignRoleDto = z.object({
|
|
userId: z.string(),
|
|
roleId: z.string(),
|
|
expiresAt: z.string().optional(),
|
|
});
|
|
|
|
const GrantPermissionDto = z.object({
|
|
userId: z.string(),
|
|
permissionId: z.string(),
|
|
expiresAt: z.string().optional(),
|
|
});
|
|
|
|
/**
|
|
* EN: RBAC Controller
|
|
* VI: Controller RBAC
|
|
*/
|
|
export class RBACController {
|
|
/**
|
|
* EN: Get user permissions
|
|
* VI: Lấy quyền của người dùng
|
|
*/
|
|
async getUserPermissions(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const userId = (req as any).user?.id || (req as any).user?.sub || req.params.userId;
|
|
|
|
if (!userId) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: { code: 'USER_ID_REQUIRED', message: 'User ID required' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const permissions = await rbacService.getUserPermissions(userId);
|
|
const roles = await rbacService.getUserRoles(userId);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
permissions,
|
|
roles,
|
|
},
|
|
});
|
|
} catch (error: any) {
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: 'GET_PERMISSIONS_FAILED',
|
|
message: error.message || 'Failed to get permissions',
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* EN: Assign role to user
|
|
* VI: Gán role cho người dùng
|
|
*/
|
|
async assignRole(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const data = AssignRoleDto.parse(req.body);
|
|
const grantedBy = (req as any).user?.id || (req as any).user?.sub;
|
|
|
|
await rbacService.assignRole(data.userId, data.roleId, {
|
|
grantedBy,
|
|
expiresAt: data.expiresAt ? new Date(data.expiresAt) : undefined,
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { message: 'Role assigned successfully' },
|
|
});
|
|
} catch (error: any) {
|
|
if (error instanceof z.ZodError) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: {
|
|
code: 'VALIDATION_ERROR',
|
|
message: 'Invalid input',
|
|
details: error.errors,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: 'ASSIGN_ROLE_FAILED',
|
|
message: error.message || 'Failed to assign role',
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* EN: Revoke role from user
|
|
* VI: Thu hồi role từ người dùng
|
|
*/
|
|
async revokeRole(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const { userId, roleId } = req.body;
|
|
|
|
if (!userId || !roleId) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: { code: 'INVALID_INPUT', message: 'userId and roleId required' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
await rbacService.revokeRole(userId, roleId);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { message: 'Role revoked successfully' },
|
|
});
|
|
} catch (error: any) {
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: 'REVOKE_ROLE_FAILED',
|
|
message: error.message || 'Failed to revoke role',
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* EN: Grant permission to user
|
|
* VI: Cấp quyền cho người dùng
|
|
*/
|
|
async grantPermission(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const data = GrantPermissionDto.parse(req.body);
|
|
const grantedBy = (req as any).user?.id || (req as any).user?.sub;
|
|
|
|
await rbacService.grantPermission(data.userId, data.permissionId, {
|
|
grantedBy,
|
|
expiresAt: data.expiresAt ? new Date(data.expiresAt) : undefined,
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { message: 'Permission granted successfully' },
|
|
});
|
|
} catch (error: any) {
|
|
if (error instanceof z.ZodError) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: {
|
|
code: 'VALIDATION_ERROR',
|
|
message: 'Invalid input',
|
|
details: error.errors,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: 'GRANT_PERMISSION_FAILED',
|
|
message: error.message || 'Failed to grant permission',
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* EN: Check permission
|
|
* VI: Kiểm tra quyền
|
|
*/
|
|
async checkPermission(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const userId = (req as any).user?.id || (req as any).user?.sub;
|
|
const { resource, action, scope } = req.query;
|
|
|
|
if (!userId || !resource || !action) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: {
|
|
code: 'INVALID_INPUT',
|
|
message: 'userId, resource, and action are required',
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const hasPermission = await rbacService.hasPermission(
|
|
userId,
|
|
resource as string,
|
|
action as string,
|
|
scope as string | undefined
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { hasPermission },
|
|
});
|
|
} catch (error: any) {
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: 'CHECK_PERMISSION_FAILED',
|
|
message: error.message || 'Failed to check permission',
|
|
},
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
export const rbacController = new RBACController();
|