Skip to content

Policy-Based Access Control (PBAC) System Architecture

Requirements

  • Provide centralized authentication via Keycloak with JWT token validation for all external API requests
  • Provide authorization with hierarchical role evaluation across Companies, Projects, and Resources
  • Support multi-tenancy with tenant isolation at all levels
  • Provide fine-grained resource sharing between users within projects (per-user or public access)
  • Implement quota and limit management at company and project levels (credits, storage, API calls)
  • Secure inter-service communication with authenticated gRPC calls
  • Track all policy changes via event sourcing for audit trail and state reconstruction
  • Provide transparent access enforcement at the API Gateway level before requests reach downstream services
  • Support dual identity storage: Keycloak for authentication, local database for policy data
  • Provide admin panel for policy management

Detailed design

Access responsibility allocation zones

The system is organized into five access responsibility zones, each handling a specific layer of the security model:

  • Authentication zone is a Keycloak-based identity provider that validates user credentials and issues JWT tokens. It supports multiple client audiences and realm-based multi-tenancy. All external requests must carry a valid JWT token. The system must extract User ID, Tenant ID, and user attributes from token claims.

  • API Gateway authorization zone is the entry point for all external API calls. Before forwarding any request to downstream services, the Gateway must verify that the authenticated user has the required access level for the target resource. The Gateway delegates policy evaluation to the PolicyVault service via an internal call.

  • Policy evaluation zone (PolicyVault) is a dedicated microservice responsible for storing and evaluating all access policies. It maintains the domain model of Users, Companies, Projects, and Resources, and provides the rules engine for hierarchical access checks. All policy state changes are recorded as events for audit purposes.

  • Inter-service security zone ensures that internal communication between microservices within the cluster is authenticated. Each service obtains tokens via the Client Credentials OAuth2 flow and attaches them to every inter-service call. Services that do not have declared dependencies must not be able to communicate with each other.

  • Resource access filtering zone represents fine-grained access control within individual services. This zone determines which specific resources (files, folders, templates) a user can access within a project, based on sharing rules. A user may have access to a project but only see a subset of its resources.

2. Domain entities and relationships

The PBAC system is built around four core entities: User, Company, Project, and Resource. These entities form a hierarchy that defines the access model.

User: Represents an authenticated person in the system. A User belongs to zero or more Companies and has access to zero or more Projects. Each User has individual quota limits and group memberships. User identity is managed by Keycloak, while policy-related data (memberships, limits, version) is stored locally.

Attribute Description
ID Unique identifier (UUID)
First Name / Last Name User's full name
Username Display name
Email Validated email address
Enabled Active/inactive status
Limits Per-user quota limits (credits, storage, calls, custom)
Projects Set of projects the user belongs to
Companies Set of companies the user belongs to
Groups Role/group assignments

Company: Represents an organization that owns projects and manages users. Each Company has a single Owner with full privileges. Other users are assigned one of four scopes (Member, Viewer, Editor, Admin). Companies define global quota limits and per-user quota limits.

Attribute Description
ID Unique identifier (string)
Name Company name
Owner User ID of the company owner
Users Map of user memberships with their scope
Global Limits Company-wide quota limits
User Limits Per-user quota overrides within the company
Projects Set of projects owned by the company

Project: Represents a workspace that contains resources and has user memberships with roles. Projects come in two types:

  • Personal Project — owned by a single user, not associated with any company. Has user memberships with roles and shared resources.
  • Company Project — owned by a company, associated with a company ID. Has user memberships with roles, shared resources, quota limits (inherited from company), and per-user usage tracking.
Attribute Description Personal Company
ID Unique identifier (string) Yes Yes
Name Project name Yes Yes
Owner User ID of the project owner Yes Yes
Company ID Associated company No Yes
Users Map of user memberships with roles Yes Yes
Shared Resources Map of resources with sharing scope Yes Yes
Limits Project-level quota limits No Yes
Quotas Per-user quota usage tracking No Yes

Resource: Represents an object in the system that can be shared and access-controlled. Resources are identified by type and path.

Resource Type Description Example
Single File An individual file asset models/v2/weights.bin
Folder A directory containing multiple assets datasets/training/
Template A template asset templates/default

Resources support parent-child containment: a file inside a folder inherits the folder's sharing scope.

