CMMC 2.0 AC.L1-3.1.1 and AC.L1-3.1.2 together require limiting access to authorized users and to only the functions each user is authorized to perform. Without a role separation model, every authenticated user has identical capabilities — a regular user can call admin APIs, delete other accounts, or access privileged reports. CWE-269 (Improper Privilege Management) and OWASP A01 explicitly cover flat-permission architectures where no distinction exists between standard and administrative access. In a DoD contractor context, role separation is also a condition of accountability: you must be able to prove who had what authority.
High because the absence of role separation allows any authenticated user to perform privileged operations without any additional credential or escalation step.
Define roles as typed constants and enforce them server-side on every privileged route. Client-side UI hiding alone does not satisfy AC.L1-3.1.2 — the check must happen in the API handler:
// lib/auth/roles.ts
export const ROLES = { ADMIN: 'admin', USER: 'user' } as const
export type Role = typeof ROLES[keyof typeof ROLES]
export function requireRole(userRole: Role | undefined, required: Role): boolean {
if (required === ROLES.ADMIN) return userRole === ROLES.ADMIN
return userRole !== undefined
}
// app/api/admin/users/route.ts
export async function DELETE(req: Request) {
const session = await getServerSession()
if (!session) return Response.json({ error: 'Unauthorized' }, { status: 401 })
if (!requireRole(session.user.role as Role, ROLES.ADMIN)) {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
// Proceed with admin action
}
Enumerate all admin endpoints and verify each has a server-side role check — role definitions in the schema that are never checked in handlers do not satisfy this practice.
ID: gov-cmmc-level-1.access-control.role-separation
Severity: high
CMMC Practice: Derived from AC.L1-3.1.1 and AC.L1-3.1.2
What to look for: Look for RBAC (role-based access control) implementation. Search for role definitions (enums, constants, or string literals like 'admin', 'user', 'viewer'), role assignment in registration or user management, middleware that checks roles before granting access to sensitive routes, and API route guards that verify role before processing privileged operations. Check whether admin-only routes and functions are protected by explicit role verification on the server side, not just client-side UI hiding.
Pass criteria: Enumerate all role definitions in the codebase. At least 2 distinct roles are defined (e.g., admin + user). Admin routes or privileged functions are protected by server-side role checks. No more than 0 admin endpoints may lack role verification. Report: "X distinct roles found, Y admin endpoints protected by role checks."
Fail criteria: No role system found in the codebase. All authenticated users share identical permissions. Should not pass when roles are defined in the database schema but never checked in route handlers — definition without enforcement is insufficient.
Skip (N/A) when: Single-user application or personal tool with no multi-user role requirements.
Detail on fail: Specify the RBAC gap. Example: "No role definitions found. All authenticated users can call POST /api/admin/users and DELETE /api/admin/projects. Admin routes have no server-side role check." Keep under 500 characters.
Remediation: Implement server-side role checking on all privileged routes:
// lib/auth/roles.ts
export const ROLES = {
ADMIN: 'admin',
USER: 'user'
} as const
export type Role = typeof ROLES[keyof typeof ROLES]
export function requireRole(userRole: Role | undefined, requiredRole: Role): boolean {
if (requiredRole === ROLES.ADMIN) return userRole === ROLES.ADMIN
return userRole !== undefined
}
// app/api/admin/users/route.ts
import { requireRole, ROLES } from '@/lib/auth/roles'
export async function DELETE(req: Request) {
const session = await getServerSession()
if (!session) return Response.json({ error: 'Unauthorized' }, { status: 401 })
if (!requireRole(session.user.role as Role, ROLES.ADMIN)) {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
// Proceed with admin action
}