shamu 0.0.18 → 0.0.19

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: dd0f794321cd3d306d36b16935e133e3ded9270b
4
- data.tar.gz: 244f8025c48879a691c7faa6f6e76b3b45efe2e0
3
+ metadata.gz: 9cde1e60d3798de927cee92894d391d3387e1619
4
+ data.tar.gz: 204c11e52933dd2d88acb0e812d9e5183d3c09c0
5
5
  SHA512:
6
- metadata.gz: 57d00e95ce2bf7a4d1ddbc224aff0a2790255a6bd1c1ac907f7450b95a30b63c7ca2cc404d0754c7ee363761386fc5c1567f1f50114957488d538be85d2cbda8
7
- data.tar.gz: e2ab805eb2ba0db7969601cb825f827c8d5346c8ebd34db8e836543a6726b7327c20b68f092681555b56f1832d8dd8512d08d8bc1ddaf3170e6f221d5b3ab1a0
6
+ metadata.gz: cb22139780ad29697e690025ad740b2cb1673b3486f854744182972f4ff382a873c7f362a59936b39c82788742c5e486237d7b29c4346acfd3f0a2532e97f582
7
+ data.tar.gz: 7d06c76f61740a24724d4261a3b72d9117a5e5f36d6c600168271981c3c45ce23d1e637a9b677809d3996229e0df616e573d3994997ecb54a20310bfa349c65f
data/.rubocop.yml CHANGED
@@ -39,7 +39,7 @@ Metrics/MethodLength:
39
39
  Max: 15
40
40
 
41
41
  Metrics/AbcSize:
42
- Max: 20
42
+ Max: 24
43
43
 
44
44
  Metrics/CyclomaticComplexity:
45
45
  Max: 10
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shamu (0.0.18)
4
+ shamu (0.0.19)
5
5
  activemodel (>= 5.0)
6
6
  activesupport (>= 5.0)
7
7
  crc32 (~> 1)
@@ -114,7 +114,7 @@ GEM
114
114
  minitest (5.10.2)
115
115
  multi_json (1.12.1)
116
116
  nenv (0.3.0)
117
- nio4r (2.0.0)
117
+ nio4r (2.1.0)
118
118
  nokogiri (1.7.2)
119
119
  mini_portile2 (~> 2.1.0)
120
120
  notiffany (0.1.1)
@@ -51,6 +51,11 @@ module Shamu
51
51
  end
52
52
  end
53
53
 
54
+ # @return [Hash] a hash with the keys for each of the given names.
55
+ def slice( *names )
56
+ to_attributes only: names
57
+ end
58
+
54
59
  # Indicates if the object has an attribute with the given name. Aliased to
55
60
  # {#key?} to make the object look like a Hash.
56
61
  def attribute?( name )
@@ -51,7 +51,7 @@ module Shamu
51
51
  # should call {Transaction#append_entity} to include any parent
52
52
  # entities in the entity path.
53
53
  # @yieldreturn [Services::Result]
54
- def audit_request( request, action: :smart, &block )
54
+ def audit_request( request, action: :smart, &block ) # rubocop:disable Metrics/PerceivedComplexity
55
55
  transaction = Transaction.new \
56
56
  user_id_chain: auditing_security_principal.user_id_chain,
57
57
  changes: request.to_attributes( only: request.assigned_attributes ),
@@ -63,7 +63,7 @@ module Shamu
63
63
  if result.valid?
64
64
  if result.entity
65
65
  transaction.append_entity result.entity
66
- elsif request.respond_to?( :id ) && defined? entity_class
66
+ elsif !transaction.entities? && request.respond_to?( :id ) && defined? entity_class
67
67
  transaction.append_entity [ entity_class, request.id ]
68
68
  end
69
69
  auditing_service.commit( transaction )
@@ -49,6 +49,12 @@ module Shamu
49
49
  end
50
50
  end
51
51
 
52
+ # @return [Boolean] true if entities have been added to the transaction.
53
+ def entities?
54
+ entities.present?
55
+ end
56
+
57
+
52
58
  private
53
59
 
54
60
  attr_reader :entities
@@ -27,12 +27,12 @@ module Shamu
27
27
  # @return [ActiveRecord::Relation]
28
28
  def by_list_scope( scope )
29
29
  criteria = all
30
- criteria = apply_custom_list_scope( criteria, scope )
31
30
  criteria = apply_paging_scope( criteria, scope ) if scope.respond_to?( :paged? )
