active_security 1.0.0 → 1.0.1

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