Hacker DirectoryHacker Directory

WEB / WEB-14.05

Resource authorization and tenant boundaries

Signing in is not enough to read a report. Follow identity, tenant, action and ownership decisions within one request.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Owner read · allow

  1. A request is not a decision

    The UI requests an action. A requested resource or client-displayed tenant label does not establish server authority.

    Browser · UI
    report.read report-atlas-01
    Server · policy evaluator
    Policy not evaluated
    Resource · verified context
    Context not loaded
  2. The request reached the server

    The server establishes the subject from a verified session/context; a name supplied by the browser is not accepted as verified identity.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Duru / member
    Resource · verified context
    Context not loaded
  3. Object and tenant context were established

    Resource tenant and ownership metadata come from a trusted server data source. Loading and authorization may be one atomic operation; this drawing separates two logical checks.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Subject / action / context ready
    Resource · verified context
    report-atlas-01 · atlas
  4. Policy conditions were evaluated

    Active identity, tenant match, action permission and owner/reviewer relationship are evaluated together. Skipping a condition breaks a different trust boundary.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    ALLOW · OWNER_READ
    Resource · verified context
    Read scope permitted
  5. The decision returned to the browser

    The response carries a resource representation only on allow. This teaching fixture explains denial reasons; production response detail should account for information disclosure.

    Browser · UI
    Resource can be displayed
    Server · policy evaluator
    ALLOW · OWNER_READ
    Resource · verified context
    Read performed
  6. Evidence preserves decision scope

    Audit records actor, action, object, tenant, decision and reason without session secrets. An allow is valid only for this action/resource/context.

    Browser · UI
    UI: allowed read
    Server · policy evaluator
    Audit: OWNER_READ
    Resource · verified context
    report-atlas-01

Other owner · deny

  1. A request is not a decision

    The UI requests an action. A requested resource or client-displayed tenant label does not establish server authority.

    Browser · UI
    report.read report-atlas-02
    Server · policy evaluator
    Policy not evaluated
    Resource · verified context
    Context not loaded
  2. The request reached the server

    The server establishes the subject from a verified session/context; a name supplied by the browser is not accepted as verified identity.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Duru / member
    Resource · verified context
    Context not loaded
  3. Object and tenant context were established

    Resource tenant and ownership metadata come from a trusted server data source. Loading and authorization may be one atomic operation; this drawing separates two logical checks.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Subject / action / context ready
    Resource · verified context
    report-atlas-02 · atlas
  4. Policy conditions were evaluated

    Active identity, tenant match, action permission and owner/reviewer relationship are evaluated together. Skipping a condition breaks a different trust boundary.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    DENY · OWNER_REQUIRED
    Resource · verified context
    Resource body withheld
  5. The decision returned to the browser

    The response carries a resource representation only on allow. This teaching fixture explains denial reasons; production response detail should account for information disclosure.

    Browser · UI
    Action denied
    Server · policy evaluator
    DENY · OWNER_REQUIRED
    Resource · verified context
    Body withheld
  6. Evidence preserves decision scope

    Audit records actor, action, object, tenant, decision and reason without session secrets. An allow is valid only for this action/resource/context.

    Browser · UI
    UI: denied action
    Server · policy evaluator
    Audit: OWNER_REQUIRED
    Resource · verified context
    report-atlas-02

Tenant mismatch · deny

  1. A request is not a decision

    The UI requests an action. A requested resource or client-displayed tenant label does not establish server authority.

    Browser · UI
    report.read report-orion-01
    Server · policy evaluator
    Policy not evaluated
    Resource · verified context
    Context not loaded
  2. The request reached the server

    The server establishes the subject from a verified session/context; a name supplied by the browser is not accepted as verified identity.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Duru / member
    Resource · verified context
    Context not loaded
  3. Object and tenant context were established

    Resource tenant and ownership metadata come from a trusted server data source. Loading and authorization may be one atomic operation; this drawing separates two logical checks.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Subject / action / context ready
    Resource · verified context
    report-orion-01 · orion
  4. Policy conditions were evaluated

    Active identity, tenant match, action permission and owner/reviewer relationship are evaluated together. Skipping a condition breaks a different trust boundary.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    DENY · TENANT_MISMATCH
    Resource · verified context
    Resource body withheld
  5. The decision returned to the browser

    The response carries a resource representation only on allow. This teaching fixture explains denial reasons; production response detail should account for information disclosure.

    Browser · UI
    Action denied
    Server · policy evaluator
    DENY · TENANT_MISMATCH
    Resource · verified context
    Body withheld
  6. Evidence preserves decision scope

    Audit records actor, action, object, tenant, decision and reason without session secrets. An allow is valid only for this action/resource/context.

    Browser · UI
    UI: denied action
    Server · policy evaluator
    Audit: TENANT_MISMATCH
    Resource · verified context
    report-orion-01

