stffn-declarative_authorization 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +2 -0
 - data/Rakefile +8 -0
 - data/app/controllers/authorization_rules_controller.rb +103 -0
 - data/app/controllers/authorization_usages_controller.rb +19 -0
 - data/app/helpers/authorization_rules_helper.rb +84 -0
 - data/app/views/authorization_rules/graph.dot.erb +49 -0
 - data/app/views/authorization_rules/graph.html.erb +39 -0
 - data/app/views/authorization_rules/index.html.erb +15 -0
 - data/app/views/authorization_usages/index.html.erb +45 -0
 - data/config/routes.rb +6 -0
 - data/lib/authorization.rb +514 -0
 - data/lib/helper.rb +51 -0
 - data/lib/in_controller.rb +311 -0
 - data/lib/in_model.rb +130 -0
 - data/lib/maintenance.rb +174 -0
 - data/lib/obligation_scope.rb +281 -0
 - data/lib/rails_legacy.rb +14 -0
 - data/lib/reader.rb +391 -0
 - data/test/authorization_test.rb +576 -0
 - data/test/controller_test.rb +361 -0
 - data/test/dsl_reader_test.rb +157 -0
 - data/test/helper_test.rb +96 -0
 - data/test/maintenance_test.rb +15 -0
 - data/test/model_test.rb +794 -0
 - data/test/schema.sql +32 -0
 - data/test/test_helper.rb +99 -0
 - metadata +26 -2
 
    
        data/CHANGELOG
    CHANGED
    
    | 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            * Added handling of Authorization::AuthorizationInController::ClassMethods.filter_access_to parameters that are of the form [:show, :update] instead of just :show, :update. [jeremyf]
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            * Added a authorization rules browser.  See README for more information [sb]
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
            * Added Model.using_access_control? to check if a model has model security activated [sb]
         
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -33,3 +33,11 @@ desc "clone the garlic repo (for running ci tasks)" 
     | 
|
| 
       33 
33 
     | 
    
         
             
            task :get_garlic do
         
     | 
| 
       34 
34 
     | 
    
         
             
              sh "git clone git://github.com/ianwhite/garlic.git garlic"
         
     | 
| 
       35 
35 
     | 
    
         
             
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            desc "Expand filelist in src gemspec"
         
     | 
| 
      
 38 
     | 
    
         
            +
            task :build_gemspec do
         
     | 
| 
      
 39 
     | 
    
         
            +
              gemspec_data = File.read("declarative_authorization.gemspec.src")
         
     | 
| 
      
 40 
     | 
    
         
            +
              gemspec_data.gsub!(/\.files = (.*)/) {|m| ".files = #{eval($1).inspect}"}
         
     | 
| 
      
 41 
     | 
    
         
            +
              File.open("declarative_authorization.gemspec", "w") {|f| f.write(gemspec_data)}
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,103 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            if Authorization::activate_authorization_rules_browser?
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            begin
         
     | 
| 
      
 4 
     | 
    
         
            +
              # for nice auth_rules output:
         
     | 
| 
      
 5 
     | 
    
         
            +
              require "parse_tree"
         
     | 
| 
      
 6 
     | 
    
         
            +
              require "parse_tree_extensions"
         
     | 
| 
      
 7 
     | 
    
         
            +
              require "ruby2ruby"
         
     | 
| 
      
 8 
     | 
    
         
            +
            rescue LoadError; end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            class AuthorizationRulesController < ApplicationController
         
     | 
| 
      
 11 
     | 
    
         
            +
              filter_access_to :all, :require => :read
         
     | 
| 
      
 12 
     | 
    
         
            +
              def index
         
     | 
| 
      
 13 
     | 
    
         
            +
                respond_to do |format|
         
     | 
| 
      
 14 
     | 
    
         
            +
                  format.html do
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @auth_rules_script = File.read("#{RAILS_ROOT}/config/authorization_rules.rb")
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              def graph
         
     | 
| 
      
 21 
     | 
    
         
            +
                if params[:format] == "svg"
         
     | 
| 
      
 22 
     | 
    
         
            +
                  render :text => dot_to_svg(auth_to_dot(graph_options)),
         
     | 
| 
      
 23 
     | 
    
         
            +
                      :content_type => "image/svg+xml"
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              private
         
     | 
| 
      
 28 
     | 
    
         
            +
              def auth_to_dot (options = {})
         
     | 
| 
      
 29 
     | 
    
         
            +
                options = {
         
     | 
| 
      
 30 
     | 
    
         
            +
                  :effective_role_privs => true,
         
     | 
| 
      
 31 
     | 
    
         
            +
                  :privilege_hierarchy => false,
         
     | 
| 
      
 32 
     | 
    
         
            +
                  :filter_roles => nil,
         
     | 
| 
      
 33 
     | 
    
         
            +
                  :filter_contexts => nil,
         
     | 
| 
      
 34 
     | 
    
         
            +
                  :highlight_privilege => nil
         
     | 
| 
      
 35 
     | 
    
         
            +
                }.merge(options)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                @highlight_privilege = options[:highlight_privilege]
         
     | 
| 
      
 38 
     | 
    
         
            +
                @roles = authorization_engine.roles
         
     | 
| 
      
 39 
     | 
    
         
            +
                @roles = @roles.select {|r| r == options[:filter_roles] } if options[:filter_roles]
         
     | 
| 
      
 40 
     | 
    
         
            +
                @role_hierarchy = authorization_engine.role_hierarchy
         
     | 
| 
      
 41 
     | 
    
         
            +
                @privilege_hierarchy = authorization_engine.privilege_hierarchy
         
     | 
| 
      
 42 
     | 
    
         
            +
                
         
     | 
| 
      
 43 
     | 
    
         
            +
                @contexts = authorization_engine.auth_rules.
         
     | 
| 
      
 44 
     | 
    
         
            +
                                collect {|ar| ar.contexts.to_a}.flatten.uniq
         
     | 
| 
      
 45 
     | 
    
         
            +
                @contexts = @contexts.select {|c| c == options[:filter_contexts] } if options[:filter_contexts]
         
     | 
| 
      
 46 
     | 
    
         
            +
                @context_privs = {}
         
     | 
| 
      
 47 
     | 
    
         
            +
                @role_privs = {}
         
     | 
| 
      
 48 
     | 
    
         
            +
                authorization_engine.auth_rules.each do |auth_rule|
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @role_privs[auth_rule.role] ||= []
         
     | 
| 
      
 50 
     | 
    
         
            +
                  auth_rule.contexts.
         
     | 
| 
      
 51 
     | 
    
         
            +
                        select {|c| options[:filter_contexts].nil? or c == options[:filter_contexts]}.
         
     | 
| 
      
 52 
     | 
    
         
            +
                        each do |context|
         
     | 
| 
      
 53 
     | 
    
         
            +
                    @context_privs[context] ||= []
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @context_privs[context] += auth_rule.privileges.to_a
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @context_privs[context].uniq!
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @role_privs[auth_rule.role] += auth_rule.privileges.collect {|p| [context, p, auth_rule.attributes.empty?, auth_rule.to_long_s]}
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
                
         
     | 
| 
      
 60 
     | 
    
         
            +
                if options[:effective_role_privs]
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @roles.each do |role|
         
     | 
| 
      
 62 
     | 
    
         
            +
                    @role_privs[role] ||= []
         
     | 
| 
      
 63 
     | 
    
         
            +
                    (@role_hierarchy[role] || []).each do |lower_role|
         
     | 
| 
      
 64 
     | 
    
         
            +
                      @role_privs[role].concat(@role_privs[lower_role]).uniq!
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
                
         
     | 
| 
      
 69 
     | 
    
         
            +
                if options[:privilege_hierarchy]
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @context_privs.each do |context, privs|
         
     | 
| 
      
 71 
     | 
    
         
            +
                    privs.each do |priv|
         
     | 
| 
      
 72 
     | 
    
         
            +
                      context_lower_privs = (@privilege_hierarchy[priv] || []).
         
     | 
| 
      
 73 
     | 
    
         
            +
                                              select {|p,c| c.nil? or c == context}.
         
     | 
| 
      
 74 
     | 
    
         
            +
                                              collect {|p,c| p}
         
     | 
| 
      
 75 
     | 
    
         
            +
                      privs.concat(context_lower_privs).uniq!
         
     | 
| 
      
 76 
     | 
    
         
            +
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
                
         
     | 
| 
      
 80 
     | 
    
         
            +
                render_to_string :template => 'authorization_rules/graph.dot.erb', :layout => false
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
              
         
     | 
| 
      
 83 
     | 
    
         
            +
              def dot_to_svg (dot_data)
         
     | 
| 
      
 84 
     | 
    
         
            +
                gv = IO.popen("/usr/bin/dot -q -Tsvg", "w+")
         
     | 
| 
      
 85 
     | 
    
         
            +
                gv.puts dot_data
         
     | 
| 
      
 86 
     | 
    
         
            +
                gv.close_write
         
     | 
| 
      
 87 
     | 
    
         
            +
                gv.read
         
     | 
| 
      
 88 
     | 
    
         
            +
              rescue IOError, Errno::EPIPE => e
         
     | 
| 
      
 89 
     | 
    
         
            +
                raise Exception, "Error in call to graphviz: #{e}"
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
              
         
     | 
| 
      
 92 
     | 
    
         
            +
              def graph_options
         
     | 
| 
      
 93 
     | 
    
         
            +
                {
         
     | 
| 
      
 94 
     | 
    
         
            +
                  :effective_role_privs => !params[:effective_role_privs].blank?,
         
     | 
| 
      
 95 
     | 
    
         
            +
                  :privilege_hierarchy => !params[:privilege_hierarchy].blank?,
         
     | 
| 
      
 96 
     | 
    
         
            +
                  :filter_roles => params[:filter_roles].blank? ? nil : params[:filter_roles].to_sym,
         
     | 
| 
      
 97 
     | 
    
         
            +
                  :filter_contexts => params[:filter_contexts].blank? ? nil : params[:filter_contexts].to_sym,
         
     | 
| 
      
 98 
     | 
    
         
            +
                  :highlight_privilege => params[:highlight_privilege].blank? ? nil : params[:highlight_privilege].to_sym
         
     | 
| 
      
 99 
     | 
    
         
            +
                }
         
     | 
