Skip to content

RBAC and Portal Permissions

The Arbitex Cloud Portal uses role-based access control (RBAC) to govern what each organization member can see and do. Every member is assigned exactly one role that determines their access across DLP rules, audit logs, billing, outpost management, and organization settings.

This guide covers the five portal roles, the permission matrix, how to invite and manage members, how permissions are enforced in the backend, and common role patterns for different team structures.


The Cloud Portal defines five roles through the OrgRole enum:

Role Purpose
org_admin Full access — manage members, configure DLP, view billing, manage outposts
dlp_viewer Read-only access to DLP rules and scan results
audit_viewer Read-only access to audit logs and events
billing_viewer Read-only access to billing, invoices, and usage data
outpost_viewer Read-only access to outpost status and configuration

Every organization starts with at least one org_admin. Additional members are invited with a specific role and can be reassigned later by any org_admin.


The following matrix shows the access level each role has across portal feature areas. Cells indicate Read (view data), Write (create, update, delete), or None (no access).

Role DLP Rules Audit Log Billing Outpost Mgmt Members Settings
org_admin Read / Write Read Read Read / Write Read / Write Read / Write
dlp_viewer Read None None None None None
audit_viewer None Read None None None None
billing_viewer None None Read None None None
outpost_viewer None None None Read None None

DLP Rules — Read / Write (org_admin):

  • View all DLP rules, scan results, and detection events
  • Create, update, enable/disable, and delete DLP rules
  • Configure DLP detection thresholds and action policies

DLP Rules — Read (dlp_viewer):

  • View all DLP rules and their configurations
  • View scan results and detection events
  • Cannot create, modify, or delete rules

Audit Log — Read (org_admin, audit_viewer):

  • View audit events with all filters (action, user, severity, date range)
  • Export audit events as CSV or JSON
  • View event details including IP address and user agent

Billing — Read (org_admin, billing_viewer):

  • View current plan, seat count, and usage summary
  • View invoice history and payment method (masked)
  • View usage breakdown by model and provider
  • Export usage data

Outpost Mgmt — Read / Write (org_admin):

  • View outpost status, configuration, and health
  • Run outpost actions (test connection, clear cache)
  • Update outpost configuration

Outpost Mgmt — Read (outpost_viewer):

  • View outpost status and configuration
  • View outpost health and sync status
  • Cannot run actions or modify configuration

Members — Read / Write (org_admin):

  • View all organization members and their roles
  • Invite new members with a specific role
  • Update existing member roles
  • Remove members from the organization

Settings — Read / Write (org_admin):

  • View and modify organization settings
  • Configure SSO, API keys, and security policies
  • Manage organization-level preferences

Organization admins invite new members via the portal Team page or the Members API.

  1. Navigate to Settings → Team
  2. Click Invite Member
  3. Enter the email address
  4. Select a role from the dropdown (defaults to org_admin)
  5. Click Send Invite

The invited user receives an email with a one-time invite link. The invite token is displayed once and not stored — if the email is lost, the admin must remove the pending member and re-invite.

POST /v1/orgs/{org_id}/members/invite
Authorization: Bearer <org_jwt>
Content-Type: application/json
{
"email": "[email protected]",
"role": "dlp_viewer"
}

Request body — InviteRequest:

Field Type Required Description
email string Yes Email address of the user to invite
role OrgRole enum No Role to assign. Default: org_admin

OrgRole enum values: org_admin, dlp_viewer, audit_viewer, billing_viewer, outpost_viewer

Response (201 Created):

{
"member": {
"user_id": "m-8f3a2b1c-...",
"email": "[email protected]",
"role": "dlp_viewer",
"created_at": "2026-03-15T10:00:00Z",
"last_login_at": null
},
"invite_token": "inv_dGhpcyBpcyBhIHNhbXBsZSB0b2tlbg..."
}

The invite_token is a one-time token prefixed with inv_ followed by 32 URL-safe base64 characters. It is generated from secrets.token_bytes(24) and displayed only once in the API response. The token is not stored in the database — if it is lost, remove the member and re-invite.

For onboarding multiple team members at once:

POST /v1/orgs/{org_id}/members/bulk-invite
Authorization: Bearer <org_jwt>
Content-Type: application/json
{
"invites": [
{"email": "[email protected]", "role": "org_admin"},
{"email": "[email protected]", "role": "dlp_viewer"},
{"email": "[email protected]", "role": "audit_viewer"},
{"email": "[email protected]", "role": "billing_viewer"}
]
}

Constraints:

  • Maximum 50 invites per request
  • Duplicate emails within the org are silently skipped (counted in skipped)

Response (200 OK):

{
"invited": 3,
"skipped": 1,
"errors": 0
}

Organization admins can change any member’s role via the portal or API.

Portal: Navigate to Settings → Team, click the role dropdown next to a member, and select the new role.

API:

PATCH /v1/orgs/{org_id}/members/{member_id}/role
Authorization: Bearer <org_jwt>
Content-Type: application/json
{
"role": "audit_viewer"
}

Request body — RoleUpdateRequest:

Field Type Required Description
role OrgRole enum Yes New role to assign

Response (200 OK):

{
"user_id": "m-8f3a2b1c-...",
"email": "[email protected]",
"role": "audit_viewer",
"created_at": "2026-03-15T10:00:00Z",
"last_login_at": "2026-03-15T14:30:00Z"
}

Role changes take effect immediately. The member’s next API call or page load will use the new role. Existing SSO sessions are re-evaluated on the next token refresh.

