Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

read_after_login

The history-dependent version of the getting-started tour (Step 4 — a decision that depends on history, which a single Cedar request cannot see): permit Read only if the same user successfully logged in within the last hour. The when temporal { … } clause reads the accumulated event history, and the { input.user: context.input.user } pin correlates the past Login response’s user with the current Read request’s user.

The trace shows both an allow and a deny:

  • @0Login request by alice. The policy gates Read, not Login, so no permit matches → deny (the login request still lands in the history).
  • @5Login response (history-only; records that the login succeeded).
  • @10 — alice reads, 10s after the login → allow (a matching login response is inside the 1h window).
  • @7200 — alice reads again, two hours later; the only login has expired (7200s > 3600s) → deny.

Referenced by guide/01-getting-started.md.

Policy

// Permit Read only if the same user successfully logged in within the last
// hour. The `when temporal { … }` clause reads the accumulated event history;
// the `{ input.user: context.input.user }` pin correlates the past login's
// user with the current request's user. This is what makes the authorizer
// stateful.
@id("read_after_login")
permit (
    principal,
    action == Drupe::Action::"Read",
    resource
)
when temporal {
    formerly within 1h Drupe::Action::"Login"::response{ input.user: context.input.user }
};

Schema

namespace Drupe {
  type LoginInput = { user: String };
  type ReadInput = { user: String };
  entity Gateway;
  entity OAuthUser = { id: String } tags String;
  action "Login" appliesTo {
    principal: [OAuthUser],
    resource: [Gateway],
    context: { input: LoginInput }
  };
  action "Read" appliesTo {
    principal: [OAuthUser],
    resource: [Gateway],
    context: { input: ReadInput }
  };
}

Trace

@0 scope(principal: Drupe::OAuthUser::"alice", resource: Drupe::Gateway::"gw1") request_context(input: { user: "alice" }) Drupe::Action::"Login"::request(input: { user: "alice" }, callerPrincipal: Drupe::OAuthUser::"alice", callerResource: Drupe::Gateway::"gw1", requestId: "u1")
@5 scope(principal: Drupe::OAuthUser::"alice", resource: Drupe::Gateway::"gw1") request_context(input: { user: "alice" }) Drupe::Action::"Login"::response(input: { user: "alice" }, callerPrincipal: Drupe::OAuthUser::"alice", callerResource: Drupe::Gateway::"gw1", requestId: "u1")
@10 scope(principal: Drupe::OAuthUser::"alice", resource: Drupe::Gateway::"gw1") request_context(input: { user: "alice" }) Drupe::Action::"Read"::request(input: { user: "alice" }, callerPrincipal: Drupe::OAuthUser::"alice", callerResource: Drupe::Gateway::"gw1", requestId: "u2")
@7200 scope(principal: Drupe::OAuthUser::"alice", resource: Drupe::Gateway::"gw1") request_context(input: { user: "alice" }) Drupe::Action::"Read"::request(input: { user: "alice" }, callerPrincipal: Drupe::OAuthUser::"alice", callerResource: Drupe::Gateway::"gw1", requestId: "u3")

Expected Output

@0 (time point 0): DENY
@10 (time point 1): ALLOW  [rules: 0]
@7200 (time point 2): DENY