Reviewer read · allow

  1. A request is not a decision

    The UI requests an action. A requested resource or client-displayed tenant label does not establish server authority.

    Browser · UI
    report.read report-atlas-01
    Server · policy evaluator
    Policy not evaluated
    Resource · verified context
    Context not loaded
  2. The request reached the server

    The server establishes the subject from a verified session/context; a name supplied by the browser is not accepted as verified identity.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Ekin / reviewer
    Resource · verified context
    Context not loaded
  3. Object and tenant context were established

    Resource tenant and ownership metadata come from a trusted server data source. Loading and authorization may be one atomic operation; this drawing separates two logical checks.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Subject / action / context ready
    Resource · verified context
    report-atlas-01 · atlas
  4. Policy conditions were evaluated

    Active identity, tenant match, action permission and owner/reviewer relationship are evaluated together. Skipping a condition breaks a different trust boundary.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    ALLOW · REVIEWER_READ
    Resource · verified context
    Read scope permitted
  5. The decision returned to the browser

    The response carries a resource representation only on allow. This teaching fixture explains denial reasons; production response detail should account for information disclosure.

    Browser · UI
    Resource can be displayed
    Server · policy evaluator
    ALLOW · REVIEWER_READ
    Resource · verified context
    Read performed
  6. Evidence preserves decision scope

    Audit records actor, action, object, tenant, decision and reason without session secrets. An allow is valid only for this action/resource/context.

    Browser · UI
    UI: allowed read
    Server · policy evaluator
    Audit: REVIEWER_READ
    Resource · verified context
    report-atlas-01

Reviewer other tenant · deny

  1. A request is not a decision

    The UI requests an action. A requested resource or client-displayed tenant label does not establish server authority.

    Browser · UI
    report.read report-orion-01
    Server · policy evaluator
    Policy not evaluated
    Resource · verified context
    Context not loaded
  2. The request reached the server

    The server establishes the subject from a verified session/context; a name supplied by the browser is not accepted as verified identity.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Ekin / reviewer
    Resource · verified context
    Context not loaded
  3. Object and tenant context were established

    Resource tenant and ownership metadata come from a trusted server data source. Loading and authorization may be one atomic operation; this drawing separates two logical checks.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Subject / action / context ready
    Resource · verified context
    report-orion-01 · orion
  4. Policy conditions were evaluated

    Active identity, tenant match, action permission and owner/reviewer relationship are evaluated together. Skipping a condition breaks a different trust boundary.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    DENY · TENANT_MISMATCH
    Resource · verified context
    Resource body withheld
  5. The decision returned to the browser

    The response carries a resource representation only on allow. This teaching fixture explains denial reasons; production response detail should account for information disclosure.

    Browser · UI
    Action denied
    Server · policy evaluator
    DENY · TENANT_MISMATCH
    Resource · verified context
    Body withheld
  6. Evidence preserves decision scope

    Audit records actor, action, object, tenant, decision and reason without session secrets. An allow is valid only for this action/resource/context.

    Browser · UI
    UI: denied action
    Server · policy evaluator
    Audit: TENANT_MISMATCH
    Resource · verified context
    report-orion-01

