Class: Reeve::Decision

Inherits:
Object
  • Object
show all
Defined in:
lib/reeve/decision.rb

Overview

The result of a policy evaluation: an outcome, and the rule that produced it.

A decision without a rule is not a decision — Constitution II requires every ledger entry to name what decided, so the rule is validated here rather than at write time. Immutable and comparable by value.

Constant Summary collapse

NO_GUARD_DECLARED =
"no_guard_declared"
NO_PRINCIPAL =
"no_principal"
POLICY_ERROR =
"policy_error"
UNKNOWN_RECORD_TYPE =
"unknown_record_type"
UNSCOPED_DERIVED_RESULT =
"unscoped_derived_result"
OUT_OF_SCOPE_RECORD =
"out_of_scope_record"
AUDIT_WRITE_FAILED =
"audit_write_failed"
TOOL_ERROR =

An allowed invocation whose tool body raised. No records reached the agent, so the ledger records it as a deny — the trace of a call that blew up is the one most worth having (R5).

"tool_error"
UNGUARDED_TOOL =

The one reserved allow rule: a tool with no guard, permitted because the host opted into :allow_with_warning. Kept out of RESERVED_RULES, which names deny paths.

"unguarded_tool"
RESERVED_RULES =

Stable strings. The testing kit and host applications match on them, so a rename here is a breaking change.

[
  NO_GUARD_DECLARED,
  NO_PRINCIPAL,
  POLICY_ERROR,
  UNKNOWN_RECORD_TYPE,
  UNSCOPED_DERIVED_RESULT,
  OUT_OF_SCOPE_RECORD,
  AUDIT_WRITE_FAILED,
  TOOL_ERROR
].freeze
OUTCOMES =
%i[allow deny].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(outcome:, rule:, detail: nil) ⇒ Decision

Returns a new instance of Decision.



51
52
53
54
55
56
# File 'lib/reeve/decision.rb', line 51

def initialize(outcome:, rule:, detail: nil)
  @outcome = validate_outcome(outcome)
  @rule    = validate_rule(rule)
  @detail  = detail&.to_s
  freeze
end

Instance Attribute Details

#detailObject (readonly)

Returns the value of attribute detail.



41
42
43
# File 'lib/reeve/decision.rb', line 41

def detail
  @detail
end

#outcomeObject (readonly)

Returns the value of attribute outcome.



41
42
43
# File 'lib/reeve/decision.rb', line 41

def outcome
  @outcome
end

#ruleObject (readonly)

Returns the value of attribute rule.



41
42
43
# File 'lib/reeve/decision.rb', line 41

def rule
  @rule
end

Class Method Details

.allow(rule:, detail: nil) ⇒ Object



43
44
45
# File 'lib/reeve/decision.rb', line 43

def self.allow(rule:, detail: nil)
  new(outcome: :allow, rule: rule, detail: detail)
end

.deny(rule:, detail: nil) ⇒ Object



47
48
49
# File 'lib/reeve/decision.rb', line 47

def self.deny(rule:, detail: nil)
  new(outcome: :deny, rule: rule, detail: detail)
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



75
76
77
78
79
80
# File 'lib/reeve/decision.rb', line 75

def ==(other)
  other.is_a?(Decision) &&
    other.outcome == outcome &&
    other.rule == rule &&
    other.detail == detail
end

#allowed?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/reeve/decision.rb', line 58

def allowed?
  outcome == :allow
end

#denied?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/reeve/decision.rb', line 62

def denied?
  outcome == :deny
end

#hashObject



83
84
85
# File 'lib/reeve/decision.rb', line 83

def hash
  [self.class, outcome, rule, detail].hash
end

#inspectObject



91
92
93
# File 'lib/reeve/decision.rb', line 91

def inspect
  "#<Reeve::Decision #{outcome} rule=#{rule.inspect} detail=#{detail.inspect}>"
end

#reserved_rule?Boolean

True when the rule came from reeve itself rather than from a host policy.

Returns:

  • (Boolean)


67
68
69
# File 'lib/reeve/decision.rb', line 67

def reserved_rule?
  RESERVED_RULES.include?(rule)
end

#to_hObject



71
72
73
# File 'lib/reeve/decision.rb', line 71

def to_h
  { outcome: outcome.to_s, rule: rule, detail: detail }
end

#to_sObject



87
88
89
# File 'lib/reeve/decision.rb', line 87

def to_s
  "#{outcome}(#{rule})"
end