class StrongAttributes::Permissions

@private

Storage format:
  Each attribute path is stored as an array of objects, usually strings in a Set. For indifferent access, 
  all symbols are converted to strings.

  Arguments can also be part of permissions control. They are simply additional elements in the attribute path array,
  and need not be strings or symbols. Permitting a string or symbol argument automatically permits both.

  When checking if paths with tainted strings/elements are permitted, only exact matches are allowed

Public Class Methods

new(permissions = nil, prefix_path = []) click to toggle source

Initialize, optionally with link to permitted paths, and the prefix to that (with copy on write semantics)

# File lib/strong_attributes/permissions.rb, line 19
def initialize(permissions = nil, prefix_path = [])
  unless permissions.nil?
    @permitted_paths = permissions.permitted_paths
    @permittable_paths = permissions.permittable_paths
    @prefix_path = permissions.prefix_path + canonicalize(prefix_path) # copy on write
  end
end

Public Instance Methods

complete?() click to toggle source

Checks whether everything is permitted. Considers :*, which permits all methods but not association methods to be complete. @return [Boolean]

# File lib/strong_attributes/permissions.rb, line 30
def complete?
  permitted? prefix_path + [:*]
end
permit(*attribute_paths) click to toggle source

Permits some attribute paths

@param [Array] prefix_path

path to prepend to each attribute path

@param [[Object, Array]*] *attribute_paths

# File lib/strong_attributes/permissions.rb, line 85
def permit *attribute_paths
  copy_on_write!
  attribute_paths = attribute_paths.map{|path|canonicalize(path).taint} # exact match
  reject_permitted(*attribute_paths).each do |attribute_path| # don't permit if already permitted
    permitted_paths << attribute_path # prefix_path = [] because of copy on write
    raw_permittable(attribute_path.slice(0...-1))
  end
  self
end
permit_all!() click to toggle source

Permits wildcard method, but not association methods. @return self

# File lib/strong_attributes/permissions.rb, line 36
def permit_all!
  copy_on_write!
  permitted_paths << [:*]
  self
end
permittable(*attribute_paths) click to toggle source
# File lib/strong_attributes/permissions.rb, line 95
def permittable *attribute_paths
  copy_on_write!
  attribute_paths.each { |attribute_path| raw_permittable(canonicalize(attribute_path)) }
  self
end
permittable?(attribute_path) click to toggle source
# File lib/strong_attributes/permissions.rb, line 53
def permittable? attribute_path
  attribute_path = canonicalize(attribute_path)
  permittable_paths.include?(prefix_path + attribute_path)
end
permitted?(attribute_path) click to toggle source

Checks if the attribute path is permitted. This is the case if any array prefix has been permitted. @param [Object, Array<Object>] prefix_path @param [Object, Array<Object>] attribute_path @return [Boolean]

# File lib/strong_attributes/permissions.rb, line 47
def permitted? attribute_path
  attribute_path = canonicalize(attribute_path)
  return true if permitted_paths.include? prefix_path + attribute_path # exact match
  !path_tainted?(attribute_path) and permitted_by_wildcard?(prefix_path + attribute_path)  # wildcard match only if not tainted
end
prefix_path() click to toggle source
# File lib/strong_attributes/permissions.rb, line 14
def prefix_path
  @prefix_path || []
end
reject_permitted(*attribute_paths) click to toggle source

Rejects the attribute paths which are permitted. Opposite of select_permitted. Returns the attribute paths which are not permitted.

@param [Array] prefix_path

namespace in which each of the given attribute paths are in

@param [[Object, Array<Object>]*] *attribute_paths

each attribute path is an object(string) or array

@return [Array<Object, Array<Object>>] array of attribute paths remaining

# File lib/strong_attributes/permissions.rb, line 76
def reject_permitted *attribute_paths
  attribute_paths.reject { |attribute_path| permitted?(attribute_path) }
end
select_permitted(*attribute_paths) click to toggle source

Selects the attribute paths which are permitted. @param [Array] prefix_path

namespace in which each of the given attribute paths are in

@param [[Object, Array<Object>]*] *attribute_paths

each attribute path is a symbol or array of symbols

@return [Array<Object, Array<Object>>] array of attribute paths permitted

# File lib/strong_attributes/permissions.rb, line 64
def select_permitted *attribute_paths
  attribute_paths.select { |attribute_path| permitted?(attribute_path) }
end

Protected Instance Methods

permittable_paths() click to toggle source

some subpaths may be permitted

# File lib/strong_attributes/permissions.rb, line 108
def permittable_paths
  @permittable_paths ||= Set.new
end
permitted_paths() click to toggle source
# File lib/strong_attributes/permissions.rb, line 103
def permitted_paths
  @permitted_paths ||= Set.new
end

Private Instance Methods

canonicalize(array) click to toggle source

Converts symbols to strings (except for wildcard symbol)

# File lib/strong_attributes/permissions.rb, line 160
def canonicalize array
  array = Array(array)
  canonical_array = array.map{|e| (e.is_a?(Array) ? e.first : e).instance_eval{ self.is_a?(Symbol) ? e.to_s : e } }
  canonical_array[-1] = array.last if array.last == :*
  canonical_array.taint if array.tainted?
  canonical_array
end
copy_on_write!() click to toggle source

Make a copy if this still references something else, since we are planning on writing soon

# File lib/strong_attributes/permissions.rb, line 127
def copy_on_write!
  if prefix_path == []
    @permitted_paths = permitted_paths.dup
    @permittable_paths = permittable_paths.dup
  elsif reference?
    @permitted_paths = dup_paths permitted_paths, prefix_path
    @permittable_paths = dup_paths permittable_paths, prefix_path
  end
  @prefix_path = nil
end
dup_paths(old_set, prefix_path) click to toggle source
# File lib/strong_attributes/permissions.rb, line 138
def dup_paths old_set, prefix_path
  new_set = Set.new
  old_set.each do |path|
    new_set << path[prefix_path.size...path.size] if path[0...prefix_path.size] == prefix_path
  end
  new_set
end
path_tainted?(attribute_path) click to toggle source
# File lib/strong_attributes/permissions.rb, line 146
def path_tainted? attribute_path
  attribute_path.tainted? or attribute_path.any? { |element| element.tainted? }
end
permitted_by_wildcard?(path) click to toggle source

Caution: Will mutate path

# File lib/strong_attributes/permissions.rb, line 151
def permitted_by_wildcard? path
  unless path.empty?
    path[-1] = :*
    return true if permitted_paths.include? path
  end
  false
end
raw_permittable(attribute_path) click to toggle source

will mutate attribute_path

# File lib/strong_attributes/permissions.rb, line 114
def raw_permittable attribute_path
  until attribute_path.empty? || permittable_paths.include?(attribute_path) do
    permittable_paths << attribute_path.dup
    attribute_path.pop
  end
end
reference?() click to toggle source

Is this still referencing another objects permissions?

# File lib/strong_attributes/permissions.rb, line 122
def reference?
  !@prefix_path.nil?
end