32
31
  criteria = apply_scoped_paging_scope( criteria, scope ) if scope.respond_to?( :scoped_page? )
33
32
  criteria = apply_window_paging_scope( criteria, scope ) if scope.respond_to?( :window_paged? )
34
33
  criteria = apply_dates_scope( criteria, scope ) if scope.respond_to?( :dated? )
35
34
  criteria = apply_sorting_scope( criteria, scope ) if scope.respond_to?( :sorted? )
35
+ criteria = apply_custom_list_scope( criteria, scope )
36
36
  criteria
37
37
  end
38
38
 
@@ -106,12 +106,16 @@ module Shamu
106
106
  def apply_custom_list_scope( criteria, scope )
107
107
  custom_list_scope_attributes( scope ).each do |name|
108
108
  scope_name = :"by_#{ name }"
109
+ apply_name = :"apply_#{ name }_list_scope"
110
+
109
111
  if criteria.respond_to?( scope_name )
110
112
  value = scope.send( name )
111
113
  criteria = criteria.send scope_name, value if value.present?
114
+ elsif criteria.respond_to?( apply_name )
115
+ criteria = criteria.send apply_name, criteria, scope
112
116
  else
113
117
  # rubocop:disable Metrics/LineLength
114
- fail ArgumentError, "Cannot apply '#{ name }' filter from #{ scope.class.name }. Add 'scope :#{ scope_name }, ->( #{ name } ) { ... }' to #{ criteria.class.name }"
118
+ fail ArgumentError, "Cannot apply '#{ name }' filter from #{ scope.class.name }. Add 'scope :#{ scope_name }, ->( #{ name } ) { ... }' or 'def self.#{ apply_name }( criteria, scope )' to #{ self.name }"
115
119
  end
116
120
  end
117
121
 
@@ -40,6 +40,17 @@ module Shamu
40
40
  # Exclude destroyed records by default.
41
41
  default_scope { except_destroyed }
42
42
 
43
+ # Apply list scoping that includes targeting `destroyed` state.
44
+ def self.apply_destroyed_list_scope( criteria, scope )
45
+ return criteria if scope.destroyed.nil?
46
+
47
+ if scope.destroyed
48
+ criteria.destroyed
49
+ else
50
+ criteria.except_destroyed
51
+ end
52
+ end
53
+
43
54
  #
44
55
  # @!endgroup Scopes
45
56
 
@@ -114,6 +114,29 @@ module Shamu
114
114
  self
115
115
  end
116
116
 
117
+ # Redact attributes on the entity.
118
+ #
119
+ # @param [Array<Symbol>,Hash] attributes to redact on the entity. Either
120
+ # a list of attributes to set to nil or a hash of attributes with their
121
+ # values.
122
+ def redact!( *attributes )
123
+ hash =
124
+ if attributes.first.is_a?( Symbol )
125
+ Hash[ attributes.zip( [ nil ] * attributes.length ) ]
126
+ else
127
+ attributes.first
128
+ end
129
+
130
+ assign_attributes hash
131
+ self
132
+ end
133
+
134
+ # @return [Entity] a modified version of the entity with the given
135
+ # attributes redacted.
136
+ def redact( attributes )
137
+ dup.redact!( attributes )
138
+ end
139
+
117
140
  private
118
141
 
119
142
  def serialize_attribute?( name, options )
@@ -35,7 +35,7 @@ module Shamu
35
35
  #
36
36
  # @!endgroup Attributes
37
37
 
38
- def self.included( base ) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
38
+ def self.included( base ) # rubocop:disable Metrics/MethodLength
39
39
  super
40
40
 
41
41
  base.attribute :first, coerce: :to_i, default: ->() { default_first }
@@ -1,3 +1,25 @@
1
+ RSpec::Matchers.define :raise_access_denied do |_expected|
2
+ def supports_block_expectations?
3
+ true
4
+ end
5
+
6
+ match do |actual|
7
+ begin
8
+ subject && subject.stub(:access_denied) do |exception|
9
+ raise exception
10
+ end
11
+
12
+ actual.call
13
+ false
14
+ rescue CanCan::AccessDenied
15
+ true
16
+ rescue Services::Security::AccessDeniedError
17
+ true
18
+ rescue
19
+ false
20
+ end
21
+ end
22
+ end
1
23
 
2
24
  RSpec::Matchers.define :be_permitted_to do |*args|
