Class: Sequel::Privacy::Policy

Inherits:
Proc
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/sequel/privacy/policy.rb

Overview

A Policy wraps a Proc/lambda with metadata about how it should be evaluated.

Policies are actor-first. Arities map to:

  • 0 args: -> { allow if Time.now.sunday? } # Global decision

  • 1 arg: ->(actor) { allow if actor.is_role?(:admin) }

  • 2 args: ->(actor, subject) { allow if subject.owner_id == actor.id }

  • 3 args: ->(actor, subject, direct_object) { … }

Any policy with arity >= 1 auto-denies for anonymous viewers (nil actor) unless declared with ‘allow_anonymous: true`. That flag is for state-gate policies that deliberately ignore actor — e.g. “post is published.”

Policies must return :allow, :deny, :pass, or an array of policies (for combinators).

Constant Summary collapse

VALID_CACHE_BY =
T.let(%i[actor subject direct_object].freeze, T::Array[Symbol])

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#commentObject (readonly)

Returns the value of attribute comment.



26
27
28
# File 'lib/sequel/privacy/policy.rb', line 26

def comment
  @comment
end

#policy_nameObject (readonly)

Returns the value of attribute policy_name.



23
24
25
# File 'lib/sequel/privacy/policy.rb', line 23

def policy_name
  @policy_name
end

Class Method Details

.create(policy_name, lam, comment = nil, cacheable: true, single_match: false, cache_by: nil, allow_anonymous: false) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
# File 'lib/sequel/privacy/policy.rb', line 41

def self.create(policy_name, lam, comment = nil, cacheable: true, single_match: false, cache_by: nil,
                allow_anonymous: false)
  new(&lam).setup(
    policy_name: policy_name,
    comment: comment,
    cacheable: cacheable,
    single_match: single_match,
    cache_by: cache_by,
    allow_anonymous: allow_anonymous
  )
end

Instance Method Details

#allow_anonymous?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/sequel/privacy/policy.rb', line 103

def allow_anonymous?
  @allow_anonymous || false
end

#cache_byObject



98
99
100
# File 'lib/sequel/privacy/policy.rb', line 98

def cache_by
  @cache_by
end

#cacheable?Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/sequel/privacy/policy.rb', line 85

def cacheable?
  @cacheable || false
end

#setup(policy_name: nil, comment: nil, cacheable: true, single_match: false, cache_by: nil, allow_anonymous: false) ⇒ Object

Configure the policy after creation, normally done with the shorthand ‘policy` call.

Parameters:

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

    Human-readable name for logging

  • comment (String, nil) (defaults to: nil)

    Description of what this policy does

  • cacheable (Boolean) (defaults to: true)

    Whether results can be cached (default: true)

  • single_match (Boolean) (defaults to: false)

    Whether only one subject/actor pair can match (default: false)

  • cache_by (Symbol, Array<Symbol>, nil) (defaults to: nil)

    Override the cache-key dimensions. By default the key is derived from the policy’s arity, but you might want to pass a subset of ‘:actor, :subject, :direct_object` to cache by only those; useful when the policy ignores inputs (e.g. an “is-admin” check that takes `(actor, subject)` but only looks at actor should use `cache_by: :actor` to share a single entry across subjects).

  • allow_anonymous (Boolean) (defaults to: false)

    If true, skip the auto-deny that normally fires when a policy of arity >= 1 is evaluated for an anonymous viewer (nil actor). This is a bit inelegant; it’d be great if we could tell that an argument isn’t used at all.



70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/sequel/privacy/policy.rb', line 70

def setup(policy_name: nil, comment: nil, cacheable: true, single_match: false, cache_by: nil,
          allow_anonymous: false)
  raise 'Privacy Policy is frozen' if @frozen

  @cacheable = cacheable
  @policy_name = policy_name.to_s
  @comment = comment
  @frozen = true
  @single_match = single_match
  @cache_by = normalize_cache_by(cache_by)
  @allow_anonymous = allow_anonymous
  self
end

#single_match?Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/sequel/privacy/policy.rb', line 93

def single_match?
  @single_match || false
end