ixtlan-guard 0.5.0 → 0.6.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/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