verifica 1.0.0 → 1.0.1

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: 8df98ff228b89e7701f41df3d5617d93183e83c32cdae5c879e347c615f367d7
4
- data.tar.gz: 956259725d32e5a6208a03f2a07d682d6facbb1dcaae107687ce59cd1cd8363c
3
+ metadata.gz: ccea0b2769fa053006c2ec3d0b4cd0d3cc65a3a4b4375ce390dabe3a093f490d
4
+ data.tar.gz: 0bb0dd63b30f08a1df1af771671312182cde2c159613094f9fd0c664201b6e24
5
5
  SHA512:
6
- metadata.gz: d3329c9e126220d43a8e9b4a7923541b30139e74f565387bed6012acea9760239d1b5850d19ee2963e63252ddde3008e39486ac66438e6e9215afe18de0d4256
7
- data.tar.gz: 1dab1e7bab877e5f63298240db13270c6f46c238643f508037e81f524a99a54f3613e3515690e54886ac032b371d394622e51d5d33d92528ddab8560d7babb2e
6
+ metadata.gz: 5c3bfab0ce9ccd36cd91baadcc4454e462c9a5d098c6295d3073040d3291030e516d7ae90d200b55b84d4cafd51cd0118c51a1e5a5e0ee075d54e18528033d01
7
+ data.tar.gz: 6f6011eaec5b7f8a1d810233350a8a68cb7fdf7cf86d8bdc069d51478826ce847461c9d53e5c5f7e83922ac956cb3c1546fd957cb973c5dfee1a71cbc7258726
data/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.0.1] - 2023-04-15
11
+
12
+ ### Added
13
+
14
+ - `Sid#country_sid` and `Sid#group_sid` helper methods
15
+
10
16
  ## [1.0.0] - 2023-01-19
11
17
 
12
18
  Initial public release