3. Roles and scopes

The system defines two levels of role assignment: company-level scopes and project-level roles. In addition, resource-level sharing scopes control fine-grained access to individual resources.

Company-level scopes

Each user in a company is assigned one of the following scopes. The Owner is a special implicit role - only one user per company can be the Owner.

Scope Description
Owner Implicit. Full control over the company, its users, projects, and settings. Can perform any operation. Only one per company.
Admin Full management capabilities. Can manage users, projects, quotas, and settings within the company.
Editor Can read and write resources, manage project content. Cannot manage company users or settings.
Viewer Read-only access to company resources and projects.
Member Base membership. Acknowledges the user belongs to the company but grants no access to resources.

Project-level roles

Each user in a project is assigned one of the following roles. The Owner is a special implicit role.

Role Description
Owner Implicit. Full control over the project, including all resources, user management, and settings. Only one per project.
Admin Full management capabilities within the project. Can manage users, roles, shared resources, and quotas.
Contributor Can read and write resources within the project. Cannot manage project settings or users.
Viewer Read-only access to project resources.
Custom An extensible custom role with a user-defined label. Grants no default access; behavior is defined by the system.

Resource sharing scopes

Each shared resource in a project has one of the following scopes:

Scope Description
Anyone The resource is visible to all project members.
Personal The resource is visible only to the project owner and a specified set of user IDs.

4. Permissions matrix by role

4.1 Company-level permissions

The following table defines which actions each company scope can perform. Access is evaluated by comparing the user's scope level against the required action level.

Scope Read Write Admin Custom
Owner Yes Yes Yes Yes
Admin Yes Yes Yes No
Editor Yes Yes No No
Viewer Yes No No No
Member No No No No

4.2 Project-level permissions

Role Read Write Admin Custom
Owner Yes Yes Yes Yes
Admin Yes Yes Yes No
Contributor Yes Yes No No
Viewer Yes No No No
Custom No No No No

4.3 Resource visibility

User type Shared as Anyone Shared as Personal
Project Owner Visible Visible
Listed in Personal scope Visible Visible
Other project member Visible Not visible
Non-member Not visible Not visible

5. Access evaluation rules

Access is evaluated hierarchically. A user must pass both company-level and project-level checks to gain access.

5.1 Company-level policy

  1. If the user is the company Owner -> Allow
  2. If the user is not a member of the company -> Deny (UserNotMemberOfCompany)
  3. If the user's scope level >= the required action level -> Allow
  4. Otherwise -> Deny (InsufficientCompanyScope)

5.2 Project-level policy

  1. If the user is the project Owner -> Allow
  2. If the user is not a member of the project -> Deny (UserNotMemberOfProject)
  3. If the user's role level >= the required action level -> Allow
  4. Otherwise -> Deny (AccessDenied)

5.3 Combined access check

For a Company Project, both checks are executed sequentially:

  1. Evaluate company-level policy
  2. If company-level passed, evaluate project-level policy
  3. If both passed -> Granted
  4. If either fails -> Denied with the corresponding error

For a Personal Project (no company), only the project-level policy is evaluated.

5.4 Resource access filtering

After the user passes the project-level access check:

  1. If the user is the project Owner -> all shared resources are accessible
  2. Otherwise, a resource is accessible if:
  3. Its scope is Anyone, OR
  4. Its scope is Personal and the user's ID is in the allowed set
  5. Parent folder containment: if a resource is inside a shared folder, the folder's scope applies to the resource

5.5 Denial reasons

Error Meaning
UserNotMemberOfCompany The user is not a member of the company
UserNotMemberOfProject The user is not a member of the project
AccessDenied The user's project role is insufficient for the requested action
InsufficientCompanyScope The user's company scope is insufficient for the requested action
AccessLimitExceeded The requested operation exceeds quota limits

6. Quota and limit management

The system supports quota enforcement at two levels: company and project. Quotas limit usage of shared resources such as compute credits, storage, and API calls.

6.1 Quota types

Quota Description
Credit Compute credits (e.g., GPU time, generation runs)
Size Storage size (e.g., dataset size, model weights)
Calls API call count (e.g., inference requests)
Custom User-defined quota type with a custom label

