read_prev_login
previous within 1h: permit a Read only if the immediately preceding
timepoint (i - 1) was a matching Login by the same user, and that event was
within the last hour. Unlike formerly, previous looks only at the single
event directly before the decision point, not the whole window. At the first
timepoint previous is always false.
The trace shows both outcomes:
@0—Loginby alice (a history-only event here; noReadpermit applies, so the decision is a deny).@100— alice reads doc1, immediately after the login → allow (the directly preceding event was a matchingLoginwithin the window).@200— alice reads doc2, but the immediately preceding event was aRead, not aLogin→ deny (previousfails even though a login exists earlier in history).
Referenced by guide/04-temporal-expressions.md.
Policy
// `previous` looks ONLY at the immediately preceding timepoint (i-1), not the
// whole window. Permit a Read only if the same user logged in at the event
// directly before this one, and that event was within the last hour. At the
// first timepoint `previous` is always false.
@id("read_after_prev_login")
permit (
principal,
action == Drupe::Action::"Read",
resource
)
when temporal {
previous within 1h Drupe::Action::"Login"::request{ input.user: context.input.user }
};
Schema
namespace Drupe {
type ContentFilterFinding = {
severityScore: decimal
};
type LoginInput = {
user: String
};
type LoginOutput = { };
type PromptAttackFinding = {
severityScore: decimal
};
type ReadInput = {
document: String,
user: String
};
type ReadOutput = { };
type SensitiveInfoFinding = {
confidenceScore: decimal
};
type SystemContext = {
now: datetime
};
entity Gateway;
entity IamEntity = {
id: String
};
entity OAuthUser = {
id: String
} tags String;
entity UnauthenticatedUser;
action "CallTool" in [Action::"Mcp"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "Http" appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "InvokeAgent" in [Action::"Http"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
input?: { },
system: SystemContext
}
};
action "InvokeLLM" in [Action::"Http"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
input?: { },
system: SystemContext
}
};
action "Login" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: LoginInput,
output?: LoginOutput,
system: SystemContext
}
};
action "Mcp" appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "Read" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: ReadInput,
output?: ReadOutput,
system: SystemContext
}
};
action "UnknownTool" in [Action::"CallTool"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
}
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")
@100 scope(principal: Drupe::OAuthUser::"alice", resource: Drupe::Gateway::"gw1") request_context(input: { document: "doc1", user: "alice" }) Drupe::Action::"Read"::request(input: { document: "doc1", user: "alice" }, callerPrincipal: Drupe::OAuthUser::"alice", callerResource: Drupe::Gateway::"gw1", requestId: "u2")
@200 scope(principal: Drupe::OAuthUser::"alice", resource: Drupe::Gateway::"gw1") request_context(input: { document: "doc2", user: "alice" }) Drupe::Action::"Read"::request(input: { document: "doc2", user: "alice" }, callerPrincipal: Drupe::OAuthUser::"alice", callerResource: Drupe::Gateway::"gw1", requestId: "u3")
Expected Output
@0 (time point 0): DENY
@100 (time point 1): ALLOW [rules: 0]
@200 (time point 2): DENY