grant 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Grant is a Ruby gem and Rails plugin that forces you to make explicit security decisions about the operations performed on your ActiveRecord models. It provides a declarative way to specify rules granting permission to perform CRUD operations on ActiveRecord objects.
4
4
 
5
- Grant does not allow you to specify which operations are restricted. Instead, it restricts all CRUD operations unless they're explicitly granted to the user. It also restricts adding or removing items to/from has_many and has_and_belongs_to_many associations. Only allowing operations explicitly granted forces you to make conscious security decisions. Grant will not help you make those decisions, but it won't let you forget to.
5
+ Grant does not allow you to specify which operations are restricted. Instead, it restricts all CRUD operations unless they're explicitly granted to the user. Only allowing operations explicitly granted forces you to make conscious security decisions. Grant will not help you make those decisions, but it won't let you forget to.
6
6
 
7
7
  Additional information beyond that found in this README is available on the wiki[https://github.com/nearinfinity/grant/wiki].
8
8
 
@@ -40,7 +40,7 @@ Grant needs to know who the current user is, but with no standard for doing so y
40
40
 
41
41
  = Usage
42
42
 
43
- To enable model security you simply include the Grant::ModelSecurity module in your model class. In the example below you see three grant statements. The first grants find (aka read) permission all the time. The second example grants create, update, and destroy permission when the passed block evaluates to true, which in this case happens when the model is editable by the current user. Similarly, the third grant statement permits additions and removals from the tags association when it's block evaluates to true. A Grant::Error is raised if any grant block evaluates to false or nil.
43
+ To enable model security you simply include the Grant::ModelSecurity module in your model class. In the example below you see two grant statements. The first grants find (aka read) permission all the time. The second example grants create, update, and destroy permission when the passed block evaluates to true, which in this case happens when the model is editable by the current user. A Grant::Error is raised if any grant block evaluates to false or nil.
44
44
 
45
45
  class Book < ActiveRecord::Base
46
46
  include Grant::ModelSecurity
@@ -48,14 +48,13 @@ To enable model security you simply include the Grant::ModelSecurity module in y
48
48
  has_many :tags
49
49
  grant(:find) { true }
50
50
  grant(:create, :update, :destroy) { |user, model| model.editable_by_user? user }
51
- grant(:add => :tags, :remove => :tags) { |user, model, associated_model| model.editable_by_user? user }
52
51
 
53
52
  def editable_by_user? user
54
53
  user.administrator? || user.has_role?(:editor)
55
54
  end
56
55
  end
57
56
 
58
- The valid actions to pass to a grant statement are :find, :create, :update, :destroy, :add, and :remove. The first four options are passed as symbols while :add and :remove are hash keys to association names they protect. Any number of options can be passed to a single grant statement, which is very useful if each of the actions share the same logic for determining access.
57
+ The valid actions to pass to a grant statement are :find, :create, :update, and :destroy. Each action can be passed as a Symbol or String. Any number of actions can be passed to a single grant statement, which is very useful if each of the actions share the same logic for determining access.
59
58
 
60
59
  = Integration
61
60
 
@@ -2,24 +2,20 @@ module Grant
2
2
  class ConfigParser
3
3
 
4
4
  def self.extract_config(args)
5
- hash = (args.pop if args.last.is_a?(::Hash)) || {}
6
- normalize_config args, hash
7
- validate_config args, hash
8
-
9
- [args, hash]
5
+ normalize_config args
6
+ validate_config args
7
+ args
10
8
  end
11
9
 
12
10
  private
13
11
 
14
- def self.normalize_config(actions, associations)
12
+ def self.normalize_config(actions)
15
13
  actions.each_with_index { |item, index| actions[index] = item.to_sym unless item.kind_of? Symbol }
16
- associations.each_pair { |k, v| associations[k.to_sym] = associations.delete(k) unless k.kind_of? Symbol }
17
14
  end
18
15
 
19
- def self.validate_config(actions, associations)
20
- raise Grant::Error.new("at least one :create, :find, :update, or :destroy action must be specified") if actions.empty? && associations.empty?
16
+ def self.validate_config(actions)
17
+ raise Grant::Error.new("at least one :create, :find, :update, or :destroy action must be specified") if actions.empty?
21
18
  raise Grant::Error.new(":create, :find, :update, and :destroy are the only valid actions") unless actions.all? { |a| [:create, :find, :update, :destroy].include? a }
22
- raise Grant::Error.new(":add and :remove are the only valid association specifications") unless associations.keys.all? { |k| [:add, :remove].include? k }
23
19
  end
24
20
 
25
21
  end
@@ -13,7 +13,7 @@ module Grant
13
13
  grant_raise_error(grant_current_user, '#{action}', self) unless grant_disabled?
14
14
  end
15
15
  RUBY
16
- base.send callback.to_sym, "grant_#{callback}".to_sym
16
+ base.send(callback.to_sym, "grant_#{callback}".to_sym)
17
17
  end
18
18
 
19
19
  base.extend ClassMethods
@@ -30,28 +30,17 @@ module Grant
30
30
  Grant::ThreadStatus.disabled? || @grant_disabled
31
31
  end
32
32
 
33
- def grant_raise_error(user, action, model, association_id=nil, associated_model=nil)
33
+ def grant_raise_error(user, action, model, association_id=nil)
34
34
  msg = ["#{action} permission",
35
35
  "not granted to #{user.class.name}:#{user.id}",
36
36
  "for resource #{model.class.name}:#{model.id}"]
37
- msg.insert(1, "to #{association_id}:#{associated_model.class.name} association") if association_id && associated_model
38
37
 
39
38
  raise Grant::Error.new(msg.join(' '))
40
39
  end
41
40
 
42
41
  module ClassMethods
43
42
  def grant(*args, &blk)
44
- actions, associations = Grant::ConfigParser.extract_config(args)
45
-
46
- associations.each_pair do |action, association_ids|
47
- Array(association_ids).each do |association_id|
48
- grant_callback = "grant_#{action}_#{association_id}".to_sym
49
- define_method(grant_callback) do |associated_model|
50
- grant_raise_error(grant_current_user, action, self, association_id, associated_model) unless grant_disabled? || blk.call(grant_current_user, self, associated_model)
51
- end
52
- end
53
- end
54
-
43
+ actions = Grant::ConfigParser.extract_config(args)
55
44
  actions.each do |action|
56
45
  grant_callback = (action.to_sym == :find ? "grant_after_find" : "grant_before_#{action}").to_sym
57
46
  define_method(grant_callback) do
@@ -59,43 +48,8 @@ module Grant
59
48
  end
60
49
  end
61
50
  end
62
-
63
- def has_and_belongs_to_many(association_id, options={}, &extension)
64
- add_grant_association_callback(:add, association_id, options)
65
- add_grant_association_callback(:remove, association_id, options)
66
- super
67
- end
68
-
69
- def has_many(association_id, options={}, &extension)
70
- add_grant_association_callback(:add, association_id, options)
71
- add_grant_association_callback(:remove, association_id, options)
72
- super
73
- end
74
-
75
- private
76
-
77
- def add_grant_association_callback(action, association_id, options)
78
- callback_name = "before_#{action}".to_sym
79
- callback = "grant_#{action}_#{association_id}".to_sym
80
- unless self.instance_methods.include? callback.to_s
81
- class_eval <<-RUBY
82
- def #{callback}(associated_model)
83
- grant_raise_error(grant_current_user, '#{action}', self, '#{association_id}', associated_model) unless grant_disabled?
84
- end
85
- RUBY
86
- end
87
-
88
- if options.has_key? callback_name
89
- if options[callback_name].kind_of? Array
90
- options[callback_name].insert(0, callback)
91
- else
92
- options[callback_name] = [callback, options[callback_name]]
93
- end
94
- else
95
- options[callback_name] = callback
96
- end
97
- end
98
-
99
51
  end
52
+
100
53
  end
54
+
101
55
  end
@@ -1,3 +1,3 @@
1
1
  module Grant
2
- VERSION = "1.0.1"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -4,56 +4,29 @@ require 'grant'
4
4
  describe Grant::ConfigParser do
5
5
 
6
6
  describe 'Configuration' do
7
- it "should parse actions and associations from a config array" do
8
- config = Grant::ConfigParser.extract_config([:create, 'update', {:add => [:people, :places], 'remove' => :people}])
7
+ it "should parse actions from a config array" do
8
+ config = Grant::ConfigParser.extract_config([:create, 'update'])
9
9
  config.should_not be_nil
10
- config.should have(2).items
11
- config[0].should =~ [:create, :update]
12
- config[1].should == {:add => [:people, :places], :remove => :people}
10
+ config.should =~ [:create, :update]
13
11
  end
14
12
 
15
- it "should parse actions from a config array when associations are absent" do
16
- config = Grant::ConfigParser.extract_config([:create, :update])
17
- config.should_not be_nil
18
- config.should have(2).items
19
- config[0].should =~ [:create, :update]
20
- config[1].should == {}
21
- end
22
-
23
- it "should parse actions and associations from a config array when options are absent" do
24
- config = Grant::ConfigParser.extract_config([:create, 'update', {:add => ['people', :places]}])
25
- config.should_not be_nil
26
- config.should have(2).items
27
- config[0].should =~ [:create, :update]
28
- config[1].should == {:add => ['people', :places]}
29
- end
30
-
31
13
  it "should parse actions" do
32
14
  config = Grant::ConfigParser.extract_config([:create])
33
15
  config.should_not be_nil
34
- config.should have(2).items
35
- config[0].should =~ [:create]
36
- config[1].should == {}
16
+ config.should =~ [:create]
37
17
  end
38
-
39
18
  end
40
19
 
41
20
  describe 'Configuration Validation' do
42
- it "should raise a Grant::Error if no action or association is specified" do
21
+ it "should raise a Grant::Error if no action is specified" do
43
22
  lambda {
44
- Grant::ConfigParser.instance_eval { validate_config([], {}) }
23
+ Grant::ConfigParser.instance_eval { validate_config([]) }
45
24
  }.should raise_error(Grant::Error)
46
25
  end
47
26
 
48
27
  it "should raise a Grant::Error if an invalid action is specified" do
49
28
  lambda {
50
- Grant::ConfigParser.instance_eval { validate_config([:create, :udate], {:add => [:people, :places]}) }
51
- }.should raise_error(Grant::Error)
52
- end
53
-
54
- it "should raise a Grant::Error if an invalid association is specified" do
55
- lambda {
56
- Grant::ConfigParser.instance_eval { validate_config([:destroy], {:add => :people, :update => :places}) }
29
+ Grant::ConfigParser.instance_eval { validate_config([:create, :udate]) }
57
30
  }.should raise_error(Grant::Error)
58
31
  end
59
32
  end
@@ -13,15 +13,6 @@ describe Grant::ModelSecurity do
13
13
  it 'should establish failing ActiveRecord callbacks for before_create, before_update, before_destroy, and after_find when included' do
14
14
  verify_standard_callbacks(new_model_class.new)
15
15
  end
16
-
17
- it 'should establish failing ActiveRecord callbacks for any has_many or has_and_belongs_to_many associations' do
18
- c = new_model_class
19
- c.instance_eval do
20
- has_many :users
21
- has_and_belongs_to_many :groups
22
- end
23
- verify_association_callbacks(c.new)
24
- end
25
16
  end
26
17
 
27
18
  describe '#grant' do
@@ -55,59 +46,12 @@ describe Grant::ModelSecurity do
55
46
  c = new_model_class.instance_eval { grant(:create, :update, :destroy, :find) { true }; self }
56
47
  verify_standard_callbacks(c.new, :create, :update, :destroy, :find)
57
48
  end
58
-
59
- it 'should allow adding to an association to succeed when granted' do
60
- c = new_model_class
61
- c.instance_eval do
62
- has_many :users
63
- has_and_belongs_to_many :groups
64
- grant(:add => [:users, :groups]) { true }
65
- end
66
- verify_association_callbacks(c.new, :add_users, :add_groups)
67
- end
68
-
69
- it 'should allow adding to an association to succeed when granted above association declarations' do
70
- c = new_model_class
71
- c.instance_eval do
72
- grant(:add => [:users, :groups]) { true }
73
- has_many :users
74
- has_and_belongs_to_many :groups, :before_add => [:bogus1, :bogus2]
75
- define_method(:bogus1) {}
76
- define_method(:bogus2) {}
77
- end
78
- verify_association_callbacks(c.new, :add_users, :add_groups)
79
- end
80
-
81
- it 'should allow removing from an association to succeed when granted' do
82
- c = new_model_class
83
- c.instance_eval do
84
- has_many :users
85
- has_and_belongs_to_many :groups, :before_remove => :bogus1
86
- grant(:remove => [:users, :groups]) { true }
87
- define_method(:bogus1) {}
88
- end
89
- verify_association_callbacks(c.new, :remove_users, :remove_groups)
90
- end
91
-
92
- it 'should allow removing from an association to succeed when granted above association declarations' do
93
- c = new_model_class
94
- c.instance_eval do
95
- grant(:remove => [:users, :groups]) { true }
96
- has_many :users
97
- has_and_belongs_to_many :groups
98
- end
99
- verify_association_callbacks(c.new, :remove_users, :remove_groups)
100
- end
101
49
  end
102
-
50
+
103
51
  def verify_standard_callbacks(instance, *succeeding_callbacks)
104
52
  verify_callbacks([:create, :update, :destroy, :find], instance, nil, succeeding_callbacks)
105
53
  end
106
54
 
107
- def verify_association_callbacks(instance, *succeeding_callbacks)
108
- verify_callbacks([:add_users, :remove_users, :add_groups, :remove_groups], instance, new_model_class.new, succeeding_callbacks)
109
- end
110
-
111
55
  def verify_callbacks(all_actions, instance, associated_model, succeeding_callbacks)
112
56
  all_actions.each do |action|
113
57
  expectation = succeeding_callbacks.include?(action) ? :should_not : :should
@@ -139,24 +83,6 @@ describe Grant::ModelSecurity do
139
83
  def self.after_find(method)
140
84
  define_method(:find) { send method }
141
85
  end
142
-
143
- def self.has_many(association_id, options, &extension)
144
- define_method("add_#{association_id}".to_sym) do |associated_model|
145
- Array(options[:before_add]).each { |callback| send(callback, associated_model) }
146
- end
147
- define_method("remove_#{association_id}".to_sym) do |associated_model|
148
- Array(options[:before_remove]).each { |callback| send(callback, associated_model) }
149
- end
150
- end
151
-
152
- def self.has_and_belongs_to_many(association_id, options, &extension)
153
- define_method("add_#{association_id}".to_sym) do |associated_model|
154
- Array(options[:before_add]).each { |callback| send(callback, associated_model) }
155
- end
156
- define_method("remove_#{association_id}".to_sym) do |associated_model|
157
- Array(options[:before_remove]).each { |callback| send(callback, associated_model) }
158
- end
159
- end
160
86
  end
161
87
 
162
88
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grant
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
- - 1
7
+ - 2
8
8
  - 0
9
- - 1
10
- version: 1.0.1
9
+ - 0
10
+ version: 2.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jeff Kunkle
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-12-29 00:00:00 -05:00
19
+ date: 2011-01-05 00:00:00 -05:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency