class Authorization::DevelopmentSupport::ChangeAnalyzer

Ideas for improvement

NOTE:

Public Instance Methods

find_approaches_for(change_action, type, options, &tests) click to toggle source
# File lib/declarative_authorization/development_support/change_analyzer.rb, line 30
def find_approaches_for (change_action, type, options, &tests)
  raise ArgumentError, "Missing options" if !options[:on] or !options[:to]

  # * strategy for removing: [remove privilege, add privilege to different role]
  @seen_states = Set.new
  # * heuristic: change of failed tests;  small number of policy items
  strategy = case [change_action, type]
             when [:remove, :permission]
               [:remove_role_from_user, :remove_privilege, :add_privilege,
                 :add_role, :assign_role_to_user]
             when [:add, :permission]
               [:add_role, :add_privilege, :assign_role_to_user]
             else
               raise ArgumentError, "Unknown change action/type: #{[change_action, type].inspect}"
             end

  candidates = []
  viable_approaches = []
  approach_checker = ApproachChecker.new(self, tests)

  starting_candidate = Approach.new(@engine, options[:users], [])
  if starting_candidate.check(approach_checker)
    viable_approaches << starting_candidate
  else
    candidates << starting_candidate
  end

  step_count = 0
  while !candidates.empty? and step_count < 100
    next_step(viable_approaches, candidates, approach_checker, options[:to], 
        options[:on], strategy)
    step_count += 1
  end

  # remove subsets

  viable_approaches.sort!
end

Protected Instance Methods

next_step(viable_approaches, candidates, approach_checker, privilege, context, strategy) click to toggle source
# File lib/declarative_authorization/development_support/change_analyzer.rb, line 164
def next_step (viable_approaches, candidates, approach_checker,
      privilege, context, strategy)
  candidate = candidates.shift
  next_in_strategy = strategy[candidate.steps.length % strategy.length]

  #if @seen_states.include?([candidate.state_hash, next_in_strategy])
  #  puts "SKIPPING #{next_in_strategy}; #{candidate.inspect}"
  #end
  return if @seen_states.include?([candidate.state_hash, next_in_strategy])
  @seen_states << [candidate.state_hash, next_in_strategy]
  candidate.steps << [next_in_strategy]
  candidates << candidate

  new_approaches = []

  #puts "#{next_in_strategy} on #{candidate.inspect}"
  case next_in_strategy
  when :add_role
    # ensure non-existent name
    approach = candidate.clone_for_step(:add_role, :new_role_for_change_analyzer)
    if AnalyzerEngine.apply_change(approach.engine, approach.changes.last)
      #AnalyzerEngine.apply_change(approach.engine, [:add_privilege, privilege, context, :new_role_for_change_analyzer])
      new_approaches << approach
    end
  when :assign_role_to_user
    candidate.users.each do |user|
      relevant_roles(candidate).each do |role|
        next if user.role_symbols.include?(role.to_sym)
        approach = candidate.clone_for_step(:assign_role_to_user, role, user)
        # beware of shallow copies!
        cloned_user = user.clone
        approach.users[approach.users.index(user)] = cloned_user
        # possible on real user objects?
        cloned_user.role_symbols << role.to_sym
        new_approaches << approach
      end
    end
  when :remove_role_from_user
    candidate.users.each do |user|
      user.role_symbols.each do |role_sym|
        approach = candidate.clone_for_step(:remove_role_from_user, role_sym, user)
        # beware of shallow copies!
        cloned_user = user.clone
        approach.users[approach.users.index(user)] = cloned_user
        # possible on real user objects?
        cloned_user.role_symbols.delete(role_sym)
        new_approaches << approach
      end
    end
  when :add_privilege
    relevant_roles(candidate).each do |role|
      approach = candidate.clone_for_step(:add_privilege, privilege, context, role)
      AnalyzerEngine.apply_change(approach.engine, approach.changes.last)
      new_approaches << approach
    end
  when :remove_privilege
    relevant_roles(candidate).each do |role|
      approach = candidate.clone_for_step(:remove_privilege, privilege, context, role)
      if AnalyzerEngine.apply_change(approach.engine, approach.changes.last)
        new_approaches << approach
      end
    end
  else
    raise "Unknown next strategy step #{next_in_strategy}"
  end

  new_approaches.each do |new_approach|
    if new_approach.check(approach_checker)
      unless viable_approaches.any? {|viable_approach| viable_approach.subset?(new_approach) }
        #puts "New: #{new_approach.changes.inspect}\n  #{viable_approaches.map(&:changes).inspect}"
        viable_approaches.delete_if {|viable_approach| new_approach.subset?(viable_approach)}
        viable_approaches << new_approach unless viable_approaches.find {|v_a| v_a.state_hash == new_approach.state_hash}
      end
    else
      candidates << new_approach
    end
  end

  candidates.sort!
end
relevant_roles(approach) click to toggle source
# File lib/declarative_authorization/development_support/change_analyzer.rb, line 245
def relevant_roles (approach)
  #return AnalyzerEngine.roles(approach.engine)
  (AnalyzerEngine.relevant_roles(approach.engine, approach.users) +
      (approach.engine.roles.include?(:new_role_for_change_analyzer) ?
         [AnalyzerEngine::Role.for_sym(:new_role_for_change_analyzer, approach.engine)] : [])).uniq
end