verifica 1.0.1 → 1.0.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: ccea0b2769fa053006c2ec3d0b4cd0d3cc65a3a4b4375ce390dabe3a093f490d
4
- data.tar.gz: 0bb0dd63b30f08a1df1af771671312182cde2c159613094f9fd0c664201b6e24
3
+ metadata.gz: f38a13446f6e2b3eb7e118a1d97d5369aca85116a3bde44d7dd1f07bfe013f7a
4
+ data.tar.gz: ae82a49612de5e13ed32992a13c34a3af57e50b18772c0651194459b5a187010
5
5
  SHA512:
6
- metadata.gz: 5c3bfab0ce9ccd36cd91baadcc4454e462c9a5d098c6295d3073040d3291030e516d7ae90d200b55b84d4cafd51cd0118c51a1e5a5e0ee075d54e18528033d01
7
- data.tar.gz: 6f6011eaec5b7f8a1d810233350a8a68cb7fdf7cf86d8bdc069d51478826ce847461c9d53e5c5f7e83922ac956cb3c1546fd957cb973c5dfee1a71cbc7258726
6
+ metadata.gz: fcaee3d318dbada1fe7f4c135bbd38219b8031126e1e0d29b5c125a15f1867e8305fb4ec5ca91a2b4eddc4040ad49d24af9e79fb6a36ddab115f2d50db70d5fe
7
+ data.tar.gz: f0a4392ff578a8444b5f922ae7f3844bd14126ceb5570bf2e9ded02463a3ff63ec69d68b248e5b4241dfa10db4ab537b6fd35b2073e1d4d070f76d74f2c6e896
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.2] - 2023-10-25
11
+
12
+ ### Changed
13
+
14
+ - Make `Authorizer#authorization_result` public for even more usage flexibility
15
+
10
16
  ## [1.0.1] - 2023-04-15
11
17
 
12
18
  ### Added
data/README.md CHANGED
@@ -20,7 +20,7 @@ for any user and resource, regardless of how complex the authorization rules are
20
20
 
21
21
  *Note: Verifica is a new open-source gem, so you may wonder if it's reliable. Internally,
22
22
  this solution has been battle-tested in several B2B products, including one with over 15M database records.
23
- But anyway, trust nothing. DYOR.*
23
+ But DYOR anyway.*
24
24
 
25
25
  ## Why Verifica? Isn't Pundit or CanCanCan enough?
26
26
 
