Class Authorization::DevelopmentSupport::ChangeAnalyzer
In: lib/declarative_authorization/development_support/change_analyzer.rb
Parent: AbstractAnalyzer

Ideas for improvement

  • Algorithm
    • Plan by tackling each condition separately
      • e.g. two users have a permission through the same role, one should lose that
    • Consider privilege hierarchy
    • Consider merging, splitting roles, role hierarchies
    • Add privilege to existing rules
  • Features
    • Show consequences from changes: which users are affected, show users in graph
    • restructure GUI layout: more room for analyzing suggestions
  • AI: planning: ADL-like, actions with preconditions and effects
  • Removing need of intention
  • Evaluation of approaches with Analyzer algorithms
  • Consider constraints

NOTE:

  • user.clone needs to clone role_symbols
  • user.role_symbols needs to respond to <<
  • user.login is needed

Methods

Classes and Modules

Class Authorization::DevelopmentSupport::ChangeAnalyzer::Approach
Class Authorization::DevelopmentSupport::ChangeAnalyzer::ApproachChecker
Class Authorization::DevelopmentSupport::ChangeAnalyzer::Step

Public Instance methods

[Source]

    # File lib/declarative_authorization/development_support/change_analyzer.rb, line 30
30:       def find_approaches_for (change_action, type, options, &tests)
31:         raise ArgumentError, "Missing options" if !options[:on] or !options[:to]
32: 
33:         # * strategy for removing: [remove privilege, add privilege to different role]
34:         @seen_states = Set.new
35:         # * heuristic: change of failed tests;  small number of policy items
36:         strategy = case [change_action, type]
37:                    when [:remove, :permission]
38:                      [:remove_role_from_user, :remove_privilege, :add_privilege,
39:                        :add_role, :assign_role_to_user]
40:                    when [:add, :permission]
41:                      [:add_role, :add_privilege, :assign_role_to_user]
42:                    else
43:                      raise ArgumentError, "Unknown change action/type: #{[change_action, type].inspect}"
44:                    end
45: 
46:         candidates = []
47:         viable_approaches = []
48:         approach_checker = ApproachChecker.new(self, tests)
49: 
50:         starting_candidate = Approach.new(@engine, options[:users], [])
51:         if starting_candidate.check(approach_checker)
52:           viable_approaches << starting_candidate
53:         else
54:           candidates << starting_candidate
55:         end
56: 
57:         step_count = 0
58:         while !candidates.empty? and step_count < 100
59:           next_step(viable_approaches, candidates, approach_checker, options[:to], 
60:               options[:on], strategy)
61:           step_count += 1
62:         end
63: 
64:         # remove subsets
65: 
66:         viable_approaches.sort!
67:       end

Protected Instance methods

[Source]

     # File lib/declarative_authorization/development_support/change_analyzer.rb, line 164
164:       def next_step (viable_approaches, candidates, approach_checker,
165:             privilege, context, strategy)
166:         candidate = candidates.shift
167:         next_in_strategy = strategy[candidate.steps.length % strategy.length]
168: 
169:         #if @seen_states.include?([candidate.state_hash, next_in_strategy])
170:         #  puts "SKIPPING #{next_in_strategy}; #{candidate.inspect}"
171:         #end
172:         return if @seen_states.include?([candidate.state_hash, next_in_strategy])
173:         @seen_states << [candidate.state_hash, next_in_strategy]
174:         candidate.steps << [next_in_strategy]
175:         candidates << candidate
176: 
177:         new_approaches = []
178: 
179:         #puts "#{next_in_strategy} on #{candidate.inspect}"
180:         case next_in_strategy
181:         when :add_role
182:           # ensure non-existent name
183:           approach = candidate.clone_for_step(:add_role, :new_role_for_change_analyzer)
184:           if AnalyzerEngine.apply_change(approach.engine, approach.changes.last)
185:             #AnalyzerEngine.apply_change(approach.engine, [:add_privilege, privilege, context, :new_role_for_change_analyzer])
186:             new_approaches << approach
187:           end
188:         when :assign_role_to_user
189:           candidate.users.each do |user|
190:             relevant_roles(candidate).each do |role|
191:               next if user.role_symbols.include?(role.to_sym)
192:               approach = candidate.clone_for_step(:assign_role_to_user, role, user)
193:               # beware of shallow copies!
194:               cloned_user = user.clone
195:               approach.users[approach.users.index(user)] = cloned_user
196:               # possible on real user objects?
197:               cloned_user.role_symbols << role.to_sym
198:               new_approaches << approach
199:             end
200:           end
201:         when :remove_role_from_user
202:           candidate.users.each do |user|
203:             user.role_symbols.each do |role_sym|
204:               approach = candidate.clone_for_step(:remove_role_from_user, role_sym, user)
205:               # beware of shallow copies!
206:               cloned_user = user.clone
207:               approach.users[approach.users.index(user)] = cloned_user
208:               # possible on real user objects?
209:               cloned_user.role_symbols.delete(role_sym)
210:               new_approaches << approach
211:             end
212:           end
213:         when :add_privilege
214:           relevant_roles(candidate).each do |role|
215:             approach = candidate.clone_for_step(:add_privilege, privilege, context, role)
216:             AnalyzerEngine.apply_change(approach.engine, approach.changes.last)
217:             new_approaches << approach
218:           end
219:         when :remove_privilege
220:           relevant_roles(candidate).each do |role|
221:             approach = candidate.clone_for_step(:remove_privilege, privilege, context, role)
222:             if AnalyzerEngine.apply_change(approach.engine, approach.changes.last)
223:               new_approaches << approach
224:             end
225:           end
226:         else
227:           raise "Unknown next strategy step #{next_in_strategy}"
228:         end
229: 
230:         new_approaches.each do |new_approach|
231:           if new_approach.check(approach_checker)
232:             unless viable_approaches.any? {|viable_approach| viable_approach.subset?(new_approach) }
233:               #puts "New: #{new_approach.changes.inspect}\n  #{viable_approaches.map(&:changes).inspect}"
234:               viable_approaches.delete_if {|viable_approach| new_approach.subset?(viable_approach)}
235:               viable_approaches << new_approach unless viable_approaches.find {|v_a| v_a.state_hash == new_approach.state_hash}
236:             end
237:           else
238:             candidates << new_approach
239:           end
240:         end
241: 
242:         candidates.sort!
243:       end

[Source]

     # File lib/declarative_authorization/development_support/change_analyzer.rb, line 245
245:       def relevant_roles (approach)
246:         #return AnalyzerEngine.roles(approach.engine)
247:         (AnalyzerEngine.relevant_roles(approach.engine, approach.users) +
248:             (approach.engine.roles.include?(:new_role_for_change_analyzer) ?
249:                [AnalyzerEngine::Role.for_sym(:new_role_for_change_analyzer, approach.engine)] : [])).uniq
250:       end

[Validate]