ixtlan-guard 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,8 +11,7 @@ module Ixtlan
11
11
 
12
12
  def allowed_groups(resource, action)
13
13
  if resource && action
14
- resource = resource.to_s
15
- groups = send(@load_method, resource)
14
+ groups = send(@load_method, resource.to_s)
16
15
  groups[action.to_s] || groups["defaults"] || []
17
16
  else
18
17
  []
@@ -1,74 +1,220 @@
1
1
  module Ixtlan
2
- module ActionController #:nodoc:
3
- module Guard #:nodoc:
2
+ module Guard
3
+ module ActionController
4
4
  def self.included(base)
5
5
  base.send(:include, InstanceMethods)
6
6
  base.send(:include, GroupsMethod)
7
7
  end
8
8
 
9
+ class Filter
10
+ attr_reader :block
11
+ def initialize(method, options, &block)
12
+ @only = options[:only]
13
+ @except = options[:except] || []
14
+ @reference = options[:reference]
15
+ @reference = @reference.to_sym if @reference
16
+ @block = block
17
+ @method = method.to_sym if method
18
+ raise "illegal arguments: either block or method name #{method}" if block && method
19
+ end
20
+
21
+ def proc(base, reference = nil)
22
+ if @block
23
+ if reference
24
+ Proc.new do |groups|
25
+ @block.call(groups, reference)
26
+ end
27
+ elsif @reference
28
+ Proc.new do |groups|
29
+ @block.call(groups, base.send(@reference))
30
+ end
31
+ else
32
+ @block
33
+ end
34
+ else
35
+ if base.respond_to?(@method) || base.private_methods.include?(@method.to_s)
36
+ if reference
37
+ Proc.new do |groups|
38
+ base.send(@method, groups, reference)
39
+ end
40
+ elsif @reference
41
+ Proc.new do |groups|
42
+ base.send(@method, groups, base.send(@reference))
43
+ end
44
+ else
45
+ base.method(@method)
46
+ end
47
+ else
48
+ if @reference
49
+ Proc.new do |groups|
50
+ base.class.send(@method, groups, base.send(@reference))
51
+ end
52
+ else
53
+ base.class.method(@method)
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def allowed?(action)
60
+ action = action.to_sym
61
+ (@only && @only.member?(action)) ||
62
+ ((@only.nil? || @only.empty?) && ! @except.member?(action))
63
+ end
64
+ end
65
+
9
66
  module GroupsMethod
10
67
 
11
68
  protected
12
69
 
13
- def groups_for_current_user
14
- if respond_to?(:current_user) && current_user
15
- current_user.groups
16
- else
17
- []
18
- end
70
+ def current_groups
71
+ @current_groups ||=
72
+ if respond_to?(:current_user) && current_user
73
+ current_user.groups
74
+ else
75
+ []
76
+ end
19
77
  end
20
78
  end
21
79
 
22
80
  module RootGroup
23
81
  protected
24
82
 
25
- def groups_for_current_user
83
+ def current_groups
26
84
  ['root']
27
85
  end
28
86
  end
29
87
 
30
88
  module InstanceMethods #:nodoc:
31
89
 
90
+ def self.included(base)
91
+ base.class_eval do
92
+
93
+ def self.guard_filter(*args, &block)
94
+ method = nil#"_intern_#{guard_filters.size}"
95
+ options = {}
96
+ case args.size
97
+ when 1
98
+ if args[0].is_a? Symbol
99
+ method = args[0]
100
+ else
101
+ options = args[0]
102
+ end
103
+ when 2
104
+ method = args[0].to_sym
105
+ options = args[1]
106
+ else
107
+ raise "argument error, expected (Symbol, Hash) or (Symbol) or (Hash)"
108
+ end
109
+
110
+ guard_filters << Filter.new(method, options, &block)
111
+ end
112
+
113
+ def self.guard_filters
114
+ @_guard_filters ||= []
115
+ end
116
+
117
+ def self.guard
118
+ Rails.application.config.guard
119
+ end
120
+
121
+ def self.allowed?(action, current_groups, reference = nil)
122
+ filter = guard_filters.detect do |f|
123
+ f.allowed?(action)
124
+ end
125
+ if filter
126
+ guard.check(self.controller_name,
127
+ action,
128
+ current_groups || []) do |groups|
129
+ filter.proc(self, reference).call(groups)
130
+ end
131
+ else
132
+ # TODO maybe do something with the reference
133
+ guard.check(controller_name,
134
+ action,
135
+ current_groups || [])
136
+ end
137
+ end
138
+ end
139
+ end
140
+
32
141
  protected
