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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +21 -1
- data/README.md +193 -2
- data/lib/active_security/base.rb +58 -14
- data/lib/active_security/finders.rb +1 -1
- data/lib/active_security/restricted_hooks.rb +2 -2
- data/lib/active_security/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +4 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2322b02db484fe2c369a43418f4e423714925bef1b9bc0ce6373f9813165fb43
|
4
|
+
data.tar.gz: 1bca4956c77105fae796d3c7b9b97780900c8d0727ca2578f2cfc3244cfe3d72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
186
|
-
|
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
|
|
data/lib/active_security/base.rb
CHANGED
@@ -10,7 +10,7 @@ module ActiveSecurity
|
|
10
10
|
#
|
11
11
|
# class Foo < ActiveRecord::Base
|
12
12
|
# include ActiveSecurity
|
13
|
-
# active_security :use =>
|
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
|
25
|
+
# ### The Basic Setup: Simple Models
|
26
26
|
#
|
27
|
-
#
|
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:
|
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)
|
48
|
-
# User.where(...).find(1)
|
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
|
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.
|
@@ -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
|
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
|
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)
|
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.
|
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.
|
369
|
-
changelog_uri: https://github.com/pboling/active_security/blob/v1.0.
|
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.
|
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
|