6.2 Limit hierarchy

  • Company global limits — maximum total usage across all projects in the company
  • Company per-user limits — maximum usage for a specific user across all company projects
  • Project limits — maximum usage within a single project (defaults to company global limit if not set)
  • User limits — per-user limits at the individual level

6.3 Quota check algorithm

When a user requests a resource that consumes quota:

  1. Calculate total company-wide usage for the quota type across all company projects
  2. Calculate project-level usage for the quota type
  3. Global remaining = company limit - total company usage
  4. Project remaining = project limit - project usage
  5. If global remaining \< requested amount -> Deny (AccessLimitExceeded)
  6. If project remaining \< requested amount -> Deny (AccessLimitExceeded)
  7. Otherwise -> Allow, return remaining capacity

7. Policy management operations

The system must support the following operations for managing users, companies, projects, and their relationships. All operations must be tracked with versioning and timestamps for audit purposes.

7.1 User management

Operation Description
Create user Register a new user from email (synced with Keycloak)
Block / Unblock user Disable user access with a reason
Delete user Soft-delete user (cascade removal from companies and projects)
Update profile Modify username, first name, last name, email, enabled status
Manage memberships Add/remove user from companies and projects
Set user limits Configure individual quota limits
Find by email Search users by email address
Sync user Reconcile local data with Keycloak identity

7.2 Company management

Operation Description
Update company Modify company name, transfer ownership
Manage users Add/remove users, change their scope
Manage projects Add/remove projects from the company
Set global limits Configure company-wide quota limits
Set user limits Configure per-user quota limits within the company
View quota summary Retrieve quota usage breakdown by project and user
Delete company Cascade removal of all user memberships

7.3 Project management

Operation Description
Update project Modify project name, transfer ownership
Manage users Add/remove users, change their role
Share resources Add/remove resources to the shared pool, set sharing scope (Personal/Anyone)
Update sharing scope Change who can see a specific resource
Set project limits Configure project-level quota limits
Track usage Update per-user quota consumption
Reset quotas Reset all usage counters in a project
Convert project type Convert between Personal and Company project
List accessible resources Get the set of resources accessible to a specific user
Get resource scope Get the sharing scope for a specific resource
Delete project Cascade removal of all user memberships

7.4 Cross-entity operations

These operations atomically update both sides of a relationship:

Operation Description
Add user to company Updates both User (adds company) and Company (adds user with scope)
Remove user from company Updates both User and Company
Add user to project Updates both User (adds project) and Project (adds user with role)
Remove user from project Updates both User and Project
Add project to company Converts project to company type, updates Company
Remove project from company Converts project to personal type, updates Company

7.5 Access check operations

Operation Description
Check resource access Verify if a user can perform an action on a specific resource
Check project access Verify if a user can perform an action on a project
Get user's project role Return the user's role in a specific project
Get user's company scope Return the user's scope in a specific company
List available projects Return all projects accessible to a user with their roles
List available companies Return all companies accessible to a user with their scopes

8. Event sourcing and audit trail

All policy state changes must be recorded as events. Each event contains the timestamp and a version number for optimistic concurrency control.

8.1 Principles

  • Every state mutation is represented as a command that produces one or more events
  • Commands are idempotent: repeating the same change returns an error (e.g., "name already set")
  • Each entity maintains a version counter that increments with every event
  • Events contain the timestamp of the change and the resulting version
  • The current state can be reconstructed by replaying events

8.2 Event categories

Category Examples
User events User created, blocked, deleted, profile updated, company/project membership changed, limit updated
Company events Name/owner updated, user added/removed, user scope changed, project added/removed, limits updated
Project events Name/owner updated, user added/removed, role changed, resource shared/unshared, scope updated, limits updated, quotas reset, project type converted
Integration events Composite events wrapping multiple entity-level events for cross-entity operations

9. Authentication and identity integration

9.1 Keycloak integration

The system uses Keycloak as the external identity provider. Authentication and identity management work as follows:

  • User authentication: All external API requests must include a valid JWT token issued by Keycloak. The system validates the token signature, issuer, and audience.
  • Multi-audience support: The system must support multiple Keycloak clients (audiences). The correct validation scheme is selected based on the JWT aud or azp claim.
  • Multi-tenancy: Tenants are identified by Keycloak realms. The tenant ID is extracted from a dedicated JWT claim (tnt) or parsed from the issuer URL.
  • Dual storage: User identity (name, email, credentials) is managed by Keycloak. Policy data (memberships, limits, version) is stored locally. Changes to user profile must be synchronized to both stores.

