grant 2.0.2 → 2.1.0

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.
@@ -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
-