grant 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +43 -0
- data/README.rdoc +15 -8
- data/Rakefile +33 -0
- data/grant.gemspec +23 -0
- data/init.rb +1 -0
- data/lib/grant.rb +21 -3
- data/lib/grant/config.rb +22 -0
- data/lib/grant/grantable.rb +41 -0
- data/lib/grant/grantor.rb +23 -0
- data/lib/grant/integration.rb +4 -46
- data/lib/grant/model_security.rb +4 -50
- data/lib/grant/spec_helpers.rb +5 -5
- data/lib/grant/status.rb +60 -0
- data/lib/grant/user.rb +5 -6
- data/lib/grant/version.rb +2 -2
- data/spec/config_spec.rb +29 -0
- data/spec/grantable_spec.rb +99 -0
- data/spec/grantor_spec.rb +40 -0
- data/spec/spec_helper.rb +2 -37
- data/spec/status_spec.rb +18 -0
- data/spec/support/db_setup.rb +48 -0
- data/spec/support/transactional_specs.rb +17 -0
- data/spec/user_spec.rb +5 -5
- metadata +72 -30
- data/lib/grant/config_parser.rb +0 -22
- data/lib/grant/thread_local.rb +0 -18
- data/lib/grant/thread_status.rb +0 -34
- data/spec/config_parser_spec.rb +0 -34
- data/spec/integration_spec.rb +0 -66
- data/spec/model_security_spec.rb +0 -88
- data/spec/thread_local_spec.rb +0 -14
- data/spec/thread_status_spec.rb +0 -16
data/CHANGELOG.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
## 2.0.0 (January 5, 2011)
|
2
|
+
|
3
|
+
Features:
|
4
|
+
|
5
|
+
- Removed the ability to protect adding and removing from association collections. See the [related issue](https://github.com/nearinfinity/grant/issues#issue/1) for details
|
6
|
+
|
7
|
+
## 1.0.1 (December 29, 2010)
|
8
|
+
|
9
|
+
Changes:
|
10
|
+
|
11
|
+
- Changed Grant from strictly a Rails plugin to a Ruby gem. No functionality changes or bug fixes.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
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/README.rdoc
CHANGED
@@ -26,7 +26,10 @@ Lastly, Grant can also be installed as a Rails plugin
|
|
26
26
|
|
27
27
|
= Setup
|
28
28
|
|
29
|
-
Grant needs to know who the current user is, but with no standard for
|
29
|
+
Grant needs to know who the current user is, but with no standard for
|
30
|
+
doing so you'll have to do a little work to set things up unless your
|
31
|
+
ApplicationController exposes a current_user method. If you don't
|
32
|
+
have a current_user method in ApplicationController, you simply need to set your current user model object as the Grant current user before any CRUD operations are performed. For example, in a Rails application you could add the following to your application_controller.rb
|
30
33
|
|
31
34
|
class ApplicationController < ActionController::Base
|
32
35
|
before_filter :set_current_user
|
@@ -38,14 +41,16 @@ Grant needs to know who the current user is, but with no standard for doing so y
|
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
44
|
+
The above is essentially what Grant does if you have a current_user
|
45
|
+
method in ApplicationController.
|
46
|
+
|
41
47
|
= Usage
|
42
48
|
|
43
|
-
To enable
|
49
|
+
To enable grant you simply start calling the grant method.
|
50
|
+
Grant will initialize itself on the first invocation of the grant method
|
51
|
+
in a 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
52
|
|
45
53
|
class Book < ActiveRecord::Base
|
46
|
-
include Grant::ModelSecurity
|
47
|
-
|
48
|
-
has_many :tags
|
49
54
|
grant(:find) { true }
|
50
55
|
grant(:create, :update, :destroy) { |user, model| model.editable_by_user? user }
|
51
56
|
|
@@ -58,10 +63,12 @@ The valid actions to pass to a grant statement are :find, :create, :update, and
|
|
58
63
|
|
59
64
|
= Integration
|
60
65
|
|
61
|
-
There may be some instances where you need to perform an action on your
|
66
|
+
There may be some instances where you need to perform an action on your
|
67
|
+
model object without Grant stepping in and stopping you. In those cases
|
68
|
+
you can include the Grant::Status module for help.
|
62
69
|
|
63
70
|
class BooksController < ApplicationController
|
64
|
-
include Grant::
|
71
|
+
include Grant::Status
|
65
72
|
|
66
73
|
def update
|
67
74
|
book = Book.find(params[:id])
|
@@ -69,4 +76,4 @@ There may be some instances where you need to perform an action on your model ob
|
|
69
76
|
end
|
70
77
|
end
|
71
78
|
|
72
|
-
Copyright (c)
|
79
|
+
Copyright (c) 2011 Near Infinity Corporation, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
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
|
+
|
data/grant.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'grant/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "grant"
|
7
|
+
s.version = Grant::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jeff Kunkle", "Matt Wizeman"]
|
10
|
+
s.homepage = "http://github.com/nearinfinity/grant"
|
11
|
+
s.summary = "Conscious security constraints for your ActiveRecord model objects"
|
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
|
+
s.license = "MIT"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_development_dependency('rspec', '2.5.0')
|
21
|
+
s.add_development_dependency('sqlite3-ruby', '1.3.3')
|
22
|
+
s.add_development_dependency('activerecord', '> 3.0.0')
|
23
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'grant'
|
data/lib/grant.rb
CHANGED
@@ -1,8 +1,26 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'grant/grantable'
|
3
|
+
|
4
|
+
# TODO: Remove these two requires when backwards compatibility with grant 2.0.0
|
5
|
+
# is no longer necessary
|
1
6
|
require 'grant/integration'
|
2
7
|
require 'grant/model_security'
|
3
|
-
require 'grant/user'
|
4
|
-
require 'grant/version'
|
5
8
|
|
6
9
|
module Grant
|
7
10
|
class Error < StandardError; end
|
8
|
-
end
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveRecord::Base.send :include, Grant::Grantable
|
14
|
+
|
15
|
+
if defined?(ActionController) and defined?(ActionController::Base)
|
16
|
+
|
17
|
+
require 'grant/user'
|
18
|
+
|
19
|
+
ActionController::Base.class_eval do
|
20
|
+
before_filter do
|
21
|
+
Grant::User.current_user = self.current_user if self.respond_to?(:current_user)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
data/lib/grant/config.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Grant
|
2
|
+
class Config
|
3
|
+
attr_reader :actions
|
4
|
+
|
5
|
+
def self.valid_actions
|
6
|
+
@valid_actions ||= [:create, :find, :update, :destroy]
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
@actions = args.map(&:to_sym)
|
11
|
+
validate_actions(@actions)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def validate_actions(actions)
|
17
|
+
raise Grant::Error.new "at least one action in #{Config.valid_actions.inspect} must be specified" if actions.empty?
|
18
|
+
raise Grant::Error.new "#{Config.valid_actions.inspect} are the only valid actions" unless actions.all? { |a| Config.valid_actions.include?(a.to_sym) }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'grant/status'
|
2
|
+
require 'grant/config'
|
3
|
+
require 'grant/grantor'
|
4
|
+
|
5
|
+
module Grant
|
6
|
+
module Grantable
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def grant(*args, &blk)
|
14
|
+
include Grant::Status unless self.included_modules.include?(Grant::Status)
|
15
|
+
initialize_grant unless grant_initialized?
|
16
|
+
|
17
|
+
config = Grant::Config.new(*args)
|
18
|
+
config.actions.each do |action|
|
19
|
+
@grant_callbacks[action.to_sym].callback = blk
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
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
|
29
|
+
end
|
30
|
+
@grant_initialized = true
|
31
|
+
end
|
32
|
+
|
33
|
+
def grant_initialized?
|
34
|
+
@grant_initialized == true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
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
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'grant/status'
|
2
|
+
|
3
|
+
module Grant
|
4
|
+
class Grantor
|
5
|
+
include Status
|
6
|
+
|
7
|
+
attr_writer :callback
|
8
|
+
|
9
|
+
def initialize(action)
|
10
|
+
self.class.send(:define_method, "#{action == :find ? 'after' : 'before'}_#{action}") do |model|
|
11
|
+
user = Grant::User.current_user
|
12
|
+
error(user, action, self) unless grant_disabled? || (@callback != nil && @callback.call(user, model))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def error(user, action, model)
|
17
|
+
msg = ["#{action} permission",
|
18
|
+
"not granted to #{user.class.name}:#{user.id}",
|
19
|
+
"for resource #{model.class.name}:#{model.id}"]
|
20
|
+
raise Grant::Error.new(msg.join(' '))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/grant/integration.rb
CHANGED
@@ -1,49 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# TODO: Remove this file when backwards compatibility with grant 2.0.0
|
2
|
+
# is no longer necessary
|
3
3
|
module Grant
|
4
4
|
module Integration
|
5
|
-
|
6
|
-
def without_grant
|
7
|
-
previously_disabled = grant_disabled?
|
8
|
-
disable_grant
|
9
|
-
|
10
|
-
begin
|
11
|
-
result = yield if block_given?
|
12
|
-
ensure
|
13
|
-
enable_grant unless previously_disabled
|
14
|
-
end
|
15
|
-
|
16
|
-
result
|
17
|
-
end
|
18
|
-
|
19
|
-
def with_grant
|
20
|
-
previously_disabled = grant_disabled?
|
21
|
-
enable_grant
|
22
|
-
|
23
|
-
begin
|
24
|
-
result = yield if block_given?
|
25
|
-
ensure
|
26
|
-
disable_grant if previously_disabled
|
27
|
-
end
|
28
|
-
|
29
|
-
result
|
30
|
-
end
|
31
|
-
|
32
|
-
def disable_grant
|
33
|
-
Grant::ThreadStatus.disable
|
34
|
-
end
|
35
|
-
|
36
|
-
def enable_grant
|
37
|
-
Grant::ThreadStatus.enable
|
38
|
-
end
|
39
|
-
|
40
|
-
def grant_disabled?
|
41
|
-
Grant::ThreadStatus.disabled?
|
42
|
-
end
|
43
|
-
|
44
|
-
def grant_enabled?
|
45
|
-
Grant::ThreadStatus.enabled?
|
46
|
-
end
|
47
|
-
|
5
|
+
include Status
|
48
6
|
end
|
49
|
-
end
|
7
|
+
end
|
data/lib/grant/model_security.rb
CHANGED
@@ -1,55 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'grant/
|
1
|
+
# TODO: Remove this file when backwards compatibility with grant 2.0.0
|
2
|
+
# is no longer necessary
|
3
|
+
require 'grant/grantable'
|
4
4
|
|
5
5
|
module Grant
|
6
6
|
module ModelSecurity
|
7
|
-
|
8
|
-
def self.included(base)
|
9
|
-
[:create, :update, :destroy, :find].each do |action|
|
10
|
-
callback = (action == :find ? "after_#{action}" : "before_#{action}")
|
11
|
-
base.class_eval <<-RUBY
|
12
|
-
def grant_#{callback}
|
13
|
-
grant_raise_error(grant_current_user, '#{action}', self) unless grant_disabled?
|
14
|
-
end
|
15
|
-
RUBY
|
16
|
-
base.send(callback.to_sym, "grant_#{callback}".to_sym)
|
17
|
-
end
|
18
|
-
|
19
|
-
base.extend ClassMethods
|
20
|
-
end
|
21
|
-
|
22
|
-
# ActiveRecord won't call the after_find handler unless it see's a specific after_find method defined
|
23
|
-
def after_find; end
|
24
|
-
|
25
|
-
def grant_current_user
|
26
|
-
Grant::User.current_user
|
27
|
-
end
|
28
|
-
|
29
|
-
def grant_disabled?
|
30
|
-
Grant::ThreadStatus.disabled? || @grant_disabled
|
31
|
-
end
|
32
|
-
|
33
|
-
def grant_raise_error(user, action, model, association_id=nil)
|
34
|
-
msg = ["#{action} permission",
|
35
|
-
"not granted to #{user.class.name}:#{user.id}",
|
36
|
-
"for resource #{model.class.name}:#{model.id}"]
|
37
|
-
|
38
|
-
raise Grant::Error.new(msg.join(' '))
|
39
|
-
end
|
40
|
-
|
41
|
-
module ClassMethods
|
42
|
-
def grant(*args, &blk)
|
43
|
-
actions = Grant::ConfigParser.extract_config(args)
|
44
|
-
actions.each do |action|
|
45
|
-
grant_callback = (action.to_sym == :find ? "grant_after_find" : "grant_before_#{action}").to_sym
|
46
|
-
define_method(grant_callback) do
|
47
|
-
grant_raise_error(grant_current_user, action, self) unless grant_disabled? || blk.call(grant_current_user, self)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
7
|
+
include Grant::Grantable unless self.included_modules.include?(Grant::Grantable)
|
53
8
|
end
|
54
|
-
|
55
9
|
end
|
data/lib/grant/spec_helpers.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
require 'grant/
|
1
|
+
require 'grant/status'
|
2
2
|
|
3
3
|
module Grant
|
4
4
|
module SpecHelpers
|
5
|
-
include Grant::
|
6
|
-
|
5
|
+
include Grant::Status
|
6
|
+
|
7
7
|
def self.included(base)
|
8
8
|
base.class_eval do
|
9
9
|
before(:each) do
|
10
10
|
disable_grant
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
after(:each) do
|
14
14
|
enable_grant
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|