Class: Verifica::Acl

Inherits:
Object
  • Object
show all
Defined in:
lib/verifica/acl.rb

Overview

Access Control List (ACL)

Access Control List consists of Access Control Entities (ACEs) and defines which actions are allowed or denied for particular Security Identifiers (SIDs).

ACL is typically associated with a resource (e.g. Post, Comment, Order) and specifies which users (or external services, or API clients) are allowed to do what actions on the given resource.

See Also:

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(aces) ⇒ Acl

Note:

Use build instead of this constructor directly.

Creates a new Access Control List with immutable state.

Parameters:

  • aces (Array<Ace>)

    list of Access Control Entries



48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/verifica/acl.rb', line 48

def initialize(aces)
  @aces = Set.new(aces).freeze
  @allow_deny_by_action = prepare_index.freeze
  @allowed_actions = Set.new
  @allow_deny_by_action.each do |action, allow_deny|
    @allowed_actions.add(action) unless allow_deny[:allowed_sids].empty?

    allow_deny[:allowed_sids].freeze
    allow_deny[:denied_sids].freeze
  end

  @allowed_actions.freeze
  freeze
end

Class Method Details

.build {|builder| ... } ⇒ Acl

Creates a new Verifica::AclBuilder and yields it to the given block.

Examples:

acl = Verifica::Acl.build do |acl|
  acl.allow "anonymous", [:read]
  acl.allow "authenticated", [:read, :comment]
  acl.deny "country:US", [:read, :comment]
end

Yields:

  • (builder)

Returns:

  • (Acl)

    Access Control List created by builder



33
34
35
36
37
# File 'lib/verifica/acl.rb', line 33

def self.build
  builder = AclBuilder.new
  yield builder
  builder.build
end

Instance Method Details

#==(other) ⇒ Object



185
186
187
# File 'lib/verifica/acl.rb', line 185

def ==(other)
  eql?(other)
end

#action_allowed?(action, sids) ⇒ Boolean

Checks whether the action is allowed for given Security Identifiers. For action to be allowed all 3 conditions should be met:

  • ACL and SIDs are not empty

  • ACL contains at least one entry that allow given action for any of the SIDs

  • ACL contains no entries that deny given action for any of the SIDs

Parameters:

  • action (Symbol, String)

    action to check

  • sids (Array<String>, Set<String>)

    list of Security Identifiers to match for

Returns:

  • (Boolean)

    true if action is allowed



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/verifica/acl.rb', line 76

def action_allowed?(action, sids)
  return false if empty? || sids.empty?

  action = action.to_sym
  allow_deny = @allow_deny_by_action[action]

  return false if allow_deny.nil? || !@allowed_actions.include?(action)

  sids = sids.to_set
  allow_deny[:allowed_sids].intersect?(sids) && !allow_deny[:denied_sids].intersect?(sids)
end

#action_denied?(action, sids) ⇒ Boolean

The opposite of #action_allowed?

Returns:

  • (Boolean)


91
92
93
# File 'lib/verifica/acl.rb', line 91

def action_denied?(action, sids)
  !action_allowed?(action, sids)
end

#allowed_actions(sids) ⇒ Array<Symbol>

Returns array of actions allowed for given Security Identifiers or empty array if none.

Parameters:

  • sids (Array<String>, Set<String>)

    list of Security Identifiers to match for

Returns:

  • (Array<Symbol>)

    array of actions allowed for given Security Identifiers or empty array if none



126
127
128
129
130
# File 'lib/verifica/acl.rb', line 126

def allowed_actions(sids)
  return EMPTY_ARRAY if sids.empty?

  @allowed_actions.select { |action| action_allowed?(action, sids) }
end

#allowed_sids(action) ⇒ Array<String>

Note:

Checking allowed SIDs isn’t enough to determine whether the action is allowed. You need to always check #denied_sids as well.

Returns array of Security Identifiers allowed for a given action or empty array if none.

Parameters:

  • action (Symbol, String)

    action to check

Returns:

  • (Array<String>)

    array of Security Identifiers allowed for a given action or empty array if none



103
104
105
106
# File 'lib/verifica/acl.rb', line 103

def allowed_sids(action)
  sids = @allow_deny_by_action.dig(action.to_sym, :allowed_sids)
  sids.nil? ? EMPTY_ARRAY : sids.to_a
end

#build {|builder| ... } ⇒ Acl

Creates a new Verifica::AclBuilder, adds existing entries to it and yields it to the given block. Use this method to extend an existing ACL with additional entries

Examples:

base_acl = Verifica::Acl.build do |acl|
  acl.allow "superuser", [:read, :write, :delete]
end

extended_acl = base_acl.build do |acl|
  acl.allow "anonymous", [:read]
  acl.allow "authenticated", [:read, :comment]
end

Yields:

  • (builder)

Returns:

  • (Acl)

    new Access Control List created by builder



148
149
150
151
152
# File 'lib/verifica/acl.rb', line 148

def build
  builder = AclBuilder.new(to_a)
  yield builder
  builder.build
end

#denied_sids(action) ⇒ Array<String>

Note:

Checking denied SIDs isn’t enough to determine whether the action is allowed. You need to always check #allowed_sids as well.

Returns array of Security Identifiers denied for a given action or empty array if none.

Parameters:

  • action (Symbol, String)

    action to check

Returns:

  • (Array<String>)

    array of Security Identifiers denied for a given action or empty array if none



116
117
118
119
# File 'lib/verifica/acl.rb', line 116

def denied_sids(action)
  sids = @allow_deny_by_action.dig(action.to_sym, :denied_sids)
  sids.nil? ? EMPTY_ARRAY : sids.to_a
end

#empty?Boolean

Returns true if there are no entries in self.

Returns:

  • (Boolean)

    true if there are no entries in self



169
170
171
# File 'lib/verifica/acl.rb', line 169

def empty?
  @aces.empty?
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


189
190
191
192
# File 'lib/verifica/acl.rb', line 189

def eql?(other)
  self.class == other.class &&
    @aces == other.aces
end

#hashObject



194
195
196
# File 'lib/verifica/acl.rb', line 194

def hash
  [self.class, @aces].hash
end

#lengthInteger Also known as: size

Returns the count of entries in self.

Returns:

  • (Integer)

    the count of entries in self



176
177
178
# File 'lib/verifica/acl.rb', line 176

def length
  @aces.length
end

#to_aArray<Ace>

Returns a new array representing self.

Examples:

acl = Verifica::Acl.build { |acl| acl.allow "root", [:read, :write] }
acl.to_a.map(&:to_h)
# => [{:sid=>"root", :action=>:read, :allow=>true}, {:sid=>"root", :action=>:write, :allow=>true}]

Returns:

  • (Array<Ace>)

    a new array representing self



162
163
164
# File 'lib/verifica/acl.rb', line 162

def to_a
  @aces.to_a
end

#to_sObject



181
182
183
# File 'lib/verifica/acl.rb', line 181

def to_s
  @aces.map(&:to_h).to_s
end