sanction 0.0.1 → 2.1.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,34 @@
1
+ module Sanction
2
+ module Whitelist
3
+ class List < Sanction::AttachedList
4
+
5
+ def allowed_ids
6
+ (wildcard_resource? || resources.include?(@key)) ? entries.map {|x| x.id} : []
7
+ end
8
+
9
+ def permitted?
10
+ return true if wildcard_resource?
11
+ return false if ids_blank?
12
+ return true if resources.include?(@key)
13
+ false
14
+ end
15
+
16
+ def blacklist?
17
+ false
18
+ end
19
+
20
+ def whitelist?
21
+ true
22
+ end
23
+
24
+ def denied_ids
25
+ []
26
+ end
27
+
28
+ def null_node_class
29
+ Sanction::Whitelist::NullNode
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,43 @@
1
+ module Sanction
2
+ module Whitelist
3
+ class Node < Sanction::Node
4
+
5
+ def permitted?
6
+ super
7
+ root? ? true : (@parent[type].permitted? && @parent[type].allowed_ids.include?(id))
8
+ end
9
+
10
+ def allow!
11
+ false
12
+ end
13
+
14
+ def deny!
15
+ @parent.resources << type
16
+ @parent.resources.uniq!
17
+ unlink
18
+ true
19
+ end
20
+
21
+ def whitelist?
22
+ true
23
+ end
24
+
25
+ def blacklist?
26
+ false
27
+ end
28
+
29
+ def mode
30
+ 'whitelist'
31
+ end
32
+
33
+ def array_class
34
+ Sanction::Whitelist::List
35
+ end
36
+
37
+ def null_array_class
38
+ Sanction::Whitelist::NullList
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ module Sanction
2
+ module Whitelist
3
+ class NullList < Sanction::Whitelist::List
4
+
5
+ def permitted?
6
+ return true if wildcard_resource?
7
+ return true if resources.include?(@key) && !ids_blank?
8
+ return false if ids_blank?
9
+ end
10
+
11
+ def persisted?
12
+ false
13
+ end
14
+
15
+ def null_node_class
16
+ Sanction::Whitelist::NullNode
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ module Sanction
2
+ module Whitelist
3
+ class NullNode < Sanction::Whitelist::Node
4
+
5
+ def permitted?
6
+ a = ancestors.reject(&:root?).map(&:permitted?)
7
+ a << false
8
+ a.all?
9
+ end
10
+
11
+ def allow!
12
+ ancestors.reject(&:persisted?).each(&:allow!)
13
+ @parent.resources << type
14
+ @parent.resources.uniq!
15
+ @parent.add_subject({
16
+ id: id,
17
+ type: type
18
+ })
19
+ end
20
+
21
+ def deny!
22
+ false
23
+ end
24
+
25
+ def persisted?
26
+ false
27
+ end
28
+
29
+ def array_class
30
+ Sanction::Whitelist::NullList
31
+ end
32
+
33
+ alias :null_array_class :array_class
34
+
35
+ end
36
+ end
37
+ end
data/lib/sanction.rb CHANGED
@@ -1,5 +1,36 @@
1
+ require "active_support"
2
+ require "active_support/core_ext"
1
3
  require "sanction/version"
2
4
 
3
5
  module Sanction
4
- # Your code goes here...
6
+
7
+ autoload :AttachedList, 'sanction/attached_list'
8
+ autoload :Tree, 'sanction/tree'
9
+ autoload :Node, 'sanction/node'
10
+ autoload :Permission, 'sanction/permission'
11
+
12
+ module Whitelist
13
+ autoload :List, 'sanction/whitelist/list'
14
+ autoload :Node, 'sanction/whitelist/node'
15
+ autoload :NullList, 'sanction/whitelist/null_list'
16
+ autoload :NullNode, 'sanction/whitelist/null_node'
17
+ end
18
+
19
+ module Blacklist
20
+ autoload :List, 'sanction/blacklist/list'
21
+ autoload :Node, 'sanction/blacklist/node'
22
+ autoload :NullList, 'sanction/blacklist/null_list'
23
+ autoload :NullNode, 'sanction/blacklist/null_node'
24
+ end
25
+
26
+ extend self
27
+
28
+ def build(hash)
29
+ "sanction/#{hash[:mode]}/node".classify.constantize.new(hash)
30
+ end
31
+
32
+ def permission(permission, *predicates)
33
+ Sanction::Permission.new(permission, *predicates)
34
+ end
35
+
5
36
  end
