Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/juescoryisus/QualityDocD/llms.txt

Use this file to discover all available pages before exploring further.

QualityDocD enforces access control at two independent layers. The primary .NET MVC application uses ASP.NET Core cookie authentication with five string-based roles stored in the SQL Server Users table. The companion Node.js API uses JWT authentication with a separate six-level role hierarchy and a module-based permission system backed by its own PostgreSQL users table. The two systems are designed for different audiences — the .NET app is used by internal document managers, while the Node.js API serves multi-tenant company integrations — but both must be satisfied for end-to-end document workflows to complete.

Part 1 — .NET MVC App Roles

The User model stores the role as a plain string with a database default of "Viewer". There are five valid role values: Admin, Manager, Reviewer, Editor, and Viewer. ASP.NET Core’s [Authorize(Roles = "...")] attribute on controller actions enforces these at the HTTP level before any service method is invoked.

Role Definitions

Admins have unrestricted access to all controller actions. In addition to every permission granted to Manager and Reviewer, Admins can:
  • Export raw MongoDB metadata via the MongoJson endpoint ([Authorize(Roles = "Admin")])
  • Access all compliance and audit reports
  • Manage users and system configuration
Protected by: [Authorize(Roles = "Admin")]
Managers can drive every stage of the document lifecycle short of raw data export. Specific permissions include:
  • Approve, reject, and request changes on documents under review
  • Mark documents as Obsolete ([Authorize(Roles = "Admin,Manager")])
  • Access compliance reports and audit logs
Protected by: [Authorize(Roles = "Admin,Manager")] for obsolete; [Authorize(Roles = "Admin,Manager,Reviewer")] for approval actions
Reviewers participate in the document approval workflow. They can:
  • Approve a document under review (moves document to Approved when all reviewers agree)
  • Reject a document (moves document to Rejected — requires a comment)
  • Request changes (moves document to PendingChanges — requires a comment)
Reviewers cannot mark documents obsolete or access raw MongoDB exports.Protected by: [Authorize(Roles = "Admin,Manager,Reviewer")]
Editors can create new documents and edit existing ones, but only when the document is in a state that allows editing. The controller enforces:
if (vm.Status is not ("Draft" or "PendingChanges"))
{
    TempData["Error"] = "Solo se pueden editar documentos en estado Borrador o Cambios Pendientes.";
    return RedirectToAction(nameof(Details), new { id });
}
Editors cannot perform approval actions or mark documents obsolete.
The default role assigned to every new user (Role = "Viewer" in User.cs). Viewers can:
  • Browse and filter the document index
  • Read document details
  • Download approved document files via GET /Documents/Download/{id}
All write actions, approval actions, and administrative endpoints are inaccessible.

.NET Permission Matrix

ActionAdminManagerReviewerEditorViewer
View document list & details
Download document file
Create document
Edit document (Draft / PendingChanges)
Submit document for review
Approve document
Reject document
Request changes
Mark document Obsolete
Export raw MongoDB metadata
Compliance reports

Controller Authorization Decorators

The DocumentsController applies [Authorize] at the class level (all authenticated users) and narrows specific actions with role-scoped attributes:
[Authorize]
public class DocumentsController : Controller
{
    // POST /Documents/Approve/5
    [Authorize(Roles = "Admin,Manager,Reviewer")]
    public async Task<IActionResult> Approve(int id, string? comments) { ... }

    // POST /Documents/Reject/5
    [Authorize(Roles = "Admin,Manager,Reviewer")]
    public async Task<IActionResult> Reject(int id, string? comments) { ... }

    // POST /Documents/RequestChanges/5
    [Authorize(Roles = "Admin,Manager,Reviewer")]
    public async Task<IActionResult> RequestChanges(int id, string? comments) { ... }

    // POST /Documents/MarkObsolete/5
    [Authorize(Roles = "Admin,Manager")]
    public async Task<IActionResult> MarkObsolete(int id) { ... }
}
The [Authorize] attribute on the class-level means every action requires an authenticated session. Unauthenticated requests are redirected to the login page. Only the role-specific attributes on individual actions add stricter role requirements on top of that baseline.

Part 2 — Node.js API Roles and Modules

