# RBAC Middleware and Permission System

# RBAC Middleware and Permission System

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [AGENTS.md](AGENTS.md)
- [README.md](README.md)
- [db-meta/patches/00000000000000_init_database.sql](db-meta/patches/00000000000000_init_database.sql)
- [docs/gitflow.md](docs/gitflow.md)
- [src/modules/auth/README.md](src/modules/auth/README.md)
- [src/modules/auth/oidc-client.ts](src/modules/auth/oidc-client.ts)
- [src/modules/auth/permissions.ts](src/modules/auth/permissions.ts)
- [src/modules/auth/rbac.middleware.ts](src/modules/auth/rbac.middleware.ts)
- [src/modules/auth/role-mapping-repo.ts](src/modules/auth/role-mapping-repo.ts)
- [src/modules/auth/role_mapping_entity.ts](src/modules/auth/role_mapping_entity.ts)
- [src/modules/auth/user-profile-repo.ts](src/modules/auth/user-profile-repo.ts)

</details>



The Primebrick backend implements a robust Role-Based Access Control (RBAC) system designed for granular security, wildcard-based permission matching, and seamless integration with external Identity Providers (IDP) like Casdoor. The system follows a **declare-first** policy where every route must explicitly state its required permissions via a specialized middleware.

## Permission Registry and Structure

The system uses a single source of truth for all available permissions defined in the `Permission` enum. Permissions follow a hierarchical dot-notation pattern: `module.action.granularity` [src/modules/auth/permissions.ts:88-89]().

### Permission Format Examples
*   `customers.read.all`: List all customers [AGENTS.md:91]().
*   `customers.read.audit`: Access customer audit trails [AGENTS.md:93]().
*   `customers.delete.bulk`: Perform bulk deletion operations [AGENTS.md:99]().
*   `*`: A global wildcard matching all permissions (effectively a super-user) [AGENTS.md:110]().

### Permission Sentinels
Two pseudo-permissions act as sentinels and are handled directly by the middleware logic rather than role expansion [src/modules/auth/permissions.ts:22-23]():
*   `Permission.PUBLIC` (`_public`): Endpoint is reachable without a JWT. In `GATEWAY` mode, the gateway secret is still verified [src/modules/auth/permissions.ts:37]().
*   `Permission.AUTHENTICATED_USER` (`_authenticated_user`): Any caller with a valid token passes, regardless of specific roles [src/modules/auth/permissions.ts:39]().

**Sources:** [src/modules/auth/permissions.ts:34-87](), [AGENTS.md:88-104]()

## Role Mappings and Expansion

Role-to-permission mappings are stored in the `role_mappings` table [src/modules/auth/role_mapping_entity.ts:16](). This table decouples IDP roles (e.g., Casdoor roles) from internal system permissions.

### Role Mapping Table Structure
| Column | Type | Description |
| :--- | :--- | :--- |
| `idp_role` | `varchar(255)` | Exact role name from the IDP (e.g., `administrators`, `guest`) [src/modules/auth/role_mapping_entity.ts:24](). |
| `permissions` | `jsonb` | Array of permission strings or wildcard patterns [src/modules/auth/role_mapping_entity.ts:30](). |
| `is_admin` | `boolean` | Flag for **Admin Bypass**. If true, all permission checks are skipped [src/modules/auth/role_mapping_entity.ts:33](). |

### Permission Expansion Flow
When a user authenticates, the `expandPermissions` function resolves the user's IDP roles into a flat set of permission patterns and an `isAdmin` flag [src/modules/auth/permissions.ts:157-160]().

**Sources:** [src/modules/auth/role_mapping_entity.ts:18-49](), [src/modules/auth/permissions.ts:157-179](), [src/modules/auth/role-mapping-repo.ts:29-44]()

## RBAC Middleware (`rbacHandler`)

The `rbacHandler` is the primary interface for securing routes. It orchestrates gateway validation, user authentication, and permission evaluation [src/modules/auth/rbac.middleware.ts:4-6]().

### Logical Semantics
*   **OR (Default):** User passes if they hold **at least one** of the declared permissions [src/modules/auth/rbac.middleware.ts:87]().
*   **AND:** User must hold **all** declared permissions. Invoked via `rbacHandler.all([...])` [src/modules/auth/rbac.middleware.ts:89]().

