Your commit message here
This commit is contained in:
116
src/lib/middleware/auth.js
Normal file
116
src/lib/middleware/auth.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import { getToken } from "next-auth/jwt"
|
||||
import { NextResponse } from "next/server"
|
||||
import db from "../db.js"
|
||||
|
||||
// Role hierarchy for permission checking
|
||||
const ROLE_HIERARCHY = {
|
||||
'admin': 4,
|
||||
'project_manager': 3,
|
||||
'user': 2,
|
||||
'read_only': 1
|
||||
}
|
||||
|
||||
export function withAuth(handler, options = {}) {
|
||||
return async (req, context) => {
|
||||
try {
|
||||
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET })
|
||||
|
||||
// Check if user is authenticated
|
||||
if (!token?.userId) {
|
||||
return NextResponse.json(
|
||||
{ error: "Authentication required" },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// Check if user account is active
|
||||
const user = db.prepare("SELECT is_active FROM users WHERE id = ?").get(token.userId)
|
||||
if (!user?.is_active) {
|
||||
return NextResponse.json(
|
||||
{ error: "Account deactivated" },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
// Check role-based permissions
|
||||
if (options.requiredRole && !hasPermission(token.role, options.requiredRole)) {
|
||||
logAuditEvent(token.userId, 'ACCESS_DENIED', options.resource || 'api', req.url)
|
||||
return NextResponse.json(
|
||||
{ error: "Insufficient permissions" },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
// Check resource-specific permissions
|
||||
if (options.checkResourceAccess) {
|
||||
const hasAccess = await options.checkResourceAccess(token, context.params)
|
||||
if (!hasAccess) {
|
||||
return NextResponse.json(
|
||||
{ error: "Access denied to this resource" },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Add user info to request
|
||||
req.user = {
|
||||
id: token.userId,
|
||||
email: token.email,
|
||||
name: token.name,
|
||||
role: token.role
|
||||
}
|
||||
|
||||
// Call the original handler
|
||||
return await handler(req, context)
|
||||
} catch (error) {
|
||||
console.error("Auth middleware error:", error)
|
||||
return NextResponse.json(
|
||||
{ error: "Internal server error" },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function hasPermission(userRole, requiredRole) {
|
||||
return ROLE_HIERARCHY[userRole] >= ROLE_HIERARCHY[requiredRole]
|
||||
}
|
||||
|
||||
// Helper for read-only operations
|
||||
export function withReadAuth(handler) {
|
||||
return withAuth(handler, { requiredRole: 'read_only' })
|
||||
}
|
||||
|
||||
// Helper for user-level operations
|
||||
export function withUserAuth(handler) {
|
||||
return withAuth(handler, { requiredRole: 'user' })
|
||||
}
|
||||
|
||||
// Helper for project manager operations
|
||||
export function withManagerAuth(handler) {
|
||||
return withAuth(handler, { requiredRole: 'project_manager' })
|
||||
}
|
||||
|
||||
// Helper for admin operations
|
||||
export function withAdminAuth(handler) {
|
||||
return withAuth(handler, { requiredRole: 'admin' })
|
||||
}
|
||||
|
||||
// Audit logging helper
|
||||
function logAuditEvent(userId, action, resourceType, resourceId, req = null) {
|
||||
try {
|
||||
db.prepare(`
|
||||
INSERT INTO audit_logs (user_id, action, resource_type, resource_id, ip_address, user_agent)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
userId,
|
||||
action,
|
||||
resourceType,
|
||||
resourceId,
|
||||
req?.ip || req?.socket?.remoteAddress || 'unknown',
|
||||
req?.headers?.['user-agent'] || 'unknown'
|
||||
)
|
||||
} catch (error) {
|
||||
console.error("Audit log error:", error)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user