zuul 0.1.1 → 0.2.0

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.
Files changed (80) hide show
  1. data/lib/generators/zuul/orm_helpers.rb +21 -0
  2. data/lib/generators/zuul/permission_generator.rb +57 -0
  3. data/lib/generators/zuul/permission_role_generator.rb +40 -0
  4. data/lib/generators/zuul/permission_subject_generator.rb +40 -0
  5. data/lib/generators/zuul/role_generator.rb +58 -0
  6. data/lib/generators/zuul/role_subject_generator.rb +40 -0
  7. data/lib/generators/zuul/subject_generator.rb +39 -0
  8. data/lib/generators/zuul/templates/permission.rb +18 -0
  9. data/lib/generators/zuul/templates/permission_existing.rb +25 -0
  10. data/lib/generators/zuul/templates/permission_role.rb +17 -0
  11. data/lib/generators/zuul/templates/permission_role_existing.rb +24 -0
  12. data/lib/generators/zuul/templates/permission_subject.rb +17 -0
  13. data/lib/generators/zuul/templates/permission_subject_existing.rb +24 -0
  14. data/lib/generators/zuul/templates/role.rb +20 -0
  15. data/lib/generators/zuul/templates/role_existing.rb +27 -0
  16. data/lib/generators/zuul/templates/role_subject.rb +17 -0
  17. data/lib/generators/zuul/templates/role_subject_existing.rb +24 -0
  18. data/lib/tasks/zuul.rake +56 -0
  19. data/lib/zuul.rb +14 -5
  20. data/lib/zuul/action_controller.rb +108 -0
  21. data/lib/zuul/action_controller/dsl.rb +384 -0
  22. data/lib/zuul/action_controller/evaluators.rb +60 -0
  23. data/lib/zuul/active_record.rb +338 -0
  24. data/lib/zuul/active_record/context.rb +38 -0
  25. data/lib/zuul/active_record/permission.rb +31 -0
  26. data/lib/zuul/active_record/permission_role.rb +29 -0
  27. data/lib/zuul/active_record/permission_subject.rb +29 -0
  28. data/lib/zuul/active_record/role.rb +117 -0
  29. data/lib/zuul/active_record/role_subject.rb +29 -0
  30. data/lib/zuul/active_record/scope.rb +71 -0
  31. data/lib/zuul/active_record/subject.rb +239 -0
  32. data/lib/zuul/configuration.rb +149 -0
  33. data/lib/zuul/context.rb +53 -0
  34. data/lib/zuul/exceptions.rb +3 -0
  35. data/lib/zuul/exceptions/access_denied.rb +9 -0
  36. data/lib/zuul/exceptions/invalid_context.rb +9 -0
  37. data/lib/zuul/exceptions/undefined_scope.rb +9 -0
  38. data/lib/zuul/railtie.rb +5 -0
  39. data/lib/zuul/version.rb +3 -0
  40. data/lib/zuul_viz.rb +195 -0
  41. data/spec/db/schema.rb +172 -0
  42. data/spec/spec_helper.rb +25 -0
  43. data/spec/support/capture_stdout.rb +12 -0
  44. data/spec/support/models.rb +167 -0
  45. data/spec/zuul/active_record/context_spec.rb +55 -0
  46. data/spec/zuul/active_record/permission_role_spec.rb +84 -0
  47. data/spec/zuul/active_record/permission_spec.rb +174 -0
  48. data/spec/zuul/active_record/permission_subject_spec.rb +84 -0
  49. data/spec/zuul/active_record/role_spec.rb +694 -0
  50. data/spec/zuul/active_record/role_subject_spec.rb +84 -0
  51. data/spec/zuul/active_record/scope_spec.rb +75 -0
  52. data/spec/zuul/active_record/subject_spec.rb +1186 -0
  53. data/spec/zuul/active_record_spec.rb +624 -0
  54. data/spec/zuul/configuration_spec.rb +254 -0
  55. data/spec/zuul/context_spec.rb +128 -0
  56. data/spec/zuul_spec.rb +15 -0
  57. metadata +181 -70
  58. data/.document +0 -5
  59. data/.gitignore +0 -23
  60. data/LICENSE +0 -20
  61. data/README.rdoc +0 -65
  62. data/Rakefile +0 -54
  63. data/VERSION +0 -1
  64. data/lib/zuul/restrict_access.rb +0 -104
  65. data/lib/zuul/valid_roles.rb +0 -37
  66. data/spec/rails_root/app/controllers/application_controller.rb +0 -2
  67. data/spec/rails_root/app/models/user.rb +0 -8
  68. data/spec/rails_root/config/boot.rb +0 -110
  69. data/spec/rails_root/config/database.yml +0 -5
  70. data/spec/rails_root/config/environment.rb +0 -7
  71. data/spec/rails_root/config/environments/test.rb +0 -7
  72. data/spec/rails_root/config/initializers/session_store.rb +0 -15
  73. data/spec/rails_root/config/routes.rb +0 -4
  74. data/spec/rails_root/db/test.sqlite3 +0 -0
  75. data/spec/rails_root/log/test.log +0 -5388
  76. data/spec/rails_root/spec/controllers/require_user_spec.rb +0 -138
  77. data/spec/rails_root/spec/controllers/restrict_access_spec.rb +0 -64
  78. data/spec/rails_root/spec/models/user_spec.rb +0 -37
  79. data/spec/rails_root/spec/spec_helper.rb +0 -34
  80. data/zuul.gemspec +0 -78
