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