active_security 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: 3c68226e5113d5925222d0e456dfbccac3866b3d526d35a018a52a4a8f2aa476
4
- data.tar.gz: 5590c5054131249ee7fa5fa3875a3fe64440154f37f7614d7c6fce0a4807b25f
3
+ metadata.gz: 2322b02db484fe2c369a43418f4e423714925bef1b9bc0ce6373f9813165fb43
4
+ data.tar.gz: 1bca4956c77105fae796d3c7b9b97780900c8d0727ca2578f2cfc3244cfe3d72
5
5
  SHA512:
6
- metadata.gz: aa9c8e50b69b7427a4f769da3132324c3c0d5fa8f6ba5a1d78955194573e7c84accfebb1666cf144520cb9fb90c3b553f23501ee3657f8841ed53553312e690d
7
- data.tar.gz: 961dd3238ba102b5b5b7100a88a080d1f42b62c619e16fbeafa25469c5d090d38c23b33bdafd032631cda644f4c6dfdcdca0d3817f4890870667ae1c2cc2ee1c
6
+ metadata.gz: 6e1e66f6d0d4e0ccfc24e5f5df8d4e652674e94a27241b4af29d0936258238e9d601e9c556f232299b40015aee24d377f3dca49391aad992ba325f41975b0262
7
+ data.tar.gz: 8319888c2fa8d89b67ab8547d861c344f6913c13ee5e5a7ea374e9e7cccf5d9d1c273867edc55d8ff5ffb008adf25c196a185c8e721f1afb48d56463e76f8365
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ Since version 2.0.0, the format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
1
7
  ## [Unreleased]
8
+ ### Added
9
+ ### Changed
10
+ ### Fixed
11
+ ### Removed
2
12
 
3
- ## [1.0.0] - 2024-07-04
13
+ ## [1.0.1] - 2024-07-05
14
+ ### Added
15
+ - Documentation
4
16
 
17
+ ## [1.0.0] - 2024-07-05
18
+ ### Added
5
19
  - Initial release
20
+
21
+ [Unreleased]: https://github.com/pboling/sanitize_email/compare/v1.0.1...HEAD
22
+ [1.0.1]: https://github.com/pboling/sanitize_email/compare/v1.0.0...v1.0.1
23
+ [1.0.1t]: https://github.com/pboling/sanitize_email/tags/v1.0.1
24
+ [1.0.0]: https://github.com/pboling/sanitize_email/compare/9caac884909193326b3f3318ad3db00ca754fabd...v1.0.0
25
+ [1.0.0t]: https://github.com/pboling/sanitize_email/tags/v1.0.0
data/README.md CHANGED
@@ -182,8 +182,199 @@ If bundler is not being used to manage dependencies, install the gem by executin
182
182
 
183
183
  ## Documentation
184
184
 