3
25
 
@@ -41,4 +63,4 @@ RSpec::Matchers.define :absolutely_be_permitted_to do |*args|
41
63
  match do |policy|
42
64
  policy.permit?( *args ) == :yes
43
65
  end
44
- end
66
+ end
@@ -47,10 +47,11 @@ module Shamu
47
47
  # when applying the refinement.
48
48
  # @return [ActiveRecord::Relation] the refined relation.
49
49
  def refine_relation( action, relation, additional_context = nil )
50
+ resolve_permissions
50
51
  refined = false
51
52
 
52
53
  refinements.each do |refinement|
53
- if refinement.match?( action, relation )
54
+ if refinement.match?( action, relation, additional_context )
54
55
  refined = true
55
56
  relation = refinement.apply( relation, additional_context ) || relation
56
57
  end
@@ -103,4 +104,4 @@ module Shamu
103
104
 
104
105
  end
105
106
  end
106
- end
107
+ end
@@ -3,7 +3,7 @@ module Shamu
3
3
 
4
4
  # Used in specs and service to service delegated requests to effectively
5
5
  # offer no policy and permit all actions.
6
- class NoPolicy
6
+ class NoPolicy < Policy
7
7
 
8
8
  # (see Policy#permit?)
9
9
  def permit?( * )
@@ -12,4 +12,4 @@ module Shamu
12
12
 
13
13
  end
14
14
  end
15
- end
15
+ end
@@ -42,17 +42,24 @@ module Shamu
42
42
 
43
43
  # @!attribute
44
44
  # @return [Principal] principal holding user identity and access credentials.
45
- attr_reader :principal
45
+ attr_reader :principal
46
46
 
47
47
  # @!attribute
48
48
  # @return [Array<Roles>] roles that have been granted to the {#principal}.
49
- attr_reader :roles
49
+ attr_reader :roles
50
+
51
+ # @!attribute
52
+ # @return [Array<Integer>] additional user ids that the {#principal} is
53
+ # may act on behalf of.
54
+ attr_reader :related_user_ids
55
+
50
56
  #
51
57
  # @!endgroup Dependencies
52
58
 
53
- def initialize( principal: nil, roles: nil )
54
- @principal = principal || Principal.new
55
- @roles = roles || []
59
+ def initialize( principal: nil, roles: nil, related_user_ids: nil )
60
+ @principal = principal || Principal.new
61
+ @roles = roles || []
62
+ @related_user_ids = Array.wrap( related_user_ids )
56
63
  end
57
64
 
58
65
  # Authorize the given `action` on the given resource. If it is not
@@ -101,7 +108,7 @@ module Shamu
101
108
  def rules
102
109
  @rules ||= begin
103
110
  @rules = []
104
- permissions
111
+ resolve_permissions
105
112
  @rules
106
113
  end
107
114
  end
@@ -123,11 +130,20 @@ module Shamu
123
130
  def principal_roles
124
131
  @principal_roles ||= begin
125
132
  expanded = self.class.expand_roles( *roles )
126
- expanded << :user if principal.user_id && self.class.role_defined?( :user )
133
+ expanded << :authenticated if principal.user_id && self.class.role_defined?( :authenticated )
127
134
  expanded
128
135
  end
129
136
  end
130
137
 
138
+ # @!visibility public
139
+ #
140
+ # @param [Integer] id of the candidate user.
141
+ # @return [Boolean] true if the given id is one of the authorized user
142
+ # ids on the principal.
143
+ def is_principal?( id ) # rubocop:disable Style/PredicateName
144
+ principal.try( :user_id ) == id || related_user_ids.include?( id )
145
+ end
146
+
131
147
  # ============================================================================
132
148
  # @!group DSL
133
149
  #
@@ -155,9 +171,24 @@ module Shamu
155
171
  #
156
172
  # @return [void]
157
173
  def permissions
158
- fail IncompleteSetupError, "Permissions have not been defined. Add a private `permissions` method to #{ self.class.name }" # rubocop:disable Metrics/LineLength
174
+ if respond_to?( :anonymous_permissions, true ) && respond_to?( :authenticated_permissions, true )
175
+ if principal.user_id
176
+ authenticated_permissions
177
+ else
178
+ anonymous_permissions
179
+ end
180
+ else
181
+ fail IncompleteSetupError, "Permissions have not been defined. Add a private `permissions` method to #{ self.class.name }" # rubocop:disable Metrics/LineLength
182
+ end
159
183
  end