9.2 Service-to-service authentication

  • Internal services authenticate using the OAuth2 Client Credentials flow
  • Each service has a dedicated Keycloak client ID and secret
  • Tokens are cached locally with automatic refresh before expiry
  • Every inter-service call must include a valid Bearer token

9.3 Identity claims

The system must extract the following information from JWT tokens:

Claim Required Purpose
User ID Yes Identifies the authenticated user (from sub, oid, uid, or sid claims)
Tenant ID Yes Identifies the tenant (from tnt claim or issuer URL)
Email No Used for search and audit context
Display name No Used for UI display

10. Permissions summary by service

The Xavier.Backend platform comprises multiple microservices. The following table summarizes which roles can perform which types of actions on each service.

Service Read Write Manage Audit
Gateway API Viewers+ Contributors+ Admins Admins
Model Registry Viewers+ Contributors+ Admins Admins
Assets Viewers+ Contributors+ Admins Admins
Generation Viewers+ Contributors+ Admins Admins
Workflows Viewers+ Contributors+ Admins Admins
Notifications All members System only Admins Admins
Telemetry All members System only Admins Admins
PolicyVault Internal only Internal only Admins Admins
Admin Panel Admins Admins Admins Admins

"Viewers+" means Viewer role and above (Viewer, Contributor, Admin, Owner). "Contributors+" means Contributor role and above (Contributor, Admin, Owner).

11. Permissions matrix by feature

Feature / Role Member Viewer Contributor / Editor Admin Owner
Company
View company info No Read Read Read Read
Manage company users No No No Manage Manage
Manage company projects No No No Manage Manage
Manage company quotas No No No Manage Manage
Transfer company ownership No No No No Manage
Project
View project resources No Read Read Read Read
Create/edit resources No No Manage Manage Manage
Manage project users No No No Manage Manage
Share/unshare resources No No No Manage Manage
Manage project quotas No No No Manage Manage
Convert project type No No No No Manage
Transfer project ownership No No No No Manage
Resources
View shared resource (Anyone scope) No Read Read Read Read
View shared resource (Personal scope) Only if listed Only if listed Only if listed Only if listed Read
Modify shared resource No No Manage Manage Manage

12. Use cases

UC-1: User requests a model from the Model Registry

  1. User sends a request to the Gateway API with a JWT token
  2. Gateway validates the JWT token (Authentication zone)
  3. Gateway extracts User ID and Tenant ID from claims
  4. Gateway calls PolicyVault to check if the user has Read access to the model resource in the target project
  5. PolicyVault evaluates: company policy -> project policy -> resource scope
  6. If access is granted, Gateway forwards the request to the Model Registry service
  7. Model Registry returns the model to the user via the Gateway

UC-2: Admin adds a user to a company

  1. Admin sends a request to add a user to a company with a specific scope
  2. Gateway verifies the admin has Admin scope in the target company
  3. PolicyVault executes a cross-entity operation: updates both User (adds company) and Company (adds user with scope)
  4. Events are recorded: UserCompanyAdded and CompanyUserAdded
  5. Both entities' version numbers are incremented

UC-3: User shares a resource with specific colleagues

  1. User (project Admin or Owner) shares a resource in a project with a Personal scope, listing specific user IDs
  2. PolicyVault updates the project's shared resources map
  3. Only the listed users and the project owner can now access this resource
  4. Other project members cannot see this resource

UC-4: System checks quota before resource allocation

  1. A service needs to allocate compute credits for a user
  2. The service calls PolicyVault to check quota limits
  3. PolicyVault calculates: company-wide remaining credits and project-level remaining credits
  4. If both limits have sufficient capacity, the operation proceeds
  5. If either limit is exceeded, the operation is denied with the remaining capacity

UC-5: Converting a personal project to a company project

  1. An admin requests to convert a personal project into a company project
  2. PolicyVault verifies the user has admin access to the target company
  3. The project is updated: CompanyId is set, quota limits are initialized from company defaults
  4. Events are recorded for the conversion
  5. The company's project set is updated to include the new project