33
142
 
34
143
  def guard
35
- Rails.application.config.guard
144
+ self.class.guard
36
145
  end
37
146
 
38
- def check(association = nil, &block)
39
- group_method = respond_to?(:current_user_groups) ? :current_user_groups : :groups_for_current_user
40
- unless guard.allowed?(params[:controller],
41
- params[:action],
42
- send(group_method),
43
- association,
44
- &block)
45
- if association
46
- raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}##{association.class}(#{association.id})'")
47
- else
48
- raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}'")
147
+ def allowed?(action_or_actions,
148
+ controller = params[:controller],
149
+ reference = nil,
150
+ &block)
151
+ case action_or_actions
152
+ when Array
153
+ action_or_actions.detect do |action|
154
+ check(action, controller, reference, &block) != nil
49
155
  end
156
+ else
157
+ check(action_or_actions, controller, reference, &block) != nil
50
158
  end
51
- true
52
159
  end
53
- alias :authorize :check
54
160
 
55
- def authorization
56
- warn "DEPRECATED: use 'authorize' instead"
57
- check
161
+ def check(action = params[:action],
162
+ controller = params[:controller],
163
+ reference = nil,
164
+ &block)
165
+ unless block
166
+ filter = self.class.guard_filters.detect do |f|
167
+ f.allowed?(action)
168
+ end
169
+ end
170
+
171
+ if filter
172
+ guard.check(controller,
173
+ action,
174
+ current_groups || []) do |groups|
175
+ filter.proc(self).call(groups)
176
+ end
177
+ else
178
+ # TODO maybe do something with the reference
179
+ guard.check(controller,
180
+ action,
181
+ current_groups || [],
182
+ &block)
183
+ end
184
+ end
185
+
186
+ def authorize
187
+ unless check
188
+ raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}'")
189
+ end
190
+ true
58
191
  end
59
192
  end
60
193
  end
61
- end
62
194
 
63
- module Allowed #:nodoc:
64
- # Inclusion hook to make #allowed available as method
65
- def self.included(base)
66
- base.send(:include, InstanceMethods)
67
- end
195
+ module Allowed
196
+ # Inclusion hook to make #allowed available as method
197
+ def self.included(base)
198
+ base.send(:include, InstanceMethods)
199
+ end
68
200
 
69
- module InstanceMethods #:nodoc:
70
- def allowed?(resource, action)
71
- controller.send(:guard).allowed?(resource, action, controller.send(:groups_for_current_user))
201
+ module InstanceMethods #:nodoc:
202
+ def allowed?(resource, action, reference = nil)
203
+ if resource.to_s != controller.class.controller_name || reference
204
+ other = "#{resource}Controller".classify.constantize
205
+ if other.respond_to?(:allowed?)
206
+ if reference
207
+ other.send(:allowed?, action, controller.current_groups, reference)
208
+ else
209
+ other.send(:allowed?, action, controller.current_groups)
210
+ end
211
+ else
212
+ raise "can not find 'allowed?' on #{other}"
213
+ end
214
+ else
215
+ controller.send(:allowed?, action, resource)
216
+ end
217
+ end
72
218
  end
73
219
  end
74
220
  end
@@ -1,5 +1,5 @@
1
1
  require 'rails'
2
- require 'ixtlan/guard/guard_ng'
2
+ require 'ixtlan/guard/guard'
3
3
  require 'ixtlan/guard/guard_rails'
4
4
  require 'logger'
5
5
  require 'fileutils'
@@ -10,6 +10,9 @@ module Ixtlan
10
10
 
11
11
  config.before_configuration do |app|
12
12
  app.config.guards_dir = File.join(Rails.root, "app", "guards")
13
+ # needs to be here ?!?
14
+ ::ActionController::Base.send(:include, Ixtlan::Guard::ActionController)
15
+ ::ActionView::Base.send(:include, Ixtlan::Guard::Allowed)
13
16
  end
14
17
 
15
18
  config.after_initialize do |app|
@@ -20,11 +23,11 @@ module Ixtlan
20
23
  }
21
24
  options[:logger] = logger unless defined?(Slf4r)
22
25
  FileUtils.mkdir_p(app.config.guards_dir)
23
- app.config.guard = Ixtlan::Guard::GuardNG.new(options)
24
26
 
