rolypoly 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8cbaff5553d1ea031bdb189ab537a9ff7c72aadd
4
- data.tar.gz: 332873a3a06a886b821e12a0977ae9da8a13a781
3
+ metadata.gz: da6a1122ac0be69431906e1943c0c0e6cbf7eb6a
4
+ data.tar.gz: e998ef6b5a0c1ed2655c1e09ebc31472bbdad714
5
5
  SHA512:
6
- metadata.gz: 1e25dbecd6ba8902d65f210a800179c6e63908bd24daf6f77775c314ea22d9e3418abfbffc06c08f496157bcb1b7462faf30d73fef054844481026851496b0d5
7
- data.tar.gz: f76e7eebd9c64d69e4bc098a2730304dab04922dbe0e3206a77227ca4d0e1b3bc9ac838e8f3e4313393b17d972d3823566b840bbf72fdccb85d6d12c1c6c4f7e
6
+ metadata.gz: 1a7ef1c1c348c1176e74c70f42e8660f516e4ef3bcfd2f68327b699261ba385701980e687da7a6bc395ec58e38c47614dfda8d3034c47e85b22aee5131a67953
7
+ data.tar.gz: 1f77bce2c1d4464c2eaa4f47ecda8e6d75c3b1687872d4048bc0cd2efad4cf595211141305a2aec41e52852d244bd515b7705e085ecdb941c010b8d5070e89ed
data/README.md CHANGED
@@ -19,7 +19,62 @@ And then execute:
19
19
  $> bundle
20
20
  ```
21
21
 
22
- ## Usage
22
+ ## Custom Usage
23
+
24
+ ```ruby
25
+ role_checker = Rolypoly.define_gatekeepers do
26
+ allow(:super_duper_admin).to_all
27
+ allow(:super_admin).on(:organization).to_all
28
+ allow(:admin).on(:team).to_access(:show, :update)
29
+ end
30
+
31
+ role_checker_options = {
32
+ organization: ['Organization', team.organization_id],
33
+ team: team
34
+ }
35
+
36
+ role_checker.allow?(role_objects, :destroy, role_checker_options)
37
+ role_checker.allow?(role_objects, :destroy, role_checker_options)
38
+ ```
39
+
40
+ ## Policy Usage
41
+
42
+ ```ruby
43
+ class TeamPolicy < Struct.new(:user, :team)
44
+
45
+ include Rolypoly::RoleDSL
46
+
47
+ allow(:super_duper_admin).to_all
48
+ allow(:super_admin).on(:organization).to_all
49
+ allow(:admin).on(:team).to_access(:show, :update)
50
+
51
+ def show?
52
+ allow?(:show)
53
+ end
54
+
55
+ def update?
56
+ allow?(:update)
57
+ end
58
+
59
+ def destroy?
60
+ allow?(:destroy)
61
+ end
62
+
63
+ def current_user_roles
64
+ current_user.role_assignments
65
+ end
66
+
67
+ def rolypoly_resource_map
68
+ {
69
+ organization: ['Organization', team.organization_id]
70
+ team: team
71
+ }
72
+ end
73
+
74
+ end
75
+ ```
76
+
77
+ ## Controller Usage
23
78
 
24
79
  ```ruby
25
80
  class ApplicationController < ActionController::Base
@@ -98,8 +153,8 @@ end
98
153
 
99
154
  This requires a method to be defined on `SomeCustomRoleObject` that checks if the resource is valid for that role.
100
155
 
101
- The `role_resource` needs to be defined on the controller to pass the resource that the role will be validated against.
102
- If `role_resource` is not defined it will be defaulted to an empty hash `{}`.
156
+ The `rolypoly_resource_map` needs to be defined on the controller to pass the resources that the role will be validated against.
157
+ If `rolypoly_resource_map` is not defined it will be defaulted to an empty hash `{}`.
103
158
 
104
159
 
105
160
  ```ruby
@@ -110,8 +165,8 @@ class SomeCustomRoleObject
110
165
  end
111
166
 
112
167
  class ProfilesController < ApplicationController
113
- allow_with_resource(:admin).to_access(:index)
114
- allow_with_resource(:owner).to_access(:edit)
168
+ allow(:admin).on(:organization).to_access(:index)
169
+ allow(:owner).on(:profile).to_access(:edit)
115
170
  publicize(:show)
116
171
 
117
172
  def index
@@ -130,8 +185,11 @@ class ProfilesController < ApplicationController
130
185
  current_user.roles # => [#<SomeCustomRoleObject to_role_string: "admin", resource?: true>, #<SomeCustomRoleObject to_role_string: "scorekeeper", resource?: false>]
131
186
  end
132
187
 
133
- private def role_resource
134
- { resource: params[:resource_id] }
188
+ private def rolypoly_resource_map
189
+ {
190
+ organization: ['Organization', tournament.org_id]
191
+ tournament: tournament
192
+ }
135
193
  end
136
194
  end
137
195
  ```
data/lib/rolypoly.rb CHANGED
@@ -1,2 +1,12 @@
1
1
  require "rolypoly/version"
