stonewall 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|