@@ -0,0 +1,27 @@
1
+ class AddZuulRoleTo<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def self.up
3
+ change_table(:<%= table_name %>) do |t|
4
+ <%= migration_data -%>
5
+
6
+ <% attributes.each do |attribute| -%>
7
+ t.<%= attribute.type %> :<%= attribute.name %>
8
+ <% end -%>
9
+
10
+ # Uncomment below if timestamps were not included in your original model.
11
+ # t.timestamps
12
+ end
13
+
14
+ add_index :<%= table_name %>, :slug
15
+ add_index :<%= table_name %>, :level
16
+ add_index :<%= table_name %>, :context_type
17
+ add_index :<%= table_name %>, :context_id
18
+ add_index :<%= table_name %>, [:slug, :context_type, :context_id], :unique => true
19
+ add_index :<%= table_name %>, [:level, :context_type, :context_id], :unique => true
20
+ end
21
+
22
+ def self.down
23
+ # By default, we don't want to make any assumption about how to roll back a migration when your
24
+ # model already existed. Please edit below which fields you would like to remove in this migration.
25
+ raise ActiveRecord::IrreversibleMigration
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ class ZuulRoleSubjectCreate<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def change
3
+ create_table(:<%= table_name %>) do |t|
4
+ <% attributes.each do |attribute| -%>
5
+ t.<%= attribute.type %> :<%= attribute.name %>
6
+ <% end -%>
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :<%= table_name %>, :<%= role_model.to_s.underscore.singularize %>_id
12
+ add_index :<%= table_name %>, :<%= subject_model.to_s.underscore.singularize %>_id
13
+ add_index :<%= table_name %>, :context_type
14
+ add_index :<%= table_name %>, :context_id
15
+ add_index :<%= table_name %>, [:<%= role_model.to_s.underscore.singularize %>_id, :<%= subject_model.to_s.underscore.singularize %>_id, :context_type, :context_id], :unique => true, :name => 'index_<%= table_name %>_on_<%= role_model.to_s.underscore.singularize %>_and_<%= subject_model.to_s.underscore.singularize %>_and_context'
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ class AddZuulRoleSubjectTo<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def self.up
3
+ change_table(:<%= table_name %>) do |t|
4
+ <% attributes.each do |attribute| -%>
5
+ t.<%= attribute.type %> :<%= attribute.name %>
6
+ <% end -%>
7
+
8
+ # Uncomment below if timestamps were not included in your original model.
9
+ # t.timestamps
10
+ end
11
+
12
+ add_index :<%= table_name %>, :<%= role_model.to_s.underscore.singularize %>_id
13
+ add_index :<%= table_name %>, :<%= subject_model.to_s.underscore.singularize %>_id
14
+ add_index :<%= table_name %>, :context_type
15
+ add_index :<%= table_name %>, :context_id
16
+ add_index :<%= table_name %>, [:<%= role_model.to_s.underscore.singularize %>_id, :<%= subject_model.to_s.underscore.singularize %>_id, :context_type, :context_id], :unique => true, :name => 'index_<%= table_name %>_on_<%= role_model.to_s.underscore.singularize %>_and_<%= subject_model.to_s.underscore.singularize %>_and_context'
17
+ end
18
+
19
+ def self.down
20
+ # By default, we don't want to make any assumption about how to roll back a migration when your
21
+ # model already existed. Please edit below which fields you would like to remove in this migration.
22
+ raise ActiveRecord::IrreversibleMigration
23
+ end
24
+ end
@@ -0,0 +1,56 @@
1
+ require 'zuul_viz'
2
+ namespace :zuul do
3
+ desc "Visualize a map of all Zuul roles, permissions and subjects -- optionally pass FORMAT=format and FILENAME=filename to control output"
4
+ task :viz => :environment do |t|
5
+ format = ENV["FORMAT"] || :png
6
+ filename = ENV["FILENAME"] || "zuul_viz.#{format.to_s}"
7
+ ZuulViz.new.graph.output format.to_sym => "#{Rails.root}/tmp/#{filename}"
8
+ puts "Zuul graph saved to #{Rails.root}/tmp/#{filename}"
9
+ end
10
+
11
+ desc "Report statistics about all Zuul roles, permissions and subjects (Not Yet Implemented)"
12
+ task :stats => :environment do |t|
13
+ puts "Not Yet Implemented"
14
+ end
15
+
16
+ namespace :viz do
17
+ desc "Visualize a map of assigned roles and permissions for a specific Zuul subject -- supports FORMAT and FILENAME parameters"
18
+ task :subject, [:subject_id] => :environment do |t,args|
19
+ if args.subject_id.nil?
20
+ puts "Please provide a subject ID using the syntax `rake #{t.name}[subject_id]`"
21
+ exit
22
+ end
23
+ format = ENV["FORMAT"] || :png
24
+ filename = ENV["FILENAME"] || "zuul_viz_subject_#{args.subject_id}.#{format.to_s}"
25
+
26
+ ZuulViz.new.graph_subject(args.subject_id.to_i).output format.to_sym => "#{Rails.root}/tmp/#{filename}"
27
+ puts "Subject graph saved to #{Rails.root}/tmp/#{filename}"
28
+ end
29
+
30
+ desc "Visualize a map of subject assignments and permissions for a specific Zuul role -- supports FORMAT and FILENAME parameters"
31
+ task :role, [:role_id] => :environment do |t,args|
32
+ if args.role_id.nil?
33
+ puts "Please provide a role ID using the syntax `rake #{t.name}[role_id]`"
34
+ exit
35
+ end
36
+ format = ENV["FORMAT"] || :png
37
+ filename = ENV["FILENAME"] || "zuul_viz_subject_#{args.role_id}.#{format.to_s}"
38
+
39
+ ZuulViz.new.graph_role(args.role_id.to_i).output format.to_sym => "#{Rails.root}/tmp/#{filename}"
40
+ puts "Role graph saved to #{Rails.root}/tmp/#{filename}"
41
+ end
42
+
43
+ desc "Visualize a map of role and subject assignments for a specific Zuul permission -- supports FORMAT and FILENAME parameters"
44
+ task :permission, [:permission_id] => :environment do |t,args|
45
+ if args.permission_id.nil?
46
+ puts "Please provide a permission ID using the syntax `rake #{t.name}[permission_id]`"
47
+ exit
48
+ end
49
+ format = ENV["FORMAT"] || :png
50
+ filename = ENV["FILENAME"] || "zuul_viz_subject_#{args.permission_id}.#{format.to_s}"
51
+
52
+ ZuulViz.new.graph_permission(args.permission_id.to_i).output format.to_sym => "#{Rails.root}/tmp/#{filename}"
53
+ puts "Permission graph saved to #{Rails.root}/tmp/#{filename}"
54
+ end
55
+ end
56
+ end
data/lib/zuul.rb CHANGED
@@ -1,8 +1,17 @@
1
- require 'zuul/valid_roles'
2
- require 'zuul/restrict_access'
1
+ require 'zuul/exceptions'
2
+ require 'zuul/configuration'
3
3
 