2
2
  require 'rolypoly/controller_role_dsl'
3
+
4
+ module Rolypoly
5
+
6
+ def self.define_gatekeepers(&block)
7
+ role_gatekeepers = RoleGatekeepers.new
8
+ role_gatekeepers.instance_eval(&block)
9
+ role_gatekeepers
10
+ end
11
+
12
+ end
@@ -1,7 +1,9 @@
1
- require 'rolypoly/role_gatekeeper'
1
+ require 'rolypoly/role_dsl'
2
+
2
3
  module Rolypoly
3
4
  FailedRoleCheckError = Class.new StandardError
4
5
  module ControllerRoleDSL
6
+
5
7
  def self.included(sub)
6
8
  sub.before_filter(:rolypoly_check_role_access!) if sub.respond_to? :before_filter
7
9
  if sub.respond_to? :rescue_from
@@ -14,101 +16,48 @@ module Rolypoly
14
16
  end
15
17
  end
16
18
 
17
- unless sub.method_defined? :current_user_roles
18
- define_method(:current_user_roles) { [] }
19
- end
20
-
21
- unless sub.method_defined? :role_resource
22
- define_method(:role_resource) { {} }
23
- end
24
- sub.send :extend, ClassMethods
25
- end
26
-
27
- def rolypoly_check_role_access!
28
- failed_role_check! unless rolypoly_role_access?
29
- end
30
-
31
- def failed_role_check!
32
- raise Rolypoly::FailedRoleCheckError
33
- end
34
-
35
- def current_roles
36
- return [] if rolypoly_gatekeepers.empty?
37
- current_gatekeepers.reduce([]) { |array, gatekeeper|
38
- if gatekeeper.role?(current_user_roles, role_resource)
39
- array += Array(gatekeeper.allowed_roles(current_user_roles, action_name, role_resource))
40
- end
41
- array
42
- }
43
- end
44
-
45
- def public?
46
- return true if rolypoly_gatekeepers.empty?
47
- current_gatekeepers.any? &:public?
48
- end
49
-
50
- def current_gatekeepers
51
- rolypoly_gatekeepers.select { |gatekeeper|
52
- gatekeeper.action? action_name
53
- }
54
- end
55
-
56
- def rolypoly_role_access?
57
- rolypoly_gatekeepers.empty? ||
58
- rolypoly_gatekeepers.any? { |gatekeeper|
59
- gatekeeper.allow?(current_roles, action_name, role_resource)
60
- }
61
- end
62
- private :rolypoly_role_access?
63
-
64
- def rolypoly_gatekeepers
65
- self.class.rolypoly_gatekeepers
19
+ sub.send(:include, RoleDSL)
20
+ sub.extend(ClassMethods)
21
+ sub.send(:include, InstanceMethods)
66
22
  end
67
- private :rolypoly_gatekeepers
68
23
 
69
- module ClassMethods
70
- def all_public
71
- build_gatekeeper(nil, nil).all_public
24
+ module InstanceMethods
25
+ def rolypoly_check_role_access!
26
+ failed_role_check! unless rolypoly_role_access?
72
27
  end
73
28
 
74
- def restrict(*actions)
75
- build_gatekeeper nil, actions
29
+ def failed_role_check!
30
+ raise Rolypoly::FailedRoleCheckError
76
31
  end
77
32
 
78
- def allow(*roles)
79
- build_gatekeeper roles, nil
33
+ def current_roles
34
+ allowed_roles(action_name)
80
35
  end
81
36
 
82
- def allow_with_resource(*roles)
83
- build_gatekeeper roles, nil, true
37
+ def public?
38
+ super(action_name)
84
39
  end
85
40
 
86
- def publicize(*actions)
87
- restrict(*actions).to_none
88
- end
89
-
90
- def rolypoly_gatekeepers
91
- @rolypoly_gatekeepers ||= Array(try_super(__method__))
41
+ def current_gatekeepers
42
+ rolypoly_gatekeepers.select { |gatekeeper|
43
+ gatekeeper.action? action_name
44
+ }
92
45
  end
93
46
 
94
- def try_super(mname)
95
- if superclass.respond_to?(mname)
96
- super_val = superclass.send(mname)
97
- super_val.respond_to?(:dup) ? super_val.dup : super_val
98
- end
47
+ def allow?(options = {})
48
+ rolypoly_gatekeepers.allow?(current_roles, action_name, rolypoly_resource_map.merge(options))
99
49
  end
100
50
 
101
- def build_gatekeeper(roles, actions, require_resource = false)
102
- RoleGatekeeper.new(roles, actions, require_resource).tap { |gatekeeper|
103
- rolypoly_gatekeepers << gatekeeper
104
- }
51
+ private def rolypoly_role_access?
52
+ allow?
105
53
  end
106
- private :build_gatekeeper
54
+ end
107
55
 
108
- def rolypoly_gatekeepers=(arry)
109
- @rolypoly_gatekeepers = Array(arry)
56
+ module ClassMethods
57
+ private def rolypoly_gatekeepers=(arry)
58
+ @rolypoly_gatekeepers = Rolypoly::RoleGatekeepers.new(arry)
110
59
  end
