thwart 0.0.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.
@@ -0,0 +1,197 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Thwart::RoleBuilder do
4
+ before do
5
+ @example_actions = double("Actionables Store", :actionables => {:view => [:view], :update => [:update], :manage => [:view, :update]})
6
+ @builder = Thwart::RoleBuilder.new(@example_actions)
7
+ end
8
+
9
+ it "should build roles" do
10
+ role = @builder.create_role :a_name
11
+ role.class.include?(Thwart::Role).should == true
12
+ end
13
+
14
+ it "should fire the build callback" do
15
+ receiver = double("receiver")
16
+ receiver.should_receive(:after_build_role)
17
+ klass = Thwart::RoleBuilder.clone
18
+ klass.set_callback :build_role, :after, receiver
19
+ role = klass.new(@example_actions).create_role :a_name
20
+ end
21
+
22
+ it "should apply the default action to built roles" do
23
+ role = @builder.create_role :a_name do
24
+ default false
25
+ end
26
+ role2 = @builder.create_role :another_name do
27
+ default true
28
+ end
29
+ role.responses.should == {}
30
+ role.default_response.should == false
31
+ role.query(nil, nil, nil).should == false
32
+
33
+ role2.responses.should == {}
34
+ role2.default_response.should == true
35
+ role2.query(nil, nil, nil).should == true
36
+ end
37
+
38
+ it "should only allow permissions to be defined using recognizable actions" do
39
+ lambda {
40
+ @builder.create_role :name do
41
+ define_permission :non_existant_action, :resource
42
+ end
43
+ }.should raise_error(ArgumentError)
44
+ end
45
+
46
+ it "should default to allowing actions" do
47
+ role1 = @builder.create_role :name do
48
+ view :foo
49
+ update :bar
50
+ end
51
+ role2 = @builder.create_role :a_name do
52
+ allow do
53
+ view :foo
54
+ update :bar
55
+ end
56
+ end
57
+ role1.responses.should == role2.responses
58
+ end
59
+
60
+ it "should provide deny blocks" do
61
+ role = @builder.create_role :name do
62
+ deny do
63
+ view :foo
64
+ update :bar
65
+ end
66
+ end
67
+ role.responses.should == {:view => {:foo => false}, :update => {:bar => false}}
68
+ end
69
+
70
+ it "should accept permissions outside of deny blocks as allows on both sides" do
71
+ role = @builder.create_role :name do
72
+ view :foo
73
+ deny do
74
+ update :foo
75
+ end
76
+ view :bar
77
+ end
78
+ role.responses.should == {:view => {:foo => true, :bar => true}, :update => {:foo => false}}
79
+ end
80
+
81
+ it "should build responses with :all" do
82
+ role = @builder.create_role :a_name do
83
+ view :all
84
+ end
85
+ role.responses.should == {:view => {:_other => true}}
86
+ end
87
+
88
+ it "should build :all responses without arguments" do
89
+ role = @builder.create_role :a_name do
90
+ view
91
+ end
92
+ role.responses.should == {:view => {:_other => true}}
93
+ end
94
+
95
+ it "should build roles which respond to simple actions" do
96
+ role = @builder.create_role :a_name do
97
+ update :this, :that
98
+ end
99
+ role.responses.should == {:update => {:this => true, :that => true}}
100
+ end
101
+
102
+ it "should build roles which respond to simple actions" do
103
+ role = @builder.create_role :a_name do
104
+ update :this, :that
105
+ end
106
+ role.responses.should == {:update => {:this => true, :that => true}}
107
+ end
108
+
109
+ it "should build roles which respond to action groups" do
110
+ role = @builder.create_role :a_name do
111
+ manage :this, :that
112
+ end
113
+ role.responses.should == {:view => {:this => true, :that => true}, :update => {:this => true, :that => true}}
114
+ end
115
+
116
+ it "should merge all declarations with others" do
117
+ role = @builder.create_role :a_name do
118
+ update :this, :that
119
+ deny do
120
+ update :all
121
+ end
122
+ end
123
+ role.responses.should == {:update => {:this => true, :that => true, :_other => false}}
124
+ end
125
+
126
+ context "passed conditions" do
127
+ before do
128
+ @good_actor = double("actor", :is_allowed => true)
129
+ @bad_actor = double("actor", :is_allowed => false)
130
+ end
131
+
132
+ context "at the resource level" do
133
+ before do
134
+ @role = @builder.create_role :a_name do
135
+ update :foo, :if => lambda {|actor| actor.is_allowed == true }
136
+ update :bar, :unless => lambda {|actor| actor.is_allowed == true }
137
+ update :baz do |actor| actor.is_allowed == true end
138
+ end
139
+ end
140
+ it "should build procs using the :if option" do
141
+ @role.responses[:update][:foo].call(@good_actor).should == true
142
+ @role.responses[:update][:foo].call(@bad_actor).should == false
143
+ end
144
+ it "should build procs using the :unless option" do
145
+ @role.responses[:update][:bar].call(@good_actor).should == false
146
+ @role.responses[:update][:bar].call(@bad_actor).should == true
147
+ end
148
+ it "should build procs using action level blocks" do
149
+ @role.responses[:update][:baz].call(@good_actor).should == true
150
+ @role.responses[:update][:baz].call(@bad_actor).should == false
151
+ end
152
+ end
153
+
154
+ context "at the action level" do
155
+ it "should build procs using the :if option" do
156
+ @role = @builder.create_role :a_name do
157
+ update :if => lambda {|actor| actor.is_allowed == true }
158
+ end
159
+ @role.responses[:update][:_other].call(@good_actor).should == true
160
+ @role.responses[:update][:_other].call(@bad_actor).should == false
161
+ end
162
+ it "should build procs using the :unless option" do
163
+ @role = @builder.create_role :a_name do
164
+ update :unless => lambda {|actor| actor.is_allowed == true }
165
+ end
166
+ @role.responses[:update][:_other].call(@good_actor).should == false
167
+ @role.responses[:update][:_other].call(@bad_actor).should == true
168
+ end
169
+ it "should build procs using action level blocks" do
170
+ @role = @builder.create_role :a_name do
171
+ update do |actor| actor.is_allowed == true end
172
+ end
173
+ @role.responses[:update][:_other].call(@good_actor).should == true
174
+ @role.responses[:update][:_other].call(@bad_actor).should == false
175
+ end
176
+ end
177
+ end
178
+
179
+ context "building roles which include other roles" do
180
+ it "should allow parents to be specified as options to the role" do
181
+ @role = @builder.create_role :name, :parents => [:foo, :bar]
182
+ @role.parents.should == [:foo, :bar]
183
+ end
184
+ it "should allow parents to be included in the role definition" do
185
+ @role = @builder.create_role :name do
186
+ include :foo
187
+ include :bar
188
+ end
189
+ end
190
+ it "should allow parents to be included on the same line in the role definition" do
191
+ @role = @builder.create_role :name do
192
+ include :foo, :bar
193
+ end
194
+ end
195
+ end
196
+
197
+ end
@@ -0,0 +1,137 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ def actor_with_role(a_role)
4
+ double("Actor", :thwart_role => a_role.name)
5
+ end
6
+
7
+ describe Thwart::RoleRegistry do
8
+ before do
9
+ @role_builder = double("Role Builder")
10
+ @role_builder.class.stub(:set_callback => true)
11
+ @registry = Thwart::RoleRegistry.new(@role_builder)
12
+ end
13
+
14
+ it "should initialize a callback on the action creator" do
15
+ store_class = Class.new do
16
+ include ActiveSupport::Callbacks
17
+ end
18
+ store_class.should_receive(:set_callback)
19
+ Thwart::RoleRegistry.new(store_class.new)
20
+ end
21
+
22
+ it "should add roles to the registry" do
23
+ role = double("Role")
24
+ @registry.add(role)
25
+ @registry.roles.should include(role)
26
+ @registry.has_role?(role).should == true
27
+ end
28
+
29
+ it "should prevent addition of duplicate roles" do
30
+ role = double("Role")
31
+ @registry.add(role)
32
+ lambda {
33
+ @registry.add(role)
34
+ }.should raise_error(Thwart::DuplicateRoleError)
35
+ end
36
+ context "role finding" do
37
+ before do
38
+ @role = double("A Role", :name => :role1)
39
+ @registry.add(@role)
40
+ end
41
+ it "should find nil for nil" do
42
+ @registry.find_actor_role(nil).should == nil
43
+ end
44
+ it "should use the thwart_role attribute" do
45
+ actor = double("Actor", :thwart_role => @role)
46
+ @registry.find_actor_role(actor).should == @role
47
+ end
48
+ it "should convert symbols to roles" do
49
+ actor = double("Actor", :thwart_role => :role1)
50
+ @registry.find_actor_role(actor).should == @role
51
+ end
52
+ it "should find nil for symbols pointing to non registered roles " do
53
+ actor = double("Actor", :thwart_role => :role2)
54
+ @registry.find_actor_role(actor).should == nil
55
+ end
56
+ end
57
+ context "resource finding" do
58
+ it "should find nil for nil" do
59
+ @registry.find_resource_identifier(nil).should == nil
60
+ end
61
+ it "should find using the thwart_name attribute" do
62
+ resource = double("Resource", :thwart_name => :balls)
63
+ @registry.find_resource_identifier(resource).should == :balls
64
+ end
65
+ it "should find using the class thwart_name attribute" do
66
+ klass = Class.new do
67
+ def thwart_name
68
+ :balls
69
+ end
70
+ end
71
+ @registry.find_resource_identifier(klass.new).should == :balls
72
+ end
73
+ it "should find using the class name if the gem wide setting is set" do
74
+ Thwart.all_classes_are_resources = true
75
+ class Bollocks; end
76
+ @registry.find_resource_identifier(Bollocks.new).should == :bollocks
77
+ Thwart.all_classes_are_resources = false
78
+ end
79
+
80
+ end
81
+ context "querying" do
82
+ it "should return the default if the role can't be found and the gem wide setting is set" do
83
+ Thwart.actor_must_play_role = false
84
+ resp = double("A Bool")
85
+ Thwart.should_receive(:default_query_response).and_return(resp)
86
+ @registry.query(nil, nil, nil).should == resp
87
+ end
88
+
89
+ it "should raise an error if the role can't be found and the gem wide setting is set" do
90
+ Thwart.actor_must_play_role = true
91
+ lambda {
92
+ @registry.query(nil, nil, nil)
93
+ }.should raise_error(Thwart::MissingRoleError)
94
+ end
95
+
96
+ context "with roles in the registry" do
97
+ before do
98
+ Thwart.actor_must_play_role = true
99
+ Thwart.default_query_response = false
100
+ @role1 = double("Low Level Role", :name => :role1, :query => false)
101
+ @role2 = double("High Level Role", :name => :role2, :query => true)
102
+ @role3 = double("No Rules Role", :name => :role3, :query => nil, :parents => [@role2, @role1])
103
+ @role4 = double("Really low role", :name => :role4, :query => nil, :parents => [@role3])
104
+ [@role1, @role2, @role3, @role4].each do |r|
105
+ @registry.add(r)
106
+ end
107
+ end
108
+
109
+ it "should query the role" do
110
+ @registry.query(actor_with_role(@role1), nil, nil).should == false
111
+ end
112
+
113
+ it "should query the parents in a breadth first order if the role query is unsuccessful" do
114
+ @role3.should_receive(:parents)
115
+ @registry.query(actor_with_role(@role3), nil, nil).should == true # this ensures role2 is checked before role1
116
+ end
117
+
118
+ it "should query more than one level of parents" do
119
+ @role3.should_receive(:parents)
120
+ @role4.should_receive(:parents)
121
+ @registry.query(actor_with_role(@role4), nil, nil).should == true # this ensures role2 is checked before role1
122
+ end
123
+
124
+ it "should return the default if the role can't be found and the gem wide setting is set" do
125
+ Thwart.actor_must_play_role = false
126
+ @registry.query(actor_with_role(double("A role not in the registry", :name => :not_present)), nil, nil).should == false
127
+ end
128
+
129
+ it "should raise an error if the role can't be found and the gem wide setting is set" do
130
+ Thwart.actor_must_play_role = true
131
+ lambda {
132
+ @registry.query(actor_with_role(double("A role not in the registry", :name => :not_present)), nil, nil)
133
+ }.should raise_error(Thwart::MissingRoleError)
134
+ end
135
+ end
136
+ end
137
+ end
data/spec/role_spec.rb ADDED
@@ -0,0 +1,146 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Thwart::Role do
4
+ it "should be queryable" do
5
+ instance_with_module(Thwart::Role).respond_to?(:query).should == true
6
+ end
7
+
8
+ it "should respond with nil if no rule applies" do
9
+ instance_with_role_definition.query(nil, nil, nil).should == nil
10
+ end
11
+
12
+ it "should respond to queries with its role level default" do
13
+ a_val = mock('default query response')
14
+ @role1 = instance_with_role_definition do
15
+ self.default_response = a_val
16
+ end
17
+ @role1.query(nil, nil, nil).should == a_val
18
+ @role1.query(:foo, :bar, :baz).should == a_val
19
+ end
20
+
21
+ it "should uniqueify its parents upon assignment" do
22
+ @role = instance_with_role_definition
23
+ @role.parents = [:a, :b]
24
+ @role.parents += [:a, :c]
25
+ @role.parents.should == [:a, :b, :c]
26
+ end
27
+
28
+ context "with simple responses set" do
29
+ before do
30
+ @role = instance_with_role_definition do
31
+ self.responses = {:view => true, :update => false}
32
+ end
33
+ end
34
+ it "should respond to simple queries with actions" do
35
+ @role.query(nil, nil, :view).should == true
36
+ @role.query(nil, nil, :update).should == false
37
+ end
38
+ it "shouldn't respond to queries it has no rules for" do
39
+ @role.query(nil, nil, nil).should == nil
40
+ end
41
+ it "should return the default if it is set" do
42
+ @role.default_response = true
43
+ @role.query(nil, nil, nil).should == true
44
+ end
45
+ end
46
+
47
+ context "with response sets scoped by resource" do
48
+ before do
49
+ @role = instance_with_role_definition do
50
+ self.responses = {:view => {:foo => true, :bar => false}, :update => false, :destroy => {:foo => true, :_other => false}}
51
+ end
52
+ end
53
+ it "should respond to simple queries with actions" do
54
+ @role.query(nil, :foo, :view).should == true
55
+ @role.query(nil, :bar, :view).should == false
56
+ @role.query(nil, :baz, :view).should == nil
57
+
58
+ @role.query(nil, :foo, :update).should == false
59
+ @role.query(nil, :bar, :update).should == false
60
+ end
61
+ end
62
+
63
+ context "with defaults at all levels" do
64
+ before do
65
+ @role = instance_with_role_definition do
66
+ self.default_response = true
67
+ self.responses = {:view => nil, :update => false, :destroy => {:foo => true, :_other => false}}
68
+ end
69
+ end
70
+ it "should respond with the role level default for an unknown action" do
71
+ @role.query(nil, :foo, :create).should == true
72
+ end
73
+ it "should respond with the action level default for known actions" do
74
+ @role.query(nil, :foo, :view).should == nil
75
+ @role.query(nil, :foo, :update).should == false
76
+ end
77
+ it "should respond with the resource level default for known action and unknown permissions" do
78
+ @role.query(nil, :foo, :destroy).should == true # Known permission
79
+ @role.query(nil, :bar, :destroy).should == false
80
+ end
81
+ end
82
+
83
+ context "with response sets with procs" do
84
+ before do
85
+ @good_actor = double("actor", :is_allowed => true)
86
+ @bad_actor = double("actor", :is_allowed => false)
87
+ end
88
+ context "procs at action level" do
89
+ before do
90
+ @role = instance_with_role_definition do
91
+ self.responses = {:view => Proc.new {|actor, resource| actor.is_allowed == true}, :update => false}
92
+ end
93
+ end
94
+ it "should run the proc" do
95
+ @good_actor.should_receive(:is_allowed).twice
96
+ @role.query(@good_actor, :foo, :view).should == true
97
+ @role.query(@good_actor, :bar, :view).should == true
98
+ @role.query(@good_actor, :foo, :update).should == false
99
+ @role.query(@good_actor, :bar, :update).should == false
100
+
101
+ @bad_actor.should_receive(:is_allowed).twice
102
+ @role.query(@bad_actor, :foo, :view).should == false
103
+ @role.query(@bad_actor, :bar, :view).should == false
104
+ @role.query(@bad_actor, :foo, :update).should == false
105
+ @role.query(@bad_actor, :bar, :update).should == false
106
+ end
107
+ end
108
+
109
+ context "procs at resource level" do
110
+ before do
111
+ @role = instance_with_role_definition do
112
+ self.responses = {:view => {:foo => true,
113
+ :bar => Proc.new {|actor, resource| actor.is_allowed == true},
114
+ :_other => Proc.new {|actor, resource| actor.is_allowed == true}},
115
+ :update => false
116
+ }
117
+ end
118
+ end
119
+
120
+ it "should respond to other actions without procs" do
121
+ @role.query(nil, :foo, :view).should == true
122
+ @role.query(nil, :foo, :update).should == false
123
+ @role.query(nil, :bar, :update).should == false
124
+ @role.query(nil, :baz, :update).should == false
125
+ end
126
+
127
+ it "should respond to actions with procs by calling them" do
128
+ @role.query(@good_actor, :bar, :view).should == true
129
+ @role.query(@bad_actor, :bar, :view).should == false
130
+ end
131
+
132
+ it "should respond to unknown resources by calling the :_other proc" do
133
+ @role.query(@good_actor, :baz, :view).should == true
134
+ @role.query(@bad_actor, :baz, :view).should == false
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ describe Thwart::DefaultRole do
141
+ it "should respond to queries with the Thwart response default" do
142
+ a_val = mock('default query')
143
+ Thwart.should_receive(:default_query_response).and_return(a_val)
144
+ subject.query(nil, nil, nil).should == a_val
145
+ end
146
+ end
@@ -0,0 +1,49 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'thwart'
4
+ require 'rspec'
5
+ require 'rspec/autorun'
6
+
7
+ RSpec.configure do |c|
8
+ end
9
+
10
+ class User
11
+ include Thwart::Actor
12
+ attr_accessor :name
13
+ end
14
+
15
+ class Post
16
+ include Thwart::Resource
17
+ attr_accessor :title
18
+ end
19
+
20
+ def generic_model(name=nil, &block)
21
+ klass = Class.new do
22
+ def self.table_name
23
+ "generics"
24
+ end
25
+ if name
26
+ class_eval "def self.name; '#{name}' end"
27
+ class_eval "def self.to_s; '#{name}' end"
28
+ end
29
+ end
30
+
31
+ klass.class_eval(&block) if block_given?
32
+ klass
33
+ end
34
+
35
+ def class_with_module(mod)
36
+ Class.new do
37
+ include mod
38
+ end
39
+ end
40
+
41
+ def instance_with_module(mod)
42
+ class_with_module(mod).new
43
+ end
44
+
45
+ def instance_with_role_definition(&block)
46
+ role = instance_with_module(Thwart::Role)
47
+ role.instance_eval(&block) if block_given?
48
+ role
49
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Thwart do
4
+ it "should autoload all the classes" do
5
+ lambda {
6
+ Thwart::Actor
7
+ Thwart::Role
8
+ Thwart::DefaultRole
9
+ Thwart::Resource
10
+ Thwart::Dsl
11
+ Thwart::Cans
12
+ Thwart::Ables
13
+ Thwart::ActionsStore
14
+ Thwart::ActionGroupBuilder
15
+ Thwart::RoleRegistry
16
+ Thwart::RoleBuilder
17
+ }.should_not raise_error
18
+ end
19
+
20
+ describe "configuration" do
21
+ it "should realize the configured values" do
22
+ Thwart.configure do
23
+ role_registry 'role_registry'
24
+ default_query_response 'false'
25
+ end
26
+ Thwart.role_registry.should == 'role_registry'
27
+ Thwart.default_query_response.should == 'false'
28
+ end
29
+ it "should create new actions" do
30
+ Thwart::Actions.should_receive(:create_action).with(:add, :addable)
31
+ Thwart.configure do
32
+ action :add, :addable
33
+ end
34
+ end
35
+ it "should create new roles" do
36
+ role_dsl = double("dsl")
37
+ Thwart::RoleBuilder.should_receive(:new).and_return(role_dsl)
38
+ role_dsl.should_receive(:create_role).with(:a)
39
+ role_dsl.should_receive(:create_role).with(:b, 'something else')
40
+ role_dsl.class.stub(:set_callback)
41
+ role_dsl.class.stub(:reset_callbacks)
42
+ Thwart.configure do
43
+ role :a
44
+ role :b, 'something else'
45
+ end
46
+ end
47
+ it "should create new action groups" do
48
+ actionables = double("actionables")
49
+ Thwart::ActionGroupBuilder.should_receive(:new).and_return(actionables)
50
+ actionables.should_receive(:create_action_group).with(:manage)
51
+ actionables.should_receive(:create_action_group).with(:inspect, [])
52
+ Thwart.configure do
53
+ action_group :manage
54
+ action_group :inspect, []
55
+ end
56
+ end
57
+ end
58
+ describe "query" do
59
+ end
60
+ end