attribute_ext 1.1.0 → 1.2.4
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.
- data/README.md +44 -0
- data/attribute_ext.gemspec +1 -1
- data/init.rb +1 -2
- data/lib/attribute_ext.rb +7 -0
- data/lib/attribute_ext/hidden_attributes.rb +44 -4
- data/lib/attribute_ext/railtie.rb +2 -3
- data/lib/attribute_ext/safe_attributes.rb +97 -5
- data/spec/safe_attributes_spec.rb +57 -0
- data/spec/support/stub.rb +5 -1
- metadata +5 -5
data/README.md
CHANGED
@@ -58,6 +58,44 @@ or
|
|
58
58
|
class User < ActiveRecord::Base
|
59
59
|
safe_attributes :login, :if => Proc.new { |user,role| role == :admin }
|
60
60
|
end
|
61
|
+
|
62
|
+
Default role and role mapper:
|
63
|
+
|
64
|
+
SafeAttributes provides helper for handling roles including a method to set
|
65
|
+
a new default role as well as a method to map roles to other values. Changes to
|
66
|
+
role will only affect SafeAttributes and will not be given to Rails 3.1 mass
|
67
|
+
assignment authorizer.
|
68
|
+
|
69
|
+
Set default role that will be used if given role is nil or :default.
|
70
|
+
|
71
|
+
AttributeExt::SafeAttributes.default_role = :new_default
|
72
|
+
|
73
|
+
Role values can be restricted to specific values using the role mapper.
|
74
|
+
|
75
|
+
AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
|
76
|
+
[:guest, :user, :admin].include?(role) ? role : :guest
|
77
|
+
end
|
78
|
+
|
79
|
+
or
|
80
|
+
|
81
|
+
AttributeExt::SafeAttributes.role_mapper do |role|
|
82
|
+
[:guest, :user, :admin].include?(role) ? role : :guest
|
83
|
+
end
|
84
|
+
|
85
|
+
The role mapper is especially usefull if you want the current user model be the
|
86
|
+
default role.
|
87
|
+
|
88
|
+
AttributeExt::SafeAttributes.role_mapper do |role|
|
89
|
+
role.is_a?(User) ? role : User.current
|
90
|
+
end
|
91
|
+
|
92
|
+
You can perform checks like this now:
|
93
|
+
|
94
|
+
class User < ActiveRecord::Base
|
95
|
+
safe_attribute :email, :if => Proc.new { |user,role| user == role or role.admin? }
|
96
|
+
end
|
97
|
+
|
98
|
+
Now the user can edit there own emails or everyons email if it is an admin.
|
61
99
|
|
62
100
|
|
63
101
|
AttributeExt::HiddenAttributes
|
@@ -115,6 +153,12 @@ By default rules *do not* apply when serializing to hash.
|
|
115
153
|
Changelog
|
116
154
|
---------
|
117
155
|
|
156
|
+
Sep 24, 2011
|
157
|
+
|
158
|
+
SafeAttributes provides methods to change default role and to map roles to
|
159
|
+
specific values before processing rules. Also added full documentation to
|
160
|
+
all public methods and methods that are usefull for testing own rules.
|
161
|
+
|
118
162
|
Sep 22, 2011
|
119
163
|
|
120
164
|
Nearly all features are successfully tested using a fake environment now.
|
data/attribute_ext.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "attribute_ext"
|
6
|
-
s.version = "1.
|
6
|
+
s.version = "1.2.4"
|
7
7
|
s.authors = ["Jan Graichen"]
|
8
8
|
s.email = ["jan.graichen@altimos.de"]
|
9
9
|
s.homepage = "https://github.com/jgraichen/attribute_ext"
|
data/init.rb
CHANGED
data/lib/attribute_ext.rb
CHANGED
@@ -2,3 +2,10 @@
|
|
2
2
|
require 'attribute_ext/hidden_attributes'
|
3
3
|
require 'attribute_ext/safe_attributes'
|
4
4
|
require 'attribute_ext/railtie' if defined?(Rails)
|
5
|
+
|
6
|
+
module AttributeExt
|
7
|
+
def AttributeExt.setup # :nodoc:
|
8
|
+
ActiveRecord::Base.send :include, AttributeExt::HiddenAttributes
|
9
|
+
ActiveRecord::Base.send :include, AttributeExt::SafeAttributes
|
10
|
+
end
|
11
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module AttributeExt
|
2
2
|
module HiddenAttributes
|
3
|
-
def self.included(base)
|
3
|
+
def self.included(base) # :nodoc:
|
4
4
|
base.extend(ClassMethods)
|
5
5
|
base.alias_method_chain :to_xml, :hidden_attrs
|
6
6
|
base.alias_method_chain :as_json, :hidden_attrs
|
@@ -8,6 +8,39 @@ module AttributeExt
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module ClassMethods
|
11
|
+
# Adds attribute to a blacklist that will be hidden when serializing if optional conditions
|
12
|
+
# are true.
|
13
|
+
#
|
14
|
+
# class User < ActiveRecord::Base
|
15
|
+
# hide_attributes :password # always hide
|
16
|
+
# hide_attributes :email, :if => Proc.new { |user| user.hide_email? }
|
17
|
+
# hide_attributes :not_in_json, :only => :json
|
18
|
+
# hide_attributes :except_xml_hash, :except => [:xml, :hash]
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# All given conditions to a rule must be true if attributes should be hidden. Attributes can
|
22
|
+
# appear in more than one rule.
|
23
|
+
#
|
24
|
+
# Options:
|
25
|
+
# [:+if+]
|
26
|
+
# Requires a Proc block to be true.
|
27
|
+
#
|
28
|
+
# [:+unless+]
|
29
|
+
# Requires a Proc block to be false.
|
30
|
+
#
|
31
|
+
# [:+only+]
|
32
|
+
# Requires export format to be in given array. A non array object will be converted in to
|
33
|
+
# an array only containing given object.
|
34
|
+
#
|
35
|
+
# [:+except+]
|
36
|
+
# Requires export format to not be in given array. A non array object will be converted in
|
37
|
+
# to an array only containing given object.
|
38
|
+
#
|
39
|
+
# [:+on_hash+]
|
40
|
+
# By default rules will not be applied when serializing to hash when no :only or :except
|
41
|
+
# rule is specified. If :on_hash is true rule will also apply to hash serialization. If an
|
42
|
+
# :only or :except option is given :on_hash does nothing.
|
43
|
+
#
|
11
44
|
def hide_attributes(*attrs)
|
12
45
|
@hidden_attributes ||= []
|
13
46
|
if attrs.empty?
|
@@ -34,14 +67,14 @@ module AttributeExt
|
|
34
67
|
end
|
35
68
|
end
|
36
69
|
|
37
|
-
def to_xml_with_hidden_attrs(options = nil, &block)
|
70
|
+
def to_xml_with_hidden_attrs(options = nil, &block) # :nodoc:
|
38
71
|
options ||= {}
|
39
72
|
options[:except] = hidden_attribute_names(:xml, options)
|
40
73
|
|
41
74
|
to_xml_without_hidden_attrs(options)
|
42
75
|
end
|
43
76
|
|
44
|
-
def as_json_with_hidden_attrs(options = nil, &block)
|
77
|
+
def as_json_with_hidden_attrs(options = nil, &block) # :nodoc:
|
45
78
|
options ||= {}
|
46
79
|
options[:except] = hidden_attribute_names(:json, options)
|
47
80
|
options[:hidden_attributes_format] = :json
|
@@ -49,13 +82,20 @@ module AttributeExt
|
|
49
82
|
as_json_without_hidden_attrs(options)
|
50
83
|
end
|
51
84
|
|
52
|
-
def serializable_hash_with_hidden_attrs(options = nil)
|
85
|
+
def serializable_hash_with_hidden_attrs(options = nil) # :nodoc:
|
53
86
|
options ||= {}
|
54
87
|
options[:except] = hidden_attribute_names((options[:hidden_attributes_format] || :hash), options)
|
55
88
|
|
56
89
|
serializable_hash_without_hidden_attrs(options)
|
57
90
|
end
|
58
91
|
|
92
|
+
# Returns an array with attributes to hide from serialization.
|
93
|
+
#
|
94
|
+
# This method should only be used to test own rules without need to run a formatter and
|
95
|
+
# validate the generated output. See AttributeExt specs for details.
|
96
|
+
#
|
97
|
+
# hidden_attribute_names :format, :options => :hash
|
98
|
+
#
|
59
99
|
def hidden_attribute_names(format, options = {})
|
60
100
|
if options[:except].is_a?(Array)
|
61
101
|
names = options[:except]
|
@@ -1,10 +1,9 @@
|
|
1
1
|
|
2
2
|
module AttributeExt
|
3
|
-
class Railtie < Rails::Railtie
|
3
|
+
class Railtie < Rails::Railtie # :nodoc:
|
4
4
|
initializer 'attribute_ext' do |app|
|
5
5
|
ActiveSupport.on_load :active_record do
|
6
|
-
|
7
|
-
ActiveRecord::Base.send :include, AttributeExt::SafeAttributes
|
6
|
+
AttributeExt.setup
|
8
7
|
end
|
9
8
|
end
|
10
9
|
end
|
@@ -1,11 +1,81 @@
|
|
1
1
|
module AttributeExt
|
2
2
|
module SafeAttributes
|
3
|
-
|
3
|
+
# Returns default role used by SafeAttributes.
|
4
|
+
# See SafeAttributes#default_role= for how to specify a default role.
|
5
|
+
def SafeAttributes.default_role
|
6
|
+
@default_role || :default
|
7
|
+
end
|
8
|
+
|
9
|
+
# Sets SafeAttributes default role that will be used when given role
|
10
|
+
# is a nil value or the :default role. The SafeAttributes default role will
|
11
|
+
# only affect this extension and will not be given to Rails 3.1 mass
|
12
|
+
# assignment authorizer.
|
13
|
+
def SafeAttributes.default_role=(role)
|
14
|
+
@default_role = role
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns current role mapper block or sets role mapper if an block is
|
18
|
+
# given. By default no role mapper is active.
|
19
|
+
#
|
20
|
+
# AttributeExt::SafeAttributes.role_mapper do |role|
|
21
|
+
# [:guest, :user, :admin].include?(role) ? role : :guest
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
def SafeAttributes.role_mapper(&block)
|
25
|
+
self.role_mapper = block if block
|
26
|
+
@role_mapper
|
27
|
+
end
|
28
|
+
|
29
|
+
# Sets current role mapper to given Proc or removes role mapper if
|
30
|
+
# a nil value is given. Any other value will do nothing.
|
31
|
+
#
|
32
|
+
# AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
|
33
|
+
# [:guest, :user, :admin].include?(role) ? role : :guest
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# See SafeAttributes#role_mapper for an short way to set a role mapper.
|
37
|
+
def SafeAttributes.role_mapper=(role_mapper)
|
38
|
+
@role_mapper = role_mapper if role_mapper.is_a?(Proc)
|
39
|
+
@role_mapper = nil if role_mapper.nil?
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.included(base) # :nodoc:
|
4
43
|
base.extend(ClassMethods)
|
5
44
|
base.alias_method_chain :mass_assignment_authorizer, :safe_attrs
|
6
45
|
end
|
7
46
|
|
47
|
+
|
8
48
|
module ClassMethods
|
49
|
+
# Adds a whitelist rule that allows mass assignment for given attributes
|
50
|
+
# based on given optional conditions.
|
51
|
+
#
|
52
|
+
# class User < ActiveRecord::Base
|
53
|
+
# # always mass assignable
|
54
|
+
# safe_attributes :name, :email
|
55
|
+
# # only when new record
|
56
|
+
# safe_attributes :login, :if => Proc.new { |user| user.new_record? }
|
57
|
+
# # only own password or as admin
|
58
|
+
# safe_attributes :password, :if => Proc.new { |user,role| user == role }
|
59
|
+
# safe_attributes :password, :as => :admin
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# All given conditions for one rule must be true to allow mass
|
63
|
+
# assignment for given attributes. Attributes can be added in more than
|
64
|
+
# one rule to allow alternatives (like password above).
|
65
|
+
#
|
66
|
+
# Available Options:
|
67
|
+
# [:+as+]
|
68
|
+
# Attributes will be assignable if mass assignment role is equal (==) given object.
|
69
|
+
#
|
70
|
+
# [:+if+]
|
71
|
+
# Makes attributes assignable if given Proc block returns true.
|
72
|
+
#
|
73
|
+
# [:+unless+]
|
74
|
+
# Attributes cannot be mass assigned if Proc block evaluates to true.
|
75
|
+
#
|
76
|
+
# The :if and :unless options must be Proc block that will be executed each time the
|
77
|
+
# mass assignment authorizer is called and they are called with current
|
78
|
+
# model and role as parameters.
|
9
79
|
def safe_attributes(*attrs)
|
10
80
|
@safe_attributes ||= []
|
11
81
|
if attrs.empty?
|
@@ -26,12 +96,34 @@ module AttributeExt
|
|
26
96
|
end
|
27
97
|
end
|
28
98
|
|
29
|
-
def mass_assignment_authorizer_with_safe_attrs(role = nil)
|
30
|
-
|
31
|
-
|
99
|
+
def mass_assignment_authorizer_with_safe_attrs(role = nil) # :nodoc:
|
100
|
+
if role.nil?
|
101
|
+
attrs = mass_assignment_authorizer_without_safe_attrs +
|
102
|
+
safe_attribute_names
|
103
|
+
else
|
104
|
+
attrs = mass_assignment_authorizer_without_safe_attrs(role) +
|
105
|
+
safe_attribute_names(role)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns new mapped role for given role used by SafeAttributes.
|
110
|
+
# This method should only be used to test own role mapper implementations without need for a
|
111
|
+
# full application. See AttributeExt specs for details.
|
112
|
+
#
|
113
|
+
# See +role_mapper+ method in SafeAttributes module for how to set a role mapper.
|
114
|
+
def safe_attributes_role(role = nil)
|
115
|
+
return AttributeExt::SafeAttributes.role_mapper.call(role) unless AttributeExt::SafeAttributes.role_mapper.nil?
|
116
|
+
return AttributeExt::SafeAttributes.default_role if role.nil? or role == :default
|
117
|
+
role
|
32
118
|
end
|
33
119
|
|
34
|
-
|
120
|
+
# Returns an array with attributes allowed to be mass assigned by given role. Role will be
|
121
|
+
# mapped before given to rules.
|
122
|
+
# This method should only be used to test own rules without need to create lots of records
|
123
|
+
# to test different situations. See AttributeExt specs for details.
|
124
|
+
def safe_attribute_names(role = nil)
|
125
|
+
role = safe_attributes_role(role)
|
126
|
+
|
35
127
|
names = []
|
36
128
|
self.class.safe_attributes.collect do |attrs, options|
|
37
129
|
next unless options[:as].empty? or options[:as].include?(role)
|
@@ -62,4 +62,61 @@ describe AttributeExt::HiddenAttributes do
|
|
62
62
|
user = User.new
|
63
63
|
user.mass_assignment_authorizer(:admin).should_not include('attribute_unless_admin')
|
64
64
|
end
|
65
|
+
|
66
|
+
it 'can provide a global default role' do
|
67
|
+
AttributeExt::SafeAttributes.default_role = :new_default
|
68
|
+
AttributeExt::SafeAttributes.default_role.should == :new_default
|
69
|
+
User.new.mass_assignment_authorizer.should include("new_default")
|
70
|
+
end
|
71
|
+
|
72
|
+
context '#role_mapper' do
|
73
|
+
it 'is nil by default' do
|
74
|
+
AttributeExt::SafeAttributes.role_mapper.should be_nil
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'accept Procs' do
|
78
|
+
proc = Proc.new { |role| role }
|
79
|
+
AttributeExt::SafeAttributes.role_mapper = proc
|
80
|
+
AttributeExt::SafeAttributes.role_mapper.should equal(proc)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'accept nil' do
|
84
|
+
AttributeExt::SafeAttributes.role_mapper = nil
|
85
|
+
AttributeExt::SafeAttributes.role_mapper.should be_nil
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'accept blocks' do
|
89
|
+
AttributeExt::SafeAttributes.role_mapper { |role| role }
|
90
|
+
AttributeExt::SafeAttributes.role_mapper.should be_an Proc
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'maps role according to given Proc' do
|
94
|
+
AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
|
95
|
+
[:guest, :user, :admin].include?(role) ? role : :guest
|
96
|
+
end
|
97
|
+
|
98
|
+
User.new.safe_attributes_role.should == :guest
|
99
|
+
User.new.safe_attributes_role(:user).should == :user
|
100
|
+
User.new.safe_attributes_role(:admin).should == :admin
|
101
|
+
User.new.safe_attributes_role(:heinz).should == :guest
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'gives role to rules' do
|
105
|
+
AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
|
106
|
+
[:guest, :user, :admin].include?(role) ? role : :guest
|
107
|
+
end
|
108
|
+
|
109
|
+
User.new.mass_assignment_authorizer.should include('role_mapper_guest')
|
110
|
+
User.new.mass_assignment_authorizer.should_not include('role_mapper_user')
|
111
|
+
User.new.mass_assignment_authorizer.should_not include('role_mapper_admin')
|
112
|
+
|
113
|
+
User.new.mass_assignment_authorizer(:user).should_not include('role_mapper_guest')
|
114
|
+
User.new.mass_assignment_authorizer(:user).should include('role_mapper_user')
|
115
|
+
User.new.mass_assignment_authorizer(:user).should_not include('role_mapper_admin')
|
116
|
+
|
117
|
+
User.new.mass_assignment_authorizer(:admin).should_not include('role_mapper_guest')
|
118
|
+
User.new.mass_assignment_authorizer(:admin).should_not include('role_mapper_user')
|
119
|
+
User.new.mass_assignment_authorizer(:admin).should include('role_mapper_admin')
|
120
|
+
end
|
121
|
+
end
|
65
122
|
end
|
data/spec/support/stub.rb
CHANGED
@@ -54,6 +54,10 @@ class User < ActiveRecord::Base
|
|
54
54
|
:if => Proc.new { |user,role| role == :admin}
|
55
55
|
safe_attributes :attribute_unless_admin,
|
56
56
|
:unless => Proc.new { |user,role| role == :admin}
|
57
|
+
safe_attributes :new_default, :as => :new_default
|
58
|
+
safe_attributes :role_mapper_guest, :if => Proc.new { |user,role| role == :guest }
|
59
|
+
safe_attributes :role_mapper_user, :as => :user
|
60
|
+
safe_attributes :role_mapper_admin, :if => Proc.new { |user,role| role == :admin }
|
57
61
|
|
58
62
|
def initialize(opts = {})
|
59
63
|
@opts = {
|
@@ -61,4 +65,4 @@ class User < ActiveRecord::Base
|
|
61
65
|
:unless => true
|
62
66
|
}.merge(opts.is_a?(Hash) ? opts : {})
|
63
67
|
end
|
64
|
-
end
|
68
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attribute_ext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 2
|
9
|
+
- 4
|
10
|
+
version: 1.2.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jan Graichen
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-09-
|
18
|
+
date: 2011-09-24 00:00:00 Z
|
19
19
|
dependencies: []
|
20
20
|
|
21
21
|
description: AttributeExt provides additional access control for rails model attributes.
|