No verified context · deny

  1. A request is not a decision

    The UI requests an action. A requested resource or client-displayed tenant label does not establish server authority.

    Browser · UI
    report.read report-atlas-01
    Server · policy evaluator
    Policy not evaluated
    Resource · verified context
    Context not loaded
  2. The request reached the server

    The server establishes the subject from a verified session/context; a name supplied by the browser is not accepted as verified identity.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Duru / member
    Resource · verified context
    Context not loaded
  3. Object and tenant context were established

    Resource tenant and ownership metadata come from a trusted server data source. Loading and authorization may be one atomic operation; this drawing separates two logical checks.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Verified context missing
    Resource · verified context
    report-atlas-01 · atlas
  4. Policy conditions were evaluated

    Active identity, tenant match, action permission and owner/reviewer relationship are evaluated together. Skipping a condition breaks a different trust boundary.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    DENY · CONTEXT_REQUIRED
    Resource · verified context
    Resource body withheld
  5. The decision returned to the browser

    The response carries a resource representation only on allow. This teaching fixture explains denial reasons; production response detail should account for information disclosure.

    Browser · UI
    Action denied
    Server · policy evaluator
    DENY · CONTEXT_REQUIRED
    Resource · verified context
    Body withheld
  6. Evidence preserves decision scope

    Audit records actor, action, object, tenant, decision and reason without session secrets. An allow is valid only for this action/resource/context.

    Browser · UI
    UI: denied action
    Server · policy evaluator
    Audit: CONTEXT_REQUIRED
    Resource · verified context
    report-atlas-01

No action grant · deny

  1. A request is not a decision

    The UI requests an action. A requested resource or client-displayed tenant label does not establish server authority.

    Browser · UI
    report.archive report-atlas-01
    Server · policy evaluator
    Policy not evaluated
    Resource · verified context
    Context not loaded
  2. The request reached the server

    The server establishes the subject from a verified session/context; a name supplied by the browser is not accepted as verified identity.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Duru / member
    Resource · verified context
    Context not loaded
  3. Object and tenant context were established

    Resource tenant and ownership metadata come from a trusted server data source. Loading and authorization may be one atomic operation; this drawing separates two logical checks.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    Subject / action / context ready
    Resource · verified context
    report-atlas-01 · atlas
  4. Policy conditions were evaluated

    Active identity, tenant match, action permission and owner/reviewer relationship are evaluated together. Skipping a condition breaks a different trust boundary.

    Browser · UI
    Waiting for decision
    Server · policy evaluator
    DENY · ACTION_NOT_GRANTED
    Resource · verified context
    Resource body withheld
  5. The decision returned to the browser

    The response carries a resource representation only on allow. This teaching fixture explains denial reasons; production response detail should account for information disclosure.

    Browser · UI
    Action denied
    Server · policy evaluator
    DENY · ACTION_NOT_GRANTED
    Resource · verified context
    Body withheld
  6. Evidence preserves decision scope

    Audit records actor, action, object, tenant, decision and reason without session secrets. An allow is valid only for this action/resource/context.

    Browser · UI
    UI: denied action
    Server · policy evaluator
    Audit: ACTION_NOT_GRANTED
    Resource · verified context
    report-atlas-01

ACTORS · STATE · DATA FLOW

Which report may Duru read?

Atlas and Orion are tenants for two separate organizations. Duru signs in as an Atlas member. report-atlas-01 belongs to Duru within Atlas. Ekin is a reviewer in the same tenant; reviewing reports is part of Ekin’s work. Orion reports are outside both users’ scope. Each actor in this example belongs to one tenant; products with multiple memberships must also validate active-tenant selection.

Authentication establishes Duru’s identity. Authorization decides whether that identity may perform a particular action on a particular report. Permission to read does not imply permission to update or export. Our business policy allows members to read their own reports in their tenant and reviewers to read other reports in that tenant. Platform-wide administration and cross-tenant sharing are outside this policy.

Four inputs, and who supplies them

The subject is the verified requesting principal: actorId, active membership and role. The object is the report: objectId, ownerId, tenantId and its state. The action is report.read. Context includes the server-verified active tenant and policy version. Role is a subject attribute; ownership is a relationship between subject and object. Neither substitutes for the other.

The client may select a report or tenant; selection is not a grant. The server resolves the actor from its session, obtains role and membership from an authoritative source, and reads tenantId and ownerId from stored metadata. Treating a submitted role or owner label as authoritative gives the requester control over the decision. Authoritative data also needs freshness: a stale cached membership may no longer describe the user’s entitlement.

Unpacking an allow decision

Duru requests read access to report-atlas-01. The handler establishes an active principal and verified Atlas context. The policy checks that the target metadata belongs to Atlas, recognizes the action, verifies that the member role participates in that action, and compares ownerId with actorId. Only after all required conditions hold does it return allow/OWNER_READ and release the protected report body.

