Class: Protege::Gateway::AccessPolicy

Inherits:
Data
  • Object
show all
Defined in:
lib/protege/gateway/access_policy.rb

Overview

One layer of the inbound access-control guardrail: an immutable value that decides whether a given sender address is permitted, given an allow-list and a deny-list of address patterns. A Gateway value type (inbound authorization) — built through the Gateway.build_access_policy factory, composed by Gateway::AccessControl, and assigned as the global ceiling via config.inbound_access or per-persona by AccessRule.policy_for:

config.inbound_access = Protege::Gateway.build_access_policy(allow: ['*@company.co'])

A policy is evaluated in a fixed precedence — an explicit deny match always wins, then an allow match admits, then the default_decision settles everyone else. The default is derived when not given: the mere presence of an allow rule flips the layer into allow-list mode (default-deny), which is the intuitive reading of "only these may enter". With no allow rules the default is :allow, so a layer carrying only deny rules behaves as a block-list.

Patterns are matched against the sender's tag-stripped, lowercased local@domain routing key (so +ceo+finance@Company.CO+ matches a ceo@company.co rule), and support a single * wildcard (e.g. *@company.co). This object knows nothing about layering; composing several policies into the org→persona guardrail is +AccessControl+'s job.

Examples:

The identity policy — a bare build_access_policy permits everyone (empty lists derive a

default-allow), so it drops out of the +AccessControl+ intersection as a no-op.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(allow: [], deny: [], default_decision: nil) ⇒ AccessPolicy

Build a policy, normalizing the pattern lists and deriving the default decision.

Parameters:

  • allow (Array<String>) (defaults to: [])

    allow-list patterns (blank/nil entries are dropped)

  • deny (Array<String>) (defaults to: [])

    deny-list patterns (blank/nil entries are dropped)

  • default_decision (Symbol, nil) (defaults to: nil)

    +:allow+/+:deny+; when nil, derived from allow (present → :deny, empty → :allow)



33
34
35
36
37
38
39
40
41
# File 'lib/protege/gateway/access_policy.rb', line 33

def initialize(allow: [], deny: [], default_decision: nil)
  allow              = normalize_patterns(allow)
  deny               = normalize_patterns(deny)
  # Derive the default decision when not given: if any allow rules exist, default-deny;
  # otherwise default-allow.
  default_decision ||= allow.empty? ? :allow : :deny

  super
end

Instance Attribute Details

#allowObject (readonly)

Returns the value of attribute allow

Returns:

  • (Object)

    the current value of allow



26
27
28
# File 'lib/protege/gateway/access_policy.rb', line 26

def allow
  @allow
end

#default_decisionObject (readonly)

Returns the value of attribute default_decision

Returns:

  • (Object)

    the current value of default_decision



26
27
28
# File 'lib/protege/gateway/access_policy.rb', line 26

def default_decision
  @default_decision
end

#denyObject (readonly)

Returns the value of attribute deny

Returns:

  • (Object)

    the current value of deny



26
27
28
# File 'lib/protege/gateway/access_policy.rb', line 26

def deny
  @deny
end

Instance Method Details

#permits?(address:) ⇒ Boolean

Decide whether a sender address is permitted by this layer.

Parameters:

  • address (String)

    the raw sender address (display name, plus-tag, and case tolerated)

Returns:

  • (Boolean)

    true when the sender may pass this layer



47
48
49
50
51
52
53
54
# File 'lib/protege/gateway/access_policy.rb', line 47

def permits?(address:)
  key = routing_key_for(address)

  return false if deny.any?  { |pattern| matches?(pattern:, key:) }
  return true  if allow.any? { |pattern| matches?(pattern:, key:) }

  default_decision == :allow
end