Skip to main content
Your progress
0 of 7 complete0%
T4 / M4.1 / L1 OF 6 / Architect LEVEL / 9 min

The policy table

Outcome

By the end of this lesson, you will be able to identify the core entities in ZopNight’s RBAC policy table, map any product surface to the policy it enforces, and explain why the gateway is the single enforcement point.


PrerequisitesT1 (operator tier) for product surface awareness
Time9 minutes

1. Concept

Permissions in ZopNight come down to one table.

Every part of the product that can be protected is named in it as an entity: resources, schedules, budgets, dashboards and so on. Against each entity sit the actions somebody might take on it.

A role is nothing more than a list of which entity-and-action pairs its members are allowed.

The table is not closed, and it should not be read as a fixed list. Every capability that ships with its own surface also ships its own entity and policies: dashboards, budgets, deployment spaces, provisioning, and the two AI capabilities all arrived this way, each flowing through the same machinery as the original operational set. The actions are mostly the same four verbs (view, create, update, delete), but not on every entity: some entities are view-only, some lack a verb, and a few carry their own verb. That is still what makes the model answerable: “what can your Admin do?” is a list of entity-and-action pairs you can read off one table.

The core entities

Terminal window
ENTITY TYPICAL ACTIONS
─────────────────────────────────────────────────────────
resource view, create, update, delete
schedule view, create, update, delete
resource-group view, create, update, delete
override view, create, update, delete
cloud-account view, create, update, delete
notification view, create, update, delete
team view, create, update, delete
role view, create, update, delete
user view, create, update, delete
organisation view, update, delete
assignment view, create, update, delete
state-history view
report view
audit-log view
recommendation view, update
smart-tags view, update
alert view, resolve
iac-validation view, run, override
Shipped later, same machinery:
dashboard view, create, update, delete
budget view, create, update, delete
space, provisioning view, create, update, delete
service, deployment view, create, update, delete
virtual-key, ai-model view, create, update, delete
(also scopable by AI provider)

The entities cluster into three groups: operational (resource, schedule, resource-group, override, recommendation), administrative (cloud-account, team, role, user, organisation, assignment, notification), and read-only forensic (state-history, report, audit-log). The grouping matters for role design; a typical Editor role gets full operational and partial administrative; a typical Auditor role gets only the forensic group plus selective view rights.

The verb set is small, not uniform

Most entities use view, create, update, delete, but read the table rather than assuming all four: recommendation has only view and update, organisation has no create, the forensic entities are view-only, and a few entities carry their own verb (alert:resolve, iac-policy:manage, iac-validation:run, iac-validation:override). Two consequences worth internalising:

  • Applying a recommendation and dismissing one are both updates on the recommendation entity, so a role cannot be given one without the other. Separating them is a job for the remediation approval gate, not for RBAC.
  • Some policies can be narrowed to a set of resources and some cannot. Organisation-management policies (role, user, assignment, organisation) are all-or-nothing by nature; the product decides which policies are resource-scopeable rather than leaving it to the role author.

The mapping from entity to backend service is internal and managed by the gateway. From the customer’s perspective the entities mirror what they see in the product: a “resource” is a thing on the Inventory page, a “schedule” is a thing on the Schedules page. No mental translation required.

Policy entity coverage

Every protected endpoint in ZopNight maps to one or more policy entities. The gateway enforces this on every request, before the request reaches the backend service.

Terminal window
SURFACE REQUIRES
─────────────────────────────────────────────────────
Inventory page resource:view
Resource detail resource:view
Start/stop action resource:update
Schedules page schedule:view
Create schedule schedule:create
Apply recommendation recommendation:update
Dismiss recommendation recommendation:update
Connect cloud account cloud-account:create
Rotate cloud-account creds cloud-account:update
View audit log audit-log:view
Export audit log audit-log:view
Invite teammate user:create
Create custom role role:create
Assign role to user assignment:create

A user without the right policy gets a 403 from the gateway (error code rbac_denied), which the app shows as “Access restricted”. The backend never sees the request. This is important: defense in depth means even a backend bug cannot bypass authorization.

Where the gateway enforces

The gateway sits in front of every backend service. Request flow:

Terminal window
client → gateway → policy check → backend service
↓
if denied: 403 (request stops here)

The policy check is a single function call against the policy table, scoped to the authenticated user’s roles. The gateway decides whether the user may take the action at all. The backend services then work out which resources the user may see (their scope, covered in L4 and L5) and filter their own queries, and a few enforce extra rules of their own. One component answers “may this user do this?”, which keeps the security boundary unambiguous.

Unmapped routes are not denied

The policy model is not default-deny. If a route has no entry in the table, the gateway treats it as “no policy required” and lets any authenticated member of the org through. Machine tokens are the exception: they are refused on any route without a policy. The one fail-closed case is a gateway that has not loaded the table yet: it returns 503 on every route until the table arrives. This is why every mutating endpoint must be given a policy when it ships.

