rolypoly 0.2.0 → 1.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.
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