protected_attributes 1.1.0 → 1.1.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
- data/README.md +24 -17
- data/lib/action_controller/accessible_params_wrapper.rb +2 -0
- data/lib/active_model/mass_assignment_security.rb +3 -3
- data/lib/active_record/mass_assignment_security.rb +0 -1
- data/lib/active_record/mass_assignment_security/associations.rb +17 -0
- data/lib/active_record/mass_assignment_security/reflection.rb +4 -0
- data/lib/active_record/mass_assignment_security/relation.rb +69 -40
- data/lib/protected_attributes.rb +0 -1
- data/lib/protected_attributes/version.rb +1 -1
- metadata +3 -4
- data/lib/protected_attributes/railtie.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aac372473a095a28495278d6c7d42f0322bc8dc4
|
4
|
+
data.tar.gz: 64888dd76e4cb9a56254e5a4efefe967524f0017
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7f64b3eba28e34e6e2dd95c54e2988070268257688b628107f2fea3a096aa35c1e6d9333d1f1334aba703d2afd625ca48abad43fddd64362c487d5e636b6207
|
7
|
+
data.tar.gz: b04a19963ae3b9715dcc16dbcab2202dc7a80ee520b3adc2f9f48a9e34848910ec02cccbda9174b811216370e414696e81716586b841fb4a1e4b98dec3f96046
|
data/README.md
CHANGED
@@ -1,30 +1,26 @@
|
|
1
|
-
#
|
1
|
+
# Protected Attributes
|
2
2
|
|
3
3
|
[](https://travis-ci.org/rails/protected_attributes)
|
4
4
|
|
5
|
-
Protect attributes from mass-assignment in
|
5
|
+
Protect attributes from mass-assignment in Active Record models.
|
6
6
|
|
7
|
-
This plugin adds `attr_accessible` and `attr_protected`
|
7
|
+
This plugin adds the class methods `attr_accessible` and `attr_protected` to your models to be able to declare white or black lists of attributes.
|
8
8
|
|
9
|
-
Note: This plugin will be officially supported until the release of Rails 5.0
|
9
|
+
Note: This plugin will be officially supported until the release of Rails 5.0.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
13
|
-
Add this line to your application's Gemfile
|
13
|
+
Add this line to your application's `Gemfile`:
|
14
14
|
|
15
15
|
gem 'protected_attributes'
|
16
16
|
|
17
17
|
And then execute:
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
Or install it yourself as:
|
22
|
-
|
23
|
-
$ gem install protected_attributes
|
19
|
+
bundle install
|
24
20
|
|
25
21
|
## Usage
|
26
22
|
|
27
|
-
Mass assignment security provides an interface for protecting attributes from end-user
|
23
|
+
Mass assignment security provides an interface for protecting attributes from end-user injection. This plugin provides two class methods in Active Record classes to control access to their attributes. The `attr_protected` method takes a list of attributes that will be ignored in mass-assignment.
|
28
24
|
|
29
25
|
For example:
|
30
26
|
```ruby
|
@@ -35,12 +31,15 @@ attr_protected :admin
|
|
35
31
|
```ruby
|
36
32
|
attr_protected :last_login, :as => :admin
|
37
33
|
```
|
38
|
-
A much better way, because it follows the whitelist-principle, is the `attr_accessible` method. It is the exact opposite of `attr_protected`, because it takes a list of attributes that will be
|
34
|
+
A much better way, because it follows the whitelist-principle, is the `attr_accessible` method. It is the exact opposite of `attr_protected`, because it takes a list of attributes that will be mass-assigned if present. Any other attributes will be ignored. This way you won’t forget to protect attributes when adding new ones in the course of development. Here is an example:
|
35
|
+
|
39
36
|
```ruby
|
40
37
|
attr_accessible :name
|
41
38
|
attr_accessible :name, :is_admin, :as => :admin
|
42
39
|
```
|
40
|
+
|
43
41
|
If you want to set a protected attribute, you will to have to assign it individually:
|
42
|
+
|
44
43
|
```ruby
|
45
44
|
params[:user] # => {:name => "owned", :is_admin => true}
|
46
45
|
@user = User.new(params[:user])
|
@@ -48,12 +47,15 @@ params[:user] # => {:name => "owned", :is_admin => true}
|
|
48
47
|
@user.is_admin = true
|
49
48
|
@user.is_admin # => true
|
50
49
|
```
|
50
|
+
|
51
51
|
When assigning attributes in Active Record using `attributes=` the `:default` role will be used. To assign attributes using different roles you should use `assign_attributes` which accepts an optional `:as` options parameter. If no `:as` option is provided then the `:default` role will be used.
|
52
|
+
|
52
53
|
You can also bypass mass-assignment security by using the `:without_protection` option. Here is an example:
|
54
|
+
|
53
55
|
```ruby
|
54
56
|
@user = User.new
|
55
57
|
|
56
|
-
@user.assign_attributes(
|
58
|
+
@user.assign_attributes(:name => 'Josh', :is_admin => true)
|
57
59
|
@user.name # => Josh
|
58
60
|
@user.is_admin # => false
|
59
61
|
|
@@ -65,7 +67,9 @@ You can also bypass mass-assignment security by using the `:without_protection`
|
|
65
67
|
@user.name # => Josh
|
66
68
|
@user.is_admin # => true
|
67
69
|
```
|
70
|
+
|
68
71
|
In a similar way, `new`, `create`, `create!`, `update_attributes` and `update_attributes!` methods all respect mass-assignment security and accept either `:as` or `:without_protection` options. For example:
|
72
|
+
|
69
73
|
```ruby
|
70
74
|
@user = User.new({ :name => 'Sebastian', :is_admin => true }, :as => :admin)
|
71
75
|
@user.name # => Sebastian
|
@@ -75,17 +79,21 @@ In a similar way, `new`, `create`, `create!`, `update_attributes` and `update_at
|
|
75
79
|
@user.name # => Sebastian
|
76
80
|
@user.is_admin # => true
|
77
81
|
```
|
82
|
+
|
78
83
|
By default the gem will create an empty whitelist of attributes available for mass-assignment for all models in your app.
|
84
|
+
|
79
85
|
As such, your models will need to explicitly whitelist or blacklist accessible parameters by using an `attr_accessible` or `attr_protected` declaration. This technique is best applied at the start of a new project. However, for an existing project with a thorough set of functional tests, it should be straightforward and relatively quick to use this application config option; run your tests, and expose each attribute (via `attr_accessible` or `attr_protected`), as dictated by your failing test.
|
80
86
|
|
81
87
|
This option can be turned off using a configuration option:
|
88
|
+
|
82
89
|
```ruby
|
83
90
|
config.active_record.whitelist_attributes = false
|
84
91
|
```
|
85
92
|
|
86
|
-
For more complex permissions, mass-assignment security may be handled outside the model by extending a non-
|
93
|
+
For more complex permissions, mass-assignment security may be handled outside the model by extending a non-Active Record class, such as a controller, with this behavior.
|
87
94
|
|
88
95
|
For example, a logged-in user may need to assign additional attributes depending on their role:
|
96
|
+
|
89
97
|
```ruby
|
90
98
|
class AccountsController < ApplicationController
|
91
99
|
include ActiveModel::MassAssignmentSecurity
|
@@ -110,14 +118,13 @@ end
|
|
110
118
|
|
111
119
|
### Errors
|
112
120
|
|
113
|
-
By default,
|
114
|
-
If you want the functionality where exceptions (`ActiveModel::MassAssignmentSecurity::Error`) are raised. Add to your config
|
115
|
-
the strict flag:
|
121
|
+
By default, attributes in the params hash which are not allowed to be updated are just ignored. If you prefer an exception to be raised configure:
|
116
122
|
|
117
123
|
```ruby
|
118
124
|
config.active_record.mass_assignment_sanitizer = :strict
|
119
125
|
```
|
120
126
|
|
127
|
+
Any protected attributes violation raises `ActiveModel::MassAssignmentSecurity::Error` then.
|
121
128
|
|
122
129
|
## Contributing
|
123
130
|
|
@@ -348,11 +348,11 @@ module ActiveModel
|
|
348
348
|
protected
|
349
349
|
|
350
350
|
def sanitize_for_mass_assignment(attributes, role = nil) #:nodoc:
|
351
|
-
|
351
|
+
if _uses_mass_assignment_security
|
352
|
+
_mass_assignment_sanitizer.sanitize(self.class, attributes, mass_assignment_authorizer(role))
|
353
|
+
else
|
352
354
|
sanitize_forbidden_attributes(attributes)
|
353
355
|
end
|
354
|
-
|
355
|
-
_mass_assignment_sanitizer.sanitize(self.class, attributes, mass_assignment_authorizer(role))
|
356
356
|
end
|
357
357
|
|
358
358
|
def mass_assignment_authorizer(role) #:nodoc:
|
@@ -19,7 +19,6 @@ class ActiveRecord::Base
|
|
19
19
|
include ActiveRecord::MassAssignmentSecurity::Core
|
20
20
|
include ActiveRecord::MassAssignmentSecurity::AttributeAssignment
|
21
21
|
include ActiveRecord::MassAssignmentSecurity::Persistence
|
22
|
-
include ActiveRecord::MassAssignmentSecurity::Relation
|
23
22
|
include ActiveRecord::MassAssignmentSecurity::Validations
|
24
23
|
include ActiveRecord::MassAssignmentSecurity::NestedAttributes
|
25
24
|
include ActiveRecord::MassAssignmentSecurity::Inheritance
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class Association
|
4
|
+
undef :build_record
|
5
|
+
|
4
6
|
def build_record(attributes, options)
|
5
7
|
reflection.build_association(attributes, options) do |record|
|
6
8
|
attributes = create_scope.except(*(record.changed - [reflection.foreign_key]))
|
@@ -12,6 +14,10 @@ module ActiveRecord
|
|
12
14
|
end
|
13
15
|
|
14
16
|
class CollectionAssociation
|
17
|
+
undef :build
|
18
|
+
undef :create
|
19
|
+
undef :create!
|
20
|
+
|
15
21
|
def build(attributes = {}, options = {}, &block)
|
16
22
|
if attributes.is_a?(Array)
|
17
23
|
attributes.collect { |attr| build(attr, options, &block) }
|
@@ -51,6 +57,9 @@ module ActiveRecord
|
|
51
57
|
end
|
52
58
|
|
53
59
|
class CollectionProxy
|
60
|
+
undef :create
|
61
|
+
undef :create!
|
62
|
+
|
54
63
|
def build(attributes = {}, options = {}, &block)
|
55
64
|
@association.build(attributes, options, &block)
|
56
65
|
end
|
@@ -66,6 +75,7 @@ module ActiveRecord
|
|
66
75
|
end
|
67
76
|
|
68
77
|
module ThroughAssociation
|
78
|
+
undef :build_record if respond_to?(:build_record, false)
|
69
79
|
|
70
80
|
private
|
71
81
|
|
@@ -82,6 +92,9 @@ module ActiveRecord
|
|
82
92
|
end
|
83
93
|
|
84
94
|
class HasManyThroughAssociation
|
95
|
+
undef :build_record
|
96
|
+
undef :options_for_through_record
|
97
|
+
|
85
98
|
def build_record(attributes, options = {})
|
86
99
|
ensure_not_nested
|
87
100
|
|
@@ -107,6 +120,10 @@ module ActiveRecord
|
|
107
120
|
end
|
108
121
|
|
109
122
|
class SingularAssociation
|
123
|
+
undef :create
|
124
|
+
undef :create!
|
125
|
+
undef :build
|
126
|
+
|
110
127
|
def create(attributes = {}, options = {}, &block)
|
111
128
|
create_record(attributes, options, &block)
|
112
129
|
end
|
@@ -2,12 +2,16 @@ module ActiveRecord
|
|
2
2
|
module Reflection
|
3
3
|
if defined?(AbstractReflection)
|
4
4
|
class AbstractReflection
|
5
|
+
undef :build_association
|
6
|
+
|
5
7
|
def build_association(*options, &block)
|
6
8
|
klass.new(*options, &block)
|
7
9
|
end
|
8
10
|
end
|
9
11
|
else
|
10
12
|
class AssociationReflection
|
13
|
+
undef :build_association
|
14
|
+
|
11
15
|
def build_association(*options, &block)
|
12
16
|
klass.new(*options, &block)
|
13
17
|
end
|
@@ -1,46 +1,75 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# # Find the first user named Penélope or create a new one.
|
10
|
-
# User.where(:first_name => 'Penélope').first_or_create
|
11
|
-
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
|
12
|
-
#
|
13
|
-
# # Find the first user named Penélope or create a new one.
|
14
|
-
# # We already have one so the existing record will be returned.
|
15
|
-
# User.where(:first_name => 'Penélope').first_or_create
|
16
|
-
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
|
17
|
-
#
|
18
|
-
# # Find the first user named Scarlett or create a new one with a particular last name.
|
19
|
-
# User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
|
20
|
-
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
|
21
|
-
#
|
22
|
-
# # Find the first user named Scarlett or create a new one with a different last name.
|
23
|
-
# # We already have one so the existing record will be returned.
|
24
|
-
# User.where(:first_name => 'Scarlett').first_or_create do |user|
|
25
|
-
# user.last_name = "O'Hara"
|
26
|
-
# end
|
27
|
-
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
|
28
|
-
def first_or_create(attributes = nil, options = {}, &block)
|
29
|
-
first || create(attributes, options, &block)
|
30
|
-
end
|
2
|
+
class Relation
|
3
|
+
undef :first_or_create
|
4
|
+
undef :first_or_create!
|
5
|
+
undef :first_or_initialize
|
6
|
+
undef :find_or_initialize_by
|
7
|
+
undef :find_or_create_by
|
8
|
+
undef :find_or_create_by!
|
31
9
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
10
|
+
# Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
|
11
|
+
#
|
12
|
+
# Expects arguments in the same format as +Base.create+.
|
13
|
+
#
|
14
|
+
# ==== Examples
|
15
|
+
# # Find the first user named Penélope or create a new one.
|
16
|
+
# User.where(:first_name => 'Penélope').first_or_create
|
17
|
+
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
|
18
|
+
#
|
19
|
+
# # Find the first user named Penélope or create a new one.
|
20
|
+
# # We already have one so the existing record will be returned.
|
21
|
+
# User.where(:first_name => 'Penélope').first_or_create
|
22
|
+
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
|
23
|
+
#
|
24
|
+
# # Find the first user named Scarlett or create a new one with a particular last name.
|
25
|
+
# User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
|
26
|
+
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
|
27
|
+
#
|
28
|
+
# # Find the first user named Scarlett or create a new one with a different last name.
|
29
|
+
# # We already have one so the existing record will be returned.
|
30
|
+
# User.where(:first_name => 'Scarlett').first_or_create do |user|
|
31
|
+
# user.last_name = "O'Hara"
|
32
|
+
# end
|
33
|
+
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
|
34
|
+
def first_or_create(attributes = nil, options = {}, &block)
|
35
|
+
first || create(attributes, options, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
|
39
|
+
#
|
40
|
+
# Expects arguments in the same format as <tt>Base.create!</tt>.
|
41
|
+
def first_or_create!(attributes = nil, options = {}, &block)
|
42
|
+
first || create!(attributes, options, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
|
46
|
+
#
|
47
|
+
# Expects arguments in the same format as <tt>Base.new</tt>.
|
48
|
+
def first_or_initialize(attributes = nil, options = {}, &block)
|
49
|
+
first || new(attributes, options, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_or_initialize_by(attributes, options = {}, &block)
|
53
|
+
find_by(attributes) || new(attributes, options, &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_or_create_by(attributes, options = {}, &block)
|
57
|
+
find_by(attributes) || create(attributes, options, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_or_create_by!(attributes, options = {}, &block)
|
61
|
+
find_by(attributes) || create!(attributes, options, &block)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module QueryMethods
|
66
|
+
protected
|
38
67
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
68
|
+
def sanitize_forbidden_attributes(attributes) #:nodoc:
|
69
|
+
if !model._uses_mass_assignment_security
|
70
|
+
sanitize_for_mass_assignment(attributes)
|
71
|
+
else
|
72
|
+
attributes
|
44
73
|
end
|
45
74
|
end
|
46
75
|
end
|
data/lib/protected_attributes.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protected_attributes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -142,7 +142,6 @@ files:
|
|
142
142
|
- lib/active_record/mass_assignment_security/relation.rb
|
143
143
|
- lib/active_record/mass_assignment_security/validations.rb
|
144
144
|
- lib/protected_attributes.rb
|
145
|
-
- lib/protected_attributes/railtie.rb
|
146
145
|
- lib/protected_attributes/version.rb
|
147
146
|
homepage: https://github.com/rails/protected_attributes
|
148
147
|
licenses:
|
@@ -164,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
163
|
version: '0'
|
165
164
|
requirements: []
|
166
165
|
rubyforge_project:
|
167
|
-
rubygems_version: 2.4.
|
166
|
+
rubygems_version: 2.4.7
|
168
167
|
signing_key:
|
169
168
|
specification_version: 4
|
170
169
|
summary: Protect attributes from mass assignment in Active Record models
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module ProtectedAttributes
|
2
|
-
class Railtie < ::Rails::Railtie
|
3
|
-
config.before_configuration do |app|
|
4
|
-
config.action_controller.permit_all_parameters = true
|
5
|
-
config.active_record.whitelist_attributes = true if config.respond_to?(:active_record)
|
6
|
-
end
|
7
|
-
|
8
|
-
initializer "protected_attributes.active_record", :before => "active_record.set_configs" do |app|
|
9
|
-
ActiveSupport.on_load :active_record do
|
10
|
-
if app.config.respond_to?(:active_record) && app.config.active_record.delete(:whitelist_attributes)
|
11
|
-
attr_accessible(nil)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|