verifica 1.0.1 → 1.0.2

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
  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