111
- private :rolypoly_gatekeepers=
112
60
  end
61
+
113
62
  end
114
63
  end
@@ -0,0 +1,57 @@
1
+ require 'forwardable'
2
+ require 'rolypoly/role_gatekeepers'
3
+
4
+ module Rolypoly
5
+ module RoleDSL
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ base.send(:include, InstanceMethods)
10
+ end
11
+
12
+ module InstanceMethods
13
+ extend Forwardable
14
+
15
+ def self.included(base)
16
+ unless base.method_defined?(:current_user_roles)
17
+ define_method(:current_user_roles) do
18
+ []
19
+ end
20
+ end
21
+
22
+ unless base.method_defined?(:rolypoly_resource_map)
23
+ define_method(:rolypoly_resource_map) do
24
+ {}
25
+ end
26
+ end
27
+ end
28
+
29
+ def_delegators 'self.class', :rolypoly_gatekeepers
30
+ def_delegators :rolypoly_gatekeepers, :public?
31
+
32
+ def allow?(action, options = {})
33
+ rolypoly_gatekeepers.allow?(current_user_roles, action, rolypoly_resource_map.merge(options))
34
+ end
35
+
36
+ def allowed_roles(action, options = {})
37
+ rolypoly_gatekeepers.allowed_roles(current_user_roles, action, rolypoly_resource_map.merge(options))
38
+ end
39
+ end
40
+
41
+ module ClassMethods
42
+ extend Forwardable
43
+
44
+ def inherited(subclass)
45
+ super
46
+ subclass.instance_variable_set('@rolypoly_gatekeepers', rolypoly_gatekeepers.dup)
47
+ end
48
+
49
+ def_delegators :rolypoly_gatekeepers, :all_public, :restrict, :allow, :allow?, :allowed_roles, :on, :public?, :publicize
50
+
51
+ def rolypoly_gatekeepers
52
+ @rolypoly_gatekeepers ||= Rolypoly::RoleGatekeepers.new
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -2,14 +2,37 @@ require 'set'
2
2
  module Rolypoly
3
3
  class RoleGatekeeper
4
4
  attr_reader :roles
5
- def initialize(roles, actions, require_resource)
5
+ def initialize(roles, actions, resource = nil)
6
6
  self.roles = Set.new Array(roles).map(&:to_s)
7
7
  self.actions = Set.new Array(actions).map(&:to_s)
8
- self.require_resource = require_resource
8
+ self.resource = resource
9
9
  self.all_actions = false
10
10
  self.public = false
11
11
  end
12
12
 
13
+ def initialize_copy(other)
14
+ @roles = @roles.dup
15
+ @actions = @actions.dup
16
+ end
17
+
18
+ # on(resource).allow(*roles).to_access(*actions)
19
+ def allow(*roles)
20
+ to(*roles)
21
+ self
22
+ end
23
+
24
+ # on(resource).restrict(*actions).to(*roles)
25
+ def restrict(*actions)
26
+ to_access(*actions)
27
+ self
28
+ end
29
+
30
+ # allow(*roles).on(resource).to_access(*actions)
31
+ def on(resource)
32
+ self.resource = resource
33
+ self
34
+ end
35
+
13
36
  # restrict(*actions).to *roles
14
37
  def to(*roles)
15
38
  self.roles = self.roles.merge roles.flatten.compact.map(&:to_s)
@@ -32,14 +55,13 @@ module Rolypoly
32
55
  self.all_actions = true
33
56
  end
34
57
 
35
- def allow?(current_roles, action, resource)
36
- action?(action) &&
37
- role?(current_roles, resource)
58
+ def allow?(current_roles, action, options = {})
59
+ action?(action) && role?(current_roles, options)
38
60
  end
39
61
 
40
- def allowed_roles(current_roles, action, resource)
62
+ def allowed_roles(current_roles, action, options = {})
41
63
  return [] if public? || !action?(action)
42
- match_roles(current_roles, resource)
64
+ match_roles(current_roles, options)
43
65
  end
44
66
 
45
67
  def all_public
@@ -47,10 +69,29 @@ module Rolypoly
47
69
  self.all_actions = true
48
70
  end
49
71
 
50
- def role?(check_roles, resource)
51
- check_roles = filter_roles_by_resource(check_roles, resource)
52
- check_roles = Set.new sanitize_role_input(check_roles)
53
- public? || !(check_roles & roles).empty?
72
+ def role?(check_roles, options = {})
73
+ return true if public?
74
+ required_resource = find_required_resource(options)
75
+
76
+ Array(check_roles).any? do |check_role|
77
+ allowed_role?(check_role) && allowed_resource?(check_role, required_resource)
78
+ end
79
+ end
80
+
81
+ private def require_resource?
82
+ !!resource
83
+ end
84
+
85
+ private def allowed_resource?(check_role, required_resource)
86
+ return true unless require_resource?
87
+
88
+ check_role.respond_to?(:resource?) && check_role.resource?(required_resource)
89
+ end
90
+
91
+ private def find_required_resource(options = {})
92
+ return resource unless %w(String Symbol).include?(resource.class.to_s)
93
+
94
+ options[resource]
54
95
  end