160
184
 
185
+ # Makes sure the {#permissions} method is invoked only once.
186
+ def resolve_permissions
187
+ return if @permissions_resolved
188
+ @permissions_resolved = true
189
+ permissions
190
+ end
191
+
161
192
  # @!visibility public
162
193
  #
163
194
  # Permit one or more `actions` to be performed on a given `resource`.
@@ -167,6 +198,8 @@ module Shamu
167
198
  # called if the resource offered to {#permit?} is a Class or Module.
168
199
  #
169
200
  # @example
201
+ # end
202
+ # end
170
203
  # permit :read, UserEntity
171
204
  # permit :show, :dashboard
172
205
  # permit :update, UserEntity do |user|
@@ -185,8 +218,9 @@ module Shamu
185
218
  # @yieldparam [Object] additional_context offered to {#permit?}.
186
219
  # @yieldreturn [:yes, :maybe, false] see {#permit?}.
187
220
  # @return [void]
188
- def permit( *actions, resource, &block )
221
+ def permit( *actions, &block )
189
222
  result = @when_elevated ? :maybe : :yes
223
+ resource, actions = extract_resource( actions )
190
224
 
191
225
  add_rule( actions, resource, result, &block )
192
226
  end
@@ -200,7 +234,8 @@ module Shamu
200
234
  # @yield (see #permit)
201
235
  # @yieldparam (see #permit)
202
236
  # @yieldreturn [Boolean] true to deny the action.
203
- def deny( *actions, resource, &block )
237
+ def deny( *actions, &block )
238
+ resource, actions = extract_resource( actions )
204
239
  add_rule( actions, resource, false, &block )
205
240
  end
206
241
 
@@ -247,9 +282,41 @@ module Shamu
247
282
  aliases[to] |= actions
248
283
  end
249
284
 
285
+ # @!visibility public
286
+ #
287
+ # Define the `resource` to {#permit} or {#deny} access to. Inside the
288
+ # block you can omit the `resource` param on DSL methods that expect
289
+ # it.
290
+ #
291
+ # @example
292
+ # resource UserEntity do
293
+ # permit :read
294
+ # permit :update do |user|
295
+ # user.id == principal.user_id
296
+ # end
297
+ #
298
+ # permit :chop, OtherKindOfEntity
299
+ # end
300
+ def resource( resource )
301
+ last_resource = @dsl_resource
302
+ @dsl_resource = resource
303
+ yield
304
+ ensure
305
+ @dsl_resource = last_resource
306
+ end
307
+
250
308
  #
251
309
  # @!endgroup DSL
252
310
 
311
+ def dsl_resource
312
+ @dsl_resource || fail( "Provide a `resource` argument or use a #resource block to declare the protected resource." ) # rubocop:disable Metrics/LineLength
313
+ end
314
+
315
+ def extract_resource( actions )
316
+ resource = actions.last.is_a?( Symbol ) ? dsl_resource : actions.pop
317
+ [ resource, actions ]
318
+ end
319
+
253
320
  def add_rule( actions, resource, result, &block )
254
321
  rules.unshift PolicyRule.new( expand_aliases( actions ), resource, result, block )
255
322
  end
@@ -33,6 +33,7 @@ module Shamu
33
33
  #
34
34
  # @return [Boolean] true if the rule is a match.
35
35
  def match?( action, resource, additional_context )
36
+ return true if actions.include? :any
36
37
  return false unless actions.include? action
37
38
  return false unless resource_match?( resource )
38
39
 
@@ -52,8 +53,11 @@ module Shamu
52
53
  def resource_match?( candidate )
53
54
  return true if resource == candidate
54
55
  return true if resource.is_a?( Module ) && candidate.is_a?( resource )
56
+
57
+ # Allow 'doubles' to match in specs
58
+ true if defined?( RSpec::Mocks::Double ) && candidate.is_a?( RSpec::Mocks::Double )
55
59
  end
56
60
 
57
61
  end
58
62
  end
59
- end
63
+ end
@@ -27,7 +27,7 @@ module Shamu
27
27
  # @param [Array<Symbol>] roles
28
28
  # @return [Array<Symbol>] the expanded roles.
29
29
  def expand_roles( *roles )
30
- expand_roles_into( roles, [] )
30
+ expand_roles_into( roles, Set.new ).to_a
31
31
  end