data/sanction.gemspec CHANGED
@@ -6,11 +6,11 @@ require 'sanction/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "sanction"
8
8
  spec.version = Sanction::VERSION
9
- spec.authors = ["JGW Maxwell"]
10
- spec.email = ["john.maxwell@boardintelligence.co.uk", "adam.carlile@boardintelligence.co.uk"]
11
- spec.summary = %q{Sanction.}
12
- spec.description = %q{Sanction.}
13
- spec.homepage = "http://www.boardintelligence.co.uk"
9
+ spec.authors = ["Adam Carlile", "John Maxwell"]
10
+ spec.email = ["adam.carlile@boardintelligence.co.uk", "john.maxwell@boardintelligence.co.uk"]
11
+ spec.summary = "A permissions gem for people who love JSON"
12
+ spec.description = "Provides a JSON format for describing complex nested permission sets"
13
+ spec.homepage = "http://github.com/boardiq/sanction"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
@@ -18,6 +18,11 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "activesupport"
22
+
23
+ spec.add_development_dependency "minitest"
21
24
  spec.add_development_dependency "bundler", "~> 1.6"
22
- spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "pry"
27
+ spec.add_development_dependency "awesome_print"
23
28
  end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ # Issues lifted from the web app
4
+
5
+ describe 'application issues' do
6
+
7
+ let(:permissions_hash) { {} }
8
+ let(:permissions) { Sanction.build(permissions_hash) }
9
+ let(:predicates) { [] }
10
+ let(:permission) { Sanction::Permission.new(permissions, *predicates)}
11
+
12
+ describe 'regular user with one allowed bookcase, but no allowed shelves' do
13
+ let(:permissions_hash) do
14
+ {
15
+ mode: "whitelist",
16
+ scope: [:read],
17
+ subjects: [
18
+ {
19
+ id: "948b9ace-784f-4326-aeb9-a2a0587d75b9",
20
+ type: 'bookcase',
21
+ mode: "whitelist",
22
+ scope: [:read, :manage],
23
+ resources: []
24
+ }
25
+ ],
26
+ resources: [:bookcase]
27
+ }
28
+ end
29
+ let(:predicates) { [Bookcase.new('948b9ace-784f-4326-aeb9-a2a0587d75b9')] }
30
+
31
+ it 'should not allow access to any shelves' do
32
+ permission.path[:shelf].permitted?.must_equal false
33
+ end
34
+
35
+ describe "allowing a new shelf" do
36
+ before do
37
+ permission.path[:shelf][1234].allow!
38
+ end
39
+
40
+ it 'should allow access to the shelf' do
41
+ permission.path[:shelf][1234].permitted?.must_equal true
42
+ end
43
+ end
44
+ end
45
+
46
+ describe 'admin user with one banned bookcase' do
47
+
48
+ let(:permissions_hash) do
49
+ {
50
+ mode: 'blacklist',
51
+ scope: [:read, :manage],
52
+ subjects: [
53
+ {
54
+ id: 'f23175aa-014b-4796-aaef-878df597e7f1',
55
+ type: 'bookcase',
56
+ mode: 'whitelist'
57
+ }
58
+ ]
59
+ }
60
+ end
61
+
62
+ let(:predicates) { [User.new(32)] }
63
+
64
+ it 'should allow access for a user and a random id' do
65
+ permission.permitted?.must_equal true
66
+ end
67
+
68
+ describe 'disalowing neseted objects' do
69
+ before do
70
+ permission.path.root[:bookcase][12][:shelf][10][:pack][14].deny!
71
+ end
72
+
73
+ it 'should not be viewable' do
74
+ permission.path.root[:bookcase][12][:shelf][10][:pack][14].permitted?.must_equal false
75
+ end
76
+ end
77
+
78
+ describe 'disalowing a user' do
79
+ before do
80
+ permission.path.root[:user][12].deny!
81
+ end
82
+
83
+ it 'should prevent access to user 12' do
84
+ permission.path.root[:user][12].permitted?.must_equal false
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+
91
+ end
data/spec/node_spec.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sanction::Node do
4
+
5
+ let(:permissions_hash) do
6
+ {
7
+ mode: 'whitelist',
8
+ scope: ['manage', 'read'],
9
+ subjects: [
10
+ {
11
+ id: 6,
12
+ type: 'bookcase',
13
+ scope: []
14
+ }
15
+ ]
16
+ }
17
+ end
18
+ let(:permissions) { Sanction.build(permissions_hash) }
19
+
20
+ it 'should let me find id: 6' do
21
+ permissions.find('bookcase', 6).id.must_equal 6
22
+ end
23
+
24
+ describe 'changing root node type' do
25
+
26
+ it 'should return the root node as being a blacklist' do
27
+ perm = permissions.root.change_type!(:blacklist)
28
+ perm.mode.must_equal 'blacklist'
29
+ end
30
+ end
31
+
32
+ describe 'with complex permissons' do
33
+ let(:permissions_hash) { PERMISSIONS }
34
+
35
+ describe 'changing a node type' do
36
+
37
+ it 'should return the node as being changed' do
38
+ perm = permissions.find('bookcase', 2).change_type!(:blacklist)
39
+ perm.find('bookcase', 2).mode.must_equal 'blacklist'
40
+ end
41
+
42
+ end
43
+
44
+ it 'should return root for a missing id and type' do
45
+ permissions.find('test', 5).root?.must_equal true
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,218 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sanction::Permission do
4
+
5
+ let(:permissions) { Sanction.build(permissions_hash) }
6
+ let(:predicates) { [] }
7
+ let(:permission) { Sanction::Permission.new(permissions, *predicates)}
8
+
9
+ let(:bookcase) { Bookcase.new(6) }
10
+ let(:shelf) { Shelf.new(4) }
11
+ let(:pack) { Pack.new(5) }
12
+
13
+ describe "With simple whitelist permissions" do
14
+
15
+ let(:permissions_hash) do
16
+ {
17
+ mode: 'whitelist',
18
+ scope: ['manage', 'read'],
19
+ resources: ['bookcase'],
20
+ subjects: [
21
+ {
22
+ id: 6,
23
+ type: 'bookcase',
24
+ scope: []
25
+ }
26
+ ]
27
+ }
28
+ end
29
+ let(:predicates) { [bookcase] }
30
+
31
+ it 'should be permitted' do
32
+ permission.permitted?.must_equal true
33
+ end
34
+
35
+ it 'should have scope manage' do
36
+ permission.permitted_with_scope?(:manage).must_equal true
37
+ end
38
+
39
+ it 'should have scope read' do
40
+ permission.permitted_with_scope?(:read).must_equal true
41
+ end
42
+
43
+ describe 'With a class predicate' do
44
+ let(:predicates) { [Shelf] }
45
+
46
+ it 'should not be permitted' do
47
+ permission.permitted?.must_equal false
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ describe "With simple blacklist permissions" do
55
+ let(:permissions_hash) do
56
+ {
57
+ mode: 'blacklist',
58
+ scope: ['manage', 'read'],
59
+ subjects: [
60
+ {
61
+ id: 6,
62
+ type: 'bookcase',
63
+ scope: []
64
+ }
65
+ ]
66
+ }
67
+ end
68
+ let(:predicates) { [bookcase] }
69
+
70
+ it 'should not be permitted' do
71
+ permission.permitted?.must_equal false
72
+ end
73
+
74
+ it 'should not be permitted for a scope of manage' do
75
+ permission.permitted_with_scope?(:manage).must_equal false
76
+ end
77
+
78
+ describe 'With a class predicate not blacklisted' do
79
+ let(:predicates) { [Shelf] }
80
+
81
+ it 'should be permitted' do
82
+ permission.permitted?.must_equal true
83
+ end
84
+
85
+ it 'should be permitted with a scope of manage' do
86
+ permission.permitted_with_scope?(:manage).must_equal true
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+
93
+ describe "With complex whitelist permissions" do
94
+ let(:permissions_hash) { PERMISSIONS }
95
+ let(:bookcase) { Bookcase.new(2) }
96
+ let(:predicates) { [bookcase, shelf] }
97
+
98
+ describe "with a blacklisted mode shelf" do
99
+ let(:shelf) { Shelf.new(7) }
100
+
101
+ describe "with a pack on the blacklist" do
102
+ let(:pack) { Pack.new(8) }
103
+ let(:predicates) { [bookcase, shelf, pack] }
104
+
105
+ it 'should not be permitted' do
106
+ permission.permitted?.must_equal false
107
+ end
108
+
109
+ end
110
+
111
+ describe "with a pack not on the blacklist" do
112
+ let(:pack) { Pack.new(100) }
113
+ let(:predicates) { [bookcase, shelf, pack] }
114
+
115
+ it 'should be permitted' do
116
+ permission.permitted?.must_equal true
117
+ end
118
+
119
+ it 'should have its parents scopes' do
120
+ permission.permitted_with_scope?(:manage).must_equal true
121
+ permission.permitted_with_scope?(:read).must_equal true
122
+ end
123
+
124
+ end
125
+
126
+ end
127
+
128
+ describe "with a whitelisted shelf" do
129
+ let(:shelf) { Shelf.new(4) }
130
+
131
+ it 'should be permitted' do
132
+ permission.permitted?.must_equal true
133
+ end
134
+
135
+ describe "with a whitelisted pack" do
136
+ let(:pack) { Pack.new(5) }
137
+ let(:predicates) { [bookcase, shelf, pack] }
138
+
139
+ it 'should be permitted' do
140
+ permission.permitted?.must_equal true
141
+ end
142
+
143
+ it 'should have a read and manage scope' do
144
+ permission.permitted_with_scope?(:manage).must_equal true
145
+ permission.permitted_with_scope?(:read).must_equal true
146
+ end
147
+
148
+ end
149
+
150
+ end
151
+
152
+ end
153
+
154
+ describe "with an incorrect object graph with a whitelisted incorrect parent" do
155
+ let(:permissions_hash) { PERMISSIONS }
156
+ let(:bookcase) { Bookcase.new(2) }
157
+ let(:shelf) { Shelf.new(6) }
158
+ let(:predicates) { [bookcase, shelf] }
159
+
160
+ it 'should not allow the shelf to be permitted' do
161
+ permission.permitted?.must_equal false
162
+ end
163
+
164
+ end
165
+
166
+ describe "With missing blacklist permissions" do
167
+ let(:permissions_hash) { PERMISSIONS }
168
+ let(:bookcase) { Bookcase.new(1) }
169
+ let(:predicates) { [bookcase, shelf] }
170
+
171
+ describe "with a blacklisted shelf" do
172
+ let(:shelf) { Shelf.new(6) }
173
+
174
+ it 'should not be permitted' do
175
+ permission.permitted?.must_equal false
176
+ end
177
+
178
+ describe "with a pack within a blacklisted shelf" do
179
+ let(:pack) { Pack.new(98) }
180
+ let(:predicates) { [bookcase, shelf, pack] }
181
+
182
+ it 'should not be permitted' do
183
+ permission.permitted?.must_equal false
184
+ end
185
+
186
+ it 'should not have a read scope' do
187
+ permission.permitted_with_scope?(:read).must_equal false
188
+ end
189
+
190
+ end
191
+
192
+ end
193
+
194
+ describe 'with a non blacklisted shelf' do
195
+ let(:shelf) { Shelf.new(31) }
196
+
197
+ it 'should be permitted' do
198
+ permission.permitted?.must_equal true
199
+ end
200
+
201
+ describe 'with a non blacklisted pack' do
202
+ let(:pack) { Pack.new(10) }
203
+ let(:predicates) { [bookcase, shelf, pack] }
204
+
205
+ it 'should be permitted' do
206
+ permission.permitted?.must_equal true
207
+ end
208
+
209
+ it 'should have a read scope' do
210
+ permission.permitted_with_scope?(:read).must_equal true
211
+ end
212
+
213
+ end
214
+
215
+ end
216
+ end
217
+
218
+ end