25
- ::ActionController::Base.send(:include, Ixtlan::ActionController::Guard)
26
- ::ActionController::Base.send(:before_filter, :authorize)
27
- ::ActionView::Base.send(:include, Ixtlan::Allowed)
27
+ controller = ::ApplicationController rescue ::ActionController::Base
28
+ controller.send(:before_filter, :authorize)
29
+
30
+ app.config.guard = Ixtlan::Guard::Guard.new(options)
28
31
  end
29
32
 
30
33
  config.generators do
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'ixtlan/guard/guard_ng'
2
+ require 'ixtlan/guard/guard'
3
3
  require 'logger'
4
4
  require 'fileutils'
5
5
 
@@ -12,11 +12,11 @@ def $logger.debug(&block)
12
12
  # info("\n\t[debug] " + block.call)
13
13
  end
14
14
 
15
- describe Ixtlan::Guard::GuardNG do
15
+ describe Ixtlan::Guard::Guard do
16
16
 
17
17
  context "without caching" do
18
18
  def not_cached
19
- $not_cached ||= Ixtlan::Guard::GuardNG.new(:guards_dir => File.dirname($target),
19
+ $not_cached ||= Ixtlan::Guard::Guard.new(:guards_dir => File.dirname($target),
20
20
  :logger => $logger )
21
21
  end
22
22
 
@@ -37,7 +37,7 @@ describe Ixtlan::Guard::GuardNG do
37
37
 
38
38
  context "with caching" do
39
39
  def cached
40
- $cached ||= Ixtlan::Guard::GuardNG.new(:guards_dir => File.dirname($target),
40
+ $cached ||= Ixtlan::Guard::Guard.new(:guards_dir => File.dirname($target),
41
41
  :logger => $logger,
42
42
  :cache => true)
43
43
  end
@@ -1,21 +1,38 @@
1
1
  require 'spec_helper'
2
- require 'ixtlan/guard/guard_ng'
2
+ require 'ixtlan/guard/guard'
3
3
  require 'logger'
4
4
 
5
- describe Ixtlan::Guard::GuardNG do
5
+ describe Ixtlan::Guard::Guard do
6
+
7
+ def assert(expected, perms)
8
+ map = {}
9
+ expected.each do |e|
10
+ map[e[:permission][:resource]] = e
11
+ if e[:permission][:actions]
12
+ e[:permission][:actions].sort!{ |n,m| n[:action][:name] <=> m[:action][:name] }
13
+ end
14
+ end
15
+ perms.each do |perm|
16
+ if perm[:actions]
17
+ perm[:actions].sort!{ |n,m| n.content[:name] <=> m.content[:name] }
18
+ end
19
+ map[perm[:resource].to_s].should == perm
20
+ end
21
+ end
6
22
 
7
23
  subject do
8
24
  logger = Logger.new(STDOUT)
9
25
  def logger.debug(&block)
10
26
  # info("\n\t[debug] " + block.call)
11
27
  end
12
- Ixtlan::Guard::GuardNG.new(:guards_dir => File.join(File.dirname(__FILE__), "guards"), :logger => logger )
28
+ Ixtlan::Guard::Guard.new(:guards_dir => File.join(File.dirname(__FILE__), "guards"), :logger => logger )
13
29
  end
14
30
 
15
31
  context '#permissions' do
16
32
 
17
33
  it 'should deny all without defaults but wildcard "*" actions' do
18
- subject.permissions(['unknown_group']).sort { |n,m| n[:resource] <=> m[:resource] }.should == [
34
+ perm = subject.permissions(['unknown_group'])
35
+ expected = [
19
36
  #allow nothing
20
37
  {:permission=>{:resource=>"accounts", :actions=>[], :deny=>false}},
21
38
  # allow anything but index
@@ -41,9 +58,12 @@ describe Ixtlan::Guard::GuardNG do
41
58
  {:permission=>{:resource=>"regions", :actions=>[], :deny=>false}},
42
59
  #allow nothing
43
60
  {:permission=>{:resource=>"users", :actions=>[], :deny=>false}}]
61
+
62
+ assert(expected, perm)
44
63
  end
45
64
  it 'should deny some without defaults but wildcard "*" actions' do
46
- subject.permissions(['no_admin']).sort { |n,m| n[:resource] <=> m[:resource] }.should == [
65
+ perm = subject.permissions(['no_admin'])
66
+ expected = [
47
67
  #allow nothing
48
68
  {:permission=>{:resource=>"accounts", :actions=>[], :deny=>false}},
49
69
  # allow anything but index
@@ -72,9 +92,12 @@ describe Ixtlan::Guard::GuardNG do
72
92
  {:permission=>{:resource=>"regions", :actions=>[], :deny=>false}},
73
93
  #allow nothing
74
94
  {:permission=>{:resource=>"users", :actions=>[], :deny=>false}}]
95
+
96
+ assert(expected, perm)
75
97
  end
76
98
  it 'should allow "root"' do
77
- subject.permissions(['root']).sort { |n,m| n[:resource] <=> m[:resource] }.should == [
99
+ perm = subject.permissions(['root'])
100
+ expected = [
78
101
  {:permission=>{:resource=>"accounts", :actions=>[], :deny=>true}},
79
102
  {:permission=>{:resource=>"allow_all_defaults", :actions=>[], :deny=>true}},
80
103
  {:permission=>{:resource=>"defaults", :actions=>[], :deny=>true}},
@@ -83,9 +106,13 @@ describe Ixtlan::Guard::GuardNG do
83
106
  {:permission=>{:resource=>"person", :actions=>[], :deny=>true}},
84
107
  {:permission=>{:resource=>"regions", :actions=>[], :deny=>true}},
85
108
  {:permission=>{:resource=>"users", :actions=>[], :deny=>true}}]
86
- end
109
+
110
+ assert(expected, perm)
111
+ end
112
+
87
113
  it 'should allow with default group' do
88
- subject.permissions(['_master']).sort { |n,m| n[:resource] <=> m[:resource] }.should == [
114
+ perm = subject.permissions(['_master'])
115
+ expected = [
89
116
  #allow nothing
90
117
  {:permission=>{:resource=>"accounts", :actions=>[], :deny=>false}},
91
118
  # allow anything but index
@@ -112,10 +139,13 @@ describe Ixtlan::Guard::GuardNG do
112
139
  {:permission=>{:resource=>"regions", :actions=>[], :deny=>false}},
113
140
  #allow nothing
114
141
  {:permission=>{:resource=>"users", :actions=>[], :deny=>false}}]
142
+
143
+ assert(expected, perm)
115
144
  end
116
145
 
117
146
  it 'should allow with non-default group' do
118
- subject.permissions(['_admin']).sort { |n,m| n[:resource] <=> m[:resource] }.should == [
147
+ perm = subject.permissions(['_admin'])
148
+ expected = [
119
149
  #allow nothing
120
150
  {:permission=>{:resource=>"accounts", :actions=>[], :deny=>false}},
121
151
  # allow anything but index
@@ -143,6 +173,8 @@ describe Ixtlan::Guard::GuardNG do
143
173
  {:permission=>{:resource=>"regions", :actions=>[], :deny=>false}},
144
174
  #allow nothing
145
175
  {:permission=>{:resource=>"users", :actions=>[], :deny=>false}}]
176
+
177
+ assert(expected, perm)
146
178
  end
147
179
 
148
180
  it 'should allow with association' do
@@ -150,18 +182,12 @@ describe Ixtlan::Guard::GuardNG do
150
182
  def group.name
151
183
  "region"
152
184
  end
153
- subject.permissions([group])do |resource, action, groups|
185
+ perm = subject.permissions([group])do |resource, groups|
154
186
  if resource == 'regions'
155
- case action
156
- when 'show'
157
- {:associations => [:europe, :asia]}
158
- else
159
- {}
160
- end
161
- else
162
- {}
187
+ [:europe, :asia]
163
188
  end
164
- end.sort { |n,m| n[:resource] <=> m[:resource] }.should == [
189
+ end
190
+ expected = [
165
191
  #allow nothing
166
192
  {:permission=>{:resource=>"accounts", :actions=>[], :deny=>false}},
167
193
  # allow anything but index
@@ -194,10 +220,12 @@ describe Ixtlan::Guard::GuardNG do
194
220
  {:resource=>"regions",
195
221
  :actions=>
196
222
  [{:action=>{:name=>"show", :associations=>[:europe, :asia]}},
197
- {:action=>{:name=>"create"}}],
223
+ {:action=>{:name=>"create", :associations=>[:europe, :asia]}}],
198
224
  :deny=>false}},
199
225
  #allow nothing
200
226
  {:permission=>{:resource=>"users", :actions=>[], :deny=>false}}]
227
+
228
+ assert(expected, perm)
201
229
  end
202
230
  end
203
231
  end