4
- # ActiveRecord::Base.send(:include, Zuul::ValidRoles::ClassMethods)
4
+ module Zuul
5
+ mattr_reader :configuration
6
+ @@configuration = Zuul::Configuration.new
5
7
 
6
- Class.class_eval do
7
- include Zuul::ValidRoles::ClassMethods
8
+ def self.configure(&block)
9
+ @@configuration.configure &block
10
+ end
8
11
  end
12
+
13
+ require 'zuul/context'
14
+ require 'zuul/active_record'
15
+ require 'zuul/action_controller'
16
+
17
+ require 'zuul/railtie' if defined?(Rails)
@@ -0,0 +1,108 @@
1
+ require 'zuul/action_controller/dsl'
2
+ require 'zuul/action_controller/evaluators'
3
+
4
+ module Zuul
5
+ module ActionController
6
+ def self.included(base)
7
+ base.send :extend, ClassMethods
8
+ base.send :include, InstanceMethods
9
+ end
10
+
11
+ module InstanceMethods
12
+ def self.included(base)
13
+ base.send :helper_method, :authorized?
14
+ base.send :helper_method, :for_role
15
+ base.send :helper_method, :for_role_or_higher
16
+ base.send :helper_method, :for_permission
17
+ base.send :helper_method, :except_for_role
18
+ base.send :helper_method, :except_for_role_or_higher
19
+ base.send :helper_method, :except_for_permission
20
+ end
21
+
22
+ def authorized?
23
+ return true if @acl_dsl.nil?
24
+ @acl_dsl.authorized?
25
+ end
26
+
27
+ def for_role(role, context=nil, force_context=nil, &block)
28
+ return Evaluators::ForRole.new(self, role, context, force_context, &block)
29
+ end
30
+
31
+ def for_role_or_higher(role, context=nil, force_context=nil, &block)
32
+ return Evaluators::ForRoleOrHigher.new(self, role, context, force_context, &block)
33
+ end
34
+
35
+ def for_permission(permission, context=nil, force_context=nil, &block)
36
+ return Evaluators::ForPermission.new(self, permission, context, force_context, &block)
37
+ end
38
+
39
+ def except_for_role(role, context=nil, force_context=nil, &block)
40
+ return Evaluators::ForRole.new(self, role, context, force_context).else(&block)
41
+ end
42
+
43
+ def except_for_role_or_higher(role, context=nil, force_context=nil, &block)
44
+ return Evaluators::ForRoleOrHigher.new(self, role, context, force_context).else(&block)
45
+ end
46
+
47
+ def except_for_permission(permission, context=nil, force_context=nil, &block)
48
+ return Evaluators::ForPermission.new(self, permission, context, force_context).else(&block)
49
+ end
50
+ end
51
+
52
+ module ClassMethods
53
+ def self.extended(base)
54
+ base.send :cattr_accessor, :used_acl_filters
55
+ base.send :class_variable_set, :@@used_acl_filters, 0
56
+ base.send :attr_accessor, :acl_dsl
57
+ end
58
+
59
+ def access_control(*args, &block)
60
+ opts, filter_args = parse_access_control_args(*args)
61
+
62
+ callback_method = "_zuul_callback_before_#{acl_filters.length+1}".to_sym
63
+ define_method callback_method do |controller|
64
+ controller.acl_dsl ||= DSL::Base.new(controller)
65
+ controller.acl_dsl.configure opts
66
+ controller.acl_dsl.execute &block
67
+ self.class.used_acl_filters += 1
68
+
69
+ if self.class.used_acl_filters == self.class.acl_filters.length
70
+ self.class.used_acl_filters = 0
71
+ raise Exceptions::AccessDenied if !controller.acl_dsl.authorized? && controller.acl_dsl.mode != :quiet
72
+ end
73
+ end
74
+ append_before_filter "#{callback_method.to_s}(self)".to_sym, filter_args
75
+ end
76
+
77
+ def acl_filters
78
+ _process_action_callbacks.select { |f| f.kind == :before && f.filter.match(/\A_zuul_callback_before_.*/) }
79
+ end
80
+
81
+ # TODO maybe implement these to be used as simple wrappers for access_control
82
+ #def allow_roles(roles, *args, &block)
83
+ #end
84
+ #alias_method :allow_role, :allow_roles
85
+
86
+ #def allow_permissions(permissions, *args, &block)
87
+ #end
88
+ #alias_method :allow_permission, :allow_permissions
89
+
90
+ #def deny_roles(roles, *args, &block)
91
+ #end
92
+ #alias_method :deny_role, :deny_roles
93
+
94
+ #def deny_permissions(permissions, *args, &block)
95
+ #end
96
+ #alias_method :deny_permission, :deny_permissions
97
+
98
+ def parse_access_control_args(*args)
99
+ args = args[0] if args.is_a?(Array)
100
+ filter_args = args.select { |k,v| [:except, :only].include?(k) }
101
+ [:except, :only].each { |k| args.delete(k) }
102
+ return [args, filter_args]
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ ActionController::Base.send :include, Zuul::ActionController
@@ -0,0 +1,384 @@
1
+ module Zuul
2
+ module ActionController
3
+ module DSL
4
+ class Base
5
+ attr_reader :default, :context, :force_context, :mode, :default_block_allow_rules, :default_block_deny_rules, :actions, :roles, :permissions, :results, :subject_method, :scope
6
+
7
+ def actions(*actions, &block)
8
+ actions = actions[0] if actions.length == 1 && actions[0].is_a?(Array)
9
+ opts = options
10
+ opts[:actions].concat(actions)
11
+ return unless opts[:actions].map(&:to_sym).include?(@controller.params[:action].to_sym)
12
+ dsl = Actions.new(@controller, opts)
13
+ dsl.instance_eval(&block) if block_given?
14
+
15
+ @results.concat dsl.results
16
+ end
17
+
18
+ def context(ctxt, &block)
19
+ opts = options.merge(:context => ctxt)
20
+ dsl = self.class.new(@controller, opts)
21
+ dsl.instance_eval(&block) if block_given?
22
+
23
+ @results.concat dsl.results
24
+ end
25
+
26
+ def force_context(flag=true, &block)
27
+ opts = options.merge(:force_context => flag)
28
+ dsl = self.class.new(@controller, opts)
29
+ dsl.instance_eval(&block) if block_given?
30
+
31
+ @results.concat dsl.results
32
+ end
33
+
34
+ def roles(*allowed, &block)
35
+ allowed = allowed[0] if allowed.length == 1 && allowed[0].is_a?(Array)
36
+ opts = options
37
+ opts[:roles].concat(allowed)
38
+ dsl = Roles.new(@controller, opts)
39
+ dsl.instance_eval(&block) if block_given?
40
+
41
+ @results.concat dsl.results
42
+ end
43
+
44
+ def permissions(*allowed, &block)
45
+ allowed = allowed[0] if allowed.length == 1 && allowed[0].is_a?(Array)
46
+ opts = options
47
+ opts[:permissions].concat(allowed)
48
+ dsl = Permissions.new(@controller, opts)
49
+ dsl.instance_eval(&block) if block_given?
50
+
51
+ @results.concat dsl.results
52
+ end
53
+
54
+ def scope(scope, &block)
55
+ opts = options.merge(:scope => scope)
56
+ dsl = self.class.new(@controller, opts)
57
+ dsl.instance_eval(&block) if block_given?
58
+
59
+ @results.concat dsl.results
60
+ end
61
+
62
+ def allow_roles(*allowed)
63
+ allowed = allowed[0] if allowed.length == 1 && allowed[0].is_a?(Array)
64
+ roles *allowed do
65
+ allow *@actions
66
+ end
67
+ end
68
+ alias_method :allow_role, :allow_roles
69
+ alias_method :allow, :allow_roles
70
+
71
+ def allow_permissions(*allowed)
72
+ allowed = allowed[0] if allowed.length == 1 && allowed[0].is_a?(Array)
73
+ permissions *allowed do
74
+ allow *@actions
75
+ end
76
+ end
77
+ alias_method :allow_permission, :allow_permissions
78
+
79
+ def deny_roles(*denied)
80
+ denied = denied[0] if denied.length == 1 && denied[0].is_a?(Array)
81
+ roles *denied do
82
+ deny *@actions
83
+ end
84
+ end
85
+ alias_method :deny_role, :deny_roles
86
+ alias_method :deny, :deny_roles
87
+
88
+ def deny_permissions(*denied)
89
+ denied = denied[0] if denied.length == 1 && denied[0].is_a?(Array)
90
+ permissions *denied do
91
+ deny *@actions
92
+ end
93
+ end
94
+ alias_method :deny_permission, :deny_permissions
95
+
96
+ def all_actions
97
+ @controller.class.action_methods.select { |act| !act.match(/^_callback_before_[\d]*$/) }.map(&:to_sym)
98
+ end
99
+
100
+ def subject
101
+ @controller.send(@subject_method)
102
+ end
103
+
104
+ def logged_out
105
+ :_zuul_logged_out
106
+ end
107
+ alias_method :anonymous, :logged_out
108
+
109
+ def logged_in
110
+ :_zuul_logged_in
111
+ end
112
+
113
+ def anyone
114
+ [logged_in, logged_out]
115
+ end
116
+
117
+ def all_roles(context=false)
118
+ return [] if subject.nil?
119
+ context = (context == false) ? @context : parse_context(context)
120
+ found_roles = subject.auth_scope(@scope).role_class.where(:context_type => context.type, :context_id => context.id).to_a
121
+ found_roles.concat(subject.auth_scope(@scope).role_class.where(:context_type => context.type, :context_id => nil).to_a) unless context.id.nil?
122
+ found_roles.concat(subject.auth_scope(@scope).role_class.where(:context_type => nil, :context_id => nil).to_a) unless context.type.nil?
123
+ found_roles
124
+ end
125
+
126
+ def all_permissions(context=false)
127
+ return [] if subject.nil?
128
+ context = (context == false) ? @context : parse_context(context)
129
+ found_permissions = subject.auth_scope(@scope).permission_class.where(:context_type => context.type, :context_id => context.id).to_a
130
+ found_permissions.concat(subject.auth_scope(@scope).permission_class.where(:context_type => context.type, :context_id => nil).to_a) unless context.id.nil?
131
+ found_permissions.concat(subject.auth_scope(@scope).permission_class.where(:context_type => nil, :context_id => nil).to_a) unless context.type.nil?
132
+ found_permissions
133
+ end
134
+
135
+ def contextual_role(slug, context=false)
136
+ return nil if subject.nil?
137
+ context = (context == false) ? @context : parse_context(context)
138
+ return subject.auth_scope(@scope) { target_role(slug, context.to_context) }
139
+ end
140
+ alias_method :role, :contextual_role
141
+
142
+ def contextual_permission(slug, context=false)
143
+ return nil if subject.nil?
144
+ context = (context == false) ? @context : parse_context(context)
145
+ return subject.auth_scope(@scope) { target_permission(slug, context.to_context) }
146
+ end
147
+ alias_method :permission, :contextual_permission
148
+
149
+ def options
150
+ {
151
+ :default => @default,
152
+ :actions => @actions.clone,
153
+ :roles => @roles.clone,
154
+ :permissions => @permissions.clone,
155
+ :context => @context.clone,
156
+ :force_context => @force_context,
157
+ :subject_method => @subject_method,
158
+ :scope => @scope,
159
+ :mode => @mode,
160
+ :collect_results => @collect_results,
161
+ :allow => (@default_block_allow_rules.nil? ? @default_block_allow_rules : @default_block_allow_rules.clone),
162
+ :deny => (@default_block_deny_rules.nil? ? @default_block_deny_rules : @default_block_deny_rules.clone),
163
+ }
164
+ end
165
+
166
+ def set_options(opts)
167
+ [:default, :actions, :roles, :permissions, :force_context, :mode, :collect_results, :subject_method, :scope].each do |key|
168
+ instance_variable_set "@#{key.to_s}", opts[key] if opts.has_key?(key)
169
+ end
170
+ [:allow, :deny].each do |key|
171
+ instance_variable_set "@default_block_#{key.to_s}_rules", opts[key] if opts.has_key?(key)
172
+ end
173
+ @context = parse_context(opts[:context]) if opts.has_key?(:context)
174
+ self
175
+ end
176
+ alias_method :configure, :set_options
177
+
178
+ def parse_context(context=nil)
179
+ if context.is_a?(String) || context.is_a?(Symbol)
180
+ if context.to_s.match(/^@.*$/)
181
+ context = @controller.send(:instance_variable_get, context)
182
+ elsif @controller.respond_to?(context.to_sym)
183
+ context = @controller.send(context)
184
+ end
185
+ end
186
+
187
+ Zuul::Context.parse(context)
188
+ end
189
+
190
+ def execute(&block)
191
+ log_timer_start = Time.now.to_f
192
+ if block_given?
193
+ instance_eval(&block)
194
+ else
195
+ instance_eval do
196
+ [:allow, :deny].each do |auth_type|
197
+ auth_opts = instance_variable_get("@default_block_#{auth_type.to_s}_rules")
198
+ next if auth_opts.nil?
199
+
200
+ auth_actions = @actions
201
+ auth_opts[:actions] = [auth_opts[:actions]] if auth_opts.has_key?(:actions) && !auth_opts[:actions].is_a?(Array)
202
+ if !auth_opts.has_key?(:actions) || auth_opts[:actions].empty?
203
+ auth_actions << @controller.params[:action].to_sym if auth_actions.empty?
204
+ else
205
+ auth_actions.concat(auth_opts[:actions])
206
+ end
207
+
208
+ actions auth_actions do
209
+ [:roles, :permissions].each do |allowable_type|
210
+ if auth_opts.has_key?(allowable_type)
211
+ send "#{auth_type.to_s}_#{allowable_type.to_s}", auth_opts[allowable_type]
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end
218
+ # only collect results if configured & there are more filters in the chain
219
+ logger.debug " \e[1;34mACL (#{((Time.now.to_f - log_timer_start) * 1000.0).round(1)}ms)\e[0m #{(authorized? ? "\e[1;32mALLOWED\e[0m" : "\e[1;31mDENIED\e[0m")} using \e[1m#{@default.to_s.upcase}\e[0m [#{results.map { |r| "\e[#{(r ? "32mallow" : "31mdeny")}\e[0m" }.join(",")}]"
220
+ collect_results if @collect_results && @controller.class.acl_filters.length > 0
221
+ end
222
+
223
+ def authorized?
224
+ if @default == :deny
225
+ !(@results.empty? || @results.any? { |result| result == false })
226
+ else
227
+ (@results.empty? || !@results.all? { |result| result == false })
228
+ end
229
+ end
230
+
231
+ def collect_results
232
+ @results = [authorized?]
233
+ end
234
+
235
+ protected
236
+
237
+ def initialize(controller, opts={})
238
+ @controller = controller
239
+ # TODO catch 22: need config for subject_method, but need subject_method to check if subject
240
+ opts = {:subject_method => Zuul.configuration.subject_method, :scope => :default}.merge(opts)
241
+ config = @controller.send(opts[:subject_method]).nil? ? Zuul.configuration : @controller.send(opts[:subject_method]).auth_scope(opts[:scope]).config
242
+ opts = {:default => config.acl_default, :force_context => config.force_context, :context => nil, :mode => config.acl_mode, :collect_results => config.acl_collect_results, :allow => nil, :deny => nil, :actions => [], :roles => [], :permissions => []}.merge(opts)
243
+ set_options opts
244
+ @results = []
245
+ end
246
+
247
+ def logger
248
+ @controller.logger
249
+ end
250
+ end
251
+
252
+ class Actions < Base
253
+ end
254
+
255
+ class Actionable < Base
256
+ def all
257
+ all_actions
258
+ end
259
+
260
+ def allow?(role_or_perm)
261
+ match? role_or_perm
262
+ end
263
+
264
+ def deny?(role_or_perm)
265
+ match? role_or_perm
266
+ end
267
+ end
268
+
269
+ class Roles < Actionable
270
+
271
+ def match?(role)
272
+ (@or_higher && subject.auth_scope(@scope, @context, @force_context) { |context, force_context| has_role_or_higher?(role, context.to_context, force_context) }) || (!@or_higher && subject.auth_scope(@scope, @context, @force_context) { |context, force_context| has_role?(role, context.to_context, force_context) })
273
+ end
274
+
275
+ def allow(*actions)
276
+ log_timer_start = Time.now.to_f
277
+ actions = actions[0] if actions.length == 1 && actions[0].is_a?(Array)
278
+ actions.concat(@actions)
279
+ return if @roles.empty? || actions.empty?
280
+ if actions.map(&:to_sym).include?(@controller.params[:action].to_sym)
281
+ @roles.each do |role|
282
+ if (role == logged_out && subject.nil?) ||
283
+ (role == logged_in && !subject.nil?)
284
+ @results << true
285
+ return
286
+ end
287
+
288
+ next if subject.nil? # keep going in case :_zuul_logged_out is specified
289
+
290
+ if allow?(role)
291
+ logger.debug " \e[1;33mACL (#{((Time.now.to_f - log_timer_start) * 1000.0).round(1)}ms)\e[0m \e[1mMATCH\e[0m for \e[32mallow\e[0m role \e[1m#{role.is_a?(subject.auth_scope(@scope).role_class) ? "#{role.slug}[#{role.context.to_s}]" : role}\e[0m"
292
+ @results << true
293
+ return
294
+ end
295
+ logger.debug " \e[1;33mACL (#{((Time.now.to_f - log_timer_start) * 1000.0).round(1)}ms)\e[0m \e[1mNO MATCH\e[0m for \e[32mallow\e[0m role \e[1m#{role.is_a?(subject.auth_scope(@scope).role_class) ? "#{role.slug}[#{role.context.to_s}]" : role}\e[0m"
296
+ end
297
+ end
298
+ end
299
+
300
+ def deny(*actions)
301
+ log_timer_start = Time.now.to_f
302
+ actions = actions[0] if actions.length == 1 && actions[0].is_a?(Array)
303
+ actions.concat(@actions)
304
+ return if @roles.empty? || actions.empty?
305
+ if actions.map(&:to_sym).include?(@controller.params[:action].to_sym)
306
+ @roles.each do |role|
307
+ if (role == logged_out && subject.nil?) ||
308
+ (role == logged_in && !subject.nil?)
309
+ @results << false
310
+ return
311
+ end
312
+
313
+ next if subject.nil? # keep going in case :_zuul_logged_out is specified
314
+
315
+ if deny?(role)
316
+ logger.debug " \e[1;33mACL (#{((Time.now.to_f - log_timer_start) * 1000.0).round(1)}ms)\e[0m \e[1mMATCH\e[0m for \e[31mdeny\e[0m role \e[1m#{role.is_a?(subject.auth_scope(@scope).role_class) ? "#{role.slug}[#{role.context.to_s}]" : role}\e[0m"
317
+ @results << false
318
+ return
319
+ end
320
+ logger.debug " \e[1;33mACL (#{((Time.now.to_f - log_timer_start) * 1000.0).round(1)}ms)\e[0m \e[1mNO MATCH\e[0m for \e[31mdeny\e[0m role \e[1m#{role.is_a?(subject.auth_scope(@scope).role_class) ? "#{role.slug}[#{role.context.to_s}]" : role}\e[0m"
321
+ end
322
+ end
323
+ end
324
+
325
+ def or_higher(&block)
326
+ opts = options.merge(:or_higher => true)
327
+ dsl = self.class.new(@controller, opts)
328
+ dsl.instance_eval(&block) if block_given?
329
+
330
+ @results.concat dsl.results
331
+ end
332
+
333
+ protected
334
+
335
+ def initialize(controller, opts={})
336
+ super
337
+ opts = {:or_higher => false}.merge(opts)
338
+ @or_higher = opts[:or_higher]
339
+ end
340
+ end
341
+
342
+ class Permissions < Actionable
343
+
344
+ def match?(permission)
345
+ subject.auth_scope(@scope, @context, @force_context) { |context, force_context| has_permission?(permission, context.to_context, force_context) }
346
+ end
347
+
348
+ def allow(*actions)
349
+ log_timer_start = Time.now.to_f
350
+ actions = actions[0] if actions.length == 1 && actions[0].is_a?(Array)
351
+ actions.concat(@actions)
352
+ return if subject.nil? || @permissions.empty? || actions.empty?
353
+ if actions.map(&:to_sym).include?(@controller.params[:action].to_sym)
354
+ @permissions.each do |permission|
355
+ if allow?(permission)
356
+ logger.debug " \e[1;33mACL (#{((Time.now.to_f - log_timer_start) * 1000.0).round(1)}ms)\e[0m \e[1mMATCH\e[0m for \e[32mallow\e[0m permission \e[1m#{permission.is_a?(subject.auth_scope(@scope).role_class) ? "#{permission.slug}[#{permission.context.to_s}]" : permission}\e[0m"
357
+ @results << true
358
+ return
359
+ end
360
+ logger.debug " \e[1;33mACL (#{((Time.now.to_f - log_timer_start) * 1000.0).round(1)}ms)\e[0m \e[1mNO MATCH\e[0m for \e[32mallow\e[0m permission \e[1m#{permission.is_a?(subject.auth_scope(@scope).role_class) ? "#{permission.slug}[#{permission.context.to_s}]" : permission}\e[0m"
361
+ end
362
+ end
363
+ end
364
+
365
+ def deny(*actions)
366
+ log_timer_start = Time.now.to_f
367
+ actions = actions[0] if actions.length == 1 && actions[0].is_a?(Array)
368
+ actions.concat(@actions)
369
+ return if subject.nil? || @permissions.empty? || actions.empty?
370
+ if actions.map(&:to_sym).include?(@controller.params[:action].to_sym)
371
+ @permissions.each do |permission|
372
+ if deny?(permission)
373
+ logger.debug " \e[1;33mACL (#{((Time.now.to_f - log_timer_start) * 1000.0).round(1)}ms)\e[0m \e[1mMATCH\e[0m for \e[31mdeny\e[0m permission \e[1m#{permission.is_a?(subject.auth_scope(@scope).role_class) ? "#{permission.slug}[#{permission.context.to_s}]" : permission}\e[0m"
374
+ @results << false
375
+ return
376
+ end
377
+ logger.debug " \e[1;33mACL (#{((Time.now.to_f - log_timer_start) * 1000.0).round(1)}ms)\e[0m \e[1mNO MATCH\e[0m for \e[31mdeny\e[0m permission \e[1m#{permission.is_a?(subject.auth_scope(@scope).role_class) ? "#{permission.slug}[#{permission.context.to_s}]" : permission}\e[0m"
378
+ end
379
+ end
380
+ end
381
+ end
382
+ end
383
+ end
384
+ end