32
32
 
33
33
  # @param [Symbol] the role to check.
@@ -39,6 +39,8 @@ module Shamu
39
39
  private
40
40
 
41
41
  def expand_roles_into( roles, expanded )
42
+ raise "No roles defined for #{ name }" unless self.roles.present?
43
+
42
44
  roles.each do |name|
43
45
  name = name.to_sym
44
46
 
@@ -59,4 +61,4 @@ module Shamu
59
61
  end
60
62
  end
61
63
  end
62
- end
64
+ end
@@ -23,7 +23,7 @@ module Shamu
23
23
 
24
24
  included do
25
25
  attr_dependency :security_principal, Security::Principal unless method_defined? :security_principal
26
- attr_dependency :roles_service, Security::RolesService unless method_defined? :roles_service
26
+ attr_dependency :roles_service, Security::RolesService
27
27
  end
28
28
 
29
29
  # @return [Policy] the security {Policy} for the service.
@@ -79,6 +79,20 @@ module Shamu
79
79
  security_principal.service_delegate?
80
80
  end
81
81
 
82
+
83
+ class_methods do
84
+
85
+ # Define the {Policy} class to use when enforcing policy on the service
86
+ # methods.
87
+ def policy_class( klass )
88
+ define_method :policy_class do
89
+ klass
90
+ end
91
+
92
+ private :policy_class
93
+ end
94
+ end
95
+
82
96
  end
83
97
  end
84
- end
98
+ end
@@ -3,6 +3,7 @@ module Shamu
3
3
  module Security
4
4
  require "shamu/security/error"
5
5
  require "shamu/security/principal"
6
+ require "shamu/security/delegate_principal"
6
7
  require "shamu/security/policy"
7
8
  require "shamu/security/policy_rule"
8
9
  require "shamu/security/no_policy"
@@ -40,4 +41,4 @@ module Shamu
40
41
  end
41
42
 
42
43
  end
43
- end
44
+ end
@@ -4,9 +4,20 @@ module Shamu
4
4
  # Helper methods useful for services that interact with {ActiveRecord::Base}
5
5
  # models.
6
6
  module ActiveRecord
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # Override to make sure we always catch ActiveRecord not found errors.
11
+ def with_request( * )
12
+ wrap_not_found do
13
+ super
14
+ end
15
+ end
16
+ end
7
17
 
8
18
  private
9
19
 
20
+
10
21
  # @!visibility public
11
22
  #
12
23
  # Watch for ActiveRecord::RecordNotFound errors and rethrow as a
@@ -43,7 +43,7 @@ module Shamu
43
43
  extend ActiveSupport::Concern
44
44
 
45
45
  # Known DSL methods defined by {ActiveRecordCrud}.
46
- DSL_METHODS = %i[ create update change destroy find list lookup finders ].freeze
46
+ DSL_METHODS = %i[ create update change destroy find list lookup finders crud ].freeze
47
47
 
48
48
  included do |base|
49
49
  base.include Shamu::Services::RequestSupport
@@ -120,7 +120,7 @@ module Shamu
120
120
  define_singleton_method( :model_class ) { model_class }
121
121
 
122
122
  ( Array( methods ) & DSL_METHODS ).each do |method|
123
- send method
123
+ send :"define_#{ method }"
124
124
  end
125
125
 
126
126
  define_build_entities( &block )
@@ -236,7 +236,7 @@ module Shamu
236
236
 
237
237
  with_request params, klass do |request, *args|
238
238
  record = default_scope.find( request.id )
239
- authorize! :destroy, build_entity( record )
239
+ authorize! :destroy, build_entity( record ), request
240
240
 
241
241
  instance_exec record, request, *args, &block if block_given?
242
242
  next record unless record.destroy
@@ -54,33 +54,35 @@ module Shamu
54
54
 
55
55
  # Execute block if the request is satisfied by the service successfully.
56
56
  def on_success( &block )
57
- @on_success_callbacks ||= []
58
- @on_success_callbacks << block
57
+ @on_success_blocks ||= []
58
+ @on_success_blocks << block
59
59
  end
60
60
 
61
61
  # Execute block if the request is not satisfied by the service.
62
62
  def on_fail( &block )
63
- @on_fail_callbacks ||= []
64
- @on_fail_callbacks << block
63
+ @on_fail_blocks ||= []
64
+ @on_fail_blocks << block
65
65
  end