For another owner’s Atlas report, the final condition fails: deny/OWNER_REQUIRED. An Orion target fails the tenant condition: deny/TENANT_MISMATCH. Ekin’s reviewer role grants an ownership exception inside Atlas; it does not remove the Orion boundary. This ordering belongs to our teaching policy, not every framework. An application may instead perform a scoped lookup that never loads another tenant’s metadata and returns not found.

Deny prevents disclosure and execution

An unknown action, missing context or undefined role has no explicit grant and yields deny. Failure of the decision service must not permit a protected operation to continue. A denied read returns no confidential body; a denied write performs no mutation. Error handling cannot become an alternative allow path. Public resources may have an explicit public policy; the tenant check in this example is not a universal requirement for every web resource.

A decision reason is an internal observation. The UI need not expose ownerId, another tenant’s name or complete policy details. Choosing 403 or an existence-concealing 404 belongs to the product’s HTTP and disclosure design. A status code alone does not reveal which permission checks actually ran.

Keep the decision across data paths

The same report may be accessed through a screen, a download or a background export job. Each path must preserve the entitlement. Tenant equality alone is insufficient: Atlas ownership and action rules still apply. The following designs carry this report policy across different paths; middleware does not automatically secure every downstream component.

  • Database: scope object lookup to the tenant and retain that scope for reads, updates, deletes and related records. Query parameterization addresses injection; it does not establish ownership or tenant permission.
  • Cache: authorize access before serving a protected body. Keys for results that vary by tenant or user entitlement include the relevant scope and, where needed, permission version. A cache hit is not permission to skip policy.
  • Files: private export metadata binds owner and tenant. The download service evaluates that relationship; publishing a file URL does not preserve the policy automatically.
  • Jobs: bind originating actor, verified tenant and action to trusted job context when scheduling an export. The worker re-establishes context and re-evaluates permissions that may have changed during the delay. Broad service-account privileges are not Duru’s entitlement.

How long is a decision valid?

Removing Ekin’s reviewer role makes an old allow decision insufficient for the next read. Policy version distinguishes rule sets; it does not itself enforce a role change. Current membership, cache invalidation and the enforcement tier must work together. Delayed or long-running operations need an explicit rule for permission changes before execution.

This lesson models reads. For updates, ownership or state may change between authorization and mutation. The required consistency may use a transaction, lock, version check or another data-platform mechanism. An animation that checks permission before a read does not prove write safety.

2. Red · Trust & Impact

Read every scenario without motion

Field boundary

  1. One allow does not cover every action

    Duru requests report.read on report-atlas-01. The same object identity and verified tenant do not automatically grant a different action.

    Operation request
    report.read
    Policy scope
    Policy not read
    Representation gate
    Fields not selected
  2. Policy scope was inspected

    atlas-report-read-v1 defines tenant-owned report.read only. Subject/object metadata belongs to the server fixture; a client label is not authority.

    Operation request
    report.read
    Policy scope
    atlas-report-read-v1
    Representation gate
    Fields not selected
  3. The operation decision was bounded

    OWN_READ produces allow with OWNER_READ. This covers only report.read/report-atlas-01/atlas context, not every property or operation.

    Operation request
    report.read
    Policy scope
    ALLOW / OWNER_READ
    Representation gate
    Fields not selected
  4. Field output was selected separately

    deliveredFields=objectId, title, body is the permitted output in this fixture. Object access and field selection are separate responsibilities; no additional private field or real data is invented.

    Operation request
    report.read
    Policy scope
    OWNER_READ
    Representation gate
    objectId,title,body
  5. The delivery gate preserved the outcome

    The allow branch prepares only objectId, title, body for delivery. The representation contract remains visible even for an allowed object.

    Operation request
    report.read
    Policy scope
    OWNER_READ
    Representation gate
    Permitted field set ready
  6. The outcome stayed within its scope

    This local model presents only the permitted field set. Cards show logical owners; the scene runs no HTTP endpoint or network packet.

    Operation request
    objectId,title,body
    Policy scope
    OWNER_READ
    Representation gate
    objectId,title,body

