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.
- data/lib/generators/zuul/orm_helpers.rb +21 -0
- data/lib/generators/zuul/permission_generator.rb +57 -0
- data/lib/generators/zuul/permission_role_generator.rb +40 -0
- data/lib/generators/zuul/permission_subject_generator.rb +40 -0
- data/lib/generators/zuul/role_generator.rb +58 -0
- data/lib/generators/zuul/role_subject_generator.rb +40 -0
- data/lib/generators/zuul/subject_generator.rb +39 -0
- data/lib/generators/zuul/templates/permission.rb +18 -0
- data/lib/generators/zuul/templates/permission_existing.rb +25 -0
- data/lib/generators/zuul/templates/permission_role.rb +17 -0
- data/lib/generators/zuul/templates/permission_role_existing.rb +24 -0
- data/lib/generators/zuul/templates/permission_subject.rb +17 -0
- data/lib/generators/zuul/templates/permission_subject_existing.rb +24 -0
- data/lib/generators/zuul/templates/role.rb +20 -0
- data/lib/generators/zuul/templates/role_existing.rb +27 -0
- data/lib/generators/zuul/templates/role_subject.rb +17 -0
- data/lib/generators/zuul/templates/role_subject_existing.rb +24 -0
- data/lib/tasks/zuul.rake +56 -0
- data/lib/zuul.rb +14 -5
- data/lib/zuul/action_controller.rb +108 -0
- data/lib/zuul/action_controller/dsl.rb +384 -0
- data/lib/zuul/action_controller/evaluators.rb +60 -0
- data/lib/zuul/active_record.rb +338 -0
- data/lib/zuul/active_record/context.rb +38 -0
- data/lib/zuul/active_record/permission.rb +31 -0
- data/lib/zuul/active_record/permission_role.rb +29 -0
- data/lib/zuul/active_record/permission_subject.rb +29 -0
- data/lib/zuul/active_record/role.rb +117 -0
- data/lib/zuul/active_record/role_subject.rb +29 -0
- data/lib/zuul/active_record/scope.rb +71 -0
- data/lib/zuul/active_record/subject.rb +239 -0
- data/lib/zuul/configuration.rb +149 -0
- data/lib/zuul/context.rb +53 -0
- data/lib/zuul/exceptions.rb +3 -0
- data/lib/zuul/exceptions/access_denied.rb +9 -0
- data/lib/zuul/exceptions/invalid_context.rb +9 -0
- data/lib/zuul/exceptions/undefined_scope.rb +9 -0
- data/lib/zuul/railtie.rb +5 -0
- data/lib/zuul/version.rb +3 -0
- data/lib/zuul_viz.rb +195 -0
- data/spec/db/schema.rb +172 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/capture_stdout.rb +12 -0
- data/spec/support/models.rb +167 -0
- data/spec/zuul/active_record/context_spec.rb +55 -0
- data/spec/zuul/active_record/permission_role_spec.rb +84 -0
- data/spec/zuul/active_record/permission_spec.rb +174 -0
- data/spec/zuul/active_record/permission_subject_spec.rb +84 -0
- data/spec/zuul/active_record/role_spec.rb +694 -0
- data/spec/zuul/active_record/role_subject_spec.rb +84 -0
- data/spec/zuul/active_record/scope_spec.rb +75 -0
- data/spec/zuul/active_record/subject_spec.rb +1186 -0
- data/spec/zuul/active_record_spec.rb +624 -0
- data/spec/zuul/configuration_spec.rb +254 -0
- data/spec/zuul/context_spec.rb +128 -0
- data/spec/zuul_spec.rb +15 -0
- metadata +181 -70
- data/.document +0 -5
- data/.gitignore +0 -23
- data/LICENSE +0 -20
- data/README.rdoc +0 -65
- data/Rakefile +0 -54
- data/VERSION +0 -1
- data/lib/zuul/restrict_access.rb +0 -104
- data/lib/zuul/valid_roles.rb +0 -37
- data/spec/rails_root/app/controllers/application_controller.rb +0 -2
- data/spec/rails_root/app/models/user.rb +0 -8
- data/spec/rails_root/config/boot.rb +0 -110
- data/spec/rails_root/config/database.yml +0 -5
- data/spec/rails_root/config/environment.rb +0 -7
- data/spec/rails_root/config/environments/test.rb +0 -7
- data/spec/rails_root/config/initializers/session_store.rb +0 -15
- data/spec/rails_root/config/routes.rb +0 -4
- data/spec/rails_root/db/test.sqlite3 +0 -0
- data/spec/rails_root/log/test.log +0 -5388
- data/spec/rails_root/spec/controllers/require_user_spec.rb +0 -138
- data/spec/rails_root/spec/controllers/restrict_access_spec.rb +0 -64
- data/spec/rails_root/spec/models/user_spec.rb +0 -37
- data/spec/rails_root/spec/spec_helper.rb +0 -34
- 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
|
data/lib/tasks/zuul.rake
ADDED
@@ -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/
|
2
|
-
require 'zuul/
|
1
|
+
require 'zuul/exceptions'
|
2
|
+
require 'zuul/configuration'
|
3
3
|
|
4
|
-
|
4
|
+
module Zuul
|
5
|
+
mattr_reader :configuration
|
6
|
+
@@configuration = Zuul::Configuration.new
|
5
7
|
|
6
|
-
|
7
|
-
|
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
|