grant 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,11 @@
1
+ ## 2.1.0 (July 5, 2011)
2
+
3
+ Features:
4
+
5
+ - Pass action to grant decision block. [#5](https://github.com/nearinfinity/grant/issues/5)
6
+ - Allow subclasses to redefine grant statements. [#4](https://github.com/nearinfinity/grant/issues/4)
7
+ - Allow checking of grant status without raising an error. [#3](https://github.com/nearinfinity/grant/issues/3)
8
+
1
9
  ## 2.0.0 (January 5, 2011)
2
10
 
3
11
  Features:
@@ -8,4 +16,4 @@ Features:
8
16
 
9
17
  Changes:
10
18
 
11
- - Changed Grant from strictly a Rails plugin to a Ruby gem. No functionality changes or bug fixes.
19
+ - Changed Grant from strictly a Rails plugin to a Ruby gem. No functionality changes or bug fixes.
@@ -11,17 +11,17 @@ Additional information beyond that found in this README is available on the wiki
11
11
  To install the Grant gem, simply run
12
12
 
13
13
  gem install grant
14
-
14
+
15
15
  To use it with a Rails 3 project or other project using Bundler, add the following line to your Gemfile
16
16
 
17
17
  gem 'grant'
18
-
18
+
19
19
  For your Rails 2.x project, add the following to your environment.rb file
20
20
 
21
21
  config.gem 'grant'
22
22
 
23
23
  Lastly, Grant can also be installed as a Rails plugin
24
-
24
+
25
25
  script/plugin install git://github.com/nearinfinity/grant.git
26
26
 
27
27
  = Setup
@@ -52,7 +52,7 @@ in a class. In the example below you see two grant statements. The first grants
52
52
 
53
53
  class Book < ActiveRecord::Base
54
54
  grant(:find) { true }
55
- grant(:create, :update, :destroy) { |user, model| model.editable_by_user? user }
55
+ grant(:create, :update, :destroy) { |user, model, action| model.editable_by_user? user }
56
56
 
57
57
  def editable_by_user? user
58
58
  user.administrator? || user.has_role?(:editor)
@@ -61,6 +61,37 @@ in a class. In the example below you see two grant statements. The first grants
61
61
 
62
62
  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.
63
63
 
64
+ If you'd like to find out if a certain action is granted without attempting
65
+ the action and having Grant potentially raise an error, you can use the
66
+ `granted?` method.
67
+
68
+ book = Book.first
69
+ book.granted?(:destroy, some_user)
70
+
71
+ If you don't pass a user as the second argument to `granted?`, the
72
+ `Grant::User.current_user` will be assumed.
73
+
74
+ = Inheritance
75
+
76
+ Subclasses inherit all the `grant` statements defined on their parent, but
77
+ are free to override any of them individually simply by redefining them.
78
+ For example:
79
+
80
+ class Book < ActiveRecord::Base
81
+ self.abstract_class = true
82
+ grant(:create, :find, :destroy) { true }
83
+ grant(:update) { false }
84
+ end
85
+
86
+ class Ebook < ActiveRecord::Base
87
+ grant(:update) { true }
88
+ grant(:destroy) { false }
89
+ end
90
+
91
+ Instances of the parent class `Book` can always be created, found, and destroyed,
92
+ but never updated. However, `Ebook` instances can be created, found, and updated,
93
+ but never destroyed.
94
+
64
95
  = Integration
65
96
 
66
97
  There may be some instances where you need to perform an action on your
@@ -12,11 +12,13 @@ Gem::Specification.new do |s|
12
12
  s.description = "Grant is a Ruby gem and Rails plugin that forces you to make explicit security decisions about the operations performed on your ActiveRecord models."
13
13
  s.license = "MIT"
14
14
 
15
- s.files = `git ls-files`.split("\n")
15
+ s.files = `git ls-files`.split("\n").reject { |path| path =~ /^(Gemfile|.gitignore|Rakefile)/ }
16
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
18
  s.require_paths = ["lib"]
19
19
 
20
+ s.add_dependency('activerecord', '> 3.0.0')
21
+
20
22
  s.add_development_dependency('rspec', '2.5.0')
21
23
  s.add_development_dependency('sqlite3-ruby', '1.3.3')
22
24
  s.add_development_dependency('activerecord', '> 3.0.0')
@@ -6,36 +6,39 @@ module Grant
6
6
  module Grantable
7
7
 
8
8
  def self.included(base)
9
- base.extend(ClassMethods)
9
+ base.send :extend, ClassMethods
10
10
  end
11
11
 
12
12
  module ClassMethods
13
13
  def grant(*args, &blk)
14
- include Grant::Status unless self.included_modules.include?(Grant::Status)
15
- initialize_grant unless grant_initialized?
14
+ unless self.included_modules.include?(InstanceMethods)
15
+ include InstanceMethods
16
+ include Grant::Status
16
17
 
17
- config = Grant::Config.new(*args)
18
- config.actions.each do |action|
19
- @grant_callbacks[action.to_sym].callback = blk
18
+ [:find, :create, :update, :destroy].each do |action|
19
+ send :class_attribute, "grantor_#{action}".to_sym
20
+ send "grantor_#{action}=".to_sym, Grant::Grantor.new(action) { false }
21
+ send "#{action == :find ? 'after' : 'before'}_#{action}".to_sym, "grant_#{action}".to_sym
22
+ end
20
23
  end
21
- end
22
24
 
23
- def initialize_grant
24
- @grant_callbacks ||= {}
25
- Grant::Config.valid_actions.each do |action|
26
- grantor = Grant::Grantor.new(action)
27
- @grant_callbacks[action] = grantor
28
- send "#{action == :find ? 'after' : 'before'}_#{action}", grantor
25
+ Grant::Config.new(*args).actions.each do |action|
26
+ send "grantor_#{action}=".to_sym, Grant::Grantor.new(action, &blk)
29
27
  end
30
- @grant_initialized = true
31
28
  end
29
+ end
30
+
31
+ module InstanceMethods
32
+ def grant_find; grantor_find.authorize!(self); end
33
+ def grant_create; grantor_create.authorize!(self); end
34
+ def grant_update; grantor_update.authorize!(self); end
35
+ def grant_destroy; grantor_destroy.authorize!(self); end
32
36
 
33
- def grant_initialized?
34
- @grant_initialized == true
37
+ def granted?(action, user=Grant::User.current_user)
38
+ grantor = send("grantor_#{action}")
39
+ grantor.authorized?(self, user)
35
40
  end
36
41
  end
37
42
 
38
- # ActiveRecord won't call the after_find handler unless it see's a specific after_find method defined
39
- def after_find; end unless method_defined?(:after_find)
40
43
  end
41
44
  end
@@ -5,12 +5,22 @@ module Grant
5
5
  class Grantor
6
6
  include Status
7
7
 
8
- attr_writer :callback
8
+ def initialize(action, &callback)
9
+ @action = action
10
+ @callback = callback
11
+ end
12
+
13
+ def authorize!(model, user=Grant::User.current_user)
14
+ unless grant_disabled?
15
+ without_grant do
16
+ raise Grant::Error.new(user, @action, model) unless @callback.call(user, model, @action)
17
+ end
18
+ end
19
+ end
9
20
 
10
- def initialize(action)
11
- self.class.send(:define_method, "#{action == :find ? 'after' : 'before'}_#{action}") do |model|
12
- user = Grant::User.current_user
13
- raise Grant::Error.new(user, action, model) unless grant_disabled? || (@callback != nil && @callback.call(user, model))
21
+ def authorized?(model, user=Grant::User.current_user)
22
+ without_grant do
23
+ @callback.call(user, model, @action)
14
24
  end
15
25
  end
16
26
  end
@@ -1,7 +1,5 @@
1
1
  # TODO: Remove this file when backwards compatibility with grant 2.0.0
2
2
  # is no longer necessary
3
3
  module Grant
4
- module Integration
5
- include Status
6
- end
4
+ Integration = Grant::Status
7
5
  end
@@ -3,7 +3,5 @@
3
3
  require 'grant/grantable'
4
4
 
5
5
  module Grant
6
- module ModelSecurity
7
- include Grant::Grantable unless self.included_modules.include?(Grant::Grantable)
8
- end
6
+ ModelSecurity = Grant::Grantable
9
7
  end
@@ -56,5 +56,6 @@ module Grant
56
56
  result
57
57
  end
58
58
 
59
+ module_function :grant_enabled?, :grant_disabled?, :disable_grant, :enable_grant, :without_grant, :with_grant, :do_as
59
60
  end
60
61
  end
@@ -1,3 +1,3 @@
1
1
  module Grant
2
- VERSION = "2.0.2"
2
+ VERSION = "2.1.0"
3
3
  end
@@ -22,22 +22,15 @@ describe Grant::Grantable do
22
22
  Model.included_modules.should include(Grant::Status)
23
23
  end
24
24
 
25
- it 'should setup failing Grant::Grantor objects for create, find, update, and destroy callbacks when initialized' do
25
+ it 'should setup failing callbacks when initialized' do
26
26
  m = Model.create
27
- Model.initialize_grant
27
+ Model.instance_eval { grant(:create) { false } }
28
28
  lambda { Model.create }.should raise_error(Grant::Error)
29
29
  lambda { Model.find(m.id) }.should raise_error(Grant::Error)
30
30
  lambda { m.update_attributes(:name => 'new') }.should raise_error(Grant::Error)
31
31
  lambda { m.destroy }.should raise_error(Grant::Error)
32
32
  end
33
33
 
34
- it 'should indicate whether Grant has been initialized' do
35
- redefine_model
36
- Model.should_not be_grant_initialized
37
- Model.initialize_grant
38
- Model.should be_grant_initialized
39
- end
40
-
41
34
  it 'should associate callbacks with active record create, find, update, and destroy callbacks' do
42
35
  redefine_model do
43
36
  grant(:create) { true }
@@ -75,11 +68,12 @@ describe Grant::Grantable do
75
68
  lambda { Model.create }.should raise_error(Grant::Error)
76
69
  end
77
70
 
78
- it 'should provide callbacks with the user and model being protected' do
71
+ it 'should provide callbacks with the user, model, and action being protected' do
79
72
  redefine_model do
80
- grant(:create) do |user, model|
73
+ grant(:create) do |user, model, action|
81
74
  user.should == Grant::User.current_user
82
75
  model.should_not == nil
76
+ action.should == :create
83
77
  true
84
78
  end
85
79
  end
@@ -87,6 +81,72 @@ describe Grant::Grantable do
87
81
  Model.create
88
82
  end
89
83
 
84
+ it 'should allow subclasses to independenty redefine grant statements' do
85
+ redefine_model do
86
+ grant(:create) { true }
87
+ grant(:find) { true }
88
+ grant(:update) { false }
89
+ grant(:destroy) { true }
90
+ end
91
+
92
+ class SubModel < Model
93
+ set_table_name 'models'
94
+ grant(:update) { true }
95
+ grant(:destroy) { false }
96
+ end
97
+
98
+ m = SubModel.create
99
+ m = SubModel.find(m.id)
100
+ m.update_attributes(:name => 'new')
101
+ lambda { m.destroy }.should raise_error(Grant::Error)
102
+
103
+ m = Model.create
104
+ m = Model.find(m.id)
105
+ lambda { m.update_attributes(:name => 'new') }.should raise_error(Grant::Error)
106
+ m.destroy
107
+ end
108
+
109
+ describe '#granted?' do
110
+ it 'should allow invoking the grant callbacks to determine whether an action has been granted' do
111
+ redefine_model do
112
+ grant(:create) { true }
113
+ grant(:find) { true }
114
+ grant(:update) { false }
115
+ grant(:destroy) { false }
116
+ end
117
+
118
+ model = Model.new
119
+ model.granted?(:create).should be_true
120
+ model.granted?(:find).should be_true
121
+ model.granted?(:update).should be_false
122
+ model.granted?(:destroy).should be_false
123
+ end
124
+
125
+ it 'should allow a non-current user to be specified' do
126
+ user = User.create
127
+
128
+ redefine_model do
129
+ grant(:create) do |u, m, a|
130
+ u.should == user
131
+ Grant::User.current_user.should_not == user
132
+ end
133
+ end
134
+
135
+ model = Model.new
136
+ model.granted?(:create, user)
137
+ end
138
+
139
+ it 'should not raise an error if the action has not been granted' do
140
+ redefine_model do
141
+ grant(:create) { false }
142
+ end
143
+
144
+ model = Model.new
145
+ lambda { model.granted?(:create) }.should_not raise_error
146
+ model.granted?(:create).should be_false
147
+ end
148
+ end
149
+
90
150
  def redefine_model(&blk)
91
151
  clazz = Class.new(ActiveRecord::Base, &blk)
92
152
  Object.send :remove_const, 'Model'
@@ -4,18 +4,55 @@ require 'grant/grantor'
4
4
 
5
5
  describe Grant::Grantor do
6
6
 
7
- describe '#initialize' do
8
- it 'should define a before_create callback method when passed create as an argument' do
9
- Grant::Grantor.new(:create).should respond_to(:before_create)
7
+ describe '#authorize!' do
8
+ it 'should pass the user, model, and action to the supplied callback block' do
9
+ model = Object.new
10
+ user = Object.new
11
+ Grant::User.current_user = user
12
+
13
+ grantor = Grant::Grantor.new(:update) do |u, m, a|
14
+ u.should == user
15
+ m.should == model
16
+ a.should == :update
17
+ true
18
+ end
19
+
20
+ grantor.authorize!(model)
10
21
  end
11
- it 'should define an after_find callback method when passed find as an argument' do
12
- Grant::Grantor.new(:find).should respond_to(:after_find)
22
+
23
+ it 'should turn off grant when calling the supplied callback block' do
24
+ grantor = Grant::Grantor.new(:destroy) do |user, model, action|
25
+ Grant::Status.grant_disabled?.should be_true
26
+ true
27
+ end
28
+
29
+ grantor.authorize!(Object.new)
13
30
  end
14
- it 'should define a before_update callback method when passed update as an argument' do
15
- Grant::Grantor.new(:update).should respond_to(:before_update)
31
+ end
32
+
33
+ describe '#authorized?' do
34
+ it 'should pass the user, model, and action to the supplied callback block' do
35
+ model = Object.new
36
+ user = Object.new
37
+ Grant::User.current_user = user
38
+
39
+ grantor = Grant::Grantor.new(:update) do |u, m, a|
40
+ u.should == user
41
+ m.should == model
42
+ a.should == :update
43
+ true
44
+ end
45
+
46
+ grantor.authorized?(model).should be_true
16
47
  end
17
- it 'should define a before_destroy callback method when passed destroy as an argument' do
18
- Grant::Grantor.new(:destroy).should respond_to(:before_destroy)
48
+
49
+ it 'should turn off grant when calling the supplied callback block' do
50
+ grantor = Grant::Grantor.new(:destroy) do |user, model, action|
51
+ Grant::Status.grant_disabled?.should be_true
52
+ false
53
+ end
54
+
55
+ grantor.authorized?(Object.new).should be_false
19
56
  end
20
57
  end
21
58
 
@@ -3,16 +3,24 @@ require 'grant/status'
3
3
 
4
4
  describe Grant::Status do
5
5
  it "should be enabled if set to enabled" do
6
- obj = Class.new { include Grant::Status }.new
7
- obj.enable_grant
8
- obj.should be_grant_enabled
9
- obj.should_not be_grant_disabled
6
+ obj = Class.new do
7
+ include Grant::Status
8
+ def enable; enable_grant; end
9
+ end.new
10
+
11
+ obj.enable
12
+ Grant::Status.grant_enabled?.should be_true
13
+ Grant::Status.grant_disabled?.should be_false
10
14
  end
11
15
 
12
16
  it "should be disabled if set to disabled" do
13
- obj = Class.new { include Grant::Status }.new
14
- obj.disable_grant
15
- obj.should_not be_grant_enabled
16
- obj.should be_grant_disabled
17
+ obj = Class.new do
18
+ include Grant::Status
19
+ def disable; disable_grant; end
20
+ end.new
21
+
22
+ obj.disable
23
+ Grant::Status.grant_enabled?.should be_false
24
+ Grant::Status.grant_disabled?.should be_true
17
25
  end
18
26
  end
metadata CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
+ - 1
8
9
  - 0
9
- - 2
10
- version: 2.0.2
10
+ version: 2.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jeff Kunkle
@@ -16,13 +16,29 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-03-22 00:00:00 -04:00
19
+ date: 2011-07-05 00:00:00 -04:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
23
- name: rspec
23
+ name: activerecord
24
24
  prerelease: false
25
25
  requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">"
29
+ - !ruby/object:Gem::Version
30
+ hash: 7
31
+ segments:
32
+ - 3
33
+ - 0
34
+ - 0
35
+ version: 3.0.0
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ prerelease: false
41
+ requirement: &id002 !ruby/object:Gem::Requirement
26
42
  none: false
27
43
  requirements:
28
44
  - - "="
@@ -34,11 +50,11 @@ dependencies:
34
50
  - 0
35
51
  version: 2.5.0
36
52
  type: :development
37
- version_requirements: *id001
53
+ version_requirements: *id002
38
54
  - !ruby/object:Gem::Dependency
39
55
  name: sqlite3-ruby
40
56
  prerelease: false
41
- requirement: &id002 !ruby/object:Gem::Requirement
57
+ requirement: &id003 !ruby/object:Gem::Requirement
42
58
  none: false
43
59
  requirements:
44
60
  - - "="
@@ -50,11 +66,11 @@ dependencies:
50
66
  - 3
51
67
  version: 1.3.3
52
68
  type: :development
53
- version_requirements: *id002
69
+ version_requirements: *id003
54
70
  - !ruby/object:Gem::Dependency
55
71
  name: activerecord
56
72
  prerelease: false
57
- requirement: &id003 !ruby/object:Gem::Requirement
73
+ requirement: &id004 !ruby/object:Gem::Requirement
58
74
  none: false
59
75
  requirements:
60
76
  - - ">"
@@ -66,7 +82,7 @@ dependencies:
66
82
  - 0
67
83
  version: 3.0.0
68
84
  type: :development
69
- version_requirements: *id003
85
+ version_requirements: *id004
70
86
  description: Grant is a Ruby gem and Rails plugin that forces you to make explicit security decisions about the operations performed on your ActiveRecord models.
71
87
  email:
72
88
  executables: []
@@ -76,13 +92,9 @@ extensions: []
76
92
  extra_rdoc_files: []
77
93
 
78
94
  files:
79
- - .gitignore
80
95
  - CHANGELOG.md
81
- - Gemfile
82
- - Gemfile.lock
83
96
  - LICENSE
84
97
  - README.rdoc
85
- - Rakefile
86
98
  - grant.gemspec
87
99
  - init.rb
88
100
  - lib/grant.rb
data/.gitignore DELETED
@@ -1,5 +0,0 @@
1
- coverage
2
- rdoc
3
- grant-*.gem
4
- tmp/
5
- pkg/
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in grant.gemspec
4
- gemspec
@@ -1,43 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- grant (2.0.1)
5
-
6
- GEM
7
- remote: http://rubygems.org/
8
- specs:
9
- activemodel (3.0.5)
10
- activesupport (= 3.0.5)
11
- builder (~> 2.1.2)
12
- i18n (~> 0.4)
13
- activerecord (3.0.5)
14
- activemodel (= 3.0.5)
15
- activesupport (= 3.0.5)
16
- arel (~> 2.0.2)
17
- tzinfo (~> 0.3.23)
18
- activesupport (3.0.5)
19
- arel (2.0.9)
20
- builder (2.1.2)
21
- diff-lcs (1.1.2)
22
- i18n (0.5.0)
23
- rspec (2.5.0)
24
- rspec-core (~> 2.5.0)
25
- rspec-expectations (~> 2.5.0)
26
- rspec-mocks (~> 2.5.0)
27
- rspec-core (2.5.1)
28
- rspec-expectations (2.5.0)
29
- diff-lcs (~> 1.1.2)
30
- rspec-mocks (2.5.0)
31
- sqlite3 (1.3.3)
32
- sqlite3-ruby (1.3.3)
33
- sqlite3 (>= 1.3.3)
34
- tzinfo (0.3.24)
35
-
36
- PLATFORMS
37
- ruby
38
-
39
- DEPENDENCIES
40
- activerecord (> 3.0.0)
41
- grant!
42
- rspec (= 2.5.0)
43
- sqlite3-ruby (= 1.3.3)
data/Rakefile DELETED
@@ -1,33 +0,0 @@
1
- $:.unshift File.expand_path("../lib", __FILE__)
2
-
3
- require 'rake'
4
- require 'rake/rdoctask'
5
- require 'rspec/core/rake_task'
6
- require 'bundler'
7
-
8
- Bundler::GemHelper.install_tasks
9
-
10
- desc 'Default: run specs'
11
- task :default => :spec
12
-
13
- desc "Run specs"
14
- RSpec::Core::RakeTask.new do |t|
15
- t.rspec_opts = %w(-fs --color)
16
- end
17
-
18
- desc "Run specs with RCov"
19
- RSpec::Core::RakeTask.new(:rcov) do |t|
20
- t.rspec_opts = %w(-fs --color)
21
- t.rcov = true
22
- t.rcov_opts = %w(--exclude "spec/*,gems/*")
23
- end
24
-
25
- desc 'Generate documentation for the gem.'
26
- Rake::RDocTask.new(:rdoc) do |rdoc|
27
- rdoc.rdoc_dir = 'rdoc'
28
- rdoc.title = 'Grant'
29
- rdoc.options << '--line-numbers' << '--inline-source'
30
- rdoc.rdoc_files.include('README.rdoc')
31
- rdoc.rdoc_files.include('lib/**/*.rb')
32
- end
33
-