How Versus authorizes every action — a five-level default role hierarchy, resource-scoped custom roles, fine-grained permission strings, external directory group mappings, and server-side enforcement that the API applies regardless of what the UI shows.
Every action in Versus — viewing a device, running a workflow, reading a secret, approving a certificate — is authorized against the signed-in user's permissions. Authorization is decided in the back end, on every request, so it holds the same whether the caller is the web UI, a script hitting the REST API, or an integration. The UI is role-aware (it only offers what you can do), but it is never the thing that enforces the rule.
Versus models access with two complementary constructs:
A user's effective permissions are the union of their default role and every scoped role assigned to them.
Five default roles ship with the platform. Each has an integer level (1–5); higher levels strictly include the operational reach of lower ones. Default roles are system-defined and read-only — you assign them, but you don't edit their permission sets.
| Level | Role | What it can do |
|---|---|---|
| 5 | Super Admin | Full control: auth-backend config, role and group mapping, user management, all devices and resources. |
| 4 | Network Admin | Manage devices, partitions, virtual servers, and certificates. Cannot configure auth backends or manage users/roles. |
| 3 | Operator | Day-to-day operational changes to applications and virtual servers; run workflows. No structural device changes. |
| 2 | Viewer | Read-only access to everything in scope. Cannot make any change. |
| 1 | Guest | Default fallback — dashboard and applications view only. |
The level is what the coarse-grained authorization checks compare against. The API exposes four privilege gates that map directly to these levels — IsSuperAdmin (≥5), IsNetworkAdmin (≥4), IsOperator (≥3), and IsViewer (≥2) — plus a tenant-scoped IsTenantAdmin for multi-tenant deployments.
Beneath the roles is a flat catalog of permission strings, each namespaced as <area>.<action>. A few representative entries:
devices.view devices.create devices.edit devices.delete
workflows.view workflows.run workflows.manage
certs.view certs.issue certs.approve certs.manage
vault.view vault.read vault.manage
firewall.view firewall.edit firewall.manage
users.view users.create users.manage
roles.view roles.manage auth.configure
audit.view system.view system.manage
The catalog also covers IPAM, security scanning, migrations, integrations, F5OS operations, consolidation planning, cloud-WAF exception handling, collector administration, self-healing, and the ITSM/Service Desk. Fetch the authoritative list at runtime:
GET /api/default-roles/permissions_list/
→ { "permissions": [ "devices.view", "devices.create", ... ] }
Each default level maps to a fixed permission bundle:
auth.configure, users.manage, roles.manage, and the platform-provisioner permission. A Network Admin runs the estate but can't reshape identity or access policy..view permissions plus applications.create/edit, workflows.run, certs.issue, and similar day-to-day actions..view/.use permission and nothing else.Fine-grained checks in the API test for a specific string rather than a level. A view protected by require_perm('devices.edit') grants access to anyone whose effective permissions include devices.edit, no matter how they obtained it — from their default level or from a scoped role.
Default roles are estate-wide. When you need "operator, but only on these two virtual servers," you build a scoped role:
resource_type of vs (virtual server), partition, or device, targeting either a specific resource ID or a wildcard (all resources of that type).POST /api/scoped-roles/
{
"name": "DMZ VIP Operator",
"description": "Operate the two internet-facing VIPs only",
"permissions": ["applications.view", "applications.edit", "workflows.run"],
"scopes": [
{ "resource_type": "vs", "resource_id": 1043, "resource_name": "vip-www" },
{ "resource_type": "vs", "resource_id": 1044, "resource_name": "vip-api" }
]
}
Scoped roles are managed by Network Admin and above. Because a user's effective permissions are the union of their default role and all their scoped roles, scoped roles add capability — you use them to grant targeted reach on top of a modest baseline (for example, a Viewer who also operates one application team's VIPs).
Two properties on a user's profile drive every decision:
effective_level — the level of the assigned default role, or a legacy fallback if none is set.effective_permissions — the sorted union of the default role's permissions and the permissions of every scoped role assigned to the user.A permission check is then simply perm in effective_permissions. This resolution lives in one place in the back end, so the web UI, the API, and automation all get the identical answer.
The signed-in user can read their own resolved access at:
GET /api/users/me/
→ {
"role": "operator",
"role_level": 3,
"role_name": "Operator",
"permissions": [ "applications.edit", "workflows.run", ... ],
"scoped_roles": [ { "id": 7, "name": "DMZ VIP Operator", ... } ],
"is_tenant_admin": false
}
The front end uses exactly this payload to decide which navigation items and buttons to render — which is why the UI only ever offers actions the API would also authorize.
Versus authenticates against pluggable auth backends: Local, AD / LDAP, SAML / SSO, and TACACS+. Each is configured once (host, base DN, attribute maps, and so on), with sensitive values — bind passwords, shared secrets, client secrets — encrypted at rest, never stored in plaintext config. Backends are tried in priority order at sign-in. Configuring them requires Super Admin (auth.configure).
For external users, roles come from group mappings. A GroupMapping ties an external directory group (scoped to its provider) to either a default role or a scoped role:
POST /api/group-mappings/
{
"external_group": "net-eng-admins",
"provider": "ldap",
"default_role": 4 // → Network Admin
}
At each successful login, the role engine runs:
This means access is re-evaluated every time an external user signs in, so revoking someone in your directory takes effect on their next login without any change inside Versus.
Day-to-day access management happens under Access Control in the console, backed by these endpoints:
| Task | Endpoint | Who |
|---|---|---|
| List the default roles | GET /api/default-roles/ | Any authenticated user |
| Create / edit scoped roles | …/api/scoped-roles/ | Network Admin (≥4) |
| Assign a scoped role to a user | POST /api/role-assignments/ | Network Admin (≥4) |
| Map a directory group to a role | …/api/group-mappings/ | Network Admin (≥4) |
| Configure an auth backend | …/api/auth-backends/ | Super Admin (5) |
Manual assignments are recorded with their assignment_source (manual vs. group_match) and who granted them, so the origin of every grant is auditable. To grant a scoped role to a specific user:
POST /api/role-assignments/
{ "user": 88, "scoped_role": 7 }
Users without sufficient access can request an elevation themselves — POST /api/users/request-role/ with operator or admin — which files a pending request and notifies administrators by email. An administrator approves or rejects it; the user's own token can never self-approve.
RBAC is one of two independent gates a request passes:
Distinct from both is license entitlement. On licensed deployments a separate middleware gates whole modules by what the deployment (on-prem) or tenant (SaaS) has paid for; a request for an unlicensed module returns HTTP 402, not 403. Entitlement answers "is this feature included in your plan," RBAC answers "are you allowed to do it" — the two are orthogonal, and both must pass.
In multi-tenant deployments, a Tenant Admin console lets a tenant administer users within its own organization only. The assignable roles there are exactly Viewer, Operator, and Tenant Admin — Super Admin is never grantable through it — and every operation is bound to the tenant ID carried in the caller's token, so no admin can reach into another tenant. Tenant isolation is additionally enforced with row-level security in the database as a defense-in-depth backstop beneath these application checks.
vault.read / vault.manage permissions and how secrets are guarded.collector.* permissions.