55
96
 
56
97
  def action?(check_actions)
@@ -67,44 +108,21 @@ module Rolypoly
67
108
  attr_accessor :actions
68
109
  attr_accessor :all_actions
69
110
  attr_accessor :public
70
- attr_accessor :require_resource
111
+ attr_accessor :resource
71
112
 
72
- def match_roles(check_roles, resource)
73
- check_roles = filter_roles_by_resource(check_roles, resource)
74
- check_roles.reduce([]) { |array, role_object|
75
- array << role_object if roles.include?(sanitize_role_object(role_object))
76
- array
77
- }
78
- end
79
- private :match_roles
113
+ private def match_roles(check_roles, options = {})
114
+ required_resource = find_required_resource(options)
80
115
 
81
- def filter_roles_by_resource(check_roles, resource)
82
- return check_roles if check_roles.nil? || !require_resource
83
- check_roles.select do |check_role|
84
- check_role.respond_to?(:resource?) && check_role.resource?(resource)
116
+ Array(check_roles).select do |check_role|
117
+ allowed_role?(check_role) && allowed_resource?(check_role, required_resource)
85
118
  end
86
119
  end
87
- private :filter_roles_by_resource
88
-
89
- def sanitize_role_input(role_objects)
90
- Array(role_objects).map { |r| sanitize_role_object(r) }
91
- end
92
- private :sanitize_role_input
93
120
 
94
- def sanitize_role_object(role_object)
95
- role_object.respond_to?(:to_role_string) ? role_object.to_role_string : role_object.to_s
96
- end
97
- private :sanitize_role_object
98
-
99
- def can_set_with_to?
100
- roles.empty?
101
- end
102
- private :can_set_with_to?
121
+ private def allowed_role?(role_object)
122
+ role_string = role_object.respond_to?(:to_role_string) ? role_object.to_role_string : role_object.to_s
103
123
 
104
- def can_set_with_access_to?
105
- actions.empty?
124
+ roles.include?(role_string.to_s)
106
125
  end
107
- private :can_set_with_access_to?
108
126
 
109
127
  def all_actions?
110
128
  !!all_actions
@@ -0,0 +1,54 @@
1
+ require 'forwardable'
2
+ require 'rolypoly/role_gatekeeper'
3
+
4
+ module Rolypoly
5
+ class RoleGatekeepers
6
+
7
+ extend Forwardable
8
+ include Enumerable
9
+
10
+ def_delegators :build_gatekeeper, :all_public, :allow, :on, :restrict
11
+ def_delegators :@gatekeepers, :clear, :each, :empty?
12
+
13
+ def initialize(gatekeepers = [])
14
+ @gatekeepers = Array(gatekeepers)
15
+ end
16
+
17
+ def initialize_copy(other)
18
+ @gatekeepers = @gatekeepers.map(&:dup)
19
+ end
20
+
21
+ def publicize(*actions)
22
+ restrict(*actions).to_none
23
+ end
24
+
25
+ def allow?(role_objects, action, options = {})
26
+ return true if empty?
27
+
28
+ any? { |gatekeeper| gatekeeper.allow?(role_objects, action, options) }
29
+ end
30
+
31
+ def allowed_roles(role_objects, action, options = {})
32
+ return [] if empty?
33
+
34
+ reduce([]) do |allowed_role_objects, gatekeeper|
35
+ allowed_role_objects | gatekeeper.allowed_roles(role_objects, action, options)
36
+ end
37
+ end
38
+
39
+ def public?(action)
40
+ return true if empty?
41
+
42
+ any? do |gatekeeper|
43
+ gatekeeper.action?(action) && gatekeeper.public?
44
+ end
45
+ end
46
+
47
+ private def build_gatekeeper(roles = nil, actions = nil, resource = nil)
48
+ new_gatekeeper = RoleGatekeeper.new(roles, actions, resource)
49
+ @gatekeepers << new_gatekeeper
50
+ new_gatekeeper
51
+ end
52
+
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  module Rolypoly
2
- VERSION = "0.2.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,9 +1,5 @@
1
1
  require 'spec_helper'
2
- class RoleObject < Struct.new :name
3
- def to_s
4
- name.to_s
5
- end
6
- end
2
+
7
3
  module Rolypoly
8
4
  describe ControllerRoleDSL do
9
5
  let(:example_controller) do
@@ -15,7 +11,7 @@ module Rolypoly
15
11
  subject { example_controller }
16
12
  it { should respond_to :restrict }
17
13
  it { should respond_to :allow }
18
- it { should respond_to :allow_with_resource }
14
+ it { should respond_to :on }
19
15
 
20
16
  describe "setting up with DSL" do
21
17
  describe "from allow side" do
@@ -78,21 +74,21 @@ module Rolypoly
78
74
  end
79
75
  end
80
76
 
81
- describe "from allow_with_resource side" do
77
+ describe "from on side" do
82
78
  let(:controller_instance) { subject.new }
