ixtlan-guard 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/generators/guard/controller/controller_generator.rb +1 -9
- data/lib/generators/guard/scaffold/scaffold_generator.rb +5 -12
- data/lib/generators/guard/templates/guard.yml +12 -0
- data/lib/ixtlan-guard.rb +0 -1
- data/lib/ixtlan/guard.rb +1 -1
- data/lib/ixtlan/guard/guard_config.rb +55 -0
- data/lib/ixtlan/guard/guard_ng.rb +157 -0
- data/lib/ixtlan/guard/guard_rails.rb +76 -0
- data/lib/ixtlan/guard/railtie.rb +15 -9
- data/spec/guard_cache_spec.rb +58 -0
- data/spec/guard_export_spec.rb +161 -0
- data/spec/guard_spec.rb +45 -86
- data/spec/guards/accounts_guard.yml +6 -0
- data/spec/guards/defaults_guard.yml +6 -0
- data/spec/guards/no_defaults_guard.yml +5 -0
- data/spec/guards/person_guard.yml +7 -0
- data/spec/guards/users1_guard.yml +2 -0
- data/spec/guards/users2_guard.yml +3 -0
- data/spec/guards/users_guard.yml +3 -0
- metadata +24 -12
- data/lib/generators/guard/scaffold/templates/guard.rb +0 -20
- data/lib/generators/guard/templates/guard.rb +0 -20
- data/lib/ixtlan/guard/guard.rb +0 -247
- data/lib/ixtlan/guard/rails_integration.rb +0 -88
- data/spec/guards/users_guard.rb +0 -13
@@ -8,15 +8,7 @@ module Guard
|
|
8
8
|
# check_class_collision :suffix => "Guard"
|
9
9
|
|
10
10
|
def create_guard_file
|
11
|
-
template 'guard.
|
11
|
+
template 'guard.yml', File.join('app', 'guards', class_path, "#{file_name}_guard.yml")
|
12
12
|
end
|
13
|
-
|
14
|
-
def guard_class_name
|
15
|
-
class_name
|
16
|
-
end
|
17
|
-
|
18
|
-
def aliases
|
19
|
-
end
|
20
|
-
|
21
13
|
end
|
22
14
|
end
|
@@ -3,24 +3,17 @@ module Guard
|
|
3
3
|
class ScaffoldGenerator < Rails::Generators::NamedBase
|
4
4
|
include Rails::Generators::ResourceHelpers
|
5
5
|
|
6
|
-
source_root File.expand_path('
|
6
|
+
source_root File.expand_path('../../templates', __FILE__)
|
7
7
|
|
8
8
|
# check_class_collision :suffix => "Guard"
|
9
9
|
|
10
10
|
def create_guard_files
|
11
|
-
template 'guard.
|
11
|
+
template 'guard.yml', File.join('app', 'guards', class_path, "#{plural_file_name}_guard.yml")
|
12
12
|
end
|
13
|
-
|
14
|
-
|
15
|
-
controller_class_name
|
16
|
-
end
|
17
|
-
|
18
|
-
def aliases
|
19
|
-
{ :create=>:new, :update=>:edit }
|
20
|
-
end
|
21
|
-
|
13
|
+
|
14
|
+
#TODO should be coming from the actual generator
|
22
15
|
def actions
|
23
|
-
['index', 'show', 'new', 'edit', 'destroy']
|
16
|
+
['index', 'show', 'new', 'create', 'edit', 'update', 'destroy']
|
24
17
|
end
|
25
18
|
end
|
26
19
|
end
|
data/lib/ixtlan-guard.rb
CHANGED
data/lib/ixtlan/guard.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require 'ixtlan/guard/
|
1
|
+
require 'ixtlan/guard/guard_ng'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
module Ixtlan
|
3
|
+
module Guard
|
4
|
+
class Config
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@guards_dir = options[:guards_dir]
|
8
|
+
@load_method = options[:cache] ? :cached_load_from_yaml_file : :load_from_yaml_file
|
9
|
+
raise GuardException.new("guards directory does not exists: #{@guards_dir}") unless File.directory?(@guards_dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
def allowed_groups(resource, action)
|
13
|
+
if resource && action
|
14
|
+
resource = resource.to_s
|
15
|
+
groups = send(@load_method, resource)
|
16
|
+
groups[action.to_s] || groups["defaults"] || []
|
17
|
+
else
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_guard?(resource)
|
23
|
+
File.exists? yaml_file(resource)
|
24
|
+
end
|
25
|
+
|
26
|
+
def map_of_all
|
27
|
+
result = {}
|
28
|
+
Dir[File.join(@guards_dir, "*_guard.yml")].each do |file|
|
29
|
+
result.merge!(YAML.load_file(file))
|
30
|
+
end
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def cached_load_from_yaml_file(resource)
|
37
|
+
@cache ||= {}
|
38
|
+
@cache[resource] ||= load_from_yaml_file(resource)
|
39
|
+
end
|
40
|
+
|
41
|
+
def yaml_file(resource)
|
42
|
+
File.join(@guards_dir, "#{resource}_guard.yml")
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_from_yaml_file(resource)
|
46
|
+
file = yaml_file(resource)
|
47
|
+
if File.exists? file
|
48
|
+
YAML.load_file(file)[resource] || {}
|
49
|
+
else
|
50
|
+
{}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'ixtlan/guard/guard_config'
|
2
|
+
|
3
|
+
module Ixtlan
|
4
|
+
module Guard
|
5
|
+
class GuardNG
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
options[:guards_dir] ||= File.expand_path(".")
|
9
|
+
@superuser = [(options[:superuser] || "root").to_s]
|
10
|
+
@config = Config.new(options)
|
11
|
+
@logger = options[:logger]
|
12
|
+
end
|
13
|
+
|
14
|
+
def block_groups(groups)
|
15
|
+
@blocked_groups = (groups || []).collect { |g| g.to_s}
|
16
|
+
@blocked_groups.delete(@superuser)
|
17
|
+
@blocked_groups
|
18
|
+
end
|
19
|
+
|
20
|
+
def blocked_groups
|
21
|
+
@blocked_groups ||= []
|
22
|
+
end
|
23
|
+
|
24
|
+
def logger
|
25
|
+
@logger ||=
|
26
|
+
if defined?(Slf4r::LoggerFactory)
|
27
|
+
Slf4r::LoggerFactory.new(Ixtlan::Guard)
|
28
|
+
else
|
29
|
+
require 'logger'
|
30
|
+
Logger.new(STDOUT)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def allowed_groups(resource, action, current_groups)
|
35
|
+
allowed = @config.allowed_groups(resource, action) - blocked_groups + @superuser
|
36
|
+
if allowed.member?('*')
|
37
|
+
current_groups
|
38
|
+
else
|
39
|
+
intersect(allowed, current_groups)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def allowed?(resource, action, current_groups, flavor = nil, &block)
|
44
|
+
current_groups = current_groups.collect { |g| g.to_s }
|
45
|
+
allowed_groups = self.allowed_groups(resource, action, current_groups)
|
46
|
+
logger.debug { "guard #{resource}##{action}: #{allowed_groups.size > 0}" }
|
47
|
+
if allowed_groups.size > 0
|
48
|
+
if block
|
49
|
+
g = allowed_groups.detect do |group|
|
50
|
+
block.call(group).member?(flavor)
|
51
|
+
end
|
52
|
+
logger.debug do
|
53
|
+
if g
|
54
|
+
"found group #{g} for #{flavor}"
|
55
|
+
else
|
56
|
+
"no group found for #{flavor}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
g != nil
|
60
|
+
else
|
61
|
+
true
|
62
|
+
end
|
63
|
+
else
|
64
|
+
unless @config.has_guard?(resource)
|
65
|
+
raise ::Ixtlan::Guard::GuardException.new("no guard config for '#{resource}'")
|
66
|
+
else
|
67
|
+
false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def permissions(current_groups, flavors = {})
|
73
|
+
perms = []
|
74
|
+
m = @config.map_of_all
|
75
|
+
m.each do |resource, actions|
|
76
|
+
nodes = []
|
77
|
+
perm = Node.new(:permission)
|
78
|
+
perm[:resource] = resource
|
79
|
+
perm[:actions] = nodes
|
80
|
+
defaults = intersect(current_groups, (actions.delete('defaults') || []) + @superuser)
|
81
|
+
deny = perm[:deny] = defaults.size != 0
|
82
|
+
actions.each do |action, groups|
|
83
|
+
node = Node.new(:action)
|
84
|
+
allowed_groups =
|
85
|
+
if groups && groups.member?('*')
|
86
|
+
current_groups
|
87
|
+
else
|
88
|
+
intersect(current_groups, (groups || []) + @superuser)
|
89
|
+
end
|
90
|
+
if (deny && allowed_groups.size == 0) || (!deny && allowed_groups.size > 0)
|
91
|
+
node[:name] = action
|
92
|
+
# f = {}
|
93
|
+
# flavors.each do |fl, block|
|
94
|
+
# f[fl] = block.call(allowed_groups)
|
95
|
+
# end
|
96
|
+
# node[:flavors] = f if f.size > 0
|
97
|
+
nodes << node
|
98
|
+
end
|
99
|
+
end
|
100
|
+
perms << perm
|
101
|
+
end
|
102
|
+
perms
|
103
|
+
end
|
104
|
+
|
105
|
+
def permission_map(current_groups, flavors = {})
|
106
|
+
# TODO fix it - think first !!
|
107
|
+
perms = {}
|
108
|
+
m = @config.map_of_all
|
109
|
+
m.each do |resource, actions|
|
110
|
+
nodes = {}
|
111
|
+
actions.each do |action, groups|
|
112
|
+
if action == 'defaults'
|
113
|
+
nodes[action] = {}
|
114
|
+
else
|
115
|
+
allowed_groups = intersect(current_groups, (groups || []) + @superuser)
|
116
|
+
if allowed_groups.size > 0
|
117
|
+
f = {}
|
118
|
+
flavors.each do |fl, block|
|
119
|
+
flav = block.call(allowed_groups)
|
120
|
+
f[fl] = flav if flav.size > 0
|
121
|
+
end
|
122
|
+
nodes[action] = f
|
123
|
+
else
|
124
|
+
nodes[action] = nil # indicates not default action
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
perms[resource] = nodes if nodes.size > 0
|
129
|
+
end
|
130
|
+
perms
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def intersect(set1, set2)
|
136
|
+
set1 - (set1 - set2)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
class Node < Hash
|
140
|
+
|
141
|
+
def initialize(name)
|
142
|
+
map = super
|
143
|
+
@content = {}
|
144
|
+
merge!({ name => @content })
|
145
|
+
end
|
146
|
+
|
147
|
+
def []=(k,v)
|
148
|
+
@content[k] = v
|
149
|
+
end
|
150
|
+
def [](k)
|
151
|
+
@content[k]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
class GuardException < Exception; end
|
155
|
+
class PermissionDenied < GuardException; end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module ActionController #:nodoc:
|
3
|
+
module Guard #:nodoc:
|
4
|
+
def self.included(base)
|
5
|
+
base.send(:include, InstanceMethods)
|
6
|
+
unless base.respond_to?(:groups_for_current_user)
|
7
|
+
base.send(:include, GroupsMethod)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module GroupsMethod
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def groups_for_current_user
|
16
|
+
if respond_to?(:current_user) && current_user
|
17
|
+
current_user.groups.collect do |group|
|
18
|
+
group.name
|
19
|
+
end
|
20
|
+
else
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module RootGroup
|
27
|
+
protected
|
28
|
+
|
29
|
+
def groups_for_current_user
|
30
|
+
['root']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods #:nodoc:
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def guard
|
39
|
+
Rails.application.config.guard
|
40
|
+
end
|
41
|
+
|
42
|
+
def check(flavor = nil, &block)
|
43
|
+
unless guard.allowed?(params[:controller],
|
44
|
+
params[:action],
|
45
|
+
groups_for_current_user,
|
46
|
+
flavor,
|
47
|
+
&block)
|
48
|
+
if flavor
|
49
|
+
raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}##{flavor}'")
|
50
|
+
else
|
51
|
+
raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}'")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def authorization
|
58
|
+
check
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module Allowed #:nodoc:
|
65
|
+
# Inclusion hook to make #allowed available as method
|
66
|
+
def self.included(base)
|
67
|
+
base.send(:include, InstanceMethods)
|
68
|
+
end
|
69
|
+
|
70
|
+
module InstanceMethods #:nodoc:
|
71
|
+
def allowed?(resource, action)
|
72
|
+
controller.send(:guard).allowed?(resource, action, controller.send(:groups_for_current_user))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/ixtlan/guard/railtie.rb
CHANGED
@@ -1,24 +1,30 @@
|
|
1
1
|
require 'rails'
|
2
|
-
require 'ixtlan/guard'
|
2
|
+
require 'ixtlan/guard/guard_ng'
|
3
|
+
require 'ixtlan/guard/guard_rails'
|
3
4
|
require 'logger'
|
5
|
+
require 'fileutils'
|
4
6
|
|
5
7
|
module Ixtlan
|
6
8
|
module Guard
|
7
9
|
class Railtie < Rails::Railtie
|
8
10
|
|
9
11
|
config.before_configuration do |app|
|
10
|
-
app.config.
|
11
|
-
Ixtlan::Guard::Guard.new(:guard_dir => File.join(Rails.root, "app", "guards"))
|
12
|
+
app.config.guards_dir = File.join(Rails.root, "app", "guards")
|
12
13
|
end
|
13
14
|
|
14
15
|
config.after_initialize do |app|
|
15
16
|
logger = app.config.logger || Rails.logger || Logger.new(STDERR)
|
16
|
-
|
17
|
-
|
18
|
-
app.config.
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
options = {
|
18
|
+
:guards_dir => app.config.guards_dir,
|
19
|
+
:cache => app.config.cache_classes
|
20
|
+
}
|
21
|
+
options[:logger] = logger unless defined?(Slf4r)
|
22
|
+
FileUtils.mkdir_p(app.config.guards_dir)
|
23
|
+
app.config.guard = Ixtlan::Guard::GuardNG.new(options)
|
24
|
+
|
25
|
+
::ActionController::Base.send(:include, Ixtlan::ActionController::Guard)
|
26
|
+
::ActionController::Base.send(:before_filter, :authorization)
|
27
|
+
::ActionView::Base.send(:include, Ixtlan::Allowed)
|
22
28
|
end
|
23
29
|
|
24
30
|
config.generators do
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ixtlan/guard/guard_ng'
|
3
|
+
require 'logger'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
$target = File.join("target", "guards", "users_guard.yml")
|
7
|
+
FileUtils.mkdir_p(File.dirname($target))
|
8
|
+
$source1 = File.join(File.dirname(__FILE__), "guards", "users1_guard.yml")
|
9
|
+
$source2 = File.join(File.dirname(__FILE__), "guards", "users2_guard.yml")
|
10
|
+
$logger = Logger.new(STDOUT)
|
11
|
+
def $logger.debug(&block)
|
12
|
+
info("\n\t[debug] " + block.call)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Ixtlan::Guard::GuardNG do
|
16
|
+
|
17
|
+
context "without caching" do
|
18
|
+
def not_cached
|
19
|
+
$not_cached ||= Ixtlan::Guard::GuardNG.new(:guards_dir => File.dirname($target),
|
20
|
+
:logger => $logger )
|
21
|
+
end
|
22
|
+
|
23
|
+
subject { not_cached }
|
24
|
+
|
25
|
+
it 'should pass' do
|
26
|
+
FileUtils.cp($source1, $target)
|
27
|
+
subject.allowed?(:users, :index, [:users]).should be_true
|
28
|
+
subject.allowed?(:users, :index, [:admin]).should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should not pass' do
|
32
|
+
FileUtils.cp($source2, $target)
|
33
|
+
subject.allowed?(:users, :index, [:users]).should be_false
|
34
|
+
subject.allowed?(:users, :index, [:admin]).should be_true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with caching" do
|
39
|
+
def cached
|
40
|
+
$cached ||= Ixtlan::Guard::GuardNG.new(:guards_dir => File.dirname($target),
|
41
|
+
:logger => $logger,
|
42
|
+
:cache => true)
|
43
|
+
end
|
44
|
+
subject { cached }
|
45
|
+
|
46
|
+
it 'should pass' do
|
47
|
+
FileUtils.cp($source1, $target)
|
48
|
+
subject.allowed?(:users, :index, [:users]).should be_true
|
49
|
+
subject.allowed?(:users, :index, [:admin]).should be_false
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should not pass' do
|
53
|
+
FileUtils.cp($source2, $target)
|
54
|
+
subject.allowed?(:users, :index, [:users]).should be_true
|
55
|
+
subject.allowed?(:users, :index, [:admin]).should be_false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ixtlan/guard/guard_ng'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
describe Ixtlan::Guard::GuardNG do
|
6
|
+
|
7
|
+
subject do
|
8
|
+
logger = Logger.new(STDOUT)
|
9
|
+
def logger.debug(&block)
|
10
|
+
info("\n\t[debug] " + block.call)
|
11
|
+
end
|
12
|
+
Ixtlan::Guard::GuardNG.new(:guards_dir => File.join(File.dirname(__FILE__), "guards"), :logger => logger )
|
13
|
+
end
|
14
|
+
|
15
|
+
context '#permissions' do
|
16
|
+
|
17
|
+
it 'should deny all without defaults but wildcard "*" actions' do
|
18
|
+
subject.permissions(['unknown_group']).should == [
|
19
|
+
#allow nothing
|
20
|
+
{:permission=>{:resource=>"users", :actions=>[], :deny=>false}},
|
21
|
+
{:permission=>
|
22
|
+
{
|
23
|
+
:resource=>"no_defaults",
|
24
|
+
:actions=>[{:action=>{:name=>"index"}}],
|
25
|
+
:deny=>false #allow
|
26
|
+
}
|
27
|
+
},
|
28
|
+
{
|
29
|
+
:permission=>
|
30
|
+
{
|
31
|
+
:resource=>"defaults",
|
32
|
+
:actions=>[{:action=>{:name=>"index"}}],
|
33
|
+
:deny=>false #allow
|
34
|
+
}
|
35
|
+
},
|
36
|
+
#allow nothing
|
37
|
+
{:permission=>{:resource=>"person", :actions=>[], :deny=>false}},
|
38
|
+
#allow nothing
|
39
|
+
{:permission=>{:resource=>"accounts", :actions=>[], :deny=>false}}]
|
40
|
+
end
|
41
|
+
it 'should deny some without defaults but wildcard "*" actions' do
|
42
|
+
subject.permissions(['no_admin']).should == [
|
43
|
+
#allow nothing
|
44
|
+
{:permission=>{:resource=>"users", :actions=>[], :deny=>false}},
|
45
|
+
{:permission=>
|
46
|
+
{
|
47
|
+
:resource=>"no_defaults",
|
48
|
+
:actions=>
|
49
|
+
[{:action=>{:name=>"edit"}},
|
50
|
+
{:action=>{:name=>"index"}},
|
51
|
+
{:action=>{:name=>"show"}}],
|
52
|
+
:deny=>false #allow
|
53
|
+
}
|
54
|
+
},
|
55
|
+
{
|
56
|
+
:permission=>
|
57
|
+
{
|
58
|
+
:resource=>"defaults",
|
59
|
+
:actions=>[{:action=>{:name=>"index"}}],
|
60
|
+
:deny=>false #allow
|
61
|
+
}
|
62
|
+
},
|
63
|
+
#allow nothing
|
64
|
+
{:permission=>{:resource=>"person", :actions=>[], :deny=>false}},
|
65
|
+
#allow nothing
|
66
|
+
{:permission=>{:resource=>"accounts", :actions=>[], :deny=>false}}]
|
67
|
+
end
|
68
|
+
it 'should allow "root"' do
|
69
|
+
subject.permissions(['root']).should == [
|
70
|
+
{:permission=>{:resource=>"users", :actions=>[], :deny=>true}},
|
71
|
+
{:permission=>{:resource=>"no_defaults", :actions=>[], :deny=>true}},
|
72
|
+
{:permission=>{:resource=>"defaults", :actions=>[], :deny=>true}},
|
73
|
+
{:permission=>{:resource=>"person", :actions=>[], :deny=>true}},
|
74
|
+
{:permission=>{:resource=>"accounts", :actions=>[], :deny=>true}}]
|
75
|
+
end
|
76
|
+
it 'should allow with default group' do
|
77
|
+
subject.permissions(['_master']).should == [
|
78
|
+
#allow nothing
|
79
|
+
{:permission=>{:resource=>"users", :actions=>[], :deny=>false}},
|
80
|
+
{:permission=>
|
81
|
+
{
|
82
|
+
:resource=>"no_defaults",
|
83
|
+
:actions=>[{:action=>{:name=>"index"}}],
|
84
|
+
:deny=>false #allow
|
85
|
+
}
|
86
|
+
},
|
87
|
+
{
|
88
|
+
:permission=>
|
89
|
+
{
|
90
|
+
:resource=>"defaults",
|
91
|
+
:actions=>[{:action=>{:name=>"show"}},
|
92
|
+
{:action=>{:name=>"destroy"}}],
|
93
|
+
:deny=>true
|
94
|
+
}
|
95
|
+
},
|
96
|
+
#allow nothing
|
97
|
+
{:permission=>{:resource=>"person", :actions=>[], :deny=>false}},
|
98
|
+
#allow nothing
|
99
|
+
{:permission=>{:resource=>"accounts", :actions=>[], :deny=>false}}]
|
100
|
+
end
|
101
|
+
it 'should allow with non-default group' do
|
102
|
+
subject.permissions(['_admin']).should == [
|
103
|
+
#allow nothing
|
104
|
+
{:permission=>{:resource=>"users", :actions=>[], :deny=>false}},
|
105
|
+
{:permission=>
|
106
|
+
{
|
107
|
+
:resource=>"no_defaults",
|
108
|
+
:actions=>[{:action=>{:name=>"index"}}],
|
109
|
+
:deny=>false #allow
|
110
|
+
}
|
111
|
+
},
|
112
|
+
{
|
113
|
+
:permission=>
|
114
|
+
{
|
115
|
+
:resource=>"defaults",
|
116
|
+
:actions=>[{:action=>{:name=>"edit"}},
|
117
|
+
{:action=>{:name=>"index"}},
|
118
|
+
{:action=>{:name=>"show"}}],
|
119
|
+
:deny=>false # allow
|
120
|
+
}
|
121
|
+
},
|
122
|
+
#allow nothing
|
123
|
+
{:permission=>{:resource=>"person", :actions=>[], :deny=>false}},
|
124
|
+
#allow nothing
|
125
|
+
{:permission=>{:resource=>"accounts", :actions=>[], :deny=>false}}]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context '#permission_map' do
|
130
|
+
it 'should export' do
|
131
|
+
pending "check expectations before implementing specs"
|
132
|
+
subject.permission_map(['admin']).should == {"users"=>{"defaults"=>nil}, "person"=>{"defaults"=>nil, "destroy"=>{}, "index"=>{}}, "accounts"=>{"defaults"=>nil, "destroy"=>{}, "show"=>nil}}
|
133
|
+
|
134
|
+
subject.permission_map(['manager']).should == {"users"=>{"defaults"=>nil}, "person"=>{"defaults"=>nil, "destroy"=>nil, "index"=>{}}, "accounts"=>{"defaults"=>nil, "destroy"=>nil, "show"=>{}}}
|
135
|
+
|
136
|
+
subject.permission_map(['manager', 'admin']).should == {"users"=>{"defaults"=>nil}, "person"=>{"defaults"=>nil, "destroy"=>{}, "index"=>{}}, "accounts"=>{"defaults"=>nil, "destroy"=>{}, "show"=>{}}}
|
137
|
+
|
138
|
+
subject.permission_map(['users']).should == {"users"=>{"defaults"=>{}}, "person"=>{"defaults"=>nil, "destroy"=>nil, "index"=>nil}, "accounts"=>{"defaults"=>nil, "destroy"=>nil, "show"=>nil}}
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should export with flavor' do
|
142
|
+
pending "check expectations before implementing specs"
|
143
|
+
|
144
|
+
flavors = { 'admin' => ['example', 'dummy'], 'manager' => ['example', 'master'] }
|
145
|
+
|
146
|
+
domains = Proc.new do |groups|
|
147
|
+
groups.collect do |g|
|
148
|
+
flavors[g] || []
|
149
|
+
end.flatten.uniq
|
150
|
+
end
|
151
|
+
|
152
|
+
subject.permission_map(['admin'], 'domains' => domains).should == {"users"=>{"defaults"=>nil}, "person"=>{"defaults"=>nil, "destroy"=>{'domains'=>["example", "dummy"]}, "index"=>{'domains'=>["example", "dummy"]}}, "accounts"=>{"defaults"=>nil, "destroy"=>{'domains'=>["example", "dummy"]}, "show"=>nil}}
|
153
|
+
|
154
|
+
subject.permission_map(['manager'], 'domains' => domains).should == {"users"=>{"defaults"=>nil}, "person"=>{"defaults"=>nil, "destroy"=>nil, "index"=>{"domains"=>["example", "master"]}}, "accounts"=>{"defaults"=>nil, "destroy"=>nil, "show"=>{"domains"=>["example", "master"]}}}
|
155
|
+
|
156
|
+
subject.permission_map(['manager', 'admin'], 'domains' => domains).should == {"users"=>{"defaults"=>nil}, "person"=>{"defaults"=>nil, "destroy"=>{"domains"=>["example", "dummy"]}, "index"=>{"domains"=>["example", "master", "dummy"]}}, "accounts"=>{"defaults"=>nil, "destroy"=>{"domains"=>["example", "dummy"]}, "show"=>{"domains"=>["example", "master"]}}}
|
157
|
+
|
158
|
+
subject.permission_map(['users'], 'domains' => domains).should == {"users"=>{"defaults"=>{}}, "person"=>{"defaults"=>nil, "destroy"=>nil, "index"=>nil}, "accounts"=>{"defaults"=>nil, "destroy"=>nil, "show"=>nil}}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/spec/guard_spec.rb
CHANGED
@@ -1,130 +1,89 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'ixtlan/guard'
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
before :all do
|
7
|
-
@guard = Ixtlan::Guard::Guard.new(:guard_dir => File.join(File.dirname(__FILE__), "guards") )
|
8
|
-
|
9
|
-
@guard.setup
|
10
|
-
@current_user = Object.new
|
11
|
-
def @current_user.groups(g = nil)
|
12
|
-
if g
|
13
|
-
@groups = g.collect do |gg|
|
14
|
-
group = Object.new
|
15
|
-
def group.name(name =nil)
|
16
|
-
@name = name if name
|
17
|
-
@name
|
18
|
-
end
|
19
|
-
group.name(gg)
|
20
|
-
group
|
21
|
-
end
|
22
|
-
end
|
23
|
-
@groups || []
|
24
|
-
end
|
2
|
+
require 'ixtlan/guard/guard_ng'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
describe Ixtlan::Guard::GuardNG do
|
25
6
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
7
|
+
subject do
|
8
|
+
logger = Logger.new(STDOUT)
|
9
|
+
def logger.debug(&block)
|
10
|
+
info("\n\t[debug] " + block.call)
|
30
11
|
end
|
31
|
-
|
12
|
+
Ixtlan::Guard::GuardNG.new(:guards_dir => File.join(File.dirname(__FILE__), "guards"), :logger => logger )
|
32
13
|
end
|
33
14
|
|
34
15
|
it 'should fail with missing guard dir' do
|
35
|
-
lambda {Ixtlan::Guard::
|
16
|
+
lambda {Ixtlan::Guard::GuardNG.new(:guards_dir => "does_not_exists") }.should raise_error(Ixtlan::Guard::GuardException)
|
36
17
|
end
|
37
18
|
|
38
19
|
it 'should initialize' do
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'should fail check without current user' do
|
43
|
-
controller = Object.new
|
44
|
-
def controller.current_user
|
45
|
-
end
|
46
|
-
@guard.check(controller, :none, :something).should be_false
|
20
|
+
subject.should_not be_nil
|
47
21
|
end
|
48
22
|
|
49
|
-
it 'should
|
50
|
-
|
51
|
-
@guard.check(@controller, :users, :show).should be_true
|
23
|
+
it 'should fail without groups' do
|
24
|
+
subject.allowed?(:users, :something, []).should be_false
|
52
25
|
end
|
53
26
|
|
54
|
-
it 'should
|
55
|
-
|
56
|
-
@guard.check(@controller, :users, :show).should be_false
|
27
|
+
it 'should pass with user being root' do
|
28
|
+
subject.allowed?(:users, :show, [:root]).should be_true
|
57
29
|
end
|
58
30
|
|
59
|
-
it 'should pass
|
60
|
-
|
61
|
-
@guard.check(@controller, :users, :index).should be_true
|
31
|
+
it 'should pass "allow all groups" with user with any groups' do
|
32
|
+
subject.allowed?(:users, :index, [:any]).should be_true
|
62
33
|
end
|
63
34
|
|
64
|
-
it 'should pass
|
65
|
-
|
66
|
-
@guard.check(@controller, :users, :edit).should be_true
|
35
|
+
it 'should pass' do
|
36
|
+
subject.allowed?(:users, :update, [:users]).should be_true
|
67
37
|
end
|
68
38
|
|
69
|
-
it 'should pass
|
70
|
-
|
71
|
-
@guard.check(@controller, :users, :update).should be_true
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'should not pass check with user when in blocked group' do
|
75
|
-
@current_user.groups([:users])
|
76
|
-
@guard.block_groups([:users])
|
39
|
+
it 'should not pass with user when in blocked group' do
|
40
|
+
subject.block_groups([:users])
|
77
41
|
begin
|
78
|
-
|
42
|
+
subject.allowed?(:users, :update, [:users]).should be_false
|
79
43
|
ensure
|
80
|
-
|
44
|
+
subject.block_groups([])
|
81
45
|
end
|
82
46
|
end
|
83
47
|
|
84
|
-
it 'should pass
|
85
|
-
|
86
|
-
@guard.block_groups([:accounts])
|
48
|
+
it 'should pass with user when not in blocked group' do
|
49
|
+
subject.block_groups([:accounts])
|
87
50
|
begin
|
88
|
-
|
51
|
+
subject.allowed?(:users, :update, [:users]).should be_true
|
89
52
|
ensure
|
90
|
-
|
53
|
+
subject.block_groups([])
|
91
54
|
end
|
92
55
|
end
|
93
56
|
|
94
|
-
it 'should
|
95
|
-
|
96
|
-
@guard.block_groups([:root])
|
57
|
+
it 'should not block root group' do
|
58
|
+
subject.block_groups([:root])
|
97
59
|
begin
|
98
|
-
|
60
|
+
subject.allowed?(:users, :update, [:root]).should be_true
|
99
61
|
ensure
|
100
|
-
|
62
|
+
subject.block_groups([])
|
101
63
|
end
|
102
64
|
end
|
103
65
|
|
104
|
-
it 'should not pass
|
105
|
-
|
106
|
-
|
66
|
+
it 'should not pass' do
|
67
|
+
subject.allowed?(:users, :update, [:accounts]).should be_false
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should should use defaults on unknown action' do
|
71
|
+
subject.allowed?(:users, :unknow, [:users]).should be_true
|
107
72
|
end
|
108
73
|
|
109
|
-
it 'should pass
|
110
|
-
|
111
|
-
@guard.check(@controller, :users, :update) do |g|
|
112
|
-
true
|
113
|
-
end.should be_true
|
74
|
+
it 'should pass with right group and allowed flavor' do
|
75
|
+
subject.allowed?(:users, :update, [:users], :example){ |g| [:example]}.should be_true
|
114
76
|
end
|
115
77
|
|
116
|
-
it 'should not pass
|
117
|
-
|
118
|
-
@guard.check(@controller, :users, :update) do |g|
|
119
|
-
false
|
120
|
-
end.should be_false
|
78
|
+
it 'should not pass with wrong group but allowed flavor' do
|
79
|
+
subject.allowed?(:users, :update, [:accounts], :example){ |g| [:example]}.should be_false
|
121
80
|
end
|
122
81
|
|
123
|
-
it 'should
|
124
|
-
|
82
|
+
it 'should not pass with wrong group but disallowed flavor' do
|
83
|
+
subject.allowed?(:users, :update, [:accounts], :example){ |g| []}.should be_false
|
125
84
|
end
|
126
85
|
|
127
|
-
it 'should
|
128
|
-
|
86
|
+
it 'should not pass with right group and disallowed flavor' do
|
87
|
+
subject.allowed?(:users, :update, [:users], :example){ |g| []}.should be_false
|
129
88
|
end
|
130
89
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: ixtlan-guard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.6.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- mkristian
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-09-05 00:00:00 +05:30
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -19,9 +19,12 @@ dependencies:
|
|
19
19
|
requirement: &id001 !ruby/object:Gem::Requirement
|
20
20
|
none: false
|
21
21
|
requirements:
|
22
|
-
- - "
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.6.0
|
25
|
+
- - <
|
23
26
|
- !ruby/object:Gem::Version
|
24
|
-
version: 0.
|
27
|
+
version: 0.6.99999
|
25
28
|
type: :runtime
|
26
29
|
version_requirements: *id001
|
27
30
|
- !ruby/object:Gem::Dependency
|
@@ -96,8 +99,7 @@ files:
|
|
96
99
|
- lib/generators/guard/controller/controller_generator.rb
|
97
100
|
- lib/generators/guard/scaffold/USAGE
|
98
101
|
- lib/generators/guard/scaffold/scaffold_generator.rb
|
99
|
-
- lib/generators/guard/
|
100
|
-
- lib/generators/guard/templates/guard.rb
|
102
|
+
- lib/generators/guard/templates/guard.yml
|
101
103
|
- lib/generators/ixtlan/user_management_scaffold/user_management_scaffold_generator.rb
|
102
104
|
- lib/generators/ixtlan/user_management_controller/USAGE
|
103
105
|
- lib/generators/ixtlan/user_management_controller/user_management_controller_generator.rb
|
@@ -114,26 +116,34 @@ files:
|
|
114
116
|
- lib/generators/active_record/templates/group_user_migration.rb
|
115
117
|
- lib/generators/active_record/templates/flavor_model.rb
|
116
118
|
- lib/ixtlan/guard.rb
|
117
|
-
- lib/ixtlan/guard/
|
118
|
-
- lib/ixtlan/guard/
|
119
|
+
- lib/ixtlan/guard/guard_ng.rb
|
120
|
+
- lib/ixtlan/guard/guard_config.rb
|
121
|
+
- lib/ixtlan/guard/guard_rails.rb
|
119
122
|
- lib/ixtlan/guard/railtie.rb
|
120
123
|
- lib/ixtlan/guard/controllers/maintenance_controller.rb
|
121
124
|
- lib/ixtlan/guard/controllers/permissions_controller.rb
|
122
125
|
- lib/ixtlan/guard/spec/user_management_models_spec.rb
|
123
126
|
- lib/ixtlan/guard/models/maintenance.rb
|
124
127
|
- lib/ixtlan/guard/models/user_update_manager.rb
|
128
|
+
- spec/guard_export_spec.rb
|
125
129
|
- spec/spec_helper.rb
|
130
|
+
- spec/guard_cache_spec.rb
|
126
131
|
- spec/guard_spec.rb
|
127
132
|
- spec/railtie_spec.rb
|
128
|
-
- spec/guards/users_guard.
|
133
|
+
- spec/guards/users_guard.yml
|
134
|
+
- spec/guards/users2_guard.yml
|
135
|
+
- spec/guards/no_defaults_guard.yml
|
136
|
+
- spec/guards/defaults_guard.yml
|
137
|
+
- spec/guards/users1_guard.yml
|
138
|
+
- spec/guards/person_guard.yml
|
139
|
+
- spec/guards/accounts_guard.yml
|
129
140
|
has_rdoc: true
|
130
141
|
homepage: http://github.com/mkristian/ixtlan-guard
|
131
142
|
licenses:
|
132
143
|
- MIT-LICENSE
|
133
144
|
post_install_message:
|
134
|
-
rdoc_options:
|
135
|
-
|
136
|
-
- README.textile
|
145
|
+
rdoc_options: []
|
146
|
+
|
137
147
|
require_paths:
|
138
148
|
- lib
|
139
149
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -156,5 +166,7 @@ signing_key:
|
|
156
166
|
specification_version: 3
|
157
167
|
summary: guard your controller actions
|
158
168
|
test_files:
|
169
|
+
- spec/guard_export_spec.rb
|
170
|
+
- spec/guard_cache_spec.rb
|
159
171
|
- spec/guard_spec.rb
|
160
172
|
- spec/railtie_spec.rb
|
@@ -1,20 +0,0 @@
|
|
1
|
-
class <%= guard_class_name %>Guard
|
2
|
-
def initialize(guard)
|
3
|
-
#guard.name = "<%= plural_file_name %>"
|
4
|
-
<% if aliases -%>
|
5
|
-
guard.aliases = <%= aliases.inspect %>
|
6
|
-
<% end -%>
|
7
|
-
guard.action_map= {
|
8
|
-
<% case actions
|
9
|
-
when Array
|
10
|
-
for action in actions -%>
|
11
|
-
:<%= action %> => [],
|
12
|
-
<% end
|
13
|
-
when Hash
|
14
|
-
actions.each do |action, groups| -%>
|
15
|
-
:<%= action %> => <%= groups.inspect %>,
|
16
|
-
<% end
|
17
|
-
end -%>
|
18
|
-
}
|
19
|
-
end
|
20
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
class <%= guard_class_name %>Guard
|
2
|
-
def initialize(guard)
|
3
|
-
#guard.name = "<%= plural_file_name %>"
|
4
|
-
<% if aliases -%>
|
5
|
-
guard.aliases = <%= aliases.inspect %>
|
6
|
-
<% end -%>
|
7
|
-
guard.action_map= {
|
8
|
-
<% case actions
|
9
|
-
when Array
|
10
|
-
for action in actions -%>
|
11
|
-
:<%= action %> => [],
|
12
|
-
<% end
|
13
|
-
when Hash
|
14
|
-
actions.each do |action, groups| -%>
|
15
|
-
:<%= action %> => <%= groups.inspect %>,
|
16
|
-
<% end
|
17
|
-
end -%>
|
18
|
-
}
|
19
|
-
end
|
20
|
-
end
|
data/lib/ixtlan/guard/guard.rb
DELETED
@@ -1,247 +0,0 @@
|
|
1
|
-
module Ixtlan
|
2
|
-
module Guard
|
3
|
-
class ControllerGuard
|
4
|
-
|
5
|
-
attr_accessor :name, :action_map, :aliases, :flavor
|
6
|
-
|
7
|
-
def initialize(name)
|
8
|
-
@name = name.sub(/_guard$/, '').to_sym
|
9
|
-
class_name = name.split(/\//).collect { |part| part.split("_").each { |pp| pp.capitalize! }.join }.join("::")
|
10
|
-
Object.const_get(class_name).new(self)
|
11
|
-
end
|
12
|
-
|
13
|
-
def flavor=(flavor)
|
14
|
-
@flavor = flavor.to_sym
|
15
|
-
end
|
16
|
-
|
17
|
-
def name=(name)
|
18
|
-
@name = name.to_sym
|
19
|
-
end
|
20
|
-
|
21
|
-
def aliases=(map)
|
22
|
-
@aliases = symbolize(map)
|
23
|
-
end
|
24
|
-
|
25
|
-
def action_map=(map)
|
26
|
-
@action_map = symbolize(map)
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def symbolize(h)
|
32
|
-
result = {}
|
33
|
-
|
34
|
-
h.each do |k, v|
|
35
|
-
if v.is_a?(Hash)
|
36
|
-
result[k.to_sym] = symbolize_keys(v) unless v.size == 0
|
37
|
-
elsif v.is_a?(Array)
|
38
|
-
val = []
|
39
|
-
v.each {|vv| val << vv.to_sym }
|
40
|
-
result[k.to_sym] = val
|
41
|
-
else
|
42
|
-
result[k.to_sym] = v.to_sym
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
result
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
class Guard
|
52
|
-
|
53
|
-
attr_accessor :logger, :guard_dir, :superuser, :groups_of_current_user
|
54
|
-
|
55
|
-
def initialize(options, &block)
|
56
|
-
@superuser = (options[:superuser] || :root).to_sym
|
57
|
-
@guard_dir = options[:guard_dir] || File.join("app", "guards")
|
58
|
-
@user_groups = (options[:user_groups] || :groups).to_sym
|
59
|
-
@user_groups_name = (options[:user_groups_name] || :name).to_sym
|
60
|
-
|
61
|
-
@map = {}
|
62
|
-
@aliases = {}
|
63
|
-
@flavor_map = {}
|
64
|
-
|
65
|
-
@groups_of_current_user =
|
66
|
-
if block
|
67
|
-
block
|
68
|
-
else
|
69
|
-
Proc.new do |controller|
|
70
|
-
# get the groups of the current_user
|
71
|
-
user = controller.send(:current_user) if controller.respond_to?(:current_user)
|
72
|
-
if user
|
73
|
-
(user.send(@user_groups) || []).collect do |group|
|
74
|
-
name = group.send(@user_groups_name)
|
75
|
-
name.to_sym if name
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def logger
|
83
|
-
@logger ||= if defined?(Slf4r::LoggerFactory)
|
84
|
-
Slf4r::LoggerFactory.new(Ixtlan::Guard)
|
85
|
-
else
|
86
|
-
require 'logger'
|
87
|
-
Logger.new(STDOUT)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def setup
|
92
|
-
if File.exists?(@guard_dir)
|
93
|
-
Dir.new(guard_dir).to_a.each do |f|
|
94
|
-
if f.match(".rb$")
|
95
|
-
require(File.join(guard_dir, f))
|
96
|
-
controller_guard = ControllerGuard.new(f.sub(/.rb$/, ''))
|
97
|
-
register(controller_guard)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
logger.debug("initialized guard . . .")
|
101
|
-
else
|
102
|
-
raise GuardException.new("guard directory #{guard_dir} not found, skip loading")
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
private
|
107
|
-
|
108
|
-
def register(controller_guard)
|
109
|
-
msg = (controller_guard.aliases || {}).collect {|k,v| "\n\t#{k} == #{v}"} + controller_guard.action_map.collect{ |k,v| "\n\t#{k} => [#{v.join(',')}]"}
|
110
|
-
logger.debug("#{controller_guard.name} guard: #{msg}")
|
111
|
-
@map[controller_guard.name] = controller_guard.action_map
|
112
|
-
@aliases[controller_guard.name] = controller_guard.aliases || {}
|
113
|
-
@flavor_map[controller_guard.name] = controller_guard.flavor if controller_guard.flavor
|
114
|
-
end
|
115
|
-
|
116
|
-
public
|
117
|
-
|
118
|
-
def flavor(controller)
|
119
|
-
@flavor_map[controller.params[:controller].to_sym]
|
120
|
-
end
|
121
|
-
|
122
|
-
def block_groups(groups)
|
123
|
-
@blocked_groups = (groups || []).collect { |g| g.to_sym}
|
124
|
-
@blocked_groups.delete(@superuser)
|
125
|
-
@blocked_groups
|
126
|
-
end
|
127
|
-
|
128
|
-
def blocked_groups
|
129
|
-
@blocked_groups ||= []
|
130
|
-
end
|
131
|
-
|
132
|
-
def current_user_restricted?(controller)
|
133
|
-
groups = @groups_of_current_user.call(controller)
|
134
|
-
if groups
|
135
|
-
# groups.select { |g| !blocked_groups.member?(g.to_sym) }.size < groups.size
|
136
|
-
(groups - blocked_groups).size < groups.size
|
137
|
-
else
|
138
|
-
nil
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def permissions(controller)
|
143
|
-
groups = (@groups_of_current_user.call(controller) || []).collect do
|
144
|
-
|g| g.to_sym
|
145
|
-
end
|
146
|
-
map = {}
|
147
|
-
@map.each do |resource, action_map|
|
148
|
-
action_map.each do |action, allowed|
|
149
|
-
if allowed.member? :*
|
150
|
-
allowed = groups.dup
|
151
|
-
end
|
152
|
-
allowed << @superuser unless allowed.member? @superuser
|
153
|
-
|
154
|
-
# intersection of allowed and groups empty ?
|
155
|
-
if (allowed - groups).size < allowed.size
|
156
|
-
permission = (map[resource] ||= {})
|
157
|
-
permission[:resource] = resource
|
158
|
-
actions = (permission[:actions] ||= [])
|
159
|
-
action_node = {:name => action}
|
160
|
-
flavors.each do |flavor, block|
|
161
|
-
flavor_list = []
|
162
|
-
(allowed - (allowed - groups)).each do |group|
|
163
|
-
list = block.call(controller, group)
|
164
|
-
# union - no duplicates
|
165
|
-
flavor_list = flavor_list - list + list
|
166
|
-
end
|
167
|
-
action_node[flavor.to_s.sub(/s$/, '') + "s"] = flavor_list if flavor_list.size > 0
|
168
|
-
end
|
169
|
-
actions << { :action => action_node }
|
170
|
-
actions << @aliases[resource][action] if @aliases[resource][action]
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
result = map.values.collect do |perm|
|
176
|
-
{ :permission => perm }
|
177
|
-
end
|
178
|
-
result.class_eval "alias :to_x :to_xml" unless map.respond_to? :to_x
|
179
|
-
def result.to_xml(options = {}, &block)
|
180
|
-
options[:root] = :permissions unless options[:root]
|
181
|
-
to_x(options, &block)
|
182
|
-
end
|
183
|
-
|
184
|
-
def result.to_json(options = {}, &block)
|
185
|
-
{:permissions => self}.to_json(options, &block)
|
186
|
-
end
|
187
|
-
result
|
188
|
-
end
|
189
|
-
|
190
|
-
def flavors
|
191
|
-
@flavors ||= {}
|
192
|
-
end
|
193
|
-
|
194
|
-
def register_flavor(flavor, &block)
|
195
|
-
flavors[flavor.to_sym] = block
|
196
|
-
end
|
197
|
-
|
198
|
-
def check(controller, resource, action, flavor_selector = nil, &block)
|
199
|
-
resource = resource.to_sym
|
200
|
-
action = action.to_sym
|
201
|
-
groups = @groups_of_current_user.call(controller)
|
202
|
-
if groups.nil?
|
203
|
-
logger.debug("check #{resource}##{action}: not authenticated")
|
204
|
-
return false
|
205
|
-
end
|
206
|
-
if (@map.key? resource)
|
207
|
-
action = @aliases[resource][action] || action
|
208
|
-
allowed = @map[resource][action]
|
209
|
-
if (allowed.nil?)
|
210
|
-
logger.warn("unknown action '#{action}' for controller '#{resource}'")
|
211
|
-
raise ::Ixtlan::Guard::GuardException.new("unknown action '#{action}' for controller '#{resource}'")
|
212
|
-
else
|
213
|
-
allowed << @superuser unless allowed.member? @superuser
|
214
|
-
allow_all_groups = allowed.member?(:*)
|
215
|
-
if(allow_all_groups && block.nil?)
|
216
|
-
logger.debug("check #{resource}##{action}: allowed for all")
|
217
|
-
return true
|
218
|
-
else
|
219
|
-
groups.each do |group|
|
220
|
-
if (allow_all_groups || allowed.member?(group.to_sym)) && !blocked_groups.member?(group.to_sym)
|
221
|
-
flavor_for_resource = flavors[@flavor_map[resource]]
|
222
|
-
if block.nil?
|
223
|
-
if(flavor_for_resource && flavor_for_resource.call(controller, group).member?(flavor_selector.to_s) || flavor_for_resource.nil?)
|
224
|
-
logger.debug("check #{resource}##{action}: true")
|
225
|
-
return true
|
226
|
-
end
|
227
|
-
elsif block.call(group)
|
228
|
-
logger.debug("check #{resource}##{action}: true")
|
229
|
-
return true
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
234
|
-
logger.debug("check #{resource}##{action}: false")
|
235
|
-
return false
|
236
|
-
end
|
237
|
-
else
|
238
|
-
logger.warn("unknown controller for '#{resource}'")
|
239
|
-
raise ::Ixtlan::Guard::GuardException.new("unknown controller for '#{resource}'")
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
class GuardException < Exception; end
|
245
|
-
class PermissionDenied < GuardException; end
|
246
|
-
end
|
247
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
require 'ixtlan/guard'
|
2
|
-
module Ixtlan
|
3
|
-
module ActionController #:nodoc:
|
4
|
-
module Guard #:nodoc:
|
5
|
-
def self.included(base)
|
6
|
-
base.send(:include, InstanceMethods)
|
7
|
-
end
|
8
|
-
module InstanceMethods #:nodoc:
|
9
|
-
|
10
|
-
protected
|
11
|
-
|
12
|
-
def guard
|
13
|
-
Rails.application.config.guard
|
14
|
-
end
|
15
|
-
|
16
|
-
def authorization(flavor = nil, &block)
|
17
|
-
if flavor.nil?
|
18
|
-
flavor = guard.flavor(self)
|
19
|
-
if flavor
|
20
|
-
method = "#{flavor}_authorization".to_sym
|
21
|
-
if self.respond_to?(method)
|
22
|
-
return send "#{flavor}_authorization".to_sym, &block
|
23
|
-
else
|
24
|
-
logger.warn "flavor #{flavor} configured in guard, but there is not method '#{method}'"
|
25
|
-
flavor = nil
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
resource_authorization(params[:controller], params[:action], flavor, &block)
|
30
|
-
end
|
31
|
-
|
32
|
-
def resource_authorization(resource, action, flavor = nil, &block)
|
33
|
-
unless guard.check(self,
|
34
|
-
resource,
|
35
|
-
action,
|
36
|
-
&flavored_block(flavor, &block))
|
37
|
-
raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{resource}##{action}'")
|
38
|
-
end
|
39
|
-
true
|
40
|
-
end
|
41
|
-
|
42
|
-
def flavored_block(flavor = nil, &block)
|
43
|
-
if block
|
44
|
-
if flavor
|
45
|
-
Proc.new do |group|
|
46
|
-
allowed_flavors = guard.flavors[flavor.to_sym].call(self, group)
|
47
|
-
block.call(allowed_flavors)
|
48
|
-
end
|
49
|
-
else
|
50
|
-
block
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
private :flavored_block
|
56
|
-
|
57
|
-
def allowed?(action, flavor = nil, &block)
|
58
|
-
guard.check(self,
|
59
|
-
params[:controller],
|
60
|
-
action,
|
61
|
-
&flavored_block(flavor, &block))
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
module Allowed #:nodoc:
|
68
|
-
# Inclusion hook to make #allowed available as method
|
69
|
-
def self.included(base)
|
70
|
-
base.send(:include, InstanceMethods)
|
71
|
-
end
|
72
|
-
|
73
|
-
module InstanceMethods #:nodoc:
|
74
|
-
def allowed?(resource, action, flavor_selector = nil, &block)
|
75
|
-
controller.send(:guard).check(controller, resource, action, flavor_selector, &block)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
ActionController::Base.send(:include, Ixtlan::ActionController::Guard)
|
82
|
-
ActionController::Base.send(:before_filter, :authorization)
|
83
|
-
ActionView::Base.send(:include, Ixtlan::Allowed)
|
84
|
-
module Erector
|
85
|
-
class Widget
|
86
|
-
include Ixtlan::Allowed
|
87
|
-
end
|
88
|
-
end
|
data/spec/guards/users_guard.rb
DELETED