How ZopNight uses the table

The policy table is defined in Go code, not a YAML file: PolicyTable() lives in the config service, which streams it to the gateway, and the gateway caches it and enforces it. The frontend’s permissions.js mirrors the policy names for UI gating. There is no standalone policy_table.yaml. Every change is a PR with security review, and the model holds three invariants:

  1. Every mutating endpoint maps to a required policy.
  2. Every policy is an entity plus a verb, usually view/create/update/delete.
  3. The frontend’s usePermission() helper matches the gateway’s enforcement (catches drift early).

This three-way check (service → gateway PolicyTable() → frontend permissions.js) keeps enforcement consistent. The set of entities is not a fixed 15: alongside the core resource types it also includes budget, dashboard, autoscaler-policy, event-readiness, unit-metric, and policy.


2. Demo

A team-platform engineer wants to start an EC2 instance from the Inventory page. The flow:

Terminal window
GET /orgs/{orgID}/resources [gateway] → resource:view → 200
GET /orgs/{orgID}/resources/i-0abc [gateway] → resource:view → 200
POST /orgs/{orgID}/resources/i-0abc/start [gateway] → resource:update → 403
^^^ user role lacks resource:update

In the policy table, the resources route maps each HTTP method to a policy: GET to resource:view, and a mutation on a resource (such as start) to resource:update.

The user’s effective role is Viewer. Viewer’s policy set is every *:view policy (except billing:view) and nothing else:

Terminal window
viewer:
resource:view
schedule:view
recommendation:view
report:view
...view-only across the entities

The gateway resolves the request, finds no resource:update in the user’s policies, returns 403. The frontend’s usePermission('resource:update') would have returned false, so the “Start” button would already be disabled, but the gateway is the actual security boundary: disabled UI is a usability nicety, not a security control.


3. Hands-on (6 min)

Open Settings → Roles in your ZopNight org. Pick any custom role (or System role for reference).

Terminal window
ROLE NAME: __________________
Count policies by entity:
resource: _____ actions allowed
schedule: _____ actions allowed
recommendation: _____ actions allowed
audit-log: _____ actions allowed
user / role: _____ actions allowed (admin-y)
Estimated tier (Viewer / Editor / Admin): __________________
One action this role CAN do that surprises you: __________________
One action this role CAN'T do that surprises you: __________________

The “surprises” are usually where role design needs refinement. A FinOps Analyst that cannot dismiss recommendations is over-restricted. A junior engineer that can rotate cloud-account credentials is over-granted.


4. Knowledge check

Q1

A user clicks “Apply Recommendation” but sees Access Restricted. The most likely cause:

A. Their role lacks recommendation:update
B. A bug somewhere in the recommendation engine
C. The recommendation has already expired
D. The cloud provider rejected the action

Show answer

Correct: A. Either assign a more permissive role, or grant the specific policy in a custom role. Confirm which role the user holds on the organisation’s member list, then read that role’s policies in Settings → Roles. Access Restricted is the gateway’s 403 response, which is always policy-based. The recommendation engine, cloud provider, and expiry would surface different errors. Always start a permission diagnosis with the user’s role.

Q2

The policy table is enforced where:

A. The frontend only: disabled buttons prevent action
B. Each backend service independently
C. A WAF rule outside the application
D. The gateway, on every request

Show answer

Correct: D. The frontend’s disabled-state is a UX nicety; the gateway is the security boundary. A scripted client that bypasses the frontend still hits the gateway and is rejected. Gateway enforcement means a single, audited code path handles all authorization. Frontend gating exists to avoid showing actions the user cannot take, but it is not the security control.

Q3

A new endpoint is added without a policy entry. Default behavior of the gateway:

A. Reject all requests (default-deny)
B. Allow all authenticated requests
C. Allow only Admin
D. Allow only the endpoint’s author

Show answer

Correct: B. A route with no policy entry resolves to “no policy required”, so any authenticated org member gets through (machine tokens excepted). The gateway only fails closed before the table has loaded. That is why every mutating endpoint must ship with a policy.


5. Apply

The full policy list is browsable when you create or edit a role in Settings → Roles. The usePermission() hook in the frontend is the canonical way for UI components to check rights before rendering. The gateway is the canonical enforcement point.

When designing a new role, start from a System role (Viewer / Editor / Admin) and remove or add specific (entity, action) pairs. The diff is the role’s defining characteristic.


Glossary terms touched

Policy entity · AI Gateway · Fail closed · RBAC


Start with the bill.

Foundations takes about five hours. The first lesson is nine minutes.

Free curriculum. No paywall. 290 lessons across 7 courses, plus an optional 24-lesson beginner course. Browse every course without an account; a free account opens the lessons, saves your progress and opens the exams for three publicly verifiable credentials. Read it on the train, take the exam on a Saturday, list the credential on your résumé Monday.

5h median time to finish Foundations
free account, every course, no paywall
3 certifications, each with a public verifier
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·