The Node.js API implements a separate multi-tenant RBAC system. Roles are stored in the PostgreSQL users table managed by Drizzle ORM, and each JWT token carries the user’s role claim. There are six roles arranged in a strict numeric weight hierarchy, and access to each feature is controlled by two orthogonal dimensions: minimum role level and module membership.

Six Roles with Weight Hierarchy

Roles are defined in src/lib/schemas.ts as a const array and enforced through the ROLE_WEIGHT map:
export const ROLE_WEIGHT: Record<UserRole, number> = {
  VIEWER:        1,
  COMMENTER:     2,
  CONTRIBUTOR:   3,
  OPERATOR:      4,
  COMPANY_ADMIN: 5,
  SUPER_ADMIN:   6,
};
RoleWeightDescription
VIEWER1Read-only access; can only consult approved documents
COMMENTER2Read access plus the ability to comment on documents
CONTRIBUTOR3Can upload documents, which are created in draft status
OPERATOR4Manages documents within their assigned module; can approve/reject
COMPANY_ADMIN5Full control within their own company; cannot manage other companies or create users above OPERATOR
SUPER_ADMIN6Unrestricted access across all companies and all data

Three Modules and Their Access Rules

Modules represent feature groups within the API. Read access (MODULE_ACCESS) and write access (MODULE_WRITE_ACCESS) are defined separately, so a role may be able to read a module’s data without being able to modify it.
Description: Approval, rejection, and full document lifecycle management.
PermissionRoles
Read (canRead)OPERATOR, COMPANY_ADMIN, SUPER_ADMIN
Write (canWrite)OPERATOR, COMPANY_ADMIN, SUPER_ADMIN
VIEWER, COMMENTER, and CONTRIBUTOR have no access to this module. This is the management surface — actions taken here propagate status changes that the .NET app monitors.

Middleware: requireMinRole

The requireMinRole middleware in src/middleware/roles.ts compares the authenticated user’s weight against the required minimum:
export function requireMinRole(minRole: UserRole) {
  return (req: Request, res: Response, next: NextFunction): void => {
    if (!req.auth) { res.status(401).json({ error: "Not authenticated" }); return; }
    if (ROLE_WEIGHT[req.auth.role] < ROLE_WEIGHT[minRole]) {
      res.status(403).json({ error: `Se requiere rol mínimo: ${minRole}` });
      return;
    }
    next();
  };
}
A request fails with 403 if the user’s role weight is strictly less than the required minimum. Equal or greater weights pass. For example, requireMinRole("OPERATOR") allows OPERATOR (4), COMPANY_ADMIN (5), and SUPER_ADMIN (6).

Middleware: requireModuleAccess

The requireModuleAccess middleware checks whether the user’s role appears in the explicit allow-list for a given module and access level:
export function requireModuleAccess(module: string, write = false) {
  return (req: Request, res: Response, next: NextFunction): void => {
    if (!req.auth) { res.status(401).json({ error: "Not authenticated" }); return; }
    const allowed = write ? MODULE_WRITE_ACCESS[module] : MODULE_ACCESS[module];
    if (!allowed?.includes(req.auth.role)) {
      res.status(403).json({ error: `Acceso denegado al ${module}` });
      return;
    }
    next();
  };
}
Unlike requireMinRole, module access is not purely hierarchical — a role must appear explicitly in the allow-list. This lets the system grant selective access without promoting a role to a higher weight.

Node.js Permission Matrix

RoleMODULE_1 ReadMODULE_1 WriteMODULE_2 ReadMODULE_2 WriteMODULE_3 ReadMODULE_3 Write
VIEWER
COMMENTER
CONTRIBUTOR
OPERATOR
COMPANY_ADMIN
SUPER_ADMIN

Multi-Tenancy Constraints

COMPANY_ADMIN is scoped to a single company. A COMPANY_ADMIN user cannot:
  • Create or manage users in another company’s companyId scope
  • Promote any user to a role above OPERATOR (weight ≥ 5)
  • Query or modify documents belonging to another company
Only SUPER_ADMIN can query across all companies and see the full dataset. All Node.js queries are implicitly filtered by the companyId claim embedded in the JWT token.

Build docs developers (and LLMs) love