83
79
  let(:admin_role) { RoleObject.new(:admin) }
84
80
  let(:scorekeeper_role) { RoleObject.new(:scorekeeper) }
85
81
  let(:current_user_roles) { [admin_role, scorekeeper_role] }
86
- let(:role_resource) { {resource: 123} }
82
+ let(:rolypoly_resource_map) { { organization: ['Organization', 123] } }
87
83
  let(:check_access!) { controller_instance.rolypoly_check_role_access! }
88
84
 
89
85
  before do
90
- subject.allow_with_resource(:admin).to_access(:index)
86
+ subject.on(:organization).allow(:admin).to_access(:index)
91
87
  subject.publicize(:landing)
92
- allow(admin_role).to receive(:resource?).and_return true
88
+ allow(admin_role).to receive(:resource?).with(rolypoly_resource_map[:organization]).and_return true
93
89
  allow(controller_instance).to receive(:current_user_roles).and_return(current_user_roles)
94
90
  allow(controller_instance).to receive(:action_name).and_return(action_name)
95
- allow(controller_instance).to receive(:role_resource).and_return(role_resource)
91
+ allow(controller_instance).to receive(:rolypoly_resource_map).and_return(rolypoly_resource_map)
96
92
  end
97
93
 
98
94
  describe "#index" do
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+
3
+ module Rolypoly
4
+ describe RoleDSL do
5
+ subject do
6
+ Class.new do
7
+ include Rolypoly::RoleDSL
8
+ end
9
+ end
10
+ after { subject.instance_variable_set('@rolypoly_gatekeepers', nil) }
11
+
12
+ it { expect(subject).to respond_to :rolypoly_gatekeepers }
13
+
14
+ it { expect(subject).to respond_to :all_public }
15
+ it 'should delegate all_public to rolypoly_gatekeepers' do
16
+ expect(subject.rolypoly_gatekeepers).to receive(:all_public)
17
+ subject.all_public
18
+ end
19
+
20
+ it { expect(subject).to respond_to :allow }
21
+ it 'should delegate allow to rolypoly_gatekeepers' do
22
+ expect(subject.rolypoly_gatekeepers).to receive(:allow).with(:role_1, :role_2)
23
+ subject.allow(:role_1, :role_2)
24
+ end
25
+
26
+ it { expect(subject).to respond_to :allow? }
27
+ it 'should delegate allow? to rolypoly_gatekeepers' do
28
+ expect(subject.rolypoly_gatekeepers).to receive(:allow?).with([:admin, :org_admin], :index)
29
+ subject.allow?([:admin, :org_admin], :index)
30
+ end
31
+
32
+ it { expect(subject).to respond_to :allowed_roles }
33
+ it 'should delegate allowed_roles to rolypoly_gatekeepers' do
34
+ expect(subject.rolypoly_gatekeepers).to receive(:allowed_roles).with([:admin, :org_admin], :index)
35
+ subject.allowed_roles([:admin, :org_admin], :index)
36
+ end
37
+
38
+ it { expect(subject).to respond_to :on }
39
+ it 'should delegate on to rolypoly_gatekeepers' do
40
+ expect(subject.rolypoly_gatekeepers).to receive(:on).with(:organization)
41
+ subject.on(:organization)
42
+ end
43
+
44
+ it { expect(subject).to respond_to :publicize }
45
+ it 'should delegate publicize to rolypoly_gatekeepers' do
46
+ expect(subject.rolypoly_gatekeepers).to receive(:publicize).with(:index, :show)
47
+ subject.publicize(:index, :show)
48
+ end
49
+
50
+ it { expect(subject).to respond_to :public? }
51
+ it 'should delegate public? to rolypoly_gatekeepers' do
52
+ expect(subject.rolypoly_gatekeepers).to receive(:public?).with(:index)
53
+ subject.public?(:index)
54
+ end
55
+
56
+ it { expect(subject).to respond_to :restrict }
57
+ it 'should delegate restrict to rolypoly_gatekeepers' do
58
+ expect(subject.rolypoly_gatekeepers).to receive(:restrict).with(:index, :show)
59
+ subject.restrict(:index, :show)
60
+ end
61
+
62
+ describe 'instance' do
63
+ let(:example_object) { subject.new }
64
+
65
+ it { expect(example_object).to respond_to :current_user_roles }
66
+ it { expect(example_object).to respond_to :rolypoly_resource_map }
67
+
68
+ it { expect(example_object).to respond_to :rolypoly_gatekeepers }
69
+ it 'should delegate rolypoly_gatekeepers to self.class' do
70
+ expect(subject).to receive(:rolypoly_gatekeepers)
71
+ example_object.rolypoly_gatekeepers
72
+ end
73
+
74
+ it { expect(subject).to respond_to :public? }
75
+ it 'should delegate public? to rolypoly_gatekeepers' do
76
+ expect(subject.rolypoly_gatekeepers).to receive(:public?).with(:index)
77
+ subject.public?(:index)
78
+ end
79
+
80
+ %w(allow? allowed_roles).each do |method_name|
81
+ it { expect(example_object).to respond_to method_name }
82
+
83
+ it "should delegate #{method_name} to rolypoly_gatekeepers" do
84
+ current_user_roles = [:admin, :org_admin]
85
+ rolypoly_resource_map = { foo: :foo, bar: :bar }
86
+ options = { bar: :baz }
87
+
88
+ expect(example_object).to receive(:current_user_roles).and_return(current_user_roles)
89
+ expect(example_object).to receive(:rolypoly_resource_map).and_return(rolypoly_resource_map)
90
+ expect(example_object.rolypoly_gatekeepers).to receive(method_name).with(current_user_roles, :index, { foo: :foo, bar: :baz })
91
+
92
+ example_object.public_send(method_name, :index, options)
93
+ end
94
+ end
95
+ end
96
+
97
+ end
98
+ end
@@ -4,22 +4,21 @@ module Rolypoly
4
4
  describe RoleGatekeeper do