Action boundary

  1. One allow does not cover every action

    Duru requests report.archive on report-atlas-01. The same object identity and verified tenant do not automatically grant a different action.

    Operation request
    report.archive
    Policy scope
    Policy not read
    Representation gate
    Fields not selected
  2. Policy scope was inspected

    atlas-report-read-v1 defines tenant-owned report.read only. Subject/object metadata belongs to the server fixture; a client label is not authority.

    Operation request
    report.archive
    Policy scope
    atlas-report-read-v1
    Representation gate
    Fields not selected
  3. The operation decision was bounded

    The UNKNOWN_ACTION fixture produces ACTION_NOT_GRANTED. A prior or different report.read allow does not change it; each action needs its own decision.

    Operation request
    report.archive
    Policy scope
    DENY / ACTION_NOT_GRANTED
    Representation gate
    Fields not selected
  4. Field output was selected separately

    deliveredFields=[] is the permitted output in this fixture. Object access and field selection are separate responsibilities; no additional private field or real data is invented.

    Operation request
    report.archive
    Policy scope
    ACTION_NOT_GRANTED
    Representation gate
    []
  5. The delivery gate preserved the outcome

    The deny branch produces no protected representation. Empty deliveredFields is a policy expectation, not a claimed real HTTP capture.

    Operation request
    report.archive
    Policy scope
    ACTION_NOT_GRANTED
    Representation gate
    Body withheld
  6. The outcome stayed within its scope

    This local model rejects the action. Cards show logical owners; the scene runs no HTTP endpoint or network packet.

    Operation request
    DENY
    Policy scope
    ACTION_NOT_GRANTED
    Representation gate
    []

ASSUMPTION · BOUNDARY · CONTROL

Which trust assumption breaks?

The normal handler establishes a trusted principal through authentication; the policy then evaluates that principal’s entitlement to this object and action. “Login succeeded, therefore the report is readable” merges distinct decisions. The fault is not failed authentication but disclosure to a verified identity without the required object permission.

The matrix expresses the expected boundary rather than an attack procedure. An allow for Duru on another owner’s report suggests ineffective ownership enforcement; an allow on Orion suggests ineffective tenant enforcement. Inspect the actual policy, authoritative metadata and delivered data together. A UI message alone does not prove the root cause.

Object, function and property ask different questions

Object-level checks ask whether this action is permitted on this report. Function-level checks ask whether a capability such as report.export is available to this subject. Property-level checks restrict which fields may be read from an allowed object or changed during an update. Passing one does not automatically pass the others.

Our member report.read view excludes internalReviewNote. If report.update were defined, ownerId and tenantId would not become ordinary editable fields; changing them needs a separate management policy. Broad serialization or input binding can turn an allowed object action into unauthorized field access. Impact depends on data sensitivity and available actions; missing checks do not all have the same consequence.

Checking the tenant only once

A tenant check on the API read path does not prove one exists on the cache or private-export path. The broken assumption is a downstream component deciding that arrival already implies permission. A worker evaluating only its service role can also widen the originating actor’s scope.

The blocking control is enforcement of the documented entitlement on every protected path, with trusted context propagation. Legitimate platform operations may have a separate explicit cross-tenant policy. Observing a cross-tenant operation alone therefore does not prove a violation. Our example defines no exception and expects Orion denial.

3. Blue · Secure development & defense

Read every scenario without motion

Deny control

  1. A policy record is one claim

    The supplied ledgerExample identifies demo-authz-001 / OTHER_TENANT. synthetic-server-policy is not a production audit source or HTTP capture.

    Decision ledger
    demo-authz-001
    Metadata / context
    Context not read
    Field receipt
    Field receipt not read
    Evidence review
    No conclusion
  2. Rule and context were matched

    Duru’s subject tenant is atlas and the report’s object tenant is orion, within one interaction. Without knowing policyVersion atlas-report-read-v1, expectations from another rule set are not imported into this decision.

    Decision ledger
    demo-authz-001
    Metadata / context
    atlas ≠ orion / v1
    Field receipt
    Field receipt not read
    Evidence review
    No conclusion
  3. The denial reason was checked separately

    OTHER_TENANT expects deny / TENANT_MISMATCH. This is an ordinary negative control; denial counts alone prove neither attack nor incorrect policy.

    Decision ledger
    deny / TENANT_MISMATCH
    Metadata / context
    Expectation matched
    Field receipt
    Field receipt not read
    Evidence review
    No conclusion
  4. Decision and delivery were separated

    The supplied ledgerExample contains deliveredFields=[]. The fixture field is known to be empty; no real empty body is claimed to have been measured on the network.

    Decision ledger
    deny / TENANT_MISMATCH
    Metadata / context
    Expectation matched
    Field receipt
    deliveredFields=[]
    Evidence review
    No conclusion
  5. All matching records were reviewed

    All supplied records within requestId/caseId/policyVersion scope are compared. A conflicting duplicate is not resolved by selecting the first row; ambiguity is preserved.

    Decision ledger
    Interaction scope
    Metadata / context
    Rule scope preserved
    Field receipt
    Empty fixture field set
    Evidence review
    Fields consistent
  6. The regression verdict was bounded

    This fixture passes the deny/empty-fields control. OWN_READ and REVIEWER_READ must remain allowed positive controls; denying everybody is not a correct policy.

    Decision ledger
    deny / TENANT_MISMATCH
    Metadata / context
    Policy expectation matched
    Field receipt
    Fixture fields=[]
    Evidence review
    PASS: supplied matrix

