Skip to main content
Your progress
0 of 6 lessons complete0%
T3 / M3.1 / L1 OF 6 / Architect TIER / 9 min

The policy table

Outcome

By the end of this lesson, you will be able to identify the 15 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.


TierArchitect
JTBD”Design a role model that lets engineers do their jobs without granting more than they need.”
PersonasPlatform Engineer · Security/Compliance · FinOps Lead
PrerequisitesT1 (operator tier) for product surface awareness
Time9 minutes
Bloom verbIdentify (Remember), Map (Apply), Explain (Understand)

1. Concept

ZopNight’s authorization model is built around a policy table that maps every protected endpoint to the policy a caller must hold. Every protected endpoint in the product maps to one or more entities. Every role is, fundamentally, a set of (entity, action) pairs that the role’s members are allowed to invoke.

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. What is fixed is the verb set, and that is what makes the model auditable: whatever the entity, the actions are the same four, so “what can your Admin role do?” is answerable by listing entities rather than by learning per-entity vocabulary.

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, create, update, delete
assignment view, create, update, delete
state-history view
report view
audit-log view
recommendation view, create, update, delete
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, notification), administrative (cloud-account, team, role, user, organisation, assignment), 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 what is fixed

Actions are uniform: view, create, update, delete on every entity. There is no per-entity vocabulary, which is the part that matters when you design a custom role. 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 Resources 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
─────────────────────────────────────────────────────
Resources 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 + report:view
Invite teammate user:create
Create custom role role:create
Assign role to user role:update + assignment:create

A user without the right policy gets a 403 Access Restricted from the gateway. 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 backend services do not re-check. This separation keeps the security boundary unambiguous: one component (the gateway) is responsible for authorization, and that component is hardened, audited, and changes infrequently.

Default-deny

The policy model is default-deny. If a new endpoint is added without a policy mapping, the gateway rejects all requests to it. This catches accidental over-exposure during development. The frontend will surface a policy_missing error during build-time integration tests if a new API call is made without a policy entry in the table.

How ZopNight uses the table

The policy table is defined in code, not a YAML file: the gateway’s Go PolicyTable() is the authoritative endpoint-to-policy map, and the frontend’s permissions.js mirrors it 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 uses the uniform view/create/update/delete verbs on an entity.
  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 Resources page. The flow:

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

In the policy table, that endpoint is declared as:

Terminal window
- path: /v1/resources/{id}/start
method: POST
policy: { entity: resource, action: manage }

The user’s effective role is Viewer. Viewer’s policy set is:

Terminal window
viewer:
- { entity: resource, action: view }
- { entity: schedule, action: view }
- { entity: recommendation, action: view }
- { entity: report, action: view }
# ...all view-only across the 15 entities

The gateway resolves the request, finds no matching (resource, manage) permission, returns 403. The frontend’s usePermission('resource', 'manage') 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. A bug in the recommendation engine
B. Their role lacks recommendation:update. Either assign a more permissive role, or grant the specific policy in a custom role. Confirm in Settings → Users → role inspector.
C. Cloud provider rejected the action
D. The recommendation expired

Show answer

Correct: B. 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 at the role inspector.

Q2

The policy table is enforced where:

A. The frontend only: disabled buttons prevent action
B. The gateway, on every request. 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.
C. Each backend service independently
D. A WAF rule outside the application

Show answer

Correct: B. 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. Allow all authenticated requests
B. Reject all requests (default-deny). The build pipeline also fails the integration test, catching the omission before merge.
C. Allow only Admin
D. Allow only the endpoint’s author

Show answer

Correct: B. Default-deny is the safe default and prevents accidental over-exposure. The integration test failing in CI is the second line of defense.


5. Apply

The full policy table is browsable in Settings → Roles → Policy reference. 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 · Gateway · Default-deny · RBAC


Start with the bill.

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

Open curriculum. No login. No paywall. 237 lessons across 7 courses, 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
0 logins, paywalls, or marketing forms
open curriculum, public credential verifier
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 30% average cloud cost cut· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 30% average cloud cost cut· 4 platforms · 1 console·