Files
webclone/services/api-gateway/src/middleware/authorization.ts
T

202 lines
6.0 KiB
TypeScript

import { Request, Response, NextFunction } from 'express';
import { RBACService } from '../services/rbac.service';
export interface AuthorizedRequest extends Request {
user?: {
id: string;
email: string;
subscription: string;
roles: string[];
[key: string]: any;
};
}
/**
* Middleware to check if user has required permission
*/
export function requirePermission(permissionId: string, projectIdParam?: string) {
return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
const userId = req.user.id;
let projectId: string | undefined;
// Extract project ID from request parameters if specified
if (projectIdParam) {
projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
}
// Check if user has the required permission
if (!RBACService.hasPermission(userId, permissionId, projectId)) {
return res.status(403).json({
error: 'Insufficient permissions',
required: permissionId,
projectId: projectId || 'global'
});
}
next();
};
}
/**
* Middleware to check if user has any of the required permissions
*/
export function requireAnyPermission(permissionIds: string[], projectIdParam?: string) {
return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
const userId = req.user.id;
let projectId: string | undefined;
// Extract project ID from request parameters if specified
if (projectIdParam) {
projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
}
// Check if user has any of the required permissions
if (!RBACService.hasAnyPermission(userId, permissionIds, projectId)) {
return res.status(403).json({
error: 'Insufficient permissions',
required: `Any of: ${permissionIds.join(', ')}`,
projectId: projectId || 'global'
});
}
next();
};
}
/**
* Middleware to check if user has all of the required permissions
*/
export function requireAllPermissions(permissionIds: string[], projectIdParam?: string) {
return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
const userId = req.user.id;
let projectId: string | undefined;
// Extract project ID from request parameters if specified
if (projectIdParam) {
projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
}
// Check if user has all of the required permissions
if (!RBACService.hasAllPermissions(userId, permissionIds, projectId)) {
return res.status(403).json({
error: 'Insufficient permissions',
required: `All of: ${permissionIds.join(', ')}`,
projectId: projectId || 'global'
});
}
next();
};
}
/**
* Middleware to check if user is admin
*/
export function requireAdmin() {
return requirePermission('admin:system');
}
/**
* Middleware to check project access
*/
export function requireProjectAccess(action: 'read' | 'update' | 'delete' | 'share' = 'read', projectIdParam: string = 'projectId') {
return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
const userId = req.user.id;
const projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
if (!projectId) {
return res.status(400).json({ error: 'Project ID required' });
}
// Check if user can access the project
if (!RBACService.canAccessProject(userId, projectId, action)) {
return res.status(403).json({
error: 'Project access denied',
action,
projectId
});
}
next();
};
}
/**
* Middleware to check if user owns the resource or is admin
*/
export function requireOwnershipOrAdmin(ownerIdParam: string = 'userId') {
return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
const userId = req.user.id;
const resourceOwnerId = req.params[ownerIdParam] || req.body[ownerIdParam] || req.query[ownerIdParam] as string;
// Allow if user is the owner or is admin
if (userId === resourceOwnerId || RBACService.isAdmin(userId)) {
return next();
}
return res.status(403).json({
error: 'Access denied - ownership or admin privileges required'
});
};
}
/**
* Utility function to get user permissions for response
*/
export function getUserPermissionsForResponse(userId: string, projectId?: string) {
return {
permissions: RBACService.getUserPermissions(userId, projectId).map(p => ({
id: p.id,
name: p.name,
resource: p.resource,
action: p.action
})),
roles: RBACService.getUserRoles(userId, projectId).map(ur => ({
roleId: ur.roleId,
projectId: ur.projectId,
grantedAt: ur.grantedAt
})),
isAdmin: RBACService.isAdmin(userId)
};
}
/**
* Middleware to add user permissions to response
*/
export function addUserPermissions(projectIdParam?: string) {
return (req: AuthorizedRequest, res: Response, next: NextFunction) => {
if (req.user) {
const userId = req.user.id;
let projectId: string | undefined;
if (projectIdParam) {
projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string;
}
// Add permissions to request for use in route handlers
(req as any).userPermissions = getUserPermissionsForResponse(userId, projectId);
}
next();
};
}