### The Evaluation Pipeline
1.  **Gateway Check:** If `AUTH_MODE=GATEWAY`, validates the `X-Gateway-Secret` [src/modules/auth/rbac.middleware.ts:120-131]().
2.  **Auth Check:** Skips for `PUBLIC`, otherwise validates JWT/Headers and populates `req.user` [src/modules/auth/rbac.middleware.ts:140-145]().
3.  **Admin Bypass:** If `req.user.isAdmin` is true, the request is immediately authorized [src/modules/auth/rbac.middleware.ts:172-175]().
4.  **Pattern Matching:** Evaluates `req.user.permissions` against the route requirements using `isPermissionGranted` [src/modules/auth/rbac.middleware.ts:180-183]().

### Code-to-System Mapping: Permission Enforcement
```mermaid
graph TD
    subgraph "Code Entity Space"
        RH["rbacHandler()"]
        AM["authMiddleware()"]
        EP["expandPermissions()"]
        IPG["isPermissionGranted()"]
        MW["matchesWildcard()"]
    end

    subgraph "Natural Language Space"
        REQ["Incoming Request"]
        JWT["JWT (Roles)"]
        DB["Role Mappings Table"]
        PASS["Access Granted"]
        FAIL["403 Forbidden"]
    end

    REQ --> RH
    RH --> AM
    AM --> JWT
    JWT --> EP
    EP --> DB
    EP --> RH
    RH --> IPG
    IPG --> MW
    MW -- "Match Found" --> PASS
    MW -- "No Match" --> FAIL
```
**Sources:** [src/modules/auth/rbac.middleware.ts:92-195](), [src/modules/auth/permissions.ts:132-146]()

## Wildcard Matching Logic

The system supports the `*` wildcard to grant access to entire modules or action groups. The `matchesWildcard` function converts these patterns into Regular Expressions for evaluation [src/modules/auth/permissions.ts:116-123]().

*   **Logic:** `customers.read.*` is converted to `/^customers\.read\..*$/` [src/modules/auth/permissions.ts:102]().
*   **Performance:** The system first attempts an exact match (fast path) before iterating through wildcard patterns [src/modules/auth/permissions.ts:134-136]().

### Permission Matching Flow
```mermaid
graph LR
    subgraph "Logic Flow"
        START["Required: 'customers.read.single'"]
        EXACT{"Exact Match?"}
        WILD{"Wildcard Match?"}
        REG["wildcardToRegex()"]
        OK["Authorized"]
        DENY["Forbidden"]
    end

    subgraph "Code Entities"
        P_ENUM["Permission Enum"]
        U_PERMS["req.user.permissions"]
    end

    START --> EXACT
    U_PERMS --> EXACT
    EXACT -- "Yes" --> OK
    EXACT -- "No" --> WILD
    WILD -- "Iterate patterns" --> REG
    REG -- "regex.test()" --> OK
    WILD -- "No matches" --> DENY
```
**Sources:** [src/modules/auth/permissions.ts:100-123](), [src/modules/auth/permissions.ts:132-146]()

## Operational Guide: Adding New Permissions

To introduce a new permission to the system, follow these steps:

1.  **Define the Constant:** Add the new permission string to the `Permission` object in `src/modules/auth/permissions.ts` [src/modules/auth/permissions.ts:42-87]().
2.  **Protect the Route:** Apply the `rbacHandler` to the target Express route:
    ```typescript
    router.post("/new-feature", rbacHandler([Permission.NEW_FEATURE_CREATE]), handler);
    ```
    [src/modules/auth/rbac.middleware.ts:39-43]()
3.  **Update Mappings:** Add the permission to the relevant roles in the `role_mappings` database table using SQL or the `RoleMappingRepo.upsertMapping` method [src/modules/auth/role-mapping-repo.ts:49-54]().
4.  **Reload:** If the application uses a cache for role mappings, restart the server to ensure the `RoleMappingRepo` loads the latest data [AGENTS.md:148]().

**Sources:** [src/modules/auth/permissions.ts:16-20](), [AGENTS.md:139-145](), [src/modules/auth/role-mapping-repo.ts:49-75]()

---