Delivery missing

  1. A policy record is one claim

    The supplied ledgerExample identifies demo-authz-001 / OTHER_TENANT. synthetic-server-policy is not a production audit source or HTTP capture.

    Decision ledger
    demo-authz-001
    Metadata / context
    Context not read
    Field receipt
    Field receipt not read
    Evidence review
    No conclusion
  2. Rule and context were matched

    Duru’s subject tenant is atlas and the report’s object tenant is orion, within one interaction. Without knowing policyVersion atlas-report-read-v1, expectations from another rule set are not imported into this decision.

    Decision ledger
    demo-authz-001
    Metadata / context
    atlas ≠ orion / v1
    Field receipt
    Field receipt not read
    Evidence review
    No conclusion
  3. The denial reason was checked separately

    OTHER_TENANT expects deny / TENANT_MISMATCH. This is an ordinary negative control; denial counts alone prove neither attack nor incorrect policy.

    Decision ledger
    deny / TENANT_MISMATCH
    Metadata / context
    Expectation matched
    Field receipt
    Field receipt not read
    Evidence review
    No conclusion
  4. Decision and delivery were separated

    The review copy omits the deliveredFields observation. A denial record alone cannot establish that a real body was empty.

    Decision ledger
    deny / TENANT_MISMATCH
    Metadata / context
    Expectation matched
    Field receipt
    Receipt missing
    Evidence review
    No conclusion
  5. All matching records were reviewed

    All supplied records within requestId/caseId/policyVersion scope are compared. A conflicting duplicate is not resolved by selecting the first row; ambiguity is preserved.

    Decision ledger
    Interaction scope
    Metadata / context
    Rule scope preserved
    Field receipt
    Receipt missing
    Evidence review
    Incomplete coverage
  6. The regression verdict was bounded

    Policy denial is known; delivery remains UNKNOWN. Additional response evidence is requested; a missing observation proves neither leakage nor successful protection.

    Decision ledger
    deny / TENANT_MISMATCH
    Metadata / context
    Policy expectation matched
    Field receipt
    UNKNOWN: delivery
    Evidence review
    UNKNOWN: delivery

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

HTTP logs and decision ledgers have different jobs

An HTTP access log may show path, status and timing; it does not by itself establish that ownership or tenant policy ran. The application policy evaluator emits a decision event with server-verified actorId, subjectTenant, objectTenant, action, objectId, decision, reason and policyVersion. These are our example’s custom fields, not an OWASP event schema.

requestId links decision and response within one interaction. A background path retains jobId and originating-actor correlation. Do not log session cookies or access tokens in plaintext for correlation. Obtain tenant and actor from verified server context rather than free-form client labels. Reading the audit store also requires permission.

Three distinct evidence outcomes

The positive control is Duru’s own-read allow/OWNER_READ with only the expected fields delivered. The negative ownership control is deny/OWNER_REQUIRED with no protected body. The cross-tenant control is deny/TENANT_MISMATCH. Expected denials are a safe baseline, not automatic attack alerts. Ekin’s Atlas reviewer-read allow is also a legitimate ownership exception.

Missing actor, objectTenant or policyVersion leaves incomplete evidence. A denial event does not prove a later component sent no data. Correlate the decision with delivered fields, response and any mutation result. Lost records, clock differences and asynchronous retries can break correlation; an unknown outcome must not be filled in as allow or attack.

Explainable review logic for this fixture