@@ -38,7 +38,7 @@ In the [Real-world example with Rails](#real-world-example-with-rails) you can s
38
38
  ## Basic example
39
39
 
40
40
  ```ruby
41
- require 'verifica'
41
+ require "verifica"
42
42
 
43
43
  User = Struct.new(:id, :role, keyword_init: true) do
44
44
  # Verifica expects each security subject to respond to #subject_id, #subject_type, and #subject_sids
@@ -78,20 +78,26 @@ superuser = User.new(id: 777, role: "root")
78
78
  video_author = User.new(id: 1000, role: "user")
79
79
  other_user = User.new(id: 2000, role: "user")
80
80
 
81
- authorizer.authorized?(superuser, private_video, :delete)
82
- # true
83
-
84
- authorizer.authorized?(video_author, private_video, :delete)
85
- # true
86
-
87
- authorizer.authorized?(other_user, private_video, :read)
88
- # false
81
+ authorizer.authorized?(superuser, private_video, :delete) # => true
82
+ authorizer.authorized?(video_author, private_video, :delete) # => true
83
+ authorizer.authorized?(other_user, private_video, :read) # => false
84
+ authorizer.authorized?(other_user, public_video, :comment) # => true
89
85
 
90
- authorizer.authorized?(other_user, public_video, :comment)
91
- # true
86
+ begin
87
+ # raises Verifica::AuthorizationError: Authorization FAILURE. Subject 'user' id='2000'. Resource 'video' id='1'. Action 'write'
88
+ authorizer.authorize(other_user, public_video, :write)
89
+ rescue Verifica::AuthorizationError => e
90
+ e.explain # => Long-form explanation of why action is not authorized, your debugging friend
91
+ end
92
92
 
93
- authorizer.authorize(other_user, public_video, :write)
94
- # raises Verifica::AuthorizationError: Authorization FAILURE. Subject 'user' id='2000'. Resource 'video' id='1'. Action 'write'
93
+ # #authorization_result returns a special object with a bunch of useful info
94
+ auth_result = authorizer.authorization_result(superuser, private_video, :delete)
95
+ auth_result.success? # => true
96
+ auth_result.subject_id # => 777
97
+ auth_result.resource_type # => :video
98
+ auth_result.action # => :delete
99
+ auth_result.allowed_actions # => [:read, :write, :delete, :comment]
100
+ auth_result.explain # => Long-form explanation of why action is authorized
95
101
  ```
96
102
 
97
103
  ## Installation
@@ -122,11 +128,11 @@ class User
122
128
  def subject_id
123
129
  123
124
130
  end
125
-
131
+
126
132
  def subject_type
127
133
  :user
128
134
  end
129
-
135
+
130
136
  def subject_sids
131
137
  ["root"] # see Security Identifier section below to understand what is this for
132
138
  end
@@ -145,7 +151,7 @@ class Post
145
151
  def resource_id
146
152
  1
147
153
  end
148
-
154
+
149
155
  def resource_type
150
156
  :post
151
157
  end
@@ -282,7 +288,7 @@ class VideoAclProvider
282
288
  author_org = video.author.organization
283
289
  allowed_countries = author_org&.allow_countries || ds.allow_countries
284
290
  denied_countries = author_org&.deny_countries || ds.deny_countries
285
-
291
+
286
292
  # ...and 30 more lines to handle all our requirements
287
293
  end
288
294
  end
@@ -365,7 +371,7 @@ class Video < ApplicationRecord
365
371
  before_save :update_read_acl
366
372
 
367
373
  def resource_type = :video
368
-
374
+
369
375
  def update_read_acl
370
376
  acl = AUTHORIZER.resource_acl(self)
371
377
  self.read_allow_sids = acl.allowed_sids(:read)
@@ -391,10 +397,10 @@ class VideosController
391
397
  .order(:name)
392
398
  .limit(50)
393
399
  end
394
-
400
+
395
401
  def show
396
402
  @video = Video.find(params[:id])
397
-
403
+
398
404
  # upon successful authorization helper object is returned with a bunch of useful info
399
405
  auth_result = AUTHORIZER.authorize(current_user, @video, :read)
400
406
 
@@ -73,6 +73,11 @@ module Verifica
73
73
 
74
74
  # The same as {#authorize} but returns true/false instead of rising an exception
75
75
  #
76
+ # @param subject (see #authorize)
77
+ # @param resource (see #authorize)
78
+ # @param action (see #authorize)
79
+ # @param context (see #authorize)
80
+ #
76
81
  # @return [Boolean] true if +action+ on +resource+ is authorized for +subject+
77
82
  # @raise [Error] if +resource.resource_type+ isn't registered in +self+
78
83
  #
@@ -81,9 +86,31 @@ module Verifica
81
86
  authorization_result(subject, resource, action, **context).success?
82
87
  end
83
88
 
84
- # @param subject [Object] subject of the authorization (e.g. current user, external service)
85
- # @param resource [Object] resource to get allowed actions for, should respond to +#resource_type+
86
- # @param **context (see #authorize)
89
+ # The same as {#authorize} but returns a special result object instead of rising an exception
90
+ #
91
+ # @param subject (see #authorize)
92
+ # @param resource (see #authorize)
93
+ # @param action (see #authorize)
94
+ # @param context (see #authorize)
95
+ #
96
+ # @return [AuthorizationResult] authorization result with all details
97
+ # @raise [Error] if +resource.resource_type+ isn't registered in +self+
98
+ #
99
+ # @api public
100
+ def authorization_result(subject, resource, action, **context)
101
+ action = action.to_sym
102
+ possible_actions = config_by_resource(resource).possible_actions
103
+ unless possible_actions.include?(action)
104
+ raise Error, "'#{action}' action is not registered as possible for '#{resource.resource_type}' resource"
105
+ end
106
+
107
+ acl = resource_acl(resource, **context)
108
+ AuthorizationResult.new(subject, resource, action, acl, **context)
109
+ end
110
+
111
+ # @param subject (see #authorize)
112
+ # @param resource (see #authorize)
113
+ # @param context (see #authorize)
87
114
  #
88
115
  # @return [Array<Symbol>] array of actions allowed for +subject+ or empty array if none
89
116
  # @raise [Error] if +resource.resource_type+ isn't registered in +self+
@@ -128,7 +155,7 @@ module Verifica
128
155
  @resources.key?(resource_type.to_sym)
129
156
  end
130
157
 
131
- # @param resource [Object] resource to get ACL for, should respond to +#resource_type+
158
+ # @param resource (see #authorize)
132
159
  # @param context [Hash] arbitrary keyword arguments to forward to +acl_provider.call+
133
160
  #
134
161
  # @return [Acl] Access Control List for +resource+
@@ -173,16 +200,5 @@ module Verifica
173
200
 
174
201
  resource_config(type)
175
202
  end
176
-
177
- private def authorization_result(subject, resource, action, **context)
178
- action = action.to_sym
179
- possible_actions = config_by_resource(resource).possible_actions
180
- unless possible_actions.include?(action)
181
- raise Error, "'#{action}' action is not registered as possible for '#{resource.resource_type}' resource"
182
- end
183
-
184
- acl = resource_acl(resource, **context)
185
- AuthorizationResult.new(subject, resource, action, acl, **context)
186
- end
187
203
  end
188
204
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Verifica
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.2"
5
5
  end
data/lib/verifica.rb CHANGED
@@ -20,7 +20,7 @@ require_relative "verifica/version"
20
20
  # (who can do what for any given resource) and execution (can +current_user+ delete this post?).
21
21
  #
22
22
  # @example
23
- # require 'verifica'
23
+ # require "verifica"
24
24
  #
25
25
  # User = Struct.new(:id, :role, keyword_init: true) do
26
26
  # # Verifica expects each security subject to respond to #subject_id, #subject_type, and #subject_sids
@@ -60,20 +60,26 @@ require_relative "verifica/version"
60
60
  # video_author = User.new(id: 1000, role: "user")
61
61
  # other_user = User.new(id: 2000, role: "user")
62
62
  #
63
- # authorizer.authorized?(superuser, private_video, :delete)
64
- # # true
63
+ # authorizer.authorized?(superuser, private_video, :delete) # => true
64
+ # authorizer.authorized?(video_author, private_video, :delete) # => true
65
+ # authorizer.authorized?(other_user, private_video, :read) # => false
66
+ # authorizer.authorized?(other_user, public_video, :comment) # => true
65
67
  #
66
- # authorizer.authorized?(video_author, private_video, :delete)
67
- # # true
68
- #
69
- # authorizer.authorized?(other_user, private_video, :read)
70
- # # false
71
- #
72
- # authorizer.authorized?(other_user, public_video, :comment)
73
- # # true
68
+ # begin
69
+ # # raises Verifica::AuthorizationError: Authorization FAILURE. Subject 'user' id='2000'. Resource 'video' id='1'. Action 'write'
70
+ # authorizer.authorize(other_user, public_video, :write)
71
+ # rescue Verifica::AuthorizationError => e
72
+ # e.explain # => Long-form explanation of why action is not authorized, your debugging friend
73
+ # end
74
74
  #
75
- # authorizer.authorize(other_user, public_video, :write)
76
- # # raises Verifica::AuthorizationError: Authorization FAILURE. Subject 'user' id='2000'. Resource 'video' id='1'. Action 'write'
75
+ # # #authorization_result returns a special object with a bunch of useful info
76
+ # auth_result = authorizer.authorization_result(superuser, private_video, :delete)
77
+ # auth_result.success? # => true
78
+ # auth_result.subject_id # => 777
79
+ # auth_result.resource_type # => :video
80
+ # auth_result.action # => :delete
81
+ # auth_result.allowed_actions # => [:read, :write, :delete, :comment]
82
+ # auth_result.explain # => Long-form explanation of why action is authorized
77
83
  #
78
84
  # @api public
79
85
  module Verifica
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.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxim Gurin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-15 00:00:00.000000000 Z
11
+ date: 2023-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -120,7 +120,7 @@ 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.12
123
+ rubygems_version: 3.4.19
124
124
  signing_key:
125
125
  specification_version: 4
126
126
  summary: The most scalable authorization solution for Ruby