185
- All documentation is in the source for now, but there is a lot of it.
186
- If you'd like to help add documentation to this readme, please submit a pull request.
185
+ All documentation is in the source so check there if the following is not enough.
186
+
187
+ ### Setting Up ActiveSecurity in Your Model
188
+
189
+ To use ActiveSecurity in your ActiveRecord models, you must first either extend or
190
+ include the ActiveSecurity module (it makes no difference), then invoke the
191
+ {ActiveSecurity::Base#active_security active_security} method to configure your desired
192
+ options:
193
+
194
+ ```ruby
195
+ class Foo < ActiveRecord::Base
196
+ include ActiveSecurity
197
+ active_security :use => {finders: {default_finders: :restricted}, scoped: {scope: :bar_id}}
198
+ end
199
+ ```
200
+
201
+ The most important option is `:use`, which you use to tell ActiveSecurity which
202
+ addons it should use. See the documentation for {ActiveSecurity::Base#active_security} for a list of all
203
+ available addons, or skim through the rest of the docs to get a high-level
204
+ overview.
205
+
206
+ *A note about single table inheritance (STI): you must extend ActiveSecurity in*
207
+ *all classes that participate in STI, both your parent classes and their*
208
+ *children.*
209
+
210
+ #### The Basic Setup: Simple Models
211
+
212
+ By default the `:restricted` plugin is the only one configured, and the `restricted`
213
+ scope must be explicitly added to every query.
214
+ This is the simplest way to use ActiveSecurity. But it is messy, and laborious.
215
+ It will ensure that finds are executed within a `where` scope:
216
+
217
+ ```ruby
218
+ class User < ActiveRecord::Base
219
+ extend ActiveSecurity
220
+ end
221
+
222
+ User.restricted.find(1) # blows up, because no scope
223
+ User.where(name: "Bart").restricted.find(1) # returns the user
224
+ ```
225
+
226
+ #### The Strict Setup: Magic Finders
227
+
228
+ The problem with the above approach is that a naked find (`User.find(1)`)
229
+ still works, and is just as insecure as before. The `:finders` plugin fixes
230
+ this problem, so you don't need to add `restricted` everywhere.
231
+
232
+ ```ruby
233
+ class User < ActiveRecord::Base
234
+ extend ActiveSecurity
235
+ active_security use: {finders: {default_finders: :restricted}}
236
+ end
237
+
238
+ User.find(1) # blows up, because no scope
239
+ User.where(name: "Bart").find(1) # returns the user
240
+ User.where(name: "Bart").restricted.find(1) # also returns the user
241
+ ```
242
+
243
+ ### Configuration: ActiveSecurity's behavior in a model
244
+
245
+ Here's a basic config.
246
+
247
+ ```ruby
248
+ class Post < ActiveRecord::Base
249
+ extend ActiveSecurity
250
+ active_security use: :finders
251
+ end
252
+ ```
253
+
254
+ Now let's get crazy secure!
255
+
256
+ When given the optional block, this method will yield the class's instance
257
+ of {ActiveSecurity::Configuration} to the block before evaluating other
258
+ arguments, so configuration values set in the block may be overwritten by
259
+ the arguments. This order was chosen to allow passing the same proc to
260
+ multiple models, while being able to override the values it sets. Here is
261
+ a contrived example:
262
+
263
+ ```ruby
264
+ $active_security_config_proc = Proc.new do |config|
265
+ config.use :finders
266
+ end
267
+
268
+ class Foo < ActiveRecord::Base
269
+ extend ActiveSecurity
270
+ active_security &$active_security_config_proc
271
+ end
272
+
273
+ class Bar < ActiveRecord::Base
274
+ extend ActiveSecurity
275
+ active_security &$active_security_config_proc
276
+ end
277
+ ```
278
+
279
+ However, it's usually better to use {ActiveSecurity.defaults} for this:
280
+
281
+ ```ruby
282
+ ActiveSecurity.defaults do |config|
283
+ config.use :finders, default_finders: :restricted
284
+ end
285
+
286
+ class Foo < ActiveRecord::Base
287
+ extend ActiveSecurity
288
+ end
289
+
290
+ class Bar < ActiveRecord::Base
291
+ extend ActiveSecurity
292
+ end
293
+ ```
294
+
295
+ In general you should use the block syntax either because of your personal
296
+ aesthetic preference, or because you need to share some functionality
297
+ between multiple models that can't be well encapsulated by
298
+ {ActiveSecurity.defaults}.
299
+
300
+ ### Order Method Calls in a Block vs Ordering Options
301
+
302
+ When calling this method without a block, you may set the hash options in
303
+ any order, so long as they either have no dependencies, or are coupled with
304
+ their respective module.
305
+
306
+ Here's an example that configures every plugin:
307
+ ```ruby
308
+ class Person < ActiveRecord::Base
309
+ active_security use: {
310
+ finders: {default_finders: :restricted},
311
+ scoped: {scope: :name},
312
+ privileged: {}
313
+ }
314
+ end
315
+
316
+ Person.find(1) # blows up, because no scope
317
+ Person.where(name: "Bart").find(1) # returns the person
318
+ Person.where(age: 29).find(1) # blows up, because name scope wasn't used
319
+ Person.where(age: 29).privileged.find(1) # returns the person, because privileged
320
+ Person.where(name: "Bart").restricted.find(1) # also returns the person, because name scope used
321
+ ```
322
+
323
+ However, when using block-style invocation, be sure to call
324
+ ActiveSecurity::Configuration's {ActiveSecurity::Configuration#use use} method
325
+ *prior* to the associated configuration options, because it will include
326
+ modules into your class, and these modules in turn may add required
327
+ configuration options to the `@active_security_configuration`'s class:
328
+
329
+ ```ruby
330
+ class Person < ActiveRecord::Base
331
+ active_security do |config|
332
+ # This will work
333
+ config.use :scoped
334
+ config.scope = "family_id"
335
+ end
336
+ end
337
+
338
+ class Person < ActiveRecord::Base
339
+ active_security do |config|
340
+ # This will fail
341
+ config.scope = "family_id"
342
+ config.use :scoped
343
+ end
344
+ end
345
+ ```
346
+
347
+ ### Including Your Own Modules
348
+
349
+ Because :use can accept a name or a Module, {ActiveSecurity.defaults defaults}
350
+ can be a convenient place to set up behavior common to all classes using
351
+ ActiveSecurity. You can include any module, or more conveniently, define one
352
+ on-the-fly. For example, let's say you want to globally override the error
353
+ that is raised when no scope is used:
354
+
355
+ ```ruby
356
+ ActiveSecurity.defaults do |config|
357
+ config.use :finders
358
+ config.use Module.new {
359
+ def self.setup(model_class)
360
+ model_class.instance_eval do
361
+ relation.class.send(:prepend, RaiseOverride)
362
+ model_class.singleton_class.send(:prepend, RaiseOverride)
363
+ end
364
+
365
+ association_relation_delegate_class = model_class.relation_delegate_class(::ActiveRecord::AssociationRelation)
366
+ association_relation_delegate_class.send(:prepend, RaiseOverride)
367
+ end
368
+
369
+ module RaiseOverride
370
+ def raise_if_not_scoped
371
+ puts "My errors are better than yours"
372
+ raise StandardError, "Calm Down"
373
+ end
374
+ end
375
+ }
376
+ end
377
+ ```
187
378
 
188
379
  ## Running Specs
189
380
 
@@ -10,7 +10,7 @@ module ActiveSecurity
10
10
  #
11
11
  # class Foo < ActiveRecord::Base
12
12
  # include ActiveSecurity
13
- # active_security :use => [:finders, :scoped], scope: :bar_id
13
+ # active_security :use => {finders: {default_finders: :restricted}, scoped: {scope: :bar_id}}
14
14
  # end
15
15
  #
16
16
  # The most important option is `:use`, which you use to tell ActiveSecurity which
@@ -22,9 +22,12 @@ module ActiveSecurity
22
22
  # *all classes that participate in STI, both your parent classes and their*
23
23
  # *children.*
24
24
  #
25
- # ### The Default Setup: Simple Models
25
+ # ### The Basic Setup: Simple Models
26
26
  #
27
- # The simplest way to use ActiveSecurity is to have it ensure that finds are executed within a scope:
27
+ # By default the `:restricted` plugin is the only one configured, and the `restricted`
28
+ # scope must be explicitly added to every query.
29
+ # This is the simplest way to use ActiveSecurity. But it is messy, and laborious.
30
+ # It will ensure that finds are executed within a `where` scope:
28
31
  #
29
32
  # class User < ActiveRecord::Base
30
33
  # extend ActiveSecurity
@@ -33,7 +36,7 @@ module ActiveSecurity
33
36
  # User.restricted.find(1) # blows up, because no scope
34
37
  # User.where(...).restricted.find(1) # returns the user
35
38
  #
36
- # ### The Strict Setup: Simple Models
39
+ # ### The Strict Setup: Magic Finders
37
40
  #
38
41
  # The problem with the above approach is that a naked find (`User.find(1)`)
39
42
  # still works, and is just as insecure as before. The `:finders` plugin fixes
@@ -44,8 +47,9 @@ module ActiveSecurity
44
47
  # active_security use: {finders: {default_finders: :restricted}}
45
48
  # end
46
49
  #
47
- # User.find(1) # blows up, because no scope
48
- # User.where(...).find(1) # returns the user
50
+ # User.find(1) # blows up, because no scope
51
+ # User.where(...).find(1) # returns the user
52
+ # User.where(...).restricted.find(1) # also returns the user
49
53
  #
50
54
  # @guide end
51
55
  module Base
@@ -99,7 +103,24 @@ module ActiveSecurity
99
103
  # ### Order Method Calls in a Block vs Ordering Options
100
104
  #
101
105
  # When calling this method without a block, you may set the hash options in
102
- # any order.
106
+ # any order, so long as they either have no dependencies, or are coupled with
107
+ # their respective module.
108
+ #
109
+ # Here's an example that configures every plugin:
110
+ #
111
+ # class Person < ActiveRecord::Base
112
+ # active_security use: {
113
+ # finders: {default_finders: :restricted},
114
+ # scoped: {scope: :name},
115
+ # privileged: {}
116
+ # }
117
+ # end
118
+ #
119
+ # Person.find(1) # blows up, because no scope
120
+ # Person.where(name: "Bart").find(1) # returns the person
121
+ # Person.where(age: 29).find(1) # blows up, because name scope wasn't used
122
+ # Person.where(age: 29).privileged.find(1) # returns the person, because privileged
123
+ # Person.where(name: "Bart").restricted.find(1) # also returns the person, because name scope used
103
124
  #
104
125
  # However, when using block-style invocation, be sure to call
105
126
  # ActiveSecurity::Configuration's {ActiveSecurity::Configuration#use use} method
@@ -128,8 +149,8 @@ module ActiveSecurity
128
149
  # Because :use can accept a name or a Module, {ActiveSecurity.defaults defaults}
129
150
  # can be a convenient place to set up behavior common to all classes using
130
151
  # ActiveSecurity. You can include any module, or more conveniently, define one
131
- # on-the-fly. For example, let's say you want to override the error that is
132
- # raised when no scope is used:
152
+ # on-the-fly. For example, let's say you want to globally override the error
153
+ # that is raised when no scope is used:
133
154
  #
134
155
  # ActiveSecurity.defaults do |config|
135
156
  # config.use :finders
@@ -153,16 +174,39 @@ module ActiveSecurity
153
174
  # }
154
175
  # end
155
176
  #
156
- #
157
- # @option options [Symbol,Module] :use The addon or name of an addon to use.
177
+ # @option options [Symbol, Module] :use The addon or name of an addon to use.
158
178
  # By default, ActiveSecurity provides {ActiveSecurity::Finders :finders},
159
179
  # {ActiveSecurity::Restricted :restricted}, {ActiveSecurity::Privileged :privileged},
160
- # and {ActiveSecurity::Scoped :scoped}.
180
+ # and {ActiveSecurity::Scoped :scoped}, or a hash where the keys are the symbolized
181
+ # module names just mentioned, and the values are hashes of options to set for each
182
+ # module.
161
183
  #
162
- # @option options [Symbol] :scope Available when using `:scoped`.
163
- # Sets the relation or column which will be considered a required scope.
184
+ # @option options [Symbol, Array[Symbol]] :scope Available when using `:scoped`.
185
+ # Sets the relation(s) or column(s) which will be considered a required scope.
164
186
  # This option has no default value.
165
187
  #
188
+ # @option options [Symbol] :default_finders Available when using `:finders`.
189
+ # Sets the type of scope enforcement to use. Must be one of :restricted or
190
+ # :privileged. Default value is :restricted.
191
+ #
192
+ # @option options [Module] :privileged_hooks Available when using `:privileged`.
193
+ # Sets the Module which defines the necessary hooks for the Privileged behavior.
194
+ # Default value is {ActiveSecurity::PrivilegedHooks}
195
+ #
196
+ # @option options [Module] :restricted_hooks Available when using `:restricted`.
197
+ # Sets the Module which defines the necessary hooks for the Restricted behavior.
198
+ # Default value is {ActiveSecurity::RestrictedHooks}
199
+ #
200
+ # @option options [Symbol, #call] :on_restricted_no_scope Available when using `:restricted`.
201
+ # Sets the Restricted behavior when the expected scope is not found. Must be one of
202
+ # the following [#call, :log, :log_and_raise, :raise].
203
+ # Default value is :log_and_raise.
204
+ #
205
+ # @option options [Symbol, #call] :on_restricted_unhandled_predicate Available when using `:restricted`.
206
+ # Sets the Restricted behavior when the scopes Arel Node has no defined handling. Must be one of
207
+ # the following [#call, :log, :log_and_raise, :raise].
208
+ # Default value is :log_and_raise.
209
+ #
166
210
  # @yield Provides access to the model class's active_security_config, which
167
211
  # allows an alternate configuration syntax, and conditional configuration
168
212
  # logic.
@@ -22,7 +22,7 @@ module ActiveSecurity
22
22
  #
23
23
  # scope :active, -> {where(active: true)}
24
24
  #
25
- # active_security use: [:finders]
25
+ # active_security use: :finders
26
26
  # end
27
27
  #
28
28
  # Restaurant.restricted.find(23) #=> blows up, because no scope!
@@ -8,7 +8,7 @@ module ActiveSecurity
8
8
  return active_security_config.on_restricted_no_scope.call(active_security_config) if active_security_config.on_restricted_no_scope.respond_to?(:call)
9
9
 
10
10
  unless VALID_CONFIG_VALUES.include?(active_security_config.on_restricted_no_scope)
11
- raise InvalidConfig, "on_restricted_no_scope must either be set to a (callable lambda/proc) or one of [:log, :log_and_raise, :raise]"
11
+ raise InvalidConfig, "on_restricted_no_scope must either be set to a callable lambda/proc or one of [:log, :log_and_raise, :raise]"
12
12
  end
13
13
 
14
14
  if /log/.match?(active_security_config.on_restricted_no_scope)
@@ -24,7 +24,7 @@ module ActiveSecurity
24
24
  return active_security_config.on_restricted_unhandled_predicate.call(active_security_config) if active_security_config.on_restricted_unhandled_predicate.respond_to?(:call)
25
25
 
26
26
  unless VALID_CONFIG_VALUES.include?(active_security_config.on_restricted_unhandled_predicate)
27
- raise InvalidConfig, "on_restricted_unhandled_predicate must either be set to a (callable lambda/proc) or one of [:log, :log_and_raise, :raise]"
27
+ raise InvalidConfig, "on_restricted_unhandled_predicate must either be set to a callable lambda/proc or one of [:log, :log_and_raise, :raise]"
28
28
  end
29
29
 
30
30
  if /log/.match?(active_security_config.on_restricted_unhandled_predicate)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveSecurity
4
4
  module Version
5
- VERSION = "1.0.0"
5
+ VERSION = "1.0.1"
6
6
  end
7
7
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_security
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
  - Peter Boling
@@ -365,10 +365,10 @@ licenses:
365
365
  - MIT
366
366
  metadata:
367
367
  homepage_uri: https://github.com/pboling/active_security
368
- source_code_uri: https://github.com/pboling/active_security/tree/v1.0.0
369
- changelog_uri: https://github.com/pboling/active_security/blob/v1.0.0/CHANGELOG.md
368
+ source_code_uri: https://github.com/pboling/active_security/tree/v1.0.1
369
+ changelog_uri: https://github.com/pboling/active_security/blob/v1.0.1/CHANGELOG.md
370
370
  bug_tracker_uri: https://github.com/pboling/active_security/issues
371
- documentation_uri: https://www.rubydoc.info/gems/active_security/1.0.0
371
+ documentation_uri: https://www.rubydoc.info/gems/active_security/1.0.1
372
372
  wiki_uri: https://github.com/pboling/active_security/wiki
373
373
  funding_uri: https://liberapay.com/pboling
374
374
  rubygems_mfa_required: 'true'
metadata.gz.sig CHANGED
Binary file