data/README.md CHANGED
@@ -1,4 +1,6 @@
1
+ [![Gem Version](https://badge.fury.io/rb/verifica.svg)](https://badge.fury.io/rb/verifica)
1
2
  [![CI](https://github.com/maximgurin/verifica/actions/workflows/ci.yml/badge.svg)](https://github.com/maximgurin/verifica/actions/workflows/ci.yml)
3
+ [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/maximgurin/verifica)
2
4
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/457e56b0bb514539844a94d85abe99f9)](https://www.codacy.com/gh/maximgurin/verifica/dashboard?utm_source=github.com&utm_medium=referral&utm_content=maximgurin/verifica&utm_campaign=Badge_Grade)
3
5
  [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/457e56b0bb514539844a94d85abe99f9)](https://www.codacy.com/gh/maximgurin/verifica/dashboard?utm_source=github.com&utm_medium=referral&utm_content=maximgurin/verifica&utm_campaign=Badge_Coverage)
4
6
  ![GitHub](https://img.shields.io/github/license/maximgurin/verifica)
@@ -22,7 +24,7 @@ But anyway, trust nothing. DYOR.*
22
24
 
23
25
  ## Why Verifica? Isn't Pundit or CanCanCan enough?
24
26
 
25
- Let's say you working on a video platform application:
27
+ Let's say you are working on a video platform application:
26
28
 
27
29
  - You have 10M videos in the database
28
30
  - 7 types of user roles
@@ -70,7 +72,7 @@ authorizer = Verifica.authorizer do |config|
70
72
  end
71
73
 
72
74
  public_video = Video.new(id: 1, author_id: 1000, public: true)
73
- private_video = Video.new(id: 2, author_id: 1000, public: true)
75
+ private_video = Video.new(id: 2, author_id: 1000, public: false)
74
76
 
75
77
  superuser = User.new(id: 777, role: "root")
76
78
  video_author = User.new(id: 1000, role: "user")
@@ -184,9 +186,9 @@ end
184
186
 
185
187
  video_acl.to_a
186
188
  # =>
187
- # [#<Verifica::Ace:0x00007fab1955dd60 @action=:view, @allow=true, @sid="authenticated">,
188
- # #<Verifica::Ace:0x00007fab1955dd10 @action=:comment, @allow=true, @sid="authenticated">,
189
- # #<Verifica::Ace:0x00007fab1955dc48 @action=:view, @allow=false, @sid="country:US">]
189
+ # [#<Verifica::Ace:0x00007fab1955dd60 @action=:read, @allow=true, @sid="authenticated">,
190
+ # #<Verifica::Ace:0x00007fab1955dd10 @action=:comment, @allow=true, @sid="authenticated">,
191
+ # #<Verifica::Ace:0x00007fab1955dc48 @action=:read, @allow=false, @sid="country:US">]
190
192
  ```
191
193
 
192
194
  ### AclProvider
@@ -210,8 +212,8 @@ end
210
212
  ### Authorizer
211
213
 
212
214
  And finally, Authorizer, the heart of Verifica. It couples all concepts above into an isolated container with no global state.
213
- Each Authorizer has a list of resource types registered with their companion AclProviders.
214
- And most importantly, Authorizer has several methods to check the Subject's rights to perform a specific action on a given resource.
215
+ Each Authorizer has a list of resource types registered with their companion AclProviders and
216
+ several methods to check the Subject's rights to perform a specific action on a given resource.
215
217
 
216
218
  Check the [Basic example](#basic-example) above to see how it all plays together.
217
219
 
@@ -318,11 +320,11 @@ class User < ApplicationRecord
318
320
  when "moderator"
319
321
  [user_sid(id), role_sid("moderator")]
320
322
  when "user"
321
- sids = [authenticated_sid, user_sid(id), "country:#{country}"]
323
+ sids = [authenticated_sid, user_sid(id), country_sid(country)]
322
324
  organization_id.try { |org_id| sids.push(organization_sid(org_id)) }
323
325
  sids
324
326
  when "organization_admin"
325
- sids = [authenticated_sid, user_sid(id), "country:#{country}"]
327
+ sids = [authenticated_sid, user_sid(id), country_sid(country)]
326
328
  sids.push(organization_sid(organization_id))
327
329
  sids.push(role_sid("organization_admin:#{organization_id}"))
328
330
  else
@@ -354,7 +356,7 @@ end
354
356
  ```
355
357
 
356
358
  ```ruby
357
- # app/models/user.rb
359
+ # app/models/video.rb
358
360
 
359
361
  class Video < ApplicationRecord
360
362
  attr_accessor :allowed_actions
@@ -383,7 +385,11 @@ end
383
385
 
384
386
  class VideosController
385
387
  def index
386
- @videos = Video.available_for(current_user).order(:name).limit(50)
388
+ @videos = Video
389
+ .includes(:distribution_setting, author: [:organization])
390
+ .available_for(current_user)
391
+ .order(:name)
392
+ .limit(50)
387
393
  end
388
394
 
389
395
  def show
@@ -415,8 +421,6 @@ run a background job to find all affected videos and update `read_allow_sids`, `
415
421
  Same applies to Distribution Settings and other dependencies.
416
422
  - **Rules change handling.** If implementation of `VideoAclProvider` changed you need to run a background job
417
423
  to update `read_allow_sids`, `read_deny_sids` columns for all videos.
418
- - **Cache, N+1 problem.** `VideoAclProvider` retrieves a chain of records associated with each video which leads to
419
- N+1 problem in a naive implementation.
420
424
 
421
425
  See also:
422
426
 
data/lib/verifica/acl.rb CHANGED
@@ -54,7 +54,6 @@ module Verifica
54
54
 
55
55
  allow_deny[:allowed_sids].freeze
56
56
  allow_deny[:denied_sids].freeze
57
- allow_deny.freeze
58
57
  end
59
58
 
60
59
  @allowed_actions.freeze
@@ -154,7 +153,7 @@ module Verifica
154
153
 
155
154
  # @example
156
155
  # acl = Verifica::Acl.build { |acl| acl.allow "root", [:read, :write] }
157
- # acl.to_a.map(:to_h)
156
+ # acl.to_a.map(&:to_h)
158
157
  # # => [{:sid=>"root", :action=>:read, :allow=>true}, {:sid=>"root", :action=>:write, :allow=>true}]
159
158
  #
160
159
  # @return [Array<Ace>] a new array representing +self+
@@ -141,6 +141,7 @@ module Verifica
141
141
  def resource_acl(resource, **context)
142
142
  config = config_by_resource(resource)
143
143
  acl = config.acl_provider.call(resource, **context)
144
+ # trade-off flexibility to increase robustness here by requiring a specific type
144
145
  unless acl.is_a?(Verifica::Acl)
145
146
  type = resource.resource_type
146
147
  raise Error, "'#{type}' resource acl_provider should respond to #call with Acl object but got '#{acl.class}'"
data/lib/verifica/sid.rb CHANGED
@@ -211,5 +211,66 @@ module Verifica
211
211
 
212
212
  "org:#{organization_id}".freeze
213
213
  end
214
+
215
+ # Security Identifier of the subject who is a member of the group with given +group_id+
216
+ #
217
+ # @note (see #user_sid)
218
+ #
219
+ # @example
220
+ # class PostAclProvider
221
+ # include Verifica::Sid
222
+ #
223
+ # def call(post, **)
224
+ # Verifica::Acl.build do |acl|
225
+ # post.editor_groups.each do |group_id|
226
+ # acl.allow group_sid(group_id), [:edit, :delete]
227
+ # end
228
+ #
229
+ # # ...
230
+ # end
231
+ # end
232
+ # end
233
+ #
234
+ # @return [String]
235
+ #
236
+ # @api public
237
+ def group_sid(group_id)
238
+ if group_id.nil?
239
+ raise ArgumentError, "Nil 'group_id' is unsafe. Use empty string if you absolutely need this behavior"
240
+ end
241
+
242
+ "group:#{group_id}".freeze
243
+ end
244
+
245
+ # Security Identifier of the subject whose country is the country with given +country_id+
246
+ #
247
+ # @note (see #user_sid)
248
+ #
249
+ # @example
250
+ # class PostAclProvider
251
+ # include Verifica::Sid
252
+ #
253
+ # def call(post, **)
254
+ # Verifica::Acl.build do |acl|
255
+ # acl.allow authenticated_sid, [:read, :comment]
256
+ # post.banned_countries.each do |country_id|
257
+ # acl.deny country_sid(country_id), [:read, :comment]
258
+ # end
259
+ #
260
+ # # ...
261
+ # end
262
+ # end
263
+ # end
264
+ #
265
+ # @return [String]
266
+ #
267
+ # @api public
268
+ def country_sid(country_id)
269
+ if country_id.nil?
270
+ raise ArgumentError, "Nil 'country_id' is unsafe. Use empty string if you absolutely need this behavior"
271
+ end
272
+
273
+ "country:#{country_id}".freeze
274
+ end
214
275
  end
215
276
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Verifica
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.1"
5
5
  end
data/lib/verifica.rb CHANGED
@@ -54,7 +54,7 @@ require_relative "verifica/version"
54
54
  # end
55
55
  #
56
56
  # public_video = Video.new(id: 1, author_id: 1000, public: true)
57
- # private_video = Video.new(id: 2, author_id: 1000, public: true)
57
+ # private_video = Video.new(id: 2, author_id: 1000, public: false)
58
58
  #
59
59
  # superuser = User.new(id: 777, role: "root")
60
60
  # video_author = User.new(id: 1000, role: "user")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: verifica
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxim Gurin
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-19 00:00:00.000000000 Z
11
+ date: 2023-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -105,7 +105,7 @@ metadata:
105
105
  source_code_uri: https://github.com/maximgurin/verifica
106
106
  bug_tracker_uri: https://github.com/maximgurin/verifica/issues
107
107
  rubygems_mfa_required: 'true'
108
- post_install_message:
108
+ post_install_message:
109
109
  rdoc_options: []
110
110
  require_paths:
111
111
  - lib
@@ -120,8 +120,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
120
  - !ruby/object:Gem::Version
121
121
  version: '0'
122
122
  requirements: []
123
- rubygems_version: 3.4.3
124
- signing_key:
123
+ rubygems_version: 3.4.12
124
+ signing_key:
125
125
  specification_version: 4
126
126
  summary: The most scalable authorization solution for Ruby
127
127
  test_files: []