66
66
 
67
67
  # Execute block when the service is done processing the request.
68
68
  def on_complete( &block )
69
- @on_complete_callbacks ||= []
70
- @on_complete_callbacks << block
69
+ @on_complete_blocks ||= []
70
+ @on_complete_blocks << block
71
71
  end
72
72
 
73
- # Run any {#on_success} or #{on_fail} callbacks.
73
+ # Mark the request as complete and run any {#on_success} or #{on_fail}
74
+ # callbacks.
75
+ #
74
76
  # @param [Boolean] success true if the request was completed
75
77
  # successfully.
76
- def run_callbacks( success )
78
+ def complete( success )
77
79
  if success
78
- @on_success_callbacks && @on_success_callbacks.each( &:call )
80
+ @on_success_blocks && @on_success_blocks.each( &:call )
79
81
  else
80
- @on_fail_callbacks && @on_fail_callbacks.each( &:call )
82
+ @on_fail_blocks && @on_fail_blocks.each( &:call )
81
83
  end
82
84
 
83
- @on_fail_callbacks && @on_fail_callbacks.each( &:call )
85
+ @on_complete_blocks && @on_complete_blocks.each( &:call )
84
86
  end
85
87
 
86
88
  class << self
@@ -91,7 +91,7 @@ module Shamu
91
91
  sources = yield( request )
92
92
 
93
93
  result = Result.coerce( sources, request: request )
94
- request.run_callbacks( result.valid? )
94
+ request.complete( result.valid? )
95
95
 
96
96
  result
97
97
  end
data/lib/shamu/version.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Shamu
4
4
  # The primary version number
5
- VERSION_NUMBER = "0.0.18"
5
+ VERSION_NUMBER = "0.0.19"
6
6
 
7
7
  # Version suffix such as 'beta' or 'alpha'
8
8
  VERSION_SUFFIX = ""
@@ -11,12 +11,32 @@ describe Shamu::Entities::Entity do
11
11
  end
12
12
  end
13
13
 
14
- describe "#to_attributes" do
14
+ context "with instance" do
15
15
  let( :user ) { OpenStruct.new( name: "Heisenberg", email: "blue@rock.com" ) }
16
16
  let( :instance ) { klass.new( user: user ) }
17
17
 
18
- it "does not include model attributes" do
19
- expect( instance.to_attributes ).not_to have_key :user
18
+ describe "#to_attributes" do
19
+
20
+ it "does not include model attributes" do
21
+ expect( instance.to_attributes ).not_to have_key :user
22
+ end
23
+ end
24
+
25
+ describe "#redact" do
26
+ it "clears the assigned attribute" do
27
+ redacted = instance.redact( :name )
28
+ expect( redacted.name ).to be_nil
29
+ end
30
+
31
+ it "it returns instance of the same type" do
32
+ redacted = instance.redact( :name )
33
+ expect( redacted ).to be_a klass
34
+ end
35
+
36
+ it "assigns redacted values" do
37
+ redacted = instance.redact( name: "REDACTED" )
38
+ expect( redacted.name ).to eq "REDACTED"
39
+ end
20
40
  end
21
41
  end
22
42
 
@@ -53,4 +73,4 @@ describe Shamu::Entities::Entity do
53
73
  expect( klass.null_entity.new.name ).to eq "Unknown"
54
74
  end
55
75
  end
56
- end
76
+ end
@@ -1,10 +1,17 @@
1
1
  require "spec_helper"
2
2
  require "shamu/active_record"
3
3
 
4
+ module ActiveRecordPolicySpec
5
+ class Policy < Shamu::Security::ActiveRecordPolicy
6
+ def permissions
7
+ end
8
+ end
9
+ end
10
+
4
11
  describe Shamu::Security::ActiveRecordPolicy do
5
12
  use_active_record
6
13
 
7
- let( :policy ) { Shamu::Security::ActiveRecordPolicy.new }
14
+ let( :policy ) { ActiveRecordPolicySpec::Policy.new }
8
15
 
9
16
  describe "#refine_relation" do
10
17
  before( :each ) do
@@ -35,4 +42,4 @@ describe Shamu::Security::ActiveRecordPolicy do
35
42
  end.to change { policy.send( :refinements ).length }
36
43
  end
37
44
  end
38
- end
45
+ end
@@ -7,11 +7,11 @@ describe Shamu::Security::Policy do
7
7
  let( :klass ) do
