restful_acl 2.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Matt Darby
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,168 @@
1
+ h2. RESTful_ACL
2
+
3
+ A Ruby on Rails plugin that provides fine grained access control through the MVC stack to RESTful resources in a Ruby on Rails 2.0+ application. Authorization is as simple as true or false.
4
+
5
+ h3. What it does
6
+
7
+ RESTful_ACL is a simple Access Control Layer for Ruby on Rails. It restricts access on a fine-grained level to any RESTful MVC stack. Every application is different and everyone likes to setup their User / Account / Role resources differently; this plugin will allow you to do your thing and keep that thing locked down.
8
+
9
+ h3. Requirements
10
+
11
+ RESTful_ACL requires the super amazing "RESTful_Authentication":https://github.com/technoweenie/restful-authentication plugin.
12
+
13
+ h3. How to Install
14
+
15
+ Install the RESTful_ACL gem:
16
+ <pre>sudo gem install mdarby-restful_acl -s http://gems.github.com</pre>
17
+
18
+ Add the gem to your environment.rb file as thus:
19
+ <pre>config.gem "mdarby-restful_acl", :lib => 'restful_acl_controller'</pre>
20
+
21
+ RESTful_ACL requires two named routes: "error" and "denied". Add the following to your routes.rb file:
22
+ <pre>
23
+ map.error '/error', :controller => 'some_controller', :action => 'error_action'
24
+ map.denied '/denied', :controller => 'some_controller', :action => 'denied_action'
25
+ </pre>
26
+
27
+ h3. How to Use
28
+
29
+ h4. Controllers
30
+
31
+ Add @before_filter :has_permission?@ into any controller that you'd like to restrict access to (or application_controller.rb for your entire app).
32
+
33
+ h4. Models
34
+
35
+ Define a parent resource (if one exists) by using the @logical_parent@ method, and define the following five methods in the model of every resource you'd like to restrict access to. The five methods can contain anything you'd like so long as they return a boolean true or false. This allows you to define your User's roles any way you wish.
36
+
37
+ <pre>
38
+ class Issue < ActiveRecord::Base
39
+ logical_parent :some_model_name
40
+
41
+ # This method checks permissions for the :index action
42
+ def self.is_indexable_by(user, parent = nil)
43
+
44
+ end
45
+
46
+ # This method checks permissions for the :create and :new action
47
+ def self.is_creatable_by(user, parent = nil)
48
+
49
+ end
50
+
51
+ # This method checks permissions for the :show action
52
+ def is_readable_by(user, parent = nil)
53
+
54
+ end
55
+
56
+ # This method checks permissions for the :update and :edit action
57
+ def is_updatable_by(user, parent = nil)
58
+
59
+ end
60
+
61
+ # This method checks permissions for the :destroy action
62
+ def is_deletable_by(user, parent = nil)
63
+
64
+ end
65
+ end
66
+ </pre>
67
+
68
+ h4. View Helpers
69
+
70
+ There are five view helpers also included in RESTful_ACL: @#indexable@, @#creatable@, @#readable@, @#updatable@, and @#deletable@. These enable you to do nifty things like:
71
+ <pre>
72
+ <%= link_to ‘Foo Index’, foos_path if indexable %>
73
+ <%= link_to 'Edit Foo', edit_foo_path(@foo) if updatable(@foo) %>
74
+ <%= link_to 'Create Foo', new_foo_path if creatable %>
75
+ <%= link_to 'View Foo', foo_path(@foo) if readable(@foo) %>
76
+ <%= link_to 'Delete Foo', foo_path(@foo) if deletable(@foo), :method => :destroy %>
77
+ </pre>
78
+
79
+ h3. Huh? Here's an example
80
+
81
+ Let's say that you have two resources: Project and Issue. A Project has many Issues, an Issue belongs to a Project. I'd like to make sure that the current user is a member of the Project before they can create a new Issue in that Project:
82
+
83
+ <pre>
84
+ class Issue < ActiveRecord::Base
85
+ logical_parent :project
86
+
87
+ belongs_to :author
88
+ belongs_to :project
89
+
90
+ def self.is_indexable_by(user, parent = nil)
91
+ user.projects.include?(parent)
92
+ end
93
+
94
+ def self.is_creatable_by(user, parent = nil)
95
+ user.projects.include?(parent)
96
+ end
97
+
98
+ def is_updatable_by(user, parent = nil)
99
+ user == author && parent.is_active?
100
+ end
101
+
102
+ def is_deletable_by(user, parent = nil)
103
+ user == author
104
+ end
105
+
106
+ def is_readable_by(user, parent = nil)
107
+ user.projects.include?(parent)
108
+ end
109
+ end
110
+ </pre>
111
+
112
+ h3. Admins RULE!
113
+
114
+ RESTful_ACL grants global access to all actions to site administrators. To enable this, make sure that your User model defines an @is_admin?@ method *and/or* an @is_admin@ attribute. If the @current_user.is_admin?@ returns true, access will be granted automatically.
115
+
116
+ h3. How to Test
117
+
118
+ I normally do something along these lines in RSpec:
119
+ <pre>
120
+ describe "Issue" do
121
+ before do
122
+ @project = mock_model(Project)
123
+ @author = mock_model(User, :projects => [@project])
124
+
125
+ @issue = Issue.factory_girl(:issue, :author => @author, :project => @project)
126
+ end
127
+
128
+ it "should be modifiable by the author when the Project is active" do
129
+ @project.stub!(:is_active? => true)
130
+ @issue.is_updatable_by(@author, @project).should be_true
131
+ end
132
+
133
+ it "should be deletable by the author" do
134
+ @issue.is_deletable_by(@author, @project).should be_true
135
+ end
136
+
137
+ it "should be readable by those assigned to the Project" do
138
+ Issue.is_readable_by(@author, @project).should be_true
139
+ end
140
+
141
+ it "should be creatable by those assigned to the Project" do
142
+ Issue.is_creatable_by(@author, @project).should be_true
143
+ end
144
+ end
145
+ </pre>
146
+
147
+ h3. Caveats
148
+
149
+ RESTful_ACL doesn't work with nested singleton resources. Wha? Yeah. Those are things in routes.rb like:
150
+
151
+ <pre>
152
+ # Note the singular forms in 'user.resource :profile'
153
+ map.resources :users do |user|
154
+ user.resource :profile
155
+ end
156
+ </pre>
157
+
158
+ In these situations I normally skip permission checking altogether as a Profile will always be mapped to the currently logged in User, regardless of the @params[:user_id]@ passed in. You don't trust those either right? Good.
159
+
160
+ h3. Help
161
+
162
+ Add a ticket to "RESTful_ACL's Lighthouse Account":http://mdarby.lighthouseapp.com/projects/28698-restful_acl/overview
163
+
164
+ h3. About the Author
165
+
166
+ My name is "Matt Darby.":http://blog.matt-darby.com I’m an IT Manager and pro-web-dev at for "Dynamix Engineering":http://dynamix-ltd.com and hold a Master’s Degree in Computer Science from "Franklin University":http://www.franklin.edu in sunny "Columbus, OH.":http://en.wikipedia.org/wiki/Columbus,_Ohio
167
+
168
+ Feel free to check out my "blog":http://blog.matt-darby.com or "recommend me":http://www.workingwithrails.com/person/10908-matt-darby
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the restful_acl plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the restful_acl plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'RestfulAcl'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/rails/init"
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,100 @@
1
+ module RestfulAclController
2
+
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ base.send :include, ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def has_permission?
11
+ return true if administrator?
12
+
13
+ begin
14
+ # Load the Model based on the controller name
15
+ klass = self.controller_name.classify.constantize
16
+
17
+ if params[:id]
18
+ # Load the object and possible parent requested
19
+ object = klass.find(params[:id])
20
+ parent = object.get_mom rescue nil
21
+ else
22
+ # No object was requested, so we need to go to the URI to figure out the parent
23
+ object = nil
24
+ parent = get_parent_from_request_uri(klass) if klass.has_parent?
25
+ end
26
+
27
+ # Let's let the Model decide what is acceptable
28
+ permission_denied unless case params[:action]
29
+ when "index" then klass.is_indexable_by(current_user, parent)
30
+ when "new", "create" then klass.is_creatable_by(current_user, parent)
31
+ when "show" then object.is_readable_by(current_user, parent)
32
+ when "edit", "update" then object.is_updatable_by(current_user, parent)
33
+ when "destroy" then object.is_deletable_by(current_user, parent)
34
+ else check_non_restful_route(current_user, klass, object, parent)
35
+ end
36
+
37
+ rescue NoMethodError => e
38
+ # Misconfiguration: A RESTful_ACL specific method is missing.
39
+ raise_error(klass, e)
40
+ rescue
41
+ # Failsafe: If any funny business is going on, log and redirect
42
+ routing_error
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def check_non_restful_route(user, klass, object, parent)
49
+ if object
50
+ object.is_readable_by(user, parent)
51
+ elsif klass
52
+ klass.is_indexable_by(user, parent)
53
+ else
54
+ # If all else fails, deny access
55
+ false
56
+ end
57
+ end
58
+
59
+ def get_method_from_error(error)
60
+ error.message.gsub('`', "'").split("'").at(1)
61
+ end
62
+
63
+ def raise_error(klass, error)
64
+ method = get_method_from_error(error)
65
+ message = (is_class_method?(method)) ? "#{klass}#self.#{method}" : "#{klass}##{method}"
66
+ raise NoMethodError, "[RESTful_ACL] #{message}(user, parent = nil) seems to be missing?"
67
+ end
68
+
69
+ def is_class_method?(method)
70
+ method =~ /(indexable|creatable)/
71
+ end
72
+
73
+ def get_parent_from_request_uri(child_klass)
74
+ parent_klass = child_klass.mom.to_s
75
+ bits = request.request_uri.split('/')
76
+ parent_id = bits.at(bits.index(parent_klass.pluralize) + 1)
77
+
78
+ parent_klass.classify.constantize.find(parent_id)
79
+ end
80
+
81
+ def administrator?
82
+ current_user.respond_to?("is_admin?") && current_user.is_admin?
83
+ end
84
+
85
+ def permission_denied
86
+ logger.info("[RESTful_ACL] Permission denied to %s at %s for %s" %
87
+ [(logged_in? ? current_user.login : 'guest'), Time.now, request.request_uri])
88
+
89
+ redirect_to denied_url
90
+ end
91
+
92
+ def routing_error
93
+ logger.info("[RESTful_ACL] Routing error by %s at %s for %s" %
94
+ [(logged_in? ? current_user.login : 'guest'), Time.now, request.request_uri])
95
+
96
+ redirect_to error_url
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,62 @@
1
+ module RestfulAclHelper
2
+ def indexable
3
+ return true if admin_enabled
4
+ klass.is_indexable_by(current_user, parent_obj)
5
+ end
6
+
7
+ def creatable
8
+ return true if admin_enabled
9
+ klass.is_creatable_by(current_user, parent_obj)
10
+ end
11
+ alias_method :createable, :creatable
12
+
13
+
14
+ def updatable(object)
15
+ return true if admin_enabled
16
+
17
+ parent = object.get_mom rescue nil
18
+ object.is_updatable_by(current_user, parent)
19
+ end
20
+ alias_method :updateable, :updatable
21
+
22
+
23
+ def deletable(object)
24
+ return true if admin_enabled
25
+
26
+ parent = object.get_mom rescue nil
27
+ object.is_deletable_by(current_user, parent)
28
+ end
29
+ alias_method :deleteable, :deletable
30
+
31
+
32
+ def readable(object)
33
+ return true if admin_enabled
34
+
35
+ parent = object.get_mom rescue nil
36
+ object.is_readable_by(current_user, parent)
37
+ end
38
+
39
+
40
+ private
41
+
42
+ def klass
43
+ params[:controller].classify.constantize
44
+ end
45
+
46
+ def parent_obj
47
+ parent_klass.find(parent_id) rescue nil
48
+ end
49
+
50
+ def parent_klass
51
+ klass.mom.to_s.classify.constantize
52
+ end
53
+
54
+ def parent_id
55
+ params["#{klass.mom.to_s}_id"]
56
+ end
57
+
58
+ def admin_enabled
59
+ current_user.respond_to?("is_admin?") && current_user.is_admin?
60
+ end
61
+
62
+ end
@@ -0,0 +1,52 @@
1
+ module RestfulAclModel
2
+
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ base.send :include, ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ attr_accessor :mom
10
+
11
+ def logical_parent(model)
12
+ self.mom = model
13
+ include RestfulAclModel::InstanceMethods
14
+ end
15
+
16
+ def has_parent?
17
+ !self.mom.nil?
18
+ end
19
+
20
+ end
21
+
22
+
23
+ module InstanceMethods
24
+
25
+ def get_mom
26
+ parent_klass.find(parent_id) if has_parent?
27
+ end
28
+
29
+ private
30
+
31
+ def klass
32
+ self.class
33
+ end
34
+
35
+ def mom
36
+ klass.mom
37
+ end
38
+
39
+ def has_parent?
40
+ !mom.nil?
41
+ end
42
+
43
+ def parent_klass
44
+ mom.to_s.classify.constantize
45
+ end
46
+
47
+ def parent_id
48
+ self.instance_eval("#{mom}_id")
49
+ end
50
+ end
51
+
52
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'restful_acl_controller'
2
+ require 'restful_acl_helper'
3
+ require 'restful_acl_model'
4
+
5
+ ActionController::Base.send :include, RestfulAclController
6
+ ActionView::Base.send :include, RestfulAclHelper
7
+ ActiveRecord::Base.send :include, RestfulAclModel
8
+
9
+ RAILS_DEFAULT_LOGGER.debug "** [RESTful_ACL] loaded"
data/uninstall.rb ADDED
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: restful_acl
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Matt Darby
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-07 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A Rails gem that provides fine grained access control to RESTful resources in a Rails 2.0+ application.
17
+ email: matt@matt-darby.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - MIT-LICENSE
26
+ - README.textile
27
+ - Rakefile
28
+ - init.rb
29
+ - install.rb
30
+ - lib/restful_acl_controller.rb
31
+ - lib/restful_acl_helper.rb
32
+ - lib/restful_acl_model.rb
33
+ - rails/init.rb
34
+ - uninstall.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/mdarby/restful_acl
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.5
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Object-level access control
63
+ test_files: []
64
+