Instead of inventing a backend-specific KQL table, use matrix checks: for each caseId compare expected and observed decision/reason, and require empty deliveredFields for deny cases. In this fixture, an allow where subjectTenant differs from objectTenant is a policy-regression candidate. That predicate needs an explicit exception model in products with legitimate cross-tenant grants.

Correlate policyVersion, current membership and object metadata rather than a role label alone. A broad denial-count alert can flag harmless role changes or obsolete bookmarks. Stronger violation evidence is an expected-deny case receiving allow, or protected data leaving after deny. Keep the matrix expectations separate from actual application observations; record the observed decision independently.

Fixes need regression evidence

When enforcement is absent on one path, inventory all report access paths and policy ownership. Establish a common trusted evaluator or equivalent controls across tiers; verify that pooled connections or reused workers do not retain another request’s tenant context. Cover private downloads and cached responses as well as the main API.

Regression means more than making a forbidden case deny. Own-read and same-tenant reviewer-read must still work; other-owner, cross-tenant, missing-context and unsupported-action cases must remain denied. Also evaluate role removal and cache hit/miss paths. The matrix defines expectations; actual enforcement requires tests of the relevant code paths and their outputs.

NOW APPLY IT TO THE CASE

Which fields and actions does object permission cover, and what proves it?

Duru’s permission to read an Atlas report does not grant archiving. Ekin’s reviewer role does not remove the tenant boundary. Policy decisions and fields visible in a response are compared separately.

Separate decisions for verified actor, tenant relation and requested action in the case. Do not substitute authentication or proof success for object permission.

Open the related case file ↗

READ THE EXAMPLE TOGETHER

Atlas report policy: decision matrix

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "policyVersion": "atlas-report-read-v1",
  "policyScope": "Tenant-owned report.read only; no global administrator or cross-tenant grant.",
  "subjects": [
    {
      "actorId": "duru",
      "tenantId": "atlas",
      "role": "member",
      "active": true
    },
    {
      "actorId": "ekin",
      "tenantId": "atlas",
      "role": "reviewer",
      "active": true
    }
  ],
  "objects": [
    {
      "objectId": "report-atlas-01",
      "tenantId": "atlas",
      "ownerId": "duru"
    },
    {
      "objectId": "report-atlas-02",
      "tenantId": "atlas",
      "ownerId": "ekin"
    },
    {
      "objectId": "report-orion-01",
      "tenantId": "orion",
      "ownerId": "sarp"
    }
  ],
  "ruleOrder": [
    "active-subject-and-verified-context",
    "tenant-match",
    "known-action",
    "known-role",
    "owner-or-reviewer",
    "explicit-allow"
  ],
  "cases": [
    {
      "caseId": "OWN_READ",
      "actorId": "duru",
      "objectId": "report-atlas-01",
      "action": "report.read",
      "verifiedContextTenant": "atlas",
      "expected": {
        "decision": "allow",
        "reason": "OWNER_READ",
        "deliveredFields": [
          "objectId",
          "title",
          "body"
        ]
      }
    },
    {
      "caseId": "OTHER_OWNER",
      "actorId": "duru",
      "objectId": "report-atlas-02",
      "action": "report.read",
      "verifiedContextTenant": "atlas",
      "expected": {
        "decision": "deny",
        "reason": "OWNER_REQUIRED",
        "deliveredFields": []
      }
    },
    {
      "caseId": "OTHER_TENANT",
      "actorId": "duru",
      "objectId": "report-orion-01",
      "action": "report.read",
      "verifiedContextTenant": "atlas",
      "expected": {
        "decision": "deny",
        "reason": "TENANT_MISMATCH",
        "deliveredFields": []
      }
    },
    {
      "caseId": "REVIEWER_READ",
      "actorId": "ekin",
      "objectId": "report-atlas-01",
      "action": "report.read",
      "verifiedContextTenant": "atlas",
      "expected": {
        "decision": "allow",
        "reason": "REVIEWER_READ",
        "deliveredFields": [
          "objectId",
          "title",
          "body"
        ]
      }
    },
    {
      "caseId": "REVIEWER_OTHER_TENANT",
      "actorId": "ekin",
      "objectId": "report-orion-01",
      "action": "report.read",
      "verifiedContextTenant": "atlas",
      "expected": {
        "decision": "deny",
        "reason": "TENANT_MISMATCH",
        "deliveredFields": []
      }
    },
    {
      "caseId": "NO_CONTEXT",
      "actorId": "duru",
      "objectId": "report-atlas-01",
      "action": "report.read",
      "verifiedContextTenant": null,
      "expected": {
        "decision": "deny",
        "reason": "CONTEXT_REQUIRED",
        "deliveredFields": []
      }
    },
    {
      "caseId": "UNKNOWN_ACTION",
      "actorId": "duru",
      "objectId": "report-atlas-01",
      "action": "report.archive",
      "verifiedContextTenant": "atlas",
      "expected": {
        "decision": "deny",
        "reason": "ACTION_NOT_GRANTED",
        "deliveredFields": []
      }
    }
  ],
  "ledgerExample": {
    "requestId": "demo-authz-001",
    "caseId": "OTHER_TENANT",
    "actorId": "duru",
    "subjectTenant": "atlas",
    "objectId": "report-orion-01",
    "objectTenant": "orion",
    "action": "report.read",
    "policyVersion": "atlas-report-read-v1",
    "decision": "deny",
    "reason": "TENANT_MISMATCH",
    "deliveredFields": [],
    "source": "synthetic-server-policy"
  }
}
subjects / objects
Seeded identity and report metadata, not client assertions of role or ownership.
verifiedContextTenant
Active context established from server-verified membership; null represents missing context.
ruleOrder
This teaching model’s check/reason order; the first failing condition determines the reason.
expected
The required policy outcome, not a runtime observation.
deliveredFields
Permitted response field names for allow; deny should release no protected fields.
requestId / policyVersion
Interaction correlation and rule-set identity, not secrets or credentials.

