stonewall 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/rails/active_record.rb +12 -0
- data/lib/stonewall/access_controller.rb +25 -3
- data/lib/stonewall/helpers.rb +30 -6
- data/lib/stonewall/parser.rb +20 -22
- data/lib/stonewall/stonewall.rb +4 -2
- data/lib/stonewall/user_extensions.rb +1 -1
- data/test/helper.rb +2 -0
- data/test/test_access_controller.rb +40 -0
- data/test/test_active_record_extensions.rb +14 -0
- data/test/test_guarded_class.rb +28 -0
- data/test/test_helpers.rb +25 -0
- data/test/test_parser.rb +24 -0
- data/test/test_stonewall.rb +6 -2
- data/test/test_user_extensions.rb +18 -0
- metadata +16 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/rails/active_record.rb
CHANGED
@@ -11,4 +11,16 @@ ActiveRecord::Base.class_eval do
|
|
11
11
|
|
12
12
|
alias_method_chain :method_missing, :stonewall
|
13
13
|
|
14
|
+
|
15
|
+
# need to fix the update_attributes, read_attribute, and write_attribute problem here.
|
16
|
+
|
17
|
+
def update_attributes_with_stonewall(*args)
|
18
|
+
update_attributes_without_stonewall(*args)
|
19
|
+
end
|
20
|
+
alias_method_chain :update_attributes, :stonewall
|
21
|
+
|
22
|
+
# design notes:
|
23
|
+
# it is intentional that we are not blocking read_attribute and write_attribute methods.
|
24
|
+
# These are rare in real world rails apps, and where they are being used, permissions
|
25
|
+
# would generally be a hinderance.
|
14
26
|
end
|
@@ -8,6 +8,7 @@ module StoneWall
|
|
8
8
|
attr_reader :variant_field
|
9
9
|
attr_accessor :actions
|
10
10
|
attr_reader :guarded_methods
|
11
|
+
attr_reader :guarded_attributes
|
11
12
|
attr_accessor :method_groups
|
12
13
|
|
13
14
|
# the matrix is the money-shot of the access controller. You can set it
|
@@ -19,9 +20,13 @@ module StoneWall
|
|
19
20
|
def initialize(guarded_class)
|
20
21
|
@guarded_class = guarded_class
|
21
22
|
@guarded_methods = Array.new
|
23
|
+
@guarded_attributes = Array.new
|
22
24
|
@actions = Hash.new
|
23
25
|
@matrix = Hash.new
|
24
26
|
@method_groups = Hash.new
|
27
|
+
@method_groups[:readers] = Array.new
|
28
|
+
@method_groups[:writers] = Array.new
|
29
|
+
|
25
30
|
end
|
26
31
|
|
27
32
|
def set_variant_field(field)
|
@@ -45,29 +50,46 @@ module StoneWall
|
|
45
50
|
# This is similar to, but not the same as, the guard method on the parser.
|
46
51
|
# The parser has to wait until the methods are reified. Since this is
|
47
52
|
# got adding guards at runtime, we don't have that restruction here.
|
48
|
-
def
|
53
|
+
def guard_method(method)
|
49
54
|
StoneWall::Helpers.guard(@guarded_class, method)
|
50
55
|
StoneWall::Helpers.fix_alias_for(@guarded_class, method)
|
51
56
|
end
|
52
57
|
|
58
|
+
def guard_attribute(attribute)
|
59
|
+
guarded_attributes << attribute
|
60
|
+
guard_method(attribute)
|
61
|
+
@method_groups[:readers] << attribute
|
62
|
+
|
63
|
+
setter = (attribute.to_s + "=").to_sym
|
64
|
+
guard_method(setter)
|
65
|
+
@method_groups[:writers] << setter
|
66
|
+
end
|
67
|
+
|
53
68
|
# --------------
|
54
69
|
# This is 1/3rd of the magic in this gem. Every method you guard is
|
55
70
|
# checked by this method. It looks at the matrix of permissions you built
|
56
71
|
# in the dsl and allows or denies based on the guarded object, the user,
|
57
72
|
# and the method being accessed. #should we fail secure?
|
73
|
+
#
|
74
|
+
# this method is too complex - needs some refactoring to make it more
|
75
|
+
# easily testable
|
58
76
|
def allowed?(guarded_object, user, method)
|
59
77
|
return true if (guarded_object.nil? || user.nil? || method.nil?)
|
60
78
|
return true unless @guarded_methods.include?(method)
|
61
79
|
|
62
80
|
# if they can always view it, no need to check variant.
|
63
|
-
always = user.
|
81
|
+
always = user.stonewall_role_info.detect do |r|
|
64
82
|
granted?(r, :all, :all) || granted?(r, :all, method)
|
65
83
|
end
|
66
84
|
return always if always
|
67
85
|
|
68
86
|
v = guarded_object.send(variant_field) &&
|
69
87
|
guarded_object.send(variant_field).to_sym
|
70
|
-
|
88
|
+
|
89
|
+
# if the variant field isn't set, is this a reasonable thing to do?
|
90
|
+
return true if v.nil?
|
91
|
+
|
92
|
+
user.stonewall_role_info.detect do |r|
|
71
93
|
granted?(r, v, :all) || granted?(r, v, method)
|
72
94
|
end || false
|
73
95
|
end
|
data/lib/stonewall/helpers.rb
CHANGED
@@ -2,7 +2,7 @@ module StoneWall
|
|
2
2
|
module Helpers
|
3
3
|
|
4
4
|
def self.symbolize_role(role)
|
5
|
-
[String, Symbol].include?(role.class) ? role.to_sym : role.name.to_sym
|
5
|
+
[String, Symbol].include?(role.class) ? role.to_sym : role.name.parameterize("_").downcase.to_sym
|
6
6
|
end
|
7
7
|
|
8
8
|
|
@@ -14,7 +14,7 @@ module StoneWall
|
|
14
14
|
|
15
15
|
# --------------
|
16
16
|
# This is 1/3rd of the magic in this gem. We earlier built a
|
17
|
-
# '
|
17
|
+
# 'schpoo_with_stonewall' method on your class, and now we use
|
18
18
|
# alias_method_chain to wrap your original 'schpoo' method.
|
19
19
|
# You will have no hope of understanding this if you don't understand
|
20
20
|
# alias_method_chain, so go memorize that documentation.
|
@@ -26,11 +26,24 @@ module StoneWall
|
|
26
26
|
end
|
27
27
|
|
28
28
|
|
29
|
-
def self.
|
30
|
-
|
31
|
-
aliased_target, punctuation = m.to_s.sub(/([?!=])$/, ''), $1
|
29
|
+
def self.build_method_names(original_method_name)
|
30
|
+
aliased_target, punctuation = original_method_name.to_s.sub(/([?!=])$/, ''), $1
|
32
31
|
checked_method = "#{aliased_target}_with_stonewall#{punctuation}"
|
33
|
-
unchecked_method = "#{aliased_target}_without_stonewall#{punctuation}"
|
32
|
+
unchecked_method = "#{aliased_target}_without_stonewall#{punctuation}"
|
33
|
+
|
34
|
+
return checked_method, unchecked_method
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
#neither of these methods belong here - they seem like access controller things.
|
39
|
+
# but I won't resolve that until we can do a proper refactoring with tests.
|
40
|
+
def self.guard_method(guarded_class, m)
|
41
|
+
guarded_class.stonewall.guarded_methods << m #put this line where it belongs, and
|
42
|
+
#this method truly becomes a helper, doing nothing but
|
43
|
+
# defining the guard.
|
44
|
+
|
45
|
+
checked_method, unchecked_method = build_method_names(m)
|
46
|
+
|
34
47
|
# --------------
|
35
48
|
# This method is defined on the guarded class, so it is callable on
|
36
49
|
# objects of that class. This is 1/3rd of the magic of this gem-
|
@@ -46,5 +59,16 @@ module StoneWall
|
|
46
59
|
end
|
47
60
|
# -------------- end of bizzaro meta-juju
|
48
61
|
end
|
62
|
+
|
63
|
+
def self.guard_attribute(guarded_class, a)
|
64
|
+
guarded_class.stonewall.guarded_attributes << a
|
65
|
+
guard_method(guarded_class, a)
|
66
|
+
guarded_class.stonewall.method_groups[:readers] << a
|
67
|
+
|
68
|
+
setter = (a.to_s + "=").to_sym
|
69
|
+
guard_method(guarded_class, setter)
|
70
|
+
guarded_class.stonewall.method_groups[:writers] << setter
|
71
|
+
end
|
72
|
+
|
49
73
|
end
|
50
74
|
end
|
data/lib/stonewall/parser.rb
CHANGED
@@ -5,27 +5,17 @@ module StoneWall
|
|
5
5
|
@method_groups = Hash.new
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
|
10
|
-
@parent.stonewall.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
def allowed_method_group(*group_names)
|
19
|
-
group_names.flatten.each do |group_name|
|
20
|
-
@parent.stonewall.method_groups[group_name].each do |m|
|
21
|
-
allowed_method m
|
8
|
+
def allow(*alloweds)
|
9
|
+
alloweds.flatten.each do |allowed|
|
10
|
+
if @parent.stonewall.method_groups.keys.include?(allowed)
|
11
|
+
@parent.stonewall.method_groups[allowed].each do |m|
|
12
|
+
@parent.stonewall.add_grant(@role, @variant, m)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
@parent.stonewall.add_grant(@role, @variant, m)
|
22
16
|
end
|
23
17
|
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def allowed_method_groups(*group_names)
|
27
|
-
allowed_method_group(group_names)
|
28
|
-
end
|
18
|
+
end
|
29
19
|
|
30
20
|
def method_group(name, methods)
|
31
21
|
@parent.stonewall.method_groups[name] = methods
|
@@ -47,11 +37,19 @@ module StoneWall
|
|
47
37
|
yield Parser.new(@parent, @role, variant_name)
|
48
38
|
end
|
49
39
|
|
50
|
-
def
|
40
|
+
def guard_attribute(*attributes)
|
41
|
+
attributes.each do |m|
|
42
|
+
StoneWall::Helpers.guard_attribute(@parent, m)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
alias_method :guard_attributes, :guard_attribute
|
46
|
+
|
47
|
+
def guard_method(*methods)
|
51
48
|
methods.each do |m|
|
52
|
-
StoneWall::Helpers.
|
49
|
+
StoneWall::Helpers.guard_method(@parent, m)
|
53
50
|
end
|
54
51
|
end
|
55
|
-
|
52
|
+
alias_method :guard_methods, :guard_method
|
53
|
+
|
56
54
|
end
|
57
55
|
end
|
data/lib/stonewall/stonewall.rb
CHANGED
@@ -21,11 +21,13 @@ module StoneWall
|
|
21
21
|
# guarded methods defined in the dsl in the class.
|
22
22
|
def self.define_attribute_methods_with_stonewall
|
23
23
|
define_attribute_methods_without_stonewall
|
24
|
-
StoneWall::Helpers.fix_aliases_for(self)
|
24
|
+
StoneWall::Helpers.fix_aliases_for(self) # if a stonewall enhanced class?
|
25
25
|
end
|
26
26
|
|
27
27
|
class << self
|
28
|
-
|
28
|
+
unless respond_to?(:define_attribute_methods_without_stonewall)
|
29
|
+
alias_method_chain :define_attribute_methods, :stonewall
|
30
|
+
end
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
@@ -11,7 +11,7 @@ module StoneWall
|
|
11
11
|
# The only thing we require is that, if your role is a separate object,
|
12
12
|
# it responds to a 'name' method with a string or a symbol that matches
|
13
13
|
# the symbol you use when defining the permissions in your dsl.
|
14
|
-
def
|
14
|
+
def stonewall_role_info
|
15
15
|
return @role_symbols if @role_symbols
|
16
16
|
@role_symbols = Array.new
|
17
17
|
if self.respond_to?(:role)
|
data/test/helper.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestAccessController < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "return true just to be a bad boy until my testing is in place" do
|
6
|
+
assert true
|
7
|
+
end
|
8
|
+
|
9
|
+
should "hold a reference to the class it is guarding"
|
10
|
+
|
11
|
+
should "keep track of the field we are varying behavior on"
|
12
|
+
|
13
|
+
should "keep a list of guarded action blocks"
|
14
|
+
|
15
|
+
should "keep a list of guarded methods"
|
16
|
+
|
17
|
+
should "keep a list of method groups"
|
18
|
+
|
19
|
+
should "tell me an r, v, m is guarded"
|
20
|
+
|
21
|
+
should "tell me an r, v, m is not guarded"
|
22
|
+
|
23
|
+
should "be able to use the 'add grant' method to add a new grant"
|
24
|
+
|
25
|
+
should "delegate to the helpers to guard and fix aliases"
|
26
|
+
|
27
|
+
should "return a 'grants' matrix"
|
28
|
+
|
29
|
+
context "the 'allowed' method" do
|
30
|
+
should "return 'true' when called on an unguarded method"
|
31
|
+
|
32
|
+
should "return 'true' if the user is nil"
|
33
|
+
|
34
|
+
should "return 'true' if the guarded object is nil"
|
35
|
+
|
36
|
+
should "return 'true' if the method is nil"
|
37
|
+
|
38
|
+
should "call user.stonewall_role_info and iterate through the results calling granted?"
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestActiveRecordExtensions < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "return true just to be a bad boy until my testing is in place" do
|
6
|
+
assert true
|
7
|
+
end
|
8
|
+
|
9
|
+
should "define a method_missing_with_stonewall"
|
10
|
+
|
11
|
+
should "call a stored action when we call a non-existent 'may_' method"
|
12
|
+
|
13
|
+
should "chain method_missing"
|
14
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestGuardedClass < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "return true just to be a bad boy until my testing is in place" do
|
6
|
+
assert true
|
7
|
+
end
|
8
|
+
|
9
|
+
context "with a user that had a role" do
|
10
|
+
context "and an object with some reasonable permissions worthy of testing" do
|
11
|
+
context "in the first state" do
|
12
|
+
should "allow an unguarded method"
|
13
|
+
should "allow a guarded method with the appropriate permissions"
|
14
|
+
should "denied a guarded method without the appropriate permissions"
|
15
|
+
should "allow a method group when all methods are allowed"
|
16
|
+
should "deny a method group when one method in the group is denied"
|
17
|
+
end
|
18
|
+
|
19
|
+
context "in the second state" do
|
20
|
+
should "allow an unguarded method"
|
21
|
+
should "allow a guarded method with the appropriate permissions"
|
22
|
+
should "denied a guarded method without the appropriate permissions"
|
23
|
+
should "allow a method group when all methods are allowed"
|
24
|
+
should "deny a method group when one method in the group is denied"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestHelpers < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "return true just to be a bad boy until my testing is in place" do
|
6
|
+
assert true
|
7
|
+
end
|
8
|
+
|
9
|
+
context "the symbolize_role method" do
|
10
|
+
should "return a Symbol when passed a Symbol"
|
11
|
+
should "return a Symbol when passed a String"
|
12
|
+
should "return a Symbol when passed a Class with a name method"
|
13
|
+
end
|
14
|
+
|
15
|
+
should "fix aliases for a guarded class"
|
16
|
+
|
17
|
+
should "fix an alias for a given guarded class and method"
|
18
|
+
|
19
|
+
should "figure out the checked method name given a method name"
|
20
|
+
|
21
|
+
should "fiure out the unchecked method name given a method name"
|
22
|
+
|
23
|
+
should "define a checked meethod on the guarded class"
|
24
|
+
|
25
|
+
end
|
data/test/test_parser.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestParser < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "return true just to be a bad boy until my testing is in place" do
|
6
|
+
assert true
|
7
|
+
end
|
8
|
+
|
9
|
+
should "have allowed_method as a dsl term"
|
10
|
+
should "have allowed_methods as a dsl term"
|
11
|
+
should "have allowed_method_group as a dsl term"
|
12
|
+
should "have allowed_method_groups as a dsl term"
|
13
|
+
should "have method_group as a dsl term"
|
14
|
+
should "have varies_on as a dsl term"
|
15
|
+
should "have action as a dsl term"
|
16
|
+
should "have role as a dsl term"
|
17
|
+
should "have variant as a dsl term"
|
18
|
+
should "have guard as a dsl term"
|
19
|
+
|
20
|
+
should "figure out a nifty way to test the recursive decent parser"
|
21
|
+
|
22
|
+
should "ensure a minimal parsed dsl results in the expected grants matrix"
|
23
|
+
|
24
|
+
end
|
data/test/test_stonewall.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class TestStonewall < Test::Unit::TestCase
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
should "return true just to be a bad boy until my testing is in place" do
|
6
|
+
assert true
|
6
7
|
end
|
8
|
+
|
9
|
+
should "have 100% test coverage by writing stuff here if the other tests don't provide it"
|
10
|
+
|
7
11
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestUserExtensions < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "return true just to be a bad boy until my testing is in place" do
|
6
|
+
assert true
|
7
|
+
end
|
8
|
+
|
9
|
+
should "define 'may_send?' on the user class"
|
10
|
+
|
11
|
+
should "define a 'stonewall_role_info' method on the user class"
|
12
|
+
|
13
|
+
context "the 'stonewall_role_info' method" do
|
14
|
+
should "call the 'role' method if it is defined on the user"
|
15
|
+
should "call the 'roles' method if it is defined on the user"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- bokmann
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-29 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -82,7 +82,13 @@ files:
|
|
82
82
|
- lib/stonewall/stonewall.rb
|
83
83
|
- lib/stonewall/user_extensions.rb
|
84
84
|
- test/helper.rb
|
85
|
+
- test/test_access_controller.rb
|
86
|
+
- test/test_active_record_extensions.rb
|
87
|
+
- test/test_guarded_class.rb
|
88
|
+
- test/test_helpers.rb
|
89
|
+
- test/test_parser.rb
|
85
90
|
- test/test_stonewall.rb
|
91
|
+
- test/test_user_extensions.rb
|
86
92
|
has_rdoc: true
|
87
93
|
homepage: http://github.com/bokmann/stonewall
|
88
94
|
licenses: []
|
@@ -115,4 +121,10 @@ specification_version: 3
|
|
115
121
|
summary: extracting the acl constructs from stonepath
|
116
122
|
test_files:
|
117
123
|
- test/helper.rb
|
124
|
+
- test/test_access_controller.rb
|
125
|
+
- test/test_active_record_extensions.rb
|
126
|
+
- test/test_guarded_class.rb
|
127
|
+
- test/test_helpers.rb
|
128
|
+
- test/test_parser.rb
|
118
129
|
- test/test_stonewall.rb
|
130
|
+
- test/test_user_extensions.rb
|