8
8
  Class.new( Shamu::Security::Policy ) do
9
9
  role :super_user, inherits: :admin
10
- role :admin, inherits: :user
11
- role :user
10
+ role :admin, inherits: :authenticated
11
+ role :authenticated
12
12
 
13
13
  public :in_role?, :add_rule, :rules, :permit, :deny, :when_elevated,
14
- :alias_action, :expand_aliases
14
+ :alias_action, :expand_aliases, :resource
15
15
  end
16
16
  end
17
17
 
@@ -40,16 +40,16 @@ describe Shamu::Security::Policy do
40
40
  end
41
41
 
42
42
  describe "#in_role?" do
43
- it "is true for :user when principal is signed in" do
43
+ it "is true for :authenticated when principal is signed in" do
44
44
  allow( policy.principal ).to receive( :user_id ).and_return 1
45
45
 
46
- expect( policy.in_role?( :user ) ).to be_truthy
46
+ expect( policy.in_role?( :authenticated ) ).to be_truthy
47
47
  end
48
48
 
49
- it "is false for :user when principal is anonymous" do
49
+ it "is false for :authenticated when principal is anonymous" do
50
50
  allow( policy.principal ).to receive( :user_id ).and_return nil
51
51
 
52
- expect( policy.in_role?( :user ) ).to be_falsy
52
+ expect( policy.in_role?( :authenticated ) ).to be_falsy
53
53
  end
54
54
 
55
55
  it "is true for an explicitly assigned role" do
@@ -110,7 +110,7 @@ describe Shamu::Security::Policy do
110
110
  it "adds a :maybe rule when defined within #when_elevated" do
111
111
  allow( policy ).to receive( :permissions ) do
112
112
  policy.when_elevated do
113
- policy.permit :do, :stuff
113
+ policy.permit :do, :stuff, nil
114
114
  end
115
115
  end
116
116
 
@@ -118,6 +118,28 @@ describe Shamu::Security::Policy do
118
118
  end
119
119
  end
120
120
 
121
+ describe "#resource" do
122
+ it "fails if no resource block and resource omitted to permit" do
123
+ expect do
124
+ policy.permit :read
125
+ end.to raise_exception( /resource/ )
126
+ end
127
+
128
+ it "uses the dsl resource when omitted" do
129
+ expect( policy ).to receive( :add_rule ).with( [ :read ], Object, :yes )
130
+ policy.resource Object do
131
+ policy.permit :read
132
+ end
133
+ end
134
+
135
+ it "uses an explicit resource when provided" do
136
+ expect( policy ).to receive( :add_rule ).with( [ :read ], Symbol, :yes )
137
+ policy.resource Object do
138
+ policy.permit :read, Symbol
139
+ end
140
+ end
141
+ end
142
+
121
143
  describe "#add_rule" do
122
144
  before( :each ) do
123
145
  allow( policy ).to receive( :permissions )
@@ -155,4 +177,4 @@ describe Shamu::Security::Policy do
155
177
  expect( policy.expand_aliases( [ :read ] ) ).to include :index
156
178
  end
157
179
  end
158
- end
180
+ end
@@ -74,7 +74,7 @@ describe Shamu::Services::ActiveRecordCrud do
74
74
 
75
75
  Shamu::Services::ActiveRecordCrud::DSL_METHODS.each do |method|
76
76
  it "defines standard DSL method `#{ method }` when passed via `:methods`" do
77
- expect( klass ).to receive( method )
77
+ expect( klass ).to receive( :"define_#{ method }" )
78
78
  klass.resource( ActiveRecordCrudSpec::FavoriteEntity, ActiveRecordSpec::Favorite, methods: method )
79
79
  end
80
80
  end
@@ -434,7 +434,8 @@ describe Shamu::Services::ActiveRecordCrud do
434
434
  it "calls #authorize!" do
435
435
  expect( service ).to receive( :authorize! ).with(
436
436
  :destroy,
437
- kind_of( ActiveRecordCrudSpec::FavoriteEntity )
437
+ kind_of( ActiveRecordCrudSpec::FavoriteEntity ),
438
+ kind_of( ActiveRecordCrudSpec::Request::Destroy )
438
439
  )
439
440
 
440
441
  service.destroy entity.id
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shamu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.18
4
+ version: 0.0.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Alexander
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-08 00:00:00.000000000 Z
11
+ date: 2017-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel