sequel-privacy 0.5.1 → 0.5.2

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
  SHA256:
3
- metadata.gz: af7fc02c1fb57d47ba358babdebb3289c2d8eb6ec43d2fce25a2c3477a3f2ccc
4
- data.tar.gz: a18b09c73b5b03540d2a74f985d32c99b6b2a090a714346f2c87ef5a2997252c
3
+ metadata.gz: 2e415c08d25c658e76c101a7c7ccbdbb791ef1f00deb6a81481ada2044a2b779
4
+ data.tar.gz: 819dfce705006ad300e092650cd2014944989c6bec4baefb6bdd5a0fe1bd6bc1
5
5
  SHA512:
6
- metadata.gz: 9f46a3f0f253061be75dcd2fb80cf797c1be229d5fd83b1c9f1c86860621dae4536ea60b06df4e38d2d6b1e66f0e90533f0d3a75dddc48be388898ac1d8517d4
7
- data.tar.gz: f4aee413ab143a08366b223e2c34d62165c69f1e63871eee400d6f82af3c096266e0646f3b4d3d4649a761c913e2c07cef8f852586f0c535d0af3daf1d02f9a6
6
+ metadata.gz: b875efc8f711c1403eb90e8ebc6ca5071b325c3e6e7856cc7c43f4dc43f5edb4add75a6081db527d30a184c856719a1461da50a2e6a754567a8a5886567daea8
7
+ data.tar.gz: b1c2f4a0525b3d4232f4f369d483affbf3bcc84235602d116f2d8a40d80c686e5bd8a677e1367bbc53012b8341a63e787b7d502377b2322f63afdbf5f10518ec
data/README.md CHANGED
@@ -221,56 +221,34 @@ system, so these VCs give you an escape hatch for things like scripts while also
221
221
  You could also create lint rules that prevent the casual creation of these viewer contexts.
222
222
 
223
223
  ```ruby
224
+ # You can use an omniscient viewer context to load the user from a session
225
+ # or however you store them. Discard this viewer context when you're done with it.
226
+ def current_user
227
+ return @current_user if @current_user
228
+ login_vc = Sequel::Privacy::ViewerContext.omniscient(:login)
229
+ user = User.for_vc(login_vc)[session_user_id]
230
+ return nil unless user
231
+
232
+ # Attach an ActorVC to the loaded user so that future calls to its fields and
233
+ # associations respect privacy .
234
+ @current_user ||= user.for_vc(Sequel::Privacy::ViewerContext.for_actor(user))
235
+ end
236
+
237
+ def current_vc
238
+ current_user&.vc || Sequel::Privacy::ViewerContext.anonymous()
239
+ end
240
+
224
241
  # Standard viewer (most common)
225
- current_vc = Sequel::Privacy::ViewerContext.for_actor(current_user)
226
242
  users_groups = Group.for_vc(current_vc).where(creator: current_user).all
227
243
 
228
- # API-specific (can be distinguished in policies)
229
- vc = Sequel::Privacy::ViewerContext.for_api_actor(current_user)
230
-
231
244
  # Anonymous viewer (logged-out users)
232
245
  logged_out_vc = Sequel::Privacy::ViewerContext.anonymous
233
246
  posts = Post.for_vc(logged_out_vc).where(published: true).all
234
247
 
235
- # Omniscient VCs can read any object in the system, but are incapable of writes.
236
- # Dispose of these ViewerContexts quickly.
237
- current_user = Sequel::Privacy::ViewerContext.omniscient(:login).then {|vc| User.for_vc(vc)[authenticated_user_id] }
238
- current_vc = Sequel::Privacy::ViewerContext.for_actor(current_user)
239
-
240
248
  # All-powerful ViewerContexts dangerously bypass all read and write checks.
241
249
  admin_vc = Sequel::Privacy::ViewerContext.all_powerful(:admin_migration)
242
250
  ```
243
251
 
244
- ### A Note Login & Authenticated Users
245
-
246
- If your User or equivalent model is privacy-aware *and* is protected by
247
- policies that would complicating fetching (or login), then you will have
248
- trouble creating a `current_user` for an `ActorVC`.
249
-
250
- In both cases you can use an `OmniscientVC` to make your initial User query.
251
-
252
- ```ruby
253
- before do
254
- if session[:user_id]
255
- current_user = Sequel::Privacy::ViewerContext.omniscient(:session).then {|vc| User.for_vc(vc)[session[:user_id]] }
256
- current_vc = Sequel::Privacy::ViewerContext.for_actor(current_user)
257
- else
258
- current_user = nil
259
- current_vc = Sequel::Privacy::ViewerContext.anonymous
260
- end
261
- end
262
-
263
- post '/auth/password' do
264
- user = Sequel::Privacy::ViewerContext.omniscient(:login).then {|vc| User.for_vc(vc).first(email: params[:email]) }
265
-
266
- pass unless user
267
- pass unless user.password == params[:password]
268
-
269
- session[:user_id] = user.id
270
- redirect '/'
271
- end
272
- ```
273
-
274
252
  ## Mutation Enforcement
275
253
 
276
254
  When a viewer context is attached, mutations are automatically checked:
@@ -3,10 +3,9 @@
3
3
 
4
4
  module Sequel
5
5
  module Privacy
6
- # Built-in policies that ship with the gem.
7
- # Applications should define their own policies using PolicyDSL.
8
6
  module BuiltInPolicies
9
- # Always deny access. Should be the last policy in every chain (fail-secure).
7
+ # Always deny access. You should specify this policy at the end of every chain, but the framework
8
+ # currently appends it. This could change; I'm not sure about this yet.
10
9
  AlwaysDeny = Policy.create(
11
10
  :AlwaysDeny,
12
11
  -> { :deny },
@@ -14,7 +13,6 @@ module Sequel
14
13
  cacheable: true
15
14
  )
16
15
 
17
- # Always allow access. Use sparingly.
18
16
  AlwaysAllow = Policy.create(
19
17
  :AlwaysAllow,
20
18
  -> { :allow },
@@ -8,7 +8,6 @@ module Sequel
8
8
  class << self
9
9
  extend T::Sig
10
10
 
11
- # Returns the in-memory cache Hash for policy results.
12
11
  sig { returns(T::Hash[Integer, Symbol]) }
13
12
  def cache
14
13
  @cache ||= T.let({}, T.nilable(T::Hash[Integer, Symbol]))
@@ -3,8 +3,6 @@
3
3
 
4
4
  module Sequel
5
5
  module Privacy
6
- # The Enforcer evaluates policy chains to determine if an action is allowed.
7
- # It handles caching, single-match optimization, and policy combinators.
8
6
  module Enforcer
9
7
  extend T::Sig
10
8
 
@@ -138,9 +136,7 @@ module Sequel
138
136
  ).returns(Symbol)
139
137
  end
140
138
  def self.evaluate_child_policies(child_policies, subject, actor, viewer_context, direct_object)
141
- unless child_policies.all? { |c| c.is_a?(Proc) }
142
- Kernel.raise "Policy combinator contains non-policy members"
143
- end
139
+ Kernel.raise 'Policy combinator contains non-policy members' unless child_policies.all? { |c| c.is_a?(Proc) }
144
140
 
145
141
  results = child_policies.map do |child_policy|
146
142
  policy_result(child_policy, subject, actor, viewer_context, direct_object)
@@ -190,14 +186,10 @@ module Sequel
190
186
  result ||= :pass
191
187
 
192
188
  # Handle combinator results
193
- if result.is_a?(Array)
194
- result = evaluate_child_policies(result, subject, actor, viewer_context, direct_object)
195
- end
189
+ result = evaluate_child_policies(result, subject, actor, viewer_context, direct_object) if result.is_a?(Array)
196
190
 
197
191
  # Cache result
198
- if policy.cacheable? && !from_cache
199
- Sequel::Privacy.cache[cache_key] = result
200
- end
192
+ Sequel::Privacy.cache[cache_key] = result if policy.cacheable? && !from_cache
201
193
 
202
194
  # Log result
203
195
  log_result(policy, result, actor, subject, from_cache, skipped_from_single_match)
@@ -227,9 +219,7 @@ module Sequel
227
219
  # Anonymous viewers (no actor) auto-deny unless the policy opts in
228
220
  # with allow_anonymous: true (for state-gate policies that examine
229
221
  # only the subject).
230
- if !actor && policy.arity >= 1 && !policy.allow_anonymous?
231
- return :deny
232
- end
222
+ return :deny if !actor && policy.arity >= 1 && !policy.allow_anonymous?
233
223
 
234
224
  case policy.arity
235
225
  when 0
@@ -259,14 +249,14 @@ module Sequel
259
249
  actor_id = actor ? actor.id : 'anonymous'
260
250
  logger.debug do
261
251
  msg = "#{result.to_s.upcase}: #{policy.policy_name || 'anonymous'} for actor[#{actor_id}] on #{subject.class}[#{subject_id(subject)}]"
262
- msg += " (cached)" if from_cache
263
- msg += " (skipped: single_match)" if skipped
252
+ msg += ' (cached)' if from_cache
253
+ msg += ' (skipped: single_match)' if skipped
264
254
  msg
265
255
  end
266
256
 
267
- if policy.comment && %i[deny allow].include?(result)
268
- logger.debug { " ⮑ #{policy.comment}" }
269
- end
257
+ return unless policy.comment && %i[deny allow].include?(result)
258
+
259
+ logger.debug { " ⮑ #{policy.comment}" }
270
260
  end
271
261
 
272
262
  sig { params(subject: TPolicySubject).returns(T.untyped) }
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Sequel
5
5
  module Privacy
6
- VERSION = '0.5.1'
6
+ VERSION = '0.5.2'
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel-privacy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Austin Bales