protected_attributes 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://api.travis-ci.org/rails/protected_attributes.svg?branch=master)](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
|