Synthetic inert JSON, not a real HTTP request, database capture or production audit. It is not a full model of membership, time or concurrency; policyVersion does not itself revoke permission. A browser teaching evaluator does not prove server enforcement.

Two reality checks

A signed-in user may access every application resource.

Authentication establishes identity. Authorization separately evaluates the object, action and, where needed, fields.

Hiding a UI button enforces server authorization.

The UI may reflect a permission decision; a trusted service layer must still enforce the protected operation.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

Ekin is a reviewer in tenant atlas. Ekin requests report.read for report-orion-01; verified context is atlas. What does the selected policy return?

  1. Deny with TENANT_MISMATCH; no protected fields are delivered.
  2. Allow for reports in all tenants because Ekin is a reviewer.
  3. OWNER_REQUIRED solely because Ekin is not the owner.
Read the explained answer

Deny with TENANT_MISMATCH; no protected fields are delivered. — Subject, object and verified context must meet within the same tenant boundary. Reviewer permission matters only after that gate passes. The deny result must leave deliveredFields empty.

What does the expected row below prove about server enforcement?

Policy expectation for OTHER_TENANT; not a runtime record

caseId
OTHER_TENANT
expected.decision / reason
deny / TENANT_MISMATCH
expected.deliveredFields
[]
evidence origin
synthetic policy matrix
  1. It specifies the required outcome; it does not measure actual response delivery.
  2. It proves no foreign-tenant field was released in production.
  3. The empty expected list proves the cache and jobs enforce the same control.
Read the explained answer

It specifies the required outcome; it does not measure actual response delivery. — The policy matrix provides a reasoned regression expectation. Enforcement evidence must additionally measure released fields and different DB/cache/file/job paths; this JSON alone does not establish a production result.

Duru requests her own atlas report, but verifiedContextTenant is null. Why is deny required despite matching ownership? How should a cache or job path preserve this?

Read the explained answer

The selected policy first requires an active subject and verified context. A null context cannot establish the tenant match; it returns CONTEXT_REQUIRED before ownership evaluation and releases no protected body/fields. A cache hit must not skip the same permission/context gate; a job consumer must verify identity and valid scope at execution time. Missing context does not prove Duru is always unauthorized; it means this request lacks required evidence.

□ I did not substitute matching ownership for verified context.

□ I explained that deny must leave protected delivery empty.

□ I preserved the same gate and current-context requirement for cache/jobs.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

MANUAL LAB

Request, session, and resource policy

Two workbooks follow actual loopback HTTP requests, cookie/store boundaries, and seeded permission decisions.

Open the prediction → observation → explanation workbook ↗Source on GitHub ↗

Sources and scope