5
5
  let(:roles) { %w[admin scorekeeper] }
6
6
  let(:actions) { %w[index show] }
7
- let(:resource) { {} }
8
7
 
9
8
  context "resource not required" do
10
- subject { described_class.new roles, actions, false }
9
+ subject { described_class.new roles, actions }
11
10
 
12
11
  shared_examples_for "allow should behave correctly" do
13
12
  it "shouldn't auto-allow" do
14
- expect(subject.allow?(nil, nil, resource)).to be false
13
+ expect(subject.allow?(nil, nil)).to be false
15
14
  end
16
15
 
17
16
  it "should allow scorekeepr access to index" do
18
- expect(subject.allow?([:scorekeeper], "index", resource)).to be true
17
+ expect(subject.allow?([:scorekeeper], "index")).to be true
19
18
  end
20
19
 
21
20
  it "should not allow scorekeepr access to edit" do
22
- expect(subject.allow?([:scorekeeper], "edit", resource)).to be false
21
+ expect(subject.allow?([:scorekeeper], "edit")).to be false
23
22
  end
24
23
 
25
24
  describe "all public" do
@@ -28,15 +27,15 @@ module Rolypoly
28
27
  end
29
28
 
30
29
  it "should allow whatever" do
31
- expect(subject.allow?(nil, nil, resource)).to be true
30
+ expect(subject.allow?(nil, nil)).to be true
32
31
  end
33
32
 
34
33
  it "should allow scorekeepr access to index" do
35
- expect(subject.allow?([:scorekeeper], "index", resource)).to be true
34
+ expect(subject.allow?([:scorekeeper], "index")).to be true
36
35
  end
37
36
 
38
37
  it "should allow scorekeepr access to edit" do
39
- expect(subject.allow?([:scorekeeper], "edit", resource)).to be true
38
+ expect(subject.allow?([:scorekeeper], "edit")).to be true
40
39
  end
41
40
  end
42
41
 
@@ -46,17 +45,17 @@ module Rolypoly
46
45
  end
47
46
 
48
47
  it "shouldn't auto-allow" do
49
- expect(subject.allow?(nil, nil, resource)).to be false
48
+ expect(subject.allow?(nil, nil)).to be false
50
49
  end
51
50
 
52
51
  it "should allow scorekeepr access to index" do
53
- expect(subject.allow?([:janitor], "index", resource)).to be true
54
- expect(subject.allow?([:admin], "index", resource)).to be true
52
+ expect(subject.allow?([:janitor], "index")).to be true
53
+ expect(subject.allow?([:admin], "index")).to be true
55
54
  end
56
55
 
57
56
  it "to should not allow scorekeepr access to edit" do
58
- expect(subject.allow?([:scorekeeper], "edit", resource)).to be false
59
- expect(subject.allow?([:janitor], "edit", resource)).to be false
57
+ expect(subject.allow?([:scorekeeper], "edit")).to be false
58
+ expect(subject.allow?([:janitor], "edit")).to be false
60
59
  end
61
60
  end
62
61
 
@@ -66,19 +65,19 @@ module Rolypoly
66
65
  end
67
66
 
68
67
  it "shouldn't auto-allow" do
69
- expect(subject.allow?(nil, nil, resource)).to be false
68
+ expect(subject.allow?(nil, nil)).to be false
70
69
  end
71
70
 
72
71
  it "should allow scorekeepr access to index" do
73
- expect(subject.allow?([:scorekeeper], "index", resource)).to be true
72
+ expect(subject.allow?([:scorekeeper], "index")).to be true
74
73
  end
75
74
 
76
75
  it "shouldn't allow janitor access to any" do
77
- expect(subject.allow?([:janitor], "index", resource)).to be false
76
+ expect(subject.allow?([:janitor], "index")).to be false
78
77
  end
79
78
 
80
79
  it "should allow scorekeepr access to edit" do
81
- expect(subject.allow?([:scorekeeper], "edit", resource)).to be true
80
+ expect(subject.allow?([:scorekeeper], "edit")).to be true
82
81
  end
83
82
  end
84
83
  end
@@ -118,11 +117,24 @@ module Rolypoly
118
117
  context "resource required" do
119
118
  let(:scorekeeper_role) { RoleObject.new(:scorekeeper) }
120
119
 
121
- subject { described_class.new roles, actions, true }
120
+ subject { described_class.new roles, actions, :resource }
122
121
 
123
122
  describe "resource does not match" do
124
123
  before do
125
- allow(scorekeeper_role).to receive(:resource?).and_return false
124
+ allow(scorekeeper_role).to receive(:resource?).with(nil).and_return false
125
+ allow(scorekeeper_role).to receive(:to_role_string).and_return 'scorekeeper'
126
+ end
127
+
128
+ it { expect(subject.allow?(nil, nil)).to be false }
129
+ it { expect(subject.allow?(scorekeeper_role, :index)).to be false }
130
+ it { expect(subject.allow?(scorekeeper_role, :edit)).to be false }
131
+ end
132
+
133
+ describe "resource does not match" do
134
+ let(:resource) { { resource: ['Organization', 123] } }
135
+
136
+ before do
137
+ allow(scorekeeper_role).to receive(:resource?).with(resource[:resource]).and_return false
126
138
  allow(scorekeeper_role).to receive(:to_role_string).and_return "scorekeeper"
127
139
  end
128
140
 
@@ -132,10 +144,10 @@ module Rolypoly
132
144
  end
133
145
 
134
146
  describe "resource matches" do
135
- let(:resource) { {resource: 123} }
147
+ let(:resource) { { resource: ['Organization', 123] } }
136
148
 
137
149
  before do
138
- allow(scorekeeper_role).to receive(:resource?).and_return true
150
+ allow(scorekeeper_role).to receive(:resource?).with(resource[:resource]).and_return true
139
151
  end
140
152
 
141
153
  it { expect(subject.allow?(nil, nil, resource)).to be false }
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+
3
+ module Rolypoly
4
+ describe RoleGatekeepers do
5
+ subject { RoleGatekeepers.new }
6
+ after { subject.clear }
7
+
8
+ # DSL class methods
9
+ describe 'rule-building methods' do
10
+ it { should respond_to :all_public }
11
+ it { expect { subject.all_public }.to change { subject.to_a.size }.by(1) }
12
+ it 'should delegate all_public to a new gatekeeper' do
13
+ expect_any_instance_of(RoleGatekeeper).to receive(:all_public)
14
+ subject.all_public
15
+ end
16
+
17
+ it { should respond_to :allow }
18
+ it { expect { subject.allow(:role_1, :role_2) }.to change { subject.to_a.size }.by(1) }
19
+ it 'should delegate allow to a new gatekeeper' do
20
+ expect_any_instance_of(RoleGatekeeper).to receive(:allow).with(:role_1, :role_2)
21
+ subject.allow(:role_1, :role_2)
22
+ end
23
+
24
+ it { should respond_to :on }
25
+ it { expect { subject.on(:organization) }.to change { subject.to_a.size }.by(1) }
26
+ it 'should delegate on to a new gatekeeper' do
27
+ expect_any_instance_of(RoleGatekeeper).to receive(:on).with(:organization)
28
+ subject.on(:organization)
29
+ end
30
+
31
+ it { should respond_to :publicize }
32
+ it { expect { subject.publicize }.to change { subject.to_a.size }.by(1) }
33
+ it 'should delegate publicize to a new gatekeeper' do
34
+ newer_gatekeeper = instance_double('RoleGatekeeper', :newer_gatekeeper)
35
+ expect_any_instance_of(RoleGatekeeper).to receive(:restrict).with(:index, :show).and_return(newer_gatekeeper)
36
+ expect(newer_gatekeeper).to receive(:to_none)
37
+ subject.publicize(:index, :show)
38
+ end
39
+
40
+ it { should respond_to :restrict }
41
+ it { expect { subject.restrict(:index, :show) }.to change { subject.to_a.size }.by(1) }
42
+ it 'should delegate restrict to a new gatekeeper' do
43
+ expect_any_instance_of(RoleGatekeeper).to receive(:restrict).with(:index, :show)
44
+ subject.restrict(:index, :show)
45
+ end
46
+ end
47
+
48
+ # DSL instance methods
49
+ it { should respond_to :allow? }
50
+ it { should respond_to :allowed_roles }
51
+ it { should respond_to :public? }
52
+
53
+ # Array methods not included in Enumerable
54
+ it { should respond_to :clear }
55
+ it { should respond_to :empty? }
56
+
57
+ describe 'setting up with DSL' do
58
+ describe 'from allow side' do
59
+ let(:current_user_roles) { [RoleObject.new(:admin), RoleObject.new(:scorekeeper)] }
60
+ before do
61
+ subject.allow(:admin).to_access(:index)
62
+ subject.publicize(:landing)
63
+ end
64
+
65
+ describe '#index' do
66
+ let(:action_name) { 'index' }
67
+
68
+ it 'is not public' do
69
+ expect(subject).to_not be_public(action_name)
70
+ end
71
+
72
+ it 'allows admin access' do
73
+ expect(subject).to be_allow(current_user_roles, action_name)
74
+ end
75
+
76
+ it 'can get current_user_roles' do
77
+ expect(subject.allowed_roles(current_user_roles, action_name)).to eq([RoleObject.new(:admin)])
78
+ end
79
+ end
80
+
81
+ describe '#show' do
82
+ let(:action_name) { 'show' }
83
+ it 'disallows admin access' do
84
+ expect(subject).to_not be_allow(current_user_roles, action_name)
85
+ end
86
+
87
+ it 'is not public' do
88
+ expect(subject).to_not be_public(action_name)
89
+ end
90
+ end
91
+
92
+ describe '#landing' do
93
+ let(:action_name) { 'landing' }
94
+ it 'allows admin access' do
95
+ expect(subject).to be_allow(current_user_roles, action_name)
96
+ end
97
+
98
+ describe 'with no role' do
99
+ let(:current_user_roles) { [] }
100
+ it 'allows admin access' do
101
+ expect(subject).to be_allow(current_user_roles, action_name)
102
+ end
103
+
104
+ it 'is public' do
105
+ expect(subject).to be_public(action_name)
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ describe 'from on side' do
112
+ let(:admin_role) { RoleObject.new(:admin) }
113
+ let(:scorekeeper_role) { RoleObject.new(:scorekeeper) }
114
+ let(:current_user_roles) { [admin_role, scorekeeper_role] }
115
+ let(:rolypoly_resource_map) { { organization: ['Organization', 123] } }
116
+
117
+ before do
118
+ subject.on(:organization).allow(:admin).to_access(:index)
119
+ subject.publicize(:landing)
120
+ allow(admin_role).to receive(:resource?).with(rolypoly_resource_map[:organization]).and_return true
121
+ end
122
+
123
+ describe '#index' do
124
+ let(:action_name) { 'index' }
125
+
126
+ it { expect(subject).to_not be_public(action_name) }
127
+ it { expect(subject).to be_allow(current_user_roles, action_name, rolypoly_resource_map) }
128
+ it { expect(subject.allowed_roles(current_user_roles, action_name, rolypoly_resource_map)).to eq([RoleObject.new(:admin)])}
129
+ end
130
+
131
+ describe '#show' do
132
+ let(:action_name) { 'show' }
133
+
134
+ it { expect(subject).to_not be_allow(current_user_roles, action_name) }
135
+ it { expect(subject).to_not be_public(action_name) }
136
+ end
137
+
138
+ describe '#landing' do
139
+ let(:action_name) { 'landing' }
140
+
141
+ it { expect(subject).to be_allow(current_user_roles, action_name) }
142
+
143
+ describe 'with no role' do
144
+ let(:current_user_roles) { [] }
145
+
146
+ it { expect(subject).to be_allow(current_user_roles, action_name) }
147
+ it { expect(subject).to be_public(action_name) }
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
data/spec/spec_helper.rb CHANGED
@@ -5,6 +5,13 @@
5
5
  #
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
7
  require 'rolypoly'
