class Authorization::DevelopmentSupport::ChangeSupporter
Ideas for improvement
-
Algorithm
-
Objective function:
-
affected user count,
-
as specific as possible (roles, privileges)
-
as little changes as necessary
-
-
Modify role, privilege hierarchy
-
Merge, split roles
-
Add privilege to existing rules
-
-
Features
-
Improve review facts: impact, affected users count
-
group similar candidates: only show abstract methods?
-
restructure GUI layout: more room for analyzing suggestions
-
changelog, previous tests, etc.
-
multiple permissions in tests
-
-
Evaluation of approaches with
Analyzer
algorithms -
Authorization
constraints
Algorithm
-
for each candidate
-
abstract actions: solving first failing test (remove privilege from role)
-
for each abstract action
-
specific actions: concrete steps (remove privilege from specific role)
-
for each specific action
-
next if reversal action of previous step
-
apply specific action on candidate
-
save as solution if no failing tests on changed_candidate
-
else: queue as candidate
-
-
-
-
equivalent states
NOTE:
-
user.clone needs to clone role_symbols
-
user.role_symbols needs to respond to <<
-
user.login is needed
Protected Class Methods
# File lib/declarative_authorization/development_support/change_supporter.rb, line 613 def self.relevant_roles (approach) (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
Public Instance Methods
Returns a list of possible approaches for changes to the current authorization rules that achieve a given goal. The goal is given as permission tests in the block. The instance method users
is available when the block is executed to refer to the then-current users, whose roles might have changed as one suggestion.
# File lib/declarative_authorization/development_support/change_supporter.rb, line 48 def find_approaches_for (options, &tests) @prohibited_actions = (options[:prohibited_actions] || []).to_set @approaches_by_actions = {} candidates = [] suggestions = [] approach_checker = ApproachChecker.new(self, tests) starting_candidate = Approach.new(@engine, options[:users], []) if starting_candidate.check(approach_checker) suggestions << starting_candidate else candidates << starting_candidate end checked_candidates = 0 while !candidates.empty? and checked_candidates < 200 checked_candidates += next_step(suggestions, candidates, approach_checker) end # remove subsets suggestions.sort! end
Returns an array of GroupedApproaches for the given array of approaches. Only groups directly adjacent approaches
# File lib/declarative_authorization/development_support/change_supporter.rb, line 75 def group_approaches (approaches) approaches.each_with_object([]) do |approach, grouped| if grouped.last and grouped.last.approach.similar_to(approach) grouped.last.similar_approaches << approach else grouped << GroupedApproach.new(approach) end end end
Protected Instance Methods
# File lib/declarative_authorization/development_support/change_supporter.rb, line 598 def add_to_approaches_by_action! (candidate) candidate.changes.each do |action| (@approaches_by_actions[action] ||= []) << candidate end end
# File lib/declarative_authorization/development_support/change_supporter.rb, line 566 def check_child_candidates! (approach_checker, viable_approaches, candidates, child_candidates) child_candidates.each do |child_candidate| if child_candidate.check(approach_checker) unless superset_of_existing?(child_candidate) remove_supersets!(viable_approaches, child_candidate) viable_approaches << child_candidate add_to_approaches_by_action!(child_candidate) end else candidates << child_candidate end child_candidate.freeze end end
# File lib/declarative_authorization/development_support/change_supporter.rb, line 550 def generate_child_candidates (candidate) child_candidates = [] abstract_actions = candidate.abstract_actions abstract_actions.each do |abstract_action| abstract_action.specific_actions(candidate).each do |specific_action| child_candidate = candidate.dup if !specific_action.resembles_any?(@prohibited_actions) and !child_candidate.reverse_of_previous?(specific_action) and child_candidate.apply(specific_action) child_candidates << child_candidate end end end child_candidates end
# File lib/declarative_authorization/development_support/change_supporter.rb, line 540 def next_step (viable_approaches, candidates, approach_checker) candidate = candidates.shift child_candidates = generate_child_candidates(candidate) check_child_candidates!(approach_checker, viable_approaches, candidates, child_candidates) candidates.sort! child_candidates.length end
# File lib/declarative_authorization/development_support/change_supporter.rb, line 610 def relevant_roles (approach) self.class.relevant_roles(approach) end
# File lib/declarative_authorization/development_support/change_supporter.rb, line 604 def remove_from_approaches_by_action! (candidate) candidate.changes.each do |action| (@approaches_by_actions[action] ||= []).delete(candidate) end end
# File lib/declarative_authorization/development_support/change_supporter.rb, line 587 def remove_supersets! (existing, candidate) candidate.changes.inject([]) do |memo, action| memo += (@approaches_by_actions[action] ||= []).select do |approach| candidate.subset?(approach) end end.uniq.each do |approach| existing.delete(approach) remove_from_approaches_by_action!(approach) end end
# File lib/declarative_authorization/development_support/change_supporter.rb, line 581 def superset_of_existing? (candidate) candidate.changes.any? do |action| (@approaches_by_actions[action] ||= []).any? {|approach| approach.subset?(candidate)} end end