- Implement OAuth 2.0 and PAT authentication methods - Add user management, roles, and profile functionality - Add database migrations and admin user scripts - Update services for authentication and user settings - Add protected routes and permission hooks - Update documentation for authentication and database access
116 lines
3.3 KiB
TypeScript
116 lines
3.3 KiB
TypeScript
/**
|
|
* Authorization Middleware
|
|
*
|
|
* Middleware functions for route protection based on authentication and permissions.
|
|
*/
|
|
|
|
import { Request, Response, NextFunction } from 'express';
|
|
import { authService, type SessionUser } from '../services/authService.js';
|
|
import { roleService } from '../services/roleService.js';
|
|
import { logger } from '../services/logger.js';
|
|
|
|
// Extend Express Request to include user info
|
|
declare global {
|
|
namespace Express {
|
|
interface Request {
|
|
sessionId?: string;
|
|
user?: SessionUser;
|
|
accessToken?: string;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Middleware to require authentication
|
|
*/
|
|
export function requireAuth(req: Request, res: Response, next: NextFunction) {
|
|
const sessionId = req.headers['x-session-id'] as string || req.cookies?.sessionId;
|
|
|
|
if (!sessionId) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
// Get session user
|
|
authService.getSession(sessionId)
|
|
.then(session => {
|
|
if (!session) {
|
|
return res.status(401).json({ error: 'Invalid or expired session' });
|
|
}
|
|
|
|
// Check if it's a local user session
|
|
if ('id' in session.user) {
|
|
req.sessionId = sessionId;
|
|
req.user = session.user as SessionUser;
|
|
req.accessToken = session.accessToken;
|
|
next();
|
|
} else {
|
|
// OAuth-only session (Jira user without local account)
|
|
// For now, allow through but user won't have permissions
|
|
req.sessionId = sessionId;
|
|
req.accessToken = session.accessToken;
|
|
next();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
logger.error('Auth middleware error:', error);
|
|
res.status(500).json({ error: 'Authentication check failed' });
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Middleware to require a specific role
|
|
*/
|
|
export function requireRole(roleName: string) {
|
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
if (!req.user || !('id' in req.user)) {
|
|
return res.status(403).json({ error: 'Permission denied' });
|
|
}
|
|
|
|
const hasRole = await roleService.userHasRole(req.user.id, roleName);
|
|
if (!hasRole) {
|
|
return res.status(403).json({ error: `Role '${roleName}' required` });
|
|
}
|
|
|
|
next();
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Middleware to require a specific permission
|
|
*/
|
|
export function requirePermission(permissionName: string) {
|
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
if (!req.user || !('id' in req.user)) {
|
|
return res.status(403).json({ error: 'Permission denied' });
|
|
}
|
|
|
|
const hasPermission = await roleService.userHasPermission(req.user.id, permissionName);
|
|
if (!hasPermission) {
|
|
return res.status(403).json({ error: `Permission '${permissionName}' required` });
|
|
}
|
|
|
|
next();
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Middleware to check permission (optional, doesn't fail if missing)
|
|
* Sets req.hasPermission flag
|
|
*/
|
|
export function checkPermission(permissionName: string) {
|
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
if (req.user && 'id' in req.user) {
|
|
const hasPermission = await roleService.userHasPermission(req.user.id, permissionName);
|
|
(req as any).hasPermission = hasPermission;
|
|
} else {
|
|
(req as any).hasPermission = false;
|
|
}
|
|
next();
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Middleware to require admin role
|
|
*/
|
|
export const requireAdmin = requireRole('administrator');
|