8
+
9
+ class RoleObject < Struct.new(:name)
10
+ def to_s
11
+ name.to_s
12
+ end
13
+ end
14
+
8
15
  RSpec.configure do |config|
9
16
  config.run_all_when_everything_filtered = true
10
17
  config.filter_run :focus
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rolypoly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Phenow
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-12-09 00:00:00.000000000 Z
12
+ date: 2017-06-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -71,11 +71,15 @@ files:
71
71
  - Rakefile
72
72
  - lib/rolypoly.rb
73
73
  - lib/rolypoly/controller_role_dsl.rb
74
+ - lib/rolypoly/role_dsl.rb
74
75
  - lib/rolypoly/role_gatekeeper.rb
76
+ - lib/rolypoly/role_gatekeepers.rb
75
77
  - lib/rolypoly/version.rb
76
78
  - rolypoly.gemspec
77
79
  - spec/lib/rolypoly/controller_role_dsl_spec.rb
80
+ - spec/lib/rolypoly/role_dsl_spec.rb
78
81
  - spec/lib/rolypoly/role_gatekeeper_spec.rb
82
+ - spec/lib/rolypoly/role_gatekeepers_spec.rb
79
83
  - spec/spec_helper.rb
80
84
  homepage: https://github.com/sportngin/rolypoly
81
85
  licenses:
@@ -97,11 +101,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
101
  version: '0'
98
102
  requirements: []
99
103
  rubyforge_project:
100
- rubygems_version: 2.4.8
104
+ rubygems_version: 2.6.10
101
105
  signing_key:
102
106
  specification_version: 4
103
107
  summary: Tools for handling per-action and per-app Role authorization
104
108
  test_files:
105
109
  - spec/lib/rolypoly/controller_role_dsl_spec.rb
110
+ - spec/lib/rolypoly/role_dsl_spec.rb
106
111
  - spec/lib/rolypoly/role_gatekeeper_spec.rb
112
+ - spec/lib/rolypoly/role_gatekeepers_spec.rb
107
113
  - spec/spec_helper.rb