| 
      
 100 
     | 
    
         
            +
              end
         
     | 
| 
      
 101 
     | 
    
         
            +
            end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
            end # activate_authorization_rules_browser?
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            if Authorization::activate_authorization_rules_browser?
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), %w{.. .. lib maintenance})
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class AuthorizationUsagesController < ApplicationController
         
     | 
| 
      
 6 
     | 
    
         
            +
              helper :authorization_rules
         
     | 
| 
      
 7 
     | 
    
         
            +
              filter_access_to :all, :require => :read
         
     | 
| 
      
 8 
     | 
    
         
            +
              # TODO set context?
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def index
         
     | 
| 
      
 11 
     | 
    
         
            +
                respond_to do |format|
         
     | 
| 
      
 12 
     | 
    
         
            +
                  format.html do
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @auth_usages_by_controller = Authorization::Maintenance::Usage.usages_by_controller
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            end # activate_authorization_rules_browser?
         
     | 
| 
         @@ -0,0 +1,84 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module AuthorizationRulesHelper
         
     | 
| 
      
 2 
     | 
    
         
            +
              def syntax_highlight (rules)
         
     | 
| 
      
 3 
     | 
    
         
            +
                regexps = {
         
     | 
| 
      
 4 
     | 
    
         
            +
                  :constant => [/(:)(\w+)/], 
         
     | 
| 
      
 5 
     | 
    
         
            +
                  :proc => ['role', 'authorization', 'privileges'],
         
     | 
| 
      
 6 
     | 
    
         
            +
                  :statement => ['has_permission_on', 'if_attribute', 'includes', 'privilege', 'to'],
         
     | 
| 
      
 7 
     | 
    
         
            +
                  :operator => ['is', 'contains'],
         
     | 
| 
      
 8 
     | 
    
         
            +
                  :special => ['user', 'true', 'false'],
         
     | 
| 
      
 9 
     | 
    
         
            +
                  :preproc => ['do', 'end', /()(=>)/, /()(\{)/, /()(\})/, /()(\[)/, /()(\])/],
         
     | 
| 
      
 10 
     | 
    
         
            +
                  :comment => [/()(#.*$)/]#,
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #:privilege => [:read],
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #:context => [:conferences]
         
     | 
| 
      
 13 
     | 
    
         
            +
                }
         
     | 
| 
      
 14 
     | 
    
         
            +
                regexps.each do |name, res|
         
     | 
| 
      
 15 
     | 
    
         
            +
                  res.each do |re|
         
     | 
| 
      
 16 
     | 
    
         
            +
                    rules.gsub!(
         
     | 
| 
      
 17 
     | 
    
         
            +
                      re.is_a?(String) ? Regexp.new("(^|[^:])\\b(#{Regexp.escape(re)})\\b") :
         
     | 
| 
      
 18 
     | 
    
         
            +
                         (re.is_a?(Symbol) ? Regexp.new("()(:#{Regexp.escape(re.to_s)})\\b") : re), 
         
     | 
| 
      
 19 
     | 
    
         
            +
                      "\\1<span class=\"#{name}\">\\2</span>")
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
                rules
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
              
         
     | 
| 
      
 25 
     | 
    
         
            +
              def link_to_graph (title, options = {})
         
     | 
| 
      
 26 
     | 
    
         
            +
                type = options[:type] || ''
         
     | 
| 
      
 27 
     | 
    
         
            +
                link_to_function title, "$$('object')[0].data = '#{url_for :action => 'index', :format => 'svg', :type => type}'"
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
              
         
     | 
| 
      
 30 
     | 
    
         
            +
              def navigation
         
     | 
| 
      
 31 
     | 
    
         
            +
                link_to("Rules", authorization_rules_path) << ' | ' <<
         
     | 
| 
      
 32 
     | 
    
         
            +
                link_to("Graphical view", graph_authorization_rules_path) << ' | ' <<
         
     | 
| 
      
 33 
     | 
    
         
            +
                link_to("Usages", authorization_usages_path) #<< ' | ' <<
         
     | 
| 
      
 34 
     | 
    
         
            +
              #  'Edit | ' <<
         
     | 
| 
      
 35 
     | 
    
         
            +
              #  link_to("XACML export", :action => 'index', :format => 'xacml')
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
              
         
     | 
| 
      
 38 
     | 
    
         
            +
              def role_color (role, fill = false)
         
     | 
| 
      
 39 
     | 
    
         
            +
                fill_colors = %w{#ffdddd #ddffdd #ddddff #ffffdd #ffddff #ddffff}
         
     | 
| 
      
 40 
     | 
    
         
            +
                colors = %w{#dd0000 #00dd00 #0000dd #dddd00 #dd00dd #00dddd}
         
     | 
| 
      
 41 
     | 
    
         
            +
                @@role_colors ||= {}
         
     | 
| 
      
 42 
     | 
    
         
            +
                @@role_colors[role] ||= begin
         
     | 
| 
      
 43 
     | 
    
         
            +
                  idx = @@role_colors.length % colors.length
         
     | 
| 
      
 44 
     | 
    
         
            +
                  [colors[idx], fill_colors[idx]]
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
                @@role_colors[role][fill ? 1 : 0]
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
              
         
     | 
| 
      
 49 
     | 
    
         
            +
              def role_fill_color (role)
         
     | 
| 
      
 50 
     | 
    
         
            +
                role_color(role, true)
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              def auth_usage_info_classes (auth_info)
         
     | 
| 
      
 54 
     | 
    
         
            +
                classes = []
         
     | 
| 
      
 55 
     | 
    
         
            +
                if auth_info[:controller_permissions]
         
     | 
| 
      
 56 
     | 
    
         
            +
                  if auth_info[:controller_permissions][0]
         
     | 
| 
      
 57 
     | 
    
         
            +
                    classes << "catch-all" if auth_info[:controller_permissions][0].actions.include?(:all)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    classes << "default-privilege" unless auth_info[:controller_permissions][0].privilege
         
     | 
| 
      
 59 
     | 
    
         
            +
                    classes << "default-context" unless auth_info[:controller_permissions][0].context
         
     | 
| 
      
 60 
     | 
    
         
            +
                    classes << "no-attribute-check" unless auth_info[:controller_permissions][0].attribute_check
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                else
         
     | 
| 
      
 63 
     | 
    
         
            +
                  classes << "unprotected"
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
                classes * " "
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              def auth_usage_info_title (auth_info)
         
     | 
| 
      
 69 
     | 
    
         
            +
                titles = []
         
     | 
| 
      
 70 
     | 
    
         
            +
                if auth_usage_info_classes(auth_info) =~ /unprotected/
         
     | 
| 
      
 71 
     | 
    
         
            +
                  titles << "No filter_access_to call protects this action"
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
                if auth_usage_info_classes(auth_info) =~ /no-attribute-check/
         
     | 
| 
      
 74 
     | 
    
         
            +
                  titles << "Action is not protected with attribute check"
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
                if auth_usage_info_classes(auth_info) =~ /default-privilege/
         
     | 
| 
      
 77 
     | 
    
         
            +
                  titles << "Privilege set automatically from action name by :all rule"
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
                if auth_usage_info_classes(auth_info) =~ /default-context/
         
     | 
| 
      
 80 
     | 
    
         
            +
                  titles << "Context set automatically from controller name by filter_access_to call without :context option"
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
                titles * ". "
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
            digraph rules {
         
     | 
| 
      
 3 
     | 
    
         
            +
              compound = true
         
     | 
| 
      
 4 
     | 
    
         
            +
              edge [arrowhead=open]
         
     | 
| 
      
 5 
     | 
    
         
            +
              node [shape=box,fontname="sans-serif",fontsize="16"]
         
     | 
| 
      
 6 
     | 
    
         
            +
              fontname="sans-serif";fontsize="16"
         
     | 
| 
      
 7 
     | 
    
         
            +
              ranksep = "0.3"
         
     | 
| 
      
 8 
     | 
    
         
            +
              //concentrate = true
         
     | 
| 
      
 9 
     | 
    
         
            +
              rankdir = TB
         
     | 
| 
      
 10 
     | 
    
         
            +
              {
         
     | 
| 
      
 11 
     | 
    
         
            +
                node [shape=ellipse,style=filled]
         
     | 
| 
      
 12 
     | 
    
         
            +
                //rank = source
         
     | 
| 
      
 13 
     | 
    
         
            +
                <% @roles.each do |role| %>
         
     | 
| 
      
 14 
     | 
    
         
            +
                "<%= role.inspect %>" [fillcolor="<%= role_fill_color(role) %>"]
         
     | 
| 
      
 15 
     | 
    
         
            +
                // ,URL="javascript:set_filter({roles: '<%= role %>'})"
         
     | 
| 
      
 16 
     | 
    
         
            +
                <% end %>
         
     | 
| 
      
 17 
     | 
    
         
            +
                <% @roles.each do |role| %>
         
     | 
| 
      
 18 
     | 
    
         
            +
                    <% (@role_hierarchy[role] || []).each do |lower_role| %>
         
     | 
| 
      
 19 
     | 
    
         
            +
                        "<%= role.inspect %>" -> "<%= lower_role.inspect %>" [constraint=false,arrowhead=empty]
         
     | 
| 
      
 20 
     | 
    
         
            +
                    <% end %>
         
     | 
| 
      
 21 
     | 
    
         
            +
                <% end %>
         
     | 
| 
      
 22 
     | 
    
         
            +
              }
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              <% @contexts.each do |context| %>
         
     | 
| 
      
 25 
     | 
    
         
            +
                subgraph cluster_<%= context %>  {
         
     | 
| 
      
 26 
     | 
    
         
            +
                  label = "<%= context.inspect %>"
         
     | 
| 
      
 27 
     | 
    
         
            +
                  style=filled; fillcolor="#eeeeee"
         
     | 
| 
      
 28 
     | 
    
         
            +
                  node[fillcolor=white,style=filled]
         
     | 
| 
      
 29 
     | 
    
         
            +
                  <% (@context_privs[context] || []).each do |priv| %>
         
     | 
| 
      
 30 
     | 
    
         
            +
                  <%= priv %>_<%= context %> [label="<%= priv.inspect %>"<%= ',fontcolor="#ff0000"' if @highlight_privilege == priv %>]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  <% end %>
         
     | 
| 
      
 32 
     | 
    
         
            +
                  <% (@context_privs[context] || []).each do |priv| %>
         
     | 
| 
      
 33 
     | 
    
         
            +
                    <% (@privilege_hierarchy[priv] || []).
         
     | 
| 
      
 34 
     | 
    
         
            +
                            select {|p,c| (c.nil? or c == context) and @context_privs[context].include?(p)}.
         
     | 
| 
      
 35 
     | 
    
         
            +
                            each do |lower_priv, c| %>
         
     | 
| 
      
 36 
     | 
    
         
            +
                  <%= priv %>_<%= context %> -> <%= lower_priv %>_<%= context %> [arrowhead=empty]
         
     | 
| 
      
 37 
     | 
    
         
            +
                    <% end %>
         
     | 
| 
      
 38 
     | 
    
         
            +
                  <% end %>
         
     | 
| 
      
 39 
     | 
    
         
            +
                  //read_conferences -> update_conferences [style=invis]
         
     | 
| 
      
 40 
     | 
    
         
            +
                  //create_conferences -> delete_conferences [style=invis]
         
     | 
| 
      
 41 
     | 
    
         
            +
                }
         
     | 
| 
      
 42 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              <% @roles.each do |role| %>
         
     | 
| 
      
 45 
     | 
    
         
            +
                <% (@role_privs[role] || []).each do |context, privilege, unconditionally, attribute_string| %>
         
     | 
| 
      
 46 
     | 
    
         
            +
              "<%= role.inspect %>" -> <%=  privilege %>_<%= context %> [color="<%= role_color(role) %>", minlen=3<%= ", arrowhead=opendot, URL=\"javascript:\", edgetooltip=\"#{attribute_string.gsub('"','')}\"" unless unconditionally %>]
         
     | 
| 
      
 47 
     | 
    
         
            +
                <% end %>
         
     | 
| 
      
 48 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 49 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <h1>Authorization Rules Graph</h1>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <p>Currently active rules in this application.</p>
         
     | 
| 
      
 3 
     | 
    
         
            +
            <p><%= navigation %></p>
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            <% javascript_tag do %>
         
     | 
| 
      
 6 
     | 
    
         
            +
                function update_graph (form) {
         
     | 
| 
      
 7 
     | 
    
         
            +
                    base_url = "<%= url_for :format => 'svg' %>";
         
     | 
| 
      
 8 
     | 
    
         
            +
                    $('graph').data = base_url + '?' + form.serialize();
         
     | 
| 
      
 9 
     | 
    
         
            +
                }
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                function set_filter (filter) {
         
     | 
| 
      
 12 
     | 
    
         
            +
                    for (f in filter) {
         
     | 
| 
      
 13 
     | 
    
         
            +
                        var select = $("filter_" + f);
         
     | 
| 
      
 14 
     | 
    
         
            +
                        if (select) {
         
     | 
| 
      
 15 
     | 
    
         
            +
                            var opt = select.down("option[value='"+ filter[f] + "']");
         
     | 
| 
      
 16 
     | 
    
         
            +
                            if (opt) {
         
     | 
| 
      
 17 
     | 
    
         
            +
                                opt.selected = true;
         
     | 
| 
      
 18 
     | 
    
         
            +
                                update_graph(select.form);
         
     | 
| 
      
 19 
     | 
    
         
            +
                            }
         
     | 
| 
      
 20 
     | 
    
         
            +
                        }
         
     | 
| 
      
 21 
     | 
    
         
            +
                    }
         
     | 
| 
      
 22 
     | 
    
         
            +
                }
         
     | 
| 
      
 23 
     | 
    
         
            +
            <% end %>
         
     | 
| 
      
 24 
     | 
    
         
            +
            <p>
         
     | 
| 
      
 25 
     | 
    
         
            +
              <% form_tag do %>
         
     | 
| 
      
 26 
     | 
    
         
            +
              <%#= link_to_graph "Rules" %>
         
     | 
| 
      
 27 
     | 
    
         
            +
              <%#= link_to_graph "Privilege hierarchy", :type => 'priv_hierarchy' %>
         
     | 
| 
      
 28 
     | 
    
         
            +
              
         
     | 
| 
      
 29 
     | 
    
         
            +
              <%= select_tag "filter_roles", options_for_select([["All roles",'']] + controller.authorization_engine.roles), :onchange => 'update_graph(this.form)' %>
         
     | 
| 
      
 30 
     | 
    
         
            +
              <%= select_tag "filter_contexts", options_for_select([["All contexts",'']] + controller.authorization_engine.auth_rules.collect {|ar| ar.contexts.to_a}.flatten.uniq), :onchange => 'update_graph(this.form)' %>
         
     | 
| 
      
 31 
     | 
    
         
            +
              <%= check_box_tag "effective_role_privs", "1", false, :onclick => 'update_graph(this.form)'  %> <%= label_tag "effective_role_privs", "Effective privileges"  %>
         
     | 
| 
      
 32 
     | 
    
         
            +
              <%= check_box_tag "privilege_hierarchy", "1", false, :onclick => 'update_graph(this.form)'  %> <%= label_tag "privilege_hierarchy", "Show full privilege hierarchy"  %>
         
     | 
| 
      
 33 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 34 
     | 
    
         
            +
            </p>
         
     | 
| 
      
 35 
     | 
    
         
            +
            <div style="margin: 1em;border:1px solid #ccc;max-width:95%">
         
     | 
| 
      
 36 
     | 
    
         
            +
            <object id="graph" data="<%= url_for :format => 'svg' %>" type="image/svg+xml" style="max-width:100%"/>
         
     | 
| 
      
 37 
     | 
    
         
            +
            </div>
         
     | 
| 
      
 38 
     | 
    
         
            +
            <%= button_to_function "Zoom in", '$("graph").style.maxWidth = "";$(this).toggle();$(this).next().toggle()' %>
         
     | 
| 
      
 39 
     | 
    
         
            +
            <%= button_to_function "Zoom out", '$("graph").style.maxWidth = "100%";$(this).toggle();$(this).previous().toggle()', :style => 'display:none' %>
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <h1>Authorization Rules</h1>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <p>Currently active rules in this application.</p>
         
     | 
| 
      
 3 
     | 
    
         
            +
            <p><%= navigation %></p>
         
     | 
| 
      
 4 
     | 
    
         
            +
            <style type="text/css">
         
     | 
| 
      
 5 
     | 
    
         
            +
              pre .constant {color: #a00;}
         
     | 
| 
      
 6 
     | 
    
         
            +
              pre .special {color: red;}
         
     | 
| 
      
 7 
     | 
    
         
            +
              pre .operator {color: red;}
         
     | 
| 
      
 8 
     | 
    
         
            +
              pre .statement {color: #00a;}
         
     | 
| 
      
 9 
     | 
    
         
            +
              pre .proc {color: #0a0;}
         
     | 
| 
      
 10 
     | 
    
         
            +
              pre .privilege, pre .context {font-weight: bold}
         
     | 
| 
      
 11 
     | 
    
         
            +
              pre .preproc, pre .comment, pre .comment span {color: grey !important;}
         
     | 
| 
      
 12 
     | 
    
         
            +
            </style>
         
     | 
| 
      
 13 
     | 
    
         
            +
            <pre>
         
     | 
| 
      
 14 
     | 
    
         
            +
            <%= syntax_highlight(h(@auth_rules_script)) %>
         
     | 
| 
      
 15 
     | 
    
         
            +
            </pre>
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <h1>Authorization Usage</h1>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <div style="margin: 1em;border:1px solid #ccc;max-width:50%;position:fixed;right:0;display:none">
         
     | 
| 
      
 3 
     | 
    
         
            +
            <object id="graph" data="<%= url_for :format => 'svg' %>" type="image/svg+xml" style="max-width:100%"/>
         
     | 
| 
      
 4 
     | 
    
         
            +
            </div>
         
     | 
| 
      
 5 
     | 
    
         
            +
            <p>Filter rules in actions by controller:</p>
         
     | 
| 
      
 6 
     | 
    
         
            +
            <p><%= navigation %></p>
         
     | 
| 
      
 7 
     | 
    
         
            +
            <style type="text/css">
         
     | 
| 
      
 8 
     | 
    
         
            +
              .auth-usages th { text-align: left; padding-top: 1em }
         
     | 
| 
      
 9 
     | 
    
         
            +
              .auth-usages td { padding-right: 1em }
         
     | 
| 
      
 10 
     | 
    
         
            +
              .auth-usages tr.action  { cursor: pointer }
         
     | 
| 
      
 11 
     | 
    
         
            +
              .auth-usages tr.unprotected  { background: #FFA399 }
         
     | 
| 
      
 12 
     | 
    
         
            +
              .auth-usages tr.no-attribute-check { background: #FFE599 }
         
     | 
| 
      
 13 
     | 
    
         
            +
              /*.auth-usages tr.catch-all td.privilege,*/
         
     | 
| 
      
 14 
     | 
    
         
            +
              .auth-usages tr.default-privilege td.privilege,
         
     | 
| 
      
 15 
     | 
    
         
            +
              .auth-usages tr.default-context td.context { color: #888888 }
         
     | 
| 
      
 16 
     | 
    
         
            +
            </style>
         
     | 
| 
      
 17 
     | 
    
         
            +
            <% javascript_tag do %>
         
     | 
| 
      
 18 
     | 
    
         
            +
                function show_graph (privilege, context) {
         
     | 
| 
      
 19 
     | 
    
         
            +
                    base_url = "<%= graph_authorization_rules_path('svg') %>";
         
     | 
| 
      
 20 
     | 
    
         
            +
                    $('graph').data = base_url + '?privilege_hierarchy=1&highlight_privilege=' +
         
     | 
| 
      
 21 
     | 
    
         
            +
                        privilege + '&filter_contexts=' + context;
         
     | 
| 
      
 22 
     | 
    
         
            +
                    $('graph').up().show();
         
     | 
| 
      
 23 
     | 
    
         
            +
                }
         
     | 
| 
      
 24 
     | 
    
         
            +
            <% end %>
         
     | 
| 
      
 25 
     | 
    
         
            +
            <table class="auth-usages">
         
     | 
| 
      
 26 
     | 
    
         
            +
              <% @auth_usages_by_controller.keys.sort {|c1, c2| c1.name <=> c2.name}.each do |controller| %>
         
     | 
| 
      
 27 
     | 
    
         
            +
                <% default_context = controller.controller_name.to_sym rescue nil %>
         
     | 
| 
      
 28 
     | 
    
         
            +
                <tr>
         
     | 
| 
      
 29 
     | 
    
         
            +
                  <th colspan="3"><%= h controller.controller_name %></th>
         
     | 
| 
      
 30 
     | 
    
         
            +
                </tr>
         
     | 
| 
      
 31 
     | 
    
         
            +
                <% @auth_usages_by_controller[controller].keys.sort {|c1, c2| c1.to_s <=> c2.to_s}.each do |action| %>
         
     | 
| 
      
 32 
     | 
    
         
            +
                  <% auth_info = @auth_usages_by_controller[controller][action] %>
         
     | 
| 
      
 33 
     | 
    
         
            +
                  <% first_permission = auth_info[:controller_permissions] && auth_info[:controller_permissions][0] %>
         
     | 
| 
      
 34 
     | 
    
         
            +
                  <tr class="action <%= auth_usage_info_classes(auth_info) %>" title="<%= auth_usage_info_title(auth_info) %>" onclick="show_graph('<%= auth_info[:privilege] || action %>','<%= auth_info[:context] || default_context %>')">
         
     | 
| 
      
 35 
     | 
    
         
            +
                    <td><%= h action %></td>
         
     | 
| 
      
 36 
     | 
    
         
            +
                    <% if first_permission %>
         
     | 
| 
      
 37 
     | 
    
         
            +
                      <td class="privilege"><%= h auth_info[:privilege] || action %></td>
         
     | 
| 
      
 38 
     | 
    
         
            +
                      <td class="context"><%= h auth_info[:context] || default_context %></td>
         
     | 
| 
      
 39 
     | 
    
         
            +
                    <% else %>
         
     | 
| 
      
 40 
     | 
    
         
            +
                      <td></td><td></td>
         
     | 
| 
      
 41 
     | 
    
         
            +
                    <% end %>
         
     | 
| 
      
 42 
     | 
    
         
            +
                  </tr>
         
     | 
| 
      
 43 
     | 
    
         
            +
                <% end %>
         
     | 
| 
      
 44 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 45 
     | 
    
         
            +
            </table>
         
     | 
    
        data/config/routes.rb
    ADDED
    
    
| 
         @@ -0,0 +1,514 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Authorization
         
     | 
| 
      
 2 
     | 
    
         
            +
            require File.dirname(__FILE__) + '/reader.rb'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "set"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Authorization
         
     | 
| 
      
 7 
     | 
    
         
            +
              # An exception raised if anything goes wrong in the Authorization realm
         
     | 
| 
      
 8 
     | 
    
         
            +
              class AuthorizationError < StandardError ; end
         
     | 
| 
      
 9 
     | 
    
         
            +
              # NotAuthorized is raised if the current user is not allowed to perform
         
     | 
| 
      
 10 
     | 
    
         
            +
              # the given operation possibly on a specific object.
         
     | 
| 
      
 11 
     | 
    
         
            +
              class NotAuthorized < AuthorizationError ; end
         
     | 
| 
      
 12 
     | 
    
         
            +
              # AttributeAuthorizationError is more specific than NotAuthorized, signalling
         
     | 
| 
      
 13 
     | 
    
         
            +
              # that the access was denied on the grounds of attribute conditions.
         
     | 
| 
      
 14 
     | 
    
         
            +
              class AttributeAuthorizationError < NotAuthorized ; end
         
     | 
| 
      
 15 
     | 
    
         
            +
              # AuthorizationUsageError is used whenever a situation is encountered
         
     | 
| 
      
 16 
     | 
    
         
            +
              # in which the application misused the plugin.  That is, if, e.g.,
         
     | 
| 
      
 17 
     | 
    
         
            +
              # authorization rules may not be evaluated.
         
     | 
| 
      
 18 
     | 
    
         
            +
              class AuthorizationUsageError < AuthorizationError ; end
         
     | 
| 
      
 19 
     | 
    
         
            +
              # NilAttributeValueError is raised by Attribute#validate? when it hits a nil attribute value.
         
     | 
| 
      
 20 
     | 
    
         
            +
              # The exception is raised to ensure that the entire rule is invalidated.
         
     | 
| 
      
 21 
     | 
    
         
            +
              class NilAttributeValueError < AuthorizationError; end
         
     | 
| 
      
 22 
     | 
    
         
            +
              
         
     | 
| 
      
 23 
     | 
    
         
            +
              AUTH_DSL_FILE = "#{RAILS_ROOT}/config/authorization_rules.rb"
         
     | 
| 
      
 24 
     | 
    
         
            +
              
         
     | 
| 
      
 25 
     | 
    
         
            +
              # Controller-independent method for retrieving the current user.
         
     | 
| 
      
 26 
     | 
    
         
            +
              # Needed for model security where the current controller is not available.
         
     | 
| 
      
 27 
     | 
    
         
            +
              def self.current_user
         
     | 
| 
      
 28 
     | 
    
         
            +
                Thread.current["current_user"] || GuestUser.new
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
              
         
     | 
| 
      
 31 
     | 
    
         
            +
              # Controller-independent method for setting the current user.
         
     | 
| 
      
 32 
     | 
    
         
            +
              def self.current_user=(user)
         
     | 
| 
      
 33 
     | 
    
         
            +
                Thread.current["current_user"] = user
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
              
         
     | 
| 
      
 36 
     | 
    
         
            +
              @@ignore_access_control = false
         
     | 
| 
      
 37 
     | 
    
         
            +
              # For use in test cases only
         
     | 
| 
      
 38 
     | 
    
         
            +
              def self.ignore_access_control (state = nil) # :nodoc:
         
     | 
| 
      
 39 
     | 
    
         
            +
                false
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              def self.activate_authorization_rules_browser? # :nodoc:
         
     | 
| 
      
 43 
     | 
    
         
            +
                ::RAILS_ENV == 'development'
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
              
         
     | 
| 
      
 46 
     | 
    
         
            +
              # Authorization::Engine implements the reference monitor.  It may be used
         
     | 
| 
      
 47 
     | 
    
         
            +
              # for querying the permission and retrieving obligations under which
         
     | 
| 
      
 48 
     | 
    
         
            +
              # a certain privilege is granted for the current user.
         
     | 
| 
      
 49 
     | 
    
         
            +
              #
         
     | 
| 
      
 50 
     | 
    
         
            +
              class Engine
         
     | 
| 
      
 51 
     | 
    
         
            +
                attr_reader :roles, :role_titles, :role_descriptions, :privileges,
         
     | 
| 
      
 52 
     | 
    
         
            +
                  :privilege_hierarchy, :auth_rules, :role_hierarchy, :rev_priv_hierarchy
         
     | 
| 
      
 53 
     | 
    
         
            +
                
         
     | 
| 
      
 54 
     | 
    
         
            +
                # If +reader+ is not given, a new one is created with the default
         
     | 
| 
      
 55 
     | 
    
         
            +
                # authorization configuration of +AUTH_DSL_FILE+.  If given, may be either
         
     | 
| 
      
 56 
     | 
    
         
            +
                # a Reader object or a path to a configuration file.
         
     | 
| 
      
 57 
     | 
    
         
            +
                def initialize (reader = nil)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  if reader.nil?
         
     | 
| 
      
 59 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 60 
     | 
    
         
            +
                      reader = Reader::DSLReader.load(AUTH_DSL_FILE)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    rescue SystemCallError
         
     | 
| 
      
 62 
     | 
    
         
            +
                      reader = Reader::DSLReader.new
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
                  elsif reader.is_a?(String)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    reader = Reader::DSLReader.load(reader)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @privileges = reader.privileges_reader.privileges
         
     | 
| 
      
 68 
     | 
    
         
            +
                  # {priv => [[priv, ctx],...]}
         
     | 
| 
      
 69 
     | 
    
         
            +
                  @privilege_hierarchy = reader.privileges_reader.privilege_hierarchy
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @auth_rules = reader.auth_rules_reader.auth_rules
         
     | 
| 
      
 71 
     | 
    
         
            +
                  @roles = reader.auth_rules_reader.roles
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @role_hierarchy = reader.auth_rules_reader.role_hierarchy
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  @role_titles = reader.auth_rules_reader.role_titles
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @role_descriptions = reader.auth_rules_reader.role_descriptions
         
     | 
| 
      
 76 
     | 
    
         
            +
                  
         
     | 
| 
      
 77 
     | 
    
         
            +
                  # {[priv, ctx] => [priv, ...]}
         
     | 
| 
      
 78 
     | 
    
         
            +
                  @rev_priv_hierarchy = {}
         
     | 
| 
      
 79 
     | 
    
         
            +
                  @privilege_hierarchy.each do |key, value|
         
     | 
| 
      
 80 
     | 
    
         
            +
                    value.each do |val| 
         
     | 
| 
      
 81 
     | 
    
         
            +
                      @rev_priv_hierarchy[val] ||= []
         
     | 
| 
      
 82 
     | 
    
         
            +
                      @rev_priv_hierarchy[val] << key
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
                
         
     | 
| 
      
 87 
     | 
    
         
            +
                # Returns true if privilege is met by the current user.  Raises
         
     | 
| 
      
 88 
     | 
    
         
            +
                # AuthorizationError otherwise.  +privilege+ may be given with or
         
     | 
| 
      
 89 
     | 
    
         
            +
                # without context.  In the latter case, the :+context+ option is
         
     | 
| 
      
 90 
     | 
    
         
            +
                # required.
         
     | 
| 
      
 91 
     | 
    
         
            +
                #  
         
     | 
| 
      
 92 
     | 
    
         
            +
                # Options:
         
     | 
| 
      
 93 
     | 
    
         
            +
                # [:+context+]
         
     | 
| 
      
 94 
     | 
    
         
            +
                #   The context part of the privilege.
         
     | 
| 
      
 95 
     | 
    
         
            +
                #   Defaults either to the +table_name+ of the given :+object+, if given.
         
     | 
| 
      
 96 
     | 
    
         
            +
                #   That is, either :+users+ for :+object+ of type User.  
         
     | 
| 
      
 97 
     | 
    
         
            +
                #   Raises AuthorizationUsageError if
         
     | 
| 
      
 98 
     | 
    
         
            +
                #   context is missing and not to be infered.
         
     | 
| 
      
 99 
     | 
    
         
            +
                # [:+object+] An context object to test attribute checks against.
         
     | 
| 
      
 100 
     | 
    
         
            +
                # [:+skip_attribute_test+]
         
     | 
| 
      
 101 
     | 
    
         
            +
                #   Skips those attribute checks in the 
         
     | 
| 
      
 102 
     | 
    
         
            +
                #   authorization rules. Defaults to false.
         
     | 
| 
      
 103 
     | 
    
         
            +
                # [:+user+] 
         
     | 
| 
      
 104 
     | 
    
         
            +
                #   The user to check the authorization for.
         
     | 
| 
      
 105 
     | 
    
         
            +
                #   Defaults to Authorization#current_user.
         
     | 
| 
      
 106 
     | 
    
         
            +
                #
         
     | 
| 
      
 107 
     | 
    
         
            +
                def permit! (privilege, options = {})
         
     | 
| 
      
 108 
     | 
    
         
            +
                  return true if Authorization.ignore_access_control
         
     | 
| 
      
 109 
     | 
    
         
            +
                  options = {
         
     | 
| 
      
 110 
     | 
    
         
            +
                    :object => nil,
         
     | 
| 
      
 111 
     | 
    
         
            +
                    :skip_attribute_test => false,
         
     | 
| 
      
 112 
     | 
    
         
            +
                    :context => nil
         
     | 
| 
      
 113 
     | 
    
         
            +
                  }.merge(options)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  
         
     | 
| 
      
 115 
     | 
    
         
            +
                  # Make sure we're handling all privileges as symbols.
         
     | 
| 
      
 116 
     | 
    
         
            +
                  privilege = privilege.is_a?( Array ) ?
         
     | 
| 
      
 117 
     | 
    
         
            +
                              privilege.flatten.collect { |priv| priv.to_sym } :
         
     | 
| 
      
 118 
     | 
    
         
            +
                              privilege.to_sym
         
     | 
| 
      
 119 
     | 
    
         
            +
                  
         
     | 
| 
      
 120 
     | 
    
         
            +
                  #
         
     | 
| 
      
 121 
     | 
    
         
            +
                  # If the object responds to :proxy_reflection, we're probably working with
         
     | 
| 
      
 122 
     | 
    
         
            +
                  # an association proxy.  Use 'new' to leverage ActiveRecord's builder
         
     | 
| 
      
 123 
     | 
    
         
            +
                  # functionality to obtain an object against which we can check permissions.
         
     | 
| 
      
 124 
     | 
    
         
            +
                  #
         
     | 
| 
      
 125 
     | 
    
         
            +
                  # Example: permit!( :edit, :object => user.posts )
         
     | 
| 
      
 126 
     | 
    
         
            +
                  #
         
     | 
| 
      
 127 
     | 
    
         
            +
                  if options[:object].respond_to?( :proxy_reflection )
         
     | 
| 
      
 128 
     | 
    
         
            +
                    options[:object] = options[:object].new
         
     | 
| 
      
 129 
     | 
    
         
            +
                  end
         
     | 
| 
      
 130 
     | 
    
         
            +
                  
         
     | 
| 
      
 131 
     | 
    
         
            +
                  options[:context] ||= options[:object] && options[:object].class.table_name.to_sym rescue NoMethodError
         
     | 
| 
      
 132 
     | 
    
         
            +
                  
         
     | 
| 
      
 133 
     | 
    
         
            +
                  user, roles, privileges = user_roles_privleges_from_options(privilege, options)
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  # find a authorization rule that matches for at least one of the roles and 
         
     | 
| 
      
 136 
     | 
    
         
            +
                  # at least one of the given privileges
         
     | 
| 
      
 137 
     | 
    
         
            +
                  attr_validator = AttributeValidator.new(self, user, options[:object])
         
     | 
| 
      
 138 
     | 
    
         
            +
                  rules = matching_auth_rules(roles, privileges, options[:context])
         
     | 
| 
      
 139 
     | 
    
         
            +
                  if rules.empty?
         
     | 
| 
      
 140 
     | 
    
         
            +
                    raise NotAuthorized, "No matching rules found for #{privilege} for #{user.inspect} " +
         
     | 
| 
      
 141 
     | 
    
         
            +
                      "(roles #{roles.inspect}, privileges #{privileges.inspect}, " +
         
     | 
| 
      
 142 
     | 
    
         
            +
                      "context #{options[:context].inspect})."
         
     | 
| 
      
 143 
     | 
    
         
            +
                  end
         
     | 
| 
      
 144 
     | 
    
         
            +
                  
         
     | 
| 
      
 145 
     | 
    
         
            +
                  # Test each rule in turn to see whether any one of them is satisfied.
         
     | 
| 
      
 146 
     | 
    
         
            +
                  grant_permission = rules.any? do |rule|
         
     | 
| 
      
 147 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 148 
     | 
    
         
            +
                      options[:skip_attribute_test] or
         
     | 
| 
      
 149 
     | 
    
         
            +
                        rule.attributes.empty? or
         
     | 
| 
      
 150 
     | 
    
         
            +
                        rule.attributes.any? do |attr|
         
     | 
| 
      
 151 
     | 
    
         
            +
                          begin
         
     | 
| 
      
 152 
     | 
    
         
            +
                            attr.validate?( attr_validator )
         
     | 
| 
      
 153 
     | 
    
         
            +
                          rescue NilAttributeValueError => e
         
     | 
| 
      
 154 
     | 
    
         
            +
                            nil # Bumping up against a nil attribute value flunks the rule.
         
     | 
| 
      
 155 
     | 
    
         
            +
                          end
         
     | 
| 
      
 156 
     | 
    
         
            +
                        end
         
     | 
| 
      
 157 
     | 
    
         
            +
                    end
         
     | 
| 
      
 158 
     | 
    
         
            +
                  end
         
     | 
| 
      
 159 
     | 
    
         
            +
                  unless grant_permission
         
     | 
| 
      
 160 
     | 
    
         
            +
                    raise AttributeAuthorizationError, "#{privilege} not allowed for #{user.inspect} on #{options[:object].inspect}."
         
     | 
| 
      
 161 
     | 
    
         
            +
                  end
         
     | 
| 
      
 162 
     | 
    
         
            +
                  true
         
     | 
| 
      
 163 
     | 
    
         
            +
                end
         
     | 
| 
      
 164 
     | 
    
         
            +
                
         
     | 
| 
      
 165 
     | 
    
         
            +
                # Calls permit! but rescues the AuthorizationException and returns false
         
     | 
| 
      
 166 
     | 
    
         
            +
                # instead.  If no exception is raised, permit? returns true and yields
         
     | 
| 
      
 167 
     | 
    
         
            +
                # to the optional block.
         
     | 
| 
      
 168 
     | 
    
         
            +
                def permit? (privilege, options = {}, &block) # :yields:
         
     | 
| 
      
 169 
     | 
    
         
            +
                  permit!(privilege, options)
         
     | 
| 
      
 170 
     | 
    
         
            +
                  yield if block_given?
         
     | 
| 
      
 171 
     | 
    
         
            +
                  true
         
     | 
| 
      
 172 
     | 
    
         
            +
                rescue NotAuthorized
         
     | 
| 
      
 173 
     | 
    
         
            +
                  false
         
     | 
| 
      
 174 
     | 
    
         
            +
                end
         
     | 
| 
      
 175 
     | 
    
         
            +
                
         
     | 
| 
      
 176 
     | 
    
         
            +
                # Returns the obligations to be met by the current user for the given 
         
     | 
| 
      
 177 
     | 
    
         
            +
                # privilege as an array of obligation hashes in form of 
         
     | 
| 
      
 178 
     | 
    
         
            +
                #   [{:object_attribute => obligation_value, ...}, ...]
         
     | 
| 
      
 179 
     | 
    
         
            +
                # where +obligation_value+ is either (recursively) another obligation hash
         
     | 
| 
      
 180 
     | 
    
         
            +
                # or a value spec, such as
         
     | 
| 
      
 181 
     | 
    
         
            +
                #   [operator, literal_value]
         
     | 
| 
      
 182 
     | 
    
         
            +
                # The obligation hashes in the array should be OR'ed, conditions inside
         
     | 
| 
      
 183 
     | 
    
         
            +
                # the hashes AND'ed.
         
     | 
| 
      
 184 
     | 
    
         
            +
                # 
         
     | 
| 
      
 185 
     | 
    
         
            +
                # Example
         
     | 
| 
      
 186 
     | 
    
         
            +
                #   {:branch => {:company => [:is, 24]}, :active => [:is, true]}
         
     | 
| 
      
 187 
     | 
    
         
            +
                # 
         
     | 
| 
      
 188 
     | 
    
         
            +
                # Options
         
     | 
| 
      
 189 
     | 
    
         
            +
                # [:+context+]  See permit!
         
     | 
| 
      
 190 
     | 
    
         
            +
                # [:+user+]  See permit!
         
     | 
| 
      
 191 
     | 
    
         
            +
                # 
         
     | 
| 
      
 192 
     | 
    
         
            +
                def obligations (privilege, options = {})
         
     | 
| 
      
 193 
     | 
    
         
            +
                  options = {:context => nil}.merge(options)
         
     | 
| 
      
 194 
     | 
    
         
            +
                  user, roles, privileges = user_roles_privleges_from_options(privilege, options)
         
     | 
| 
      
 195 
     | 
    
         
            +
                  attr_validator = AttributeValidator.new(self, user)
         
     | 
| 
      
 196 
     | 
    
         
            +
                  matching_auth_rules(roles, privileges, options[:context]).collect do |rule|
         
     | 
| 
      
 197 
     | 
    
         
            +
                    obligation = rule.attributes.collect {|attr| attr.obligation(attr_validator) }
         
     | 
| 
      
 198 
     | 
    
         
            +
                    obligation.empty? ? [{}] : obligation
         
     | 
| 
      
 199 
     | 
    
         
            +
                  end.flatten
         
     | 
| 
      
 200 
     | 
    
         
            +
                end
         
     | 
| 
      
 201 
     | 
    
         
            +
                
         
     | 
| 
      
 202 
     | 
    
         
            +
                # Returns the description for the given role.  The description may be
         
     | 
| 
      
 203 
     | 
    
         
            +
                # specified with the authorization rules.  Returns +nil+ if none was
         
     | 
| 
      
 204 
     | 
    
         
            +
                # given.
         
     | 
| 
      
 205 
     | 
    
         
            +
                def description_for (role)
         
     | 
| 
      
 206 
     | 
    
         
            +
                  role_descriptions[role]
         
     | 
| 
      
 207 
     | 
    
         
            +
                end
         
     | 
| 
      
 208 
     | 
    
         
            +
                
         
     | 
| 
      
 209 
     | 
    
         
            +
                # Returns the title for the given role.  The title may be
         
     | 
| 
      
 210 
     | 
    
         
            +
                # specified with the authorization rules.  Returns +nil+ if none was
         
     | 
| 
      
 211 
     | 
    
         
            +
                # given.
         
     | 
| 
      
 212 
     | 
    
         
            +
                def title_for (role)
         
     | 
| 
      
 213 
     | 
    
         
            +
                  role_titles[role]
         
     | 
| 
      
 214 
     | 
    
         
            +
                end
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                # Returns the role symbols of the given user.
         
     | 
| 
      
 217 
     | 
    
         
            +
                def roles_for (user)
         
     | 
| 
      
 218 
     | 
    
         
            +
                  raise AuthorizationUsageError, "User object doesn't respond to roles" \
         
     | 
| 
      
 219 
     | 
    
         
            +
                    if !user.respond_to?(:role_symbols) and !user.respond_to?(:roles)
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
                  RAILS_DEFAULT_LOGGER.info("The use of user.roles is deprecated.  Please add a method " +
         
     | 
| 
      
 222 
     | 
    
         
            +
                      "role_symbols to your User model.") if defined?(RAILS_DEFAULT_LOGGER) and !user.respond_to?(:role_symbols)
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                  roles = user.respond_to?(:role_symbols) ? user.role_symbols : user.roles
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
      
 226 
     | 
    
         
            +
                  raise AuthorizationUsageError, "User.#{user.respond_to?(:role_symbols) ? 'role_symbols' : 'roles'} " +
         
     | 
| 
      
 227 
     | 
    
         
            +
                    "doesn't return an Array of Symbols (#{roles.inspect})" \
         
     | 
| 
      
 228 
     | 
    
         
            +
                        if !roles.is_a?(Array) or (!roles.empty? and !roles[0].is_a?(Symbol))
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                  (roles.empty? ? [:guest] : roles)
         
     | 
| 
      
 231 
     | 
    
         
            +
                end
         
     | 
| 
      
 232 
     | 
    
         
            +
                
         
     | 
| 
      
 233 
     | 
    
         
            +
                # Returns an instance of Engine, which is created if there isn't one
         
     | 
| 
      
 234 
     | 
    
         
            +
                # yet.  If +dsl_file+ is given, it is passed on to Engine.new and 
         
     | 
| 
      
 235 
     | 
    
         
            +
                # a new instance is always created.
         
     | 
| 
      
 236 
     | 
    
         
            +
                def self.instance (dsl_file = nil)
         
     | 
| 
      
 237 
     | 
    
         
            +
                  if dsl_file or ENV['RAILS_ENV'] == 'development'
         
     | 
| 
      
 238 
     | 
    
         
            +
                    @@instance = new(dsl_file)
         
     | 
| 
      
 239 
     | 
    
         
            +
                  else
         
     | 
| 
      
 240 
     | 
    
         
            +
                    @@instance ||= new
         
     | 
| 
      
 241 
     | 
    
         
            +
                  end
         
     | 
| 
      
 242 
     | 
    
         
            +
                end
         
     | 
| 
      
 243 
     | 
    
         
            +
                
         
     | 
| 
      
 244 
     | 
    
         
            +
                class AttributeValidator # :nodoc:
         
     | 
| 
      
 245 
     | 
    
         
            +
                  attr_reader :user, :object, :engine
         
     | 
| 
      
 246 
     | 
    
         
            +
                  def initialize (engine, user, object = nil)
         
     | 
| 
      
 247 
     | 
    
         
            +
                    @engine = engine
         
     | 
| 
      
 248 
     | 
    
         
            +
                    @user = user
         
     | 
| 
      
 249 
     | 
    
         
            +
                    @object = object
         
     | 
| 
      
 250 
     | 
    
         
            +
                  end
         
     | 
| 
      
 251 
     | 
    
         
            +
                  
         
     | 
| 
      
 252 
     | 
    
         
            +
                  def evaluate (value_block)
         
     | 
| 
      
 253 
     | 
    
         
            +
                    # TODO cache?
         
     | 
| 
      
 254 
     | 
    
         
            +
                    instance_eval(&value_block)
         
     | 
| 
      
 255 
     | 
    
         
            +
                  end
         
     | 
| 
      
 256 
     | 
    
         
            +
                end
         
     | 
| 
      
 257 
     | 
    
         
            +
                
         
     | 
| 
      
 258 
     | 
    
         
            +
                private
         
     | 
| 
      
 259 
     | 
    
         
            +
                def user_roles_privleges_from_options(privilege, options)
         
     | 
| 
      
 260 
     | 
    
         
            +
                  options = {
         
     | 
| 
      
 261 
     | 
    
         
            +
                    :user => nil,
         
     | 
| 
      
 262 
     | 
    
         
            +
                    :context => nil
         
     | 
| 
      
 263 
     | 
    
         
            +
                  }.merge(options)
         
     | 
| 
      
 264 
     | 
    
         
            +
                  user = options[:user] || Authorization.current_user
         
     | 
| 
      
 265 
     | 
    
         
            +
                  privileges = privilege.is_a?(Array) ? privilege : [privilege]
         
     | 
| 
      
 266 
     | 
    
         
            +
                  
         
     | 
| 
      
 267 
     | 
    
         
            +
                  raise AuthorizationUsageError, "No user object given (#{user.inspect})" \
         
     | 
| 
      
 268 
     | 
    
         
            +
                    unless user
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
      
 270 
     | 
    
         
            +
                  roles = flatten_roles(roles_for(user))
         
     | 
| 
      
 271 
     | 
    
         
            +
                  privileges = flatten_privileges privileges, options[:context]
         
     | 
| 
      
 272 
     | 
    
         
            +
                  [user, roles, privileges]
         
     | 
| 
      
 273 
     | 
    
         
            +
                end
         
     | 
| 
      
 274 
     | 
    
         
            +
                
         
     | 
| 
      
 275 
     | 
    
         
            +
                def flatten_roles (roles)
         
     | 
| 
      
 276 
     | 
    
         
            +
                  # TODO caching?
         
     | 
| 
      
 277 
     | 
    
         
            +
                  flattened_roles = roles.clone.to_a
         
     | 
| 
      
 278 
     | 
    
         
            +
                  flattened_roles.each do |role|
         
     | 
| 
      
 279 
     | 
    
         
            +
                    flattened_roles.concat(@role_hierarchy[role]).uniq! if @role_hierarchy[role]
         
     | 
| 
      
 280 
     | 
    
         
            +
                  end
         
     | 
| 
      
 281 
     | 
    
         
            +
                end
         
     | 
| 
      
 282 
     | 
    
         
            +
                
         
     | 
| 
      
 283 
     | 
    
         
            +
                # Returns the privilege hierarchy flattened for given privileges in context.
         
     | 
| 
      
 284 
     | 
    
         
            +
                def flatten_privileges (privileges, context = nil)
         
     | 
| 
      
 285 
     | 
    
         
            +
                  # TODO caching?
         
     | 
| 
      
 286 
     | 
    
         
            +
                  #if context.nil?
         
     | 
| 
      
 287 
     | 
    
         
            +
                  #  context = privileges.collect { |p| p.to_s.split('_') }.
         
     | 
| 
      
 288 
     | 
    
         
            +
                  #                       reject { |p_p| p_p.length < 2 }.
         
     | 
| 
      
 289 
     | 
    
         
            +
                  #                       collect { |p_p| (p_p[1..-1] * '_').to_sym }.first
         
     | 
| 
      
 290 
     | 
    
         
            +
                  #  raise AuthorizationUsageError, "No context given or inferable from privileges #{privileges.inspect}" unless context
         
     | 
| 
      
 291 
     | 
    
         
            +
                  #end
         
     | 
| 
      
 292 
     | 
    
         
            +
                  raise AuthorizationUsageError, "No context given or inferable from object" unless context
         
     | 
| 
      
 293 
     | 
    
         
            +
                  #context_regex = Regexp.new "_#{context}$"
         
     | 
| 
      
 294 
     | 
    
         
            +
                  # TODO work with contextless privileges
         
     | 
| 
      
 295 
     | 
    
         
            +
                  #flattened_privileges = privileges.collect {|p| p.to_s.sub(context_regex, '')}
         
     | 
| 
      
 296 
     | 
    
         
            +
                  flattened_privileges = privileges.clone #collect {|p| p.to_s.end_with?(context.to_s) ?
         
     | 
| 
      
 297 
     | 
    
         
            +
                                                          #       p : [p, "#{p}_#{context}".to_sym] }.flatten
         
     | 
| 
      
 298 
     | 
    
         
            +
                  flattened_privileges.each do |priv|
         
     | 
| 
      
 299 
     | 
    
         
            +
                    flattened_privileges.concat(@rev_priv_hierarchy[[priv, nil]]).uniq! if @rev_priv_hierarchy[[priv, nil]]
         
     | 
| 
      
 300 
     | 
    
         
            +
                    flattened_privileges.concat(@rev_priv_hierarchy[[priv, context]]).uniq! if @rev_priv_hierarchy[[priv, context]]
         
     | 
| 
      
 301 
     | 
    
         
            +
                  end
         
     | 
| 
      
 302 
     | 
    
         
            +
                end
         
     | 
| 
      
 303 
     | 
    
         
            +
                
         
     | 
| 
      
 304 
     | 
    
         
            +
                def matching_auth_rules (roles, privileges, context)
         
     | 
| 
      
 305 
     | 
    
         
            +
                  @auth_rules.select {|rule| rule.matches? roles, privileges, context}
         
     | 
| 
      
 306 
     | 
    
         
            +
                end
         
     | 
| 
      
 307 
     | 
    
         
            +
              end
         
     | 
| 
      
 308 
     | 
    
         
            +
              
         
     | 
| 
      
 309 
     | 
    
         
            +
              class AuthorizationRule
         
     | 
| 
      
 310 
     | 
    
         
            +
                attr_reader :attributes, :contexts, :role, :privileges
         
     | 
| 
      
 311 
     | 
    
         
            +
                
         
     | 
| 
      
 312 
     | 
    
         
            +
                def initialize (role, privileges = [], contexts = nil)
         
     | 
| 
      
 313 
     | 
    
         
            +
                  @role = role
         
     | 
| 
      
 314 
     | 
    
         
            +
                  @privileges = Set.new(privileges)
         
     | 
| 
      
 315 
     | 
    
         
            +
                  @contexts = Set.new((contexts && !contexts.is_a?(Array) ? [contexts] : contexts))
         
     | 
| 
      
 316 
     | 
    
         
            +
                  @attributes = []
         
     | 
| 
      
 317 
     | 
    
         
            +
                end
         
     | 
| 
      
 318 
     | 
    
         
            +
                
         
     | 
| 
      
 319 
     | 
    
         
            +
                def append_privileges (privs)
         
     | 
| 
      
 320 
     | 
    
         
            +
                  @privileges.merge(privs)
         
     | 
| 
      
 321 
     | 
    
         
            +
                end
         
     | 
| 
      
 322 
     | 
    
         
            +
                
         
     | 
| 
      
 323 
     | 
    
         
            +
                def append_attribute (attribute)
         
     | 
| 
      
 324 
     | 
    
         
            +
                  @attributes << attribute
         
     | 
| 
      
 325 
     | 
    
         
            +
                end
         
     | 
| 
      
 326 
     | 
    
         
            +
                
         
     | 
| 
      
 327 
     | 
    
         
            +
                def matches? (roles, privs, context = nil)
         
     | 
| 
      
 328 
     | 
    
         
            +
                  roles = [roles] unless roles.is_a?(Array)
         
     | 
| 
      
 329 
     | 
    
         
            +
                  @contexts.include?(context) and roles.include?(@role) and 
         
     | 
| 
      
 330 
     | 
    
         
            +
                    not (@privileges & privs).empty?
         
     | 
| 
      
 331 
     | 
    
         
            +
                end
         
     | 
| 
      
 332 
     | 
    
         
            +
             
     | 
| 
      
 333 
     | 
    
         
            +
                def to_long_s
         
     | 
| 
      
 334 
     | 
    
         
            +
                  attributes.collect {|attr| attr.to_long_s } * "; "
         
     | 
| 
      
 335 
     | 
    
         
            +
                end
         
     | 
| 
      
 336 
     | 
    
         
            +
              end
         
     | 
| 
      
 337 
     | 
    
         
            +
              
         
     | 
| 
      
 338 
     | 
    
         
            +
              class Attribute
         
     | 
| 
      
 339 
     | 
    
         
            +
                # attr_conditions_hash of form
         
     | 
| 
      
 340 
     | 
    
         
            +
                # { :object_attribute => [operator, value_block], ... }
         
     | 
| 
      
 341 
     | 
    
         
            +
                # { :object_attribute => { :attr => ... } }
         
     | 
| 
      
 342 
     | 
    
         
            +
                def initialize (conditions_hash)
         
     | 
| 
      
 343 
     | 
    
         
            +
                  @conditions_hash = conditions_hash
         
     | 
| 
      
 344 
     | 
    
         
            +
                end
         
     | 
| 
      
 345 
     | 
    
         
            +
                
         
     | 
| 
      
 346 
     | 
    
         
            +
                def validate? (attr_validator, object = nil, hash = nil)
         
     | 
| 
      
 347 
     | 
    
         
            +
                  object ||= attr_validator.object
         
     | 
| 
      
 348 
     | 
    
         
            +
                  return false unless object
         
     | 
| 
      
 349 
     | 
    
         
            +
                  
         
     | 
| 
      
 350 
     | 
    
         
            +
                  (hash || @conditions_hash).all? do |attr, value|
         
     | 
| 
      
 351 
     | 
    
         
            +
                    attr_value = object_attribute_value(object, attr)
         
     | 
| 
      
 352 
     | 
    
         
            +
                    if value.is_a?(Hash)
         
     | 
| 
      
 353 
     | 
    
         
            +
                      if attr_value.is_a?(Array)
         
     | 
| 
      
 354 
     | 
    
         
            +
                        raise AuthorizationUsageError, "Unable evaluate multiple attributes " +
         
     | 
| 
      
 355 
     | 
    
         
            +
                          "on a collection.  Cannot use '=>' operator on #{attr.inspect} " +
         
     | 
| 
      
 356 
     | 
    
         
            +
                          "(#{attr_value.inspect}) for attributes #{value.inspect}."
         
     | 
| 
      
 357 
     | 
    
         
            +
                      elsif attr_value.nil?
         
     | 
| 
      
 358 
     | 
    
         
            +
                        raise NilAttributeValueError, "Attribute #{attr.inspect} is nil in #{object.inspect}."
         
     | 
| 
      
 359 
     | 
    
         
            +
                      end
         
     | 
| 
      
 360 
     | 
    
         
            +
                      validate?(attr_validator, attr_value, value)
         
     | 
| 
      
 361 
     | 
    
         
            +
                    elsif value.is_a?(Array) and value.length == 2
         
     | 
| 
      
 362 
     | 
    
         
            +
                      evaluated = if value[1].is_a?(Proc)
         
     | 
| 
      
 363 
     | 
    
         
            +
                                    attr_validator.evaluate(value[1])
         
     | 
| 
      
 364 
     | 
    
         
            +
                                  else
         
     | 
| 
      
 365 
     | 
    
         
            +
                                    value[1]
         
     | 
| 
      
 366 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 367 
     | 
    
         
            +
                      case value[0]
         
     | 
| 
      
 368 
     | 
    
         
            +
                      when :is
         
     | 
| 
      
 369 
     | 
    
         
            +
                        attr_value == evaluated
         
     | 
| 
      
 370 
     | 
    
         
            +
                      when :is_not
         
     | 
| 
      
 371 
     | 
    
         
            +
                        attr_value != evaluated
         
     | 
| 
      
 372 
     | 
    
         
            +
                      when :contains
         
     | 
| 
      
 373 
     | 
    
         
            +
                        attr_value.include?(evaluated)
         
     | 
| 
      
 374 
     | 
    
         
            +
                      when :does_not_contain
         
     | 
| 
      
 375 
     | 
    
         
            +
                        !attr_value.include?(evaluated)
         
     | 
| 
      
 376 
     | 
    
         
            +
                      when :is_in
         
     | 
| 
      
 377 
     | 
    
         
            +
                        evaluated.include?(attr_value)
         
     | 
| 
      
 378 
     | 
    
         
            +
                      when :is_not_in
         
     | 
| 
      
 379 
     | 
    
         
            +
                        !evaluated.include?(attr_value)
         
     | 
| 
      
 380 
     | 
    
         
            +
                      else
         
     | 
| 
      
 381 
     | 
    
         
            +
                        raise AuthorizationError, "Unknown operator #{value[0]}"
         
     | 
| 
      
 382 
     | 
    
         
            +
                      end
         
     | 
| 
      
 383 
     | 
    
         
            +
                    else
         
     | 
| 
      
 384 
     | 
    
         
            +
                      raise AuthorizationError, "Wrong conditions hash format"
         
     | 
| 
      
 385 
     | 
    
         
            +
                    end
         
     | 
| 
      
 386 
     | 
    
         
            +
                  end
         
     | 
| 
      
 387 
     | 
    
         
            +
                end
         
     | 
| 
      
 388 
     | 
    
         
            +
                
         
     | 
| 
      
 389 
     | 
    
         
            +
                # resolves all the values in condition_hash
         
     | 
| 
      
 390 
     | 
    
         
            +
                def obligation (attr_validator, hash = nil)
         
     | 
| 
      
 391 
     | 
    
         
            +
                  hash = (hash || @conditions_hash).clone
         
     | 
| 
      
 392 
     | 
    
         
            +
                  hash.each do |attr, value|
         
     | 
| 
      
 393 
     | 
    
         
            +
                    if value.is_a?(Hash)
         
     | 
| 
      
 394 
     | 
    
         
            +
                      hash[attr] = obligation(attr_validator, value)
         
     | 
| 
      
 395 
     | 
    
         
            +
                    elsif value.is_a?(Array) and value.length == 2
         
     | 
| 
      
 396 
     | 
    
         
            +
                      hash[attr] = [value[0], attr_validator.evaluate(value[1])]
         
     | 
| 
      
 397 
     | 
    
         
            +
                    else
         
     | 
| 
      
 398 
     | 
    
         
            +
                      raise AuthorizationError, "Wrong conditions hash format"
         
     | 
| 
      
 399 
     | 
    
         
            +
                    end
         
     | 
| 
      
 400 
     | 
    
         
            +
                  end
         
     | 
| 
      
 401 
     | 
    
         
            +
                  hash
         
     | 
| 
      
 402 
     | 
    
         
            +
                end
         
     | 
| 
      
 403 
     | 
    
         
            +
             
     | 
| 
      
 404 
     | 
    
         
            +
                def to_long_s (hash = nil)
         
     | 
| 
      
 405 
     | 
    
         
            +
                  if hash
         
     | 
| 
      
 406 
     | 
    
         
            +
                    hash.inject({}) do |memo, key_val|
         
     | 
| 
      
 407 
     | 
    
         
            +
                      key, val = key_val
         
     | 
| 
      
 408 
     | 
    
         
            +
                      memo[key] = case val
         
     | 
| 
      
 409 
     | 
    
         
            +
                                  when Array then "#{val[0]} { #{val[1].respond_to?(:to_ruby) ? val[1].to_ruby.gsub(/^proc \{\n?(.*)\n?\}$/m, '\1') : "..."} }"
         
     | 
| 
      
 410 
     | 
    
         
            +
                                  when Hash then to_long_s(val)
         
     | 
| 
      
 411 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 412 
     | 
    
         
            +
                      memo
         
     | 
| 
      
 413 
     | 
    
         
            +
                    end
         
     | 
| 
      
 414 
     | 
    
         
            +
                  else
         
     | 
| 
      
 415 
     | 
    
         
            +
                    "if_attribute #{to_long_s(@conditions_hash).inspect}"
         
     | 
| 
      
 416 
     | 
    
         
            +
                  end
         
     | 
| 
      
 417 
     | 
    
         
            +
                end
         
     | 
| 
      
 418 
     | 
    
         
            +
             
     | 
| 
      
 419 
     | 
    
         
            +
                protected
         
     | 
| 
      
 420 
     | 
    
         
            +
                def object_attribute_value (object, attr)
         
     | 
| 
      
 421 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 422 
     | 
    
         
            +
                    object.send(attr)
         
     | 
| 
      
 423 
     | 
    
         
            +
                  rescue ArgumentError, NoMethodError => e
         
     | 
| 
      
 424 
     | 
    
         
            +
                    raise AuthorizationUsageError, "Error when calling #{attr} on " +
         
     | 
| 
      
 425 
     | 
    
         
            +
                     "#{object.inspect} for validating attribute: #{e}"
         
     | 
| 
      
 426 
     | 
    
         
            +
                  end
         
     | 
| 
      
 427 
     | 
    
         
            +
                end
         
     | 
| 
      
 428 
     | 
    
         
            +
              end
         
     | 
| 
      
 429 
     | 
    
         
            +
             
     | 
| 
      
 430 
     | 
    
         
            +
              # An attribute condition that uses existing rules to decide validation
         
     | 
| 
      
 431 
     | 
    
         
            +
              # and create obligations.
         
     | 
| 
      
 432 
     | 
    
         
            +
              class AttributeWithPermission < Attribute
         
     | 
| 
      
 433 
     | 
    
         
            +
                # E.g. privilege :read, attr_or_hash either :attribute or
         
     | 
| 
      
 434 
     | 
    
         
            +
                # { :attribute => :deeper_attribute }
         
     | 
| 
      
 435 
     | 
    
         
            +
                def initialize (privilege, attr_or_hash, context = nil)
         
     | 
| 
      
 436 
     | 
    
         
            +
                  @privilege = privilege
         
     | 
| 
      
 437 
     | 
    
         
            +
                  @context = context
         
     | 
| 
      
 438 
     | 
    
         
            +
                  @attr_hash = attr_or_hash
         
     | 
| 
      
 439 
     | 
    
         
            +
                end
         
     | 
| 
      
 440 
     | 
    
         
            +
             
     | 
| 
      
 441 
     | 
    
         
            +
                def validate? (attr_validator, object = nil, hash_or_attr = nil)
         
     | 
| 
      
 442 
     | 
    
         
            +
                  object ||= attr_validator.object
         
     | 
| 
      
 443 
     | 
    
         
            +
                  hash_or_attr ||= @attr_hash
         
     | 
| 
      
 444 
     | 
    
         
            +
                  return false unless object
         
     | 
| 
      
 445 
     | 
    
         
            +
             
     | 
| 
      
 446 
     | 
    
         
            +
                  case hash_or_attr
         
     | 
| 
      
 447 
     | 
    
         
            +
                  when Symbol
         
     | 
| 
      
 448 
     | 
    
         
            +
                    attr_value = object_attribute_value(object, hash_or_attr)
         
     | 
| 
      
 449 
     | 
    
         
            +
                    attr_validator.engine.permit? @privilege, :object => attr_value, :user => attr_validator.user
         
     | 
| 
      
 450 
     | 
    
         
            +
                  when Hash
         
     | 
| 
      
 451 
     | 
    
         
            +
                    hash_or_attr.all? do |attr, sub_hash|
         
     | 
| 
      
 452 
     | 
    
         
            +
                      attr_value = object_attribute_value(object, attr)
         
     | 
| 
      
 453 
     | 
    
         
            +
                      if attr_value.nil?
         
     | 
| 
      
 454 
     | 
    
         
            +
                        raise AuthorizationError, "Attribute #{attr.inspect} is nil in #{object.inspect}."
         
     | 
| 
      
 455 
     | 
    
         
            +
                      end
         
     | 
| 
      
 456 
     | 
    
         
            +
                      validate?(attr_validator, attr_value, sub_hash)
         
     | 
| 
      
 457 
     | 
    
         
            +
                    end
         
     | 
| 
      
 458 
     | 
    
         
            +
                  else
         
     | 
| 
      
 459 
     | 
    
         
            +
                    raise AuthorizationError, "Wrong conditions hash format: #{hash_or_attr.inspect}"
         
     | 
| 
      
 460 
     | 
    
         
            +
                  end
         
     | 
| 
      
 461 
     | 
    
         
            +
                end
         
     | 
| 
      
 462 
     | 
    
         
            +
             
     | 
| 
      
 463 
     | 
    
         
            +
                # may return an array of obligations to be OR'ed
         
     | 
| 
      
 464 
     | 
    
         
            +
                def obligation (attr_validator, hash_or_attr = nil)
         
     | 
| 
      
 465 
     | 
    
         
            +
                  hash_or_attr ||= @attr_hash
         
     | 
| 
      
 466 
     | 
    
         
            +
                  case hash_or_attr
         
     | 
| 
      
 467 
     | 
    
         
            +
                  when Symbol
         
     | 
| 
      
 468 
     | 
    
         
            +
                    obligations = attr_validator.engine.obligations(@privilege,
         
     | 
| 
      
 469 
     | 
    
         
            +
                                      :context => @context || hash_or_attr.to_s.pluralize.to_sym,
         
     | 
| 
      
 470 
     | 
    
         
            +
                                      :user    => attr_validator.user)
         
     | 
| 
      
 471 
     | 
    
         
            +
                    obligations.collect {|obl| {hash_or_attr => obl} }
         
     | 
| 
      
 472 
     | 
    
         
            +
                  when Hash
         
     | 
| 
      
 473 
     | 
    
         
            +
                    obligations_array_attrs = []
         
     | 
| 
      
 474 
     | 
    
         
            +
                    obligations =
         
     | 
| 
      
 475 
     | 
    
         
            +
                        hash_or_attr.inject({}) do |all, pair|
         
     | 
| 
      
 476 
     | 
    
         
            +
                          attr, sub_hash = pair
         
     | 
| 
      
 477 
     | 
    
         
            +
                          all[attr] = obligation(attr_validator, sub_hash)
         
     | 
| 
      
 478 
     | 
    
         
            +
                          if all[attr].length > 1
         
     | 
| 
      
 479 
     | 
    
         
            +
                            obligations_array_attrs << attr
         
     | 
| 
      
 480 
     | 
    
         
            +
                          else
         
     | 
| 
      
 481 
     | 
    
         
            +
                            all[attr] = all[attr].first
         
     | 
| 
      
 482 
     | 
    
         
            +
                          end
         
     | 
| 
      
 483 
     | 
    
         
            +
                          all
         
     | 
| 
      
 484 
     | 
    
         
            +
                        end
         
     | 
| 
      
 485 
     | 
    
         
            +
                    obligations = [obligations]
         
     | 
| 
      
 486 
     | 
    
         
            +
                    obligations_array_attrs.each do |attr|
         
     | 
| 
      
 487 
     | 
    
         
            +
                      next_array_size = obligations.first[attr].length
         
     | 
| 
      
 488 
     | 
    
         
            +
                      obligations = obligations.collect do |obls|
         
     | 
| 
      
 489 
     | 
    
         
            +
                        (0...next_array_size).collect do |idx|
         
     | 
| 
      
 490 
     | 
    
         
            +
                          obls_wo_array = obls.clone
         
     | 
| 
      
 491 
     | 
    
         
            +
                          obls_wo_array[attr] = obls_wo_array[attr][idx]
         
     | 
| 
      
 492 
     | 
    
         
            +
                          obls_wo_array
         
     | 
| 
      
 493 
     | 
    
         
            +
                        end
         
     | 
| 
      
 494 
     | 
    
         
            +
                      end.flatten
         
     | 
| 
      
 495 
     | 
    
         
            +
                    end
         
     | 
| 
      
 496 
     | 
    
         
            +
                    obligations
         
     | 
| 
      
 497 
     | 
    
         
            +
                  else
         
     | 
| 
      
 498 
     | 
    
         
            +
                    raise AuthorizationError, "Wrong conditions hash format: #{hash_or_attr.inspect}"
         
     | 
| 
      
 499 
     | 
    
         
            +
                  end
         
     | 
| 
      
 500 
     | 
    
         
            +
                end
         
     | 
| 
      
 501 
     | 
    
         
            +
             
     | 
| 
      
 502 
     | 
    
         
            +
                def to_long_s
         
     | 
| 
      
 503 
     | 
    
         
            +
                  "if_permitted_to #{@privilege.inspect}, #{@attr_hash.inspect}"
         
     | 
| 
      
 504 
     | 
    
         
            +
                end
         
     | 
| 
      
 505 
     | 
    
         
            +
              end
         
     | 
| 
      
 506 
     | 
    
         
            +
              
         
     | 
| 
      
 507 
     | 
    
         
            +
              # Represents a pseudo-user to facilitate guest users in applications
         
     | 
| 
      
 508 
     | 
    
         
            +
              class GuestUser
         
     | 
| 
      
 509 
     | 
    
         
            +
                attr_reader :role_symbols
         
     | 
| 
      
 510 
     | 
    
         
            +
                def initialize (roles = [:guest])
         
     | 
| 
      
 511 
     | 
    
         
            +
                  @role_symbols = roles
         
     | 
| 
      
 512 
     | 
    
         
            +
                end
         
     | 
| 
      
 513 
     | 
    
         
            +
              end
         
     | 
| 
      
 514 
     | 
    
         
            +
            end
         
     |