DELETE /v1/orgs/{org_id}/members/{member_id}
Authorization: Bearer <org_jwt>

Response: 204 No Content

Constraints:

  • Cannot remove yourself
  • Cannot remove the last org_admin — the organization must always have at least one admin

The Cloud Portal backend enforces permissions using the require_role() dependency. This function is injected into FastAPI route handlers as a dependency and checks the authenticated user’s role against a list of allowed roles before the handler executes.

1. Client sends request with Bearer JWT
2. JWT is decoded → extracts org_id and user_id
3. User's role is loaded from the org_admins table
4. require_role(allowed_roles) checks:
- Is the user's role in the allowed_roles set?
- If yes → request proceeds to the handler
- If no → 403 Forbidden response

Token role claim flow:

SSO Login
→ Look up user in org_admins table
→ Read role column (org_admin / dlp_viewer / audit_viewer / ...)
→ Embed role in SSO session JWT as "role" claim
→ Frontend uses role claim for UI gating
→ Backend re-validates role on every API call via require_role()
@router.get("/v1/orgs/{org_id}/dlp/rules")
async def list_dlp_rules(
org_id: str,
org_ctx: OrgContext = Depends(get_org_context),
_role: str = Depends(require_role(["org_admin", "dlp_viewer"])),
):
# Only org_admin and dlp_viewer can reach this handler
...
@router.post("/v1/orgs/{org_id}/members/invite")
async def invite_member(
org_id: str,
body: InviteRequest,
org_ctx: OrgContext = Depends(get_org_context),
_role: str = Depends(require_role(["org_admin"])),
):
# Only org_admin can invite new members
...

In addition to role checks, every org-scoped endpoint enforces tenant isolation:

if org_ctx.org_id != org_id:
raise HTTPException(status_code=403, detail="Forbidden: org_id mismatch")

This prevents a user from one organization from accessing resources in another organization, even if they have the correct role. The org_id in the JWT must match the org_id in the URL path.

Status Condition
401 Unauthorized Missing or invalid JWT
403 Forbidden Valid JWT but role not in allowed_roles, or org_id mismatch
404 Not Found Resource does not exist within the authenticated org

A security analyst who needs to review DLP detections and audit trails but should not modify any rules or settings.

Roles: dlp_viewer + audit_viewer

Since each member can have only one role, assign audit_viewer and grant DLP access separately, or create a combined viewer role if your organization’s workflow requires it. In practice, the recommended approach is:

POST /v1/orgs/{org_id}/members/invite
{
"email": "[email protected]",
"role": "audit_viewer"
}

The analyst can view audit events and export them for analysis. For DLP visibility, the dlp_viewer role provides read access to DLP rules and scan results.

An external or internal auditor who needs access to audit logs and billing data for compliance reviews.

Recommended role: audit_viewer

The audit viewer role provides read access to all audit events, including filtering by action, user, severity, and date range. Combined with the audit export API, auditors can pull evidence for HIPAA, PCI-DSS, SOC 2, and other frameworks.

For auditors who also need billing visibility (e.g., for cost attestation in SOC 2 reports), assign billing_viewer instead and provide audit log exports via the API.

An infrastructure team member who monitors outpost health and connectivity.

Recommended role: outpost_viewer

The outpost viewer can see:

  • Outpost connection status and last heartbeat
  • Configuration (read-only)
  • Sync status and certificate expiry
  • Health check results

They cannot run actions (test connection, clear cache) or modify outpost configuration — those operations require org_admin.

Role: org_admin

Organization admins have unrestricted access to all portal features. Use this role for:

  • Team leads who manage member access
  • Security administrators who configure DLP rules
  • Platform administrators who manage outpost infrastructure
  • Billing administrators who need full visibility

The org_admin role is the most privileged role in the Cloud Portal. Only organization admins can:

  • Manage members: invite, update roles, remove members
  • Manage roles: assign or change any member’s role
  • Configure DLP: create, update, enable/disable, and delete DLP rules
  • Manage outposts: run actions, update configuration
  • Modify settings: update organization preferences, SSO configuration, API keys
  • Access billing: view and manage billing, invoices, and payment methods
  1. Only org_admin can assign roles. No other role can invite members or change role assignments.
  2. Cannot remove the last admin. The API prevents removing or demoting the last org_admin to ensure the organization always has at least one admin.
  3. Cannot remove yourself. Members cannot remove their own membership — another admin must do it.
  4. Role changes are immediate. When an admin changes a member’s role, the new role takes effect on the member’s next API call or page load.

When a user signs up for Arbitex through the self-serve flow, they are automatically assigned the org_admin role as the founding member of their organization. This ensures they have full access to configure DLP, invite team members, and set up outposts.


When the RBAC system is enabled for an existing organization, all current members receive the org_admin role by default. This ensures no disruption to existing workflows — all members retain their current access levels.

After migration, organization admins should review the member list and assign appropriate roles:

  1. Navigate to Settings → Team
  2. Review each member’s current role (all will show org_admin)
  3. Click the role dropdown and select the appropriate role for each member
  4. Members who only need read access to specific areas should be downgraded to the appropriate viewer role
Step Action
1 Identify who needs full admin access (typically 1–3 people)
2 Keep those members as org_admin
3 Assign dlp_viewer to security analysts
4 Assign audit_viewer to compliance team members
5 Assign billing_viewer to finance team members
6 Assign outpost_viewer to infrastructure/ops team members
7 Verify each member can access what they need (and nothing more)