adriand-restful_acl 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.
@@ -0,0 +1,205 @@
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
+ This is a fork of "mdarby's version":http://github.com/mdarby/restful_acl/tree/master
6
+
7
+ h3. Differences From mdarby's version
8
+
9
+ h4. Handling Resources Accessible As Nested And As Unnested
10
+
11
+ It handles cases where a logical parent id may not exist in the URL for a resource that is sometimes accessed via a nested route, and sometimes not.
12
+
13
+ Suppose you provide a nested and an unnested way to access a resource:
14
+
15
+ <pre>
16
+ map.resources :providers do |provider|
17
+ provider.resources :locations, :name_prefix => "provider_"
18
+ end
19
+ map.resources :locations
20
+ </pre>
21
+
22
+ "Idea for this is outlined here":http://weblog.jamisbuck.org/2007/2/5/nesting-resources
23
+
24
+ In this case, Location has Provider as its logical parent. However, both of these URLs are acceptable for accessing locations:
25
+
26
+ /locations (all locations) AND /providers/1/locations (locations scoped down to a particular provider).
27
+
28
+ Formerly, restful_acl would break in this instance, now, it does not - if a logical parent cannot be determined from the URL, it returns nil as the parent. You will need to check for parent == nil in your authentication methods, in your models.
29
+
30
+ h4. Added Ability to Specify Class in View Helpers
31
+
32
+ In restful_acl, you can check for indexable and createable in your views, eg.:
33
+
34
+ <pre>= link_to "Users", if indexable</pre>
35
+
36
+ If you are in a view that corresponds with the users controller, this works fine. However, what if you wish to display a link to users somewhere else?
37
+
38
+ This change allows you to optionally specify the class you are concerned with:
39
+
40
+ <pre>= link_to "Users", if indexable(User)</pre>
41
+
42
+ h3. What it does
43
+
44
+ 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.
45
+
46
+ h3. Requirements
47
+
48
+ RESTful_ACL requires the super amazing "RESTful_Authentication":https://github.com/technoweenie/restful-authentication plugin.
49
+
50
+ h3. How to Install
51
+
52
+ Install the RESTful_ACL gem:
53
+ <pre>sudo gem install adriand-restful_acl -s http://gems.github.com</pre>
54
+
55
+ Add the gem to your environment.rb file as thus:
56
+ <pre>config.gem "adriand-restful_acl", :lib => 'restful_acl_controller'</pre>
57
+
58
+ RESTful_ACL requires two named routes: "error" and "denied". Add the following to your routes.rb file:
59
+ <pre>
60
+ map.error '/error', :controller => 'some_controller', :action => 'error_action'
61
+ map.denied '/denied', :controller => 'some_controller', :action => 'denied_action'
62
+ </pre>
63
+
64
+ h3. How to Use
65
+
66
+ h4. Controllers
67
+
68
+ Add @before_filter :has_permission?@ into any controller that you'd like to restrict access to (or application_controller.rb for your entire app).
69
+
70
+ h4. Models
71
+
72
+ 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.
73
+
74
+ <pre>
75
+ class Issue < ActiveRecord::Base
76
+ logical_parent :some_model_name
77
+
78
+ # This method checks permissions for the :index action
79
+ def self.is_indexable_by(user, parent = nil)
80
+
81
+ end
82
+
83
+ # This method checks permissions for the :create and :new action
84
+ def self.is_creatable_by(user, parent = nil)
85
+
86
+ end
87
+
88
+ # This method checks permissions for the :show action
89
+ def is_readable_by(user, parent = nil)
90
+
91
+ end
92
+
93
+ # This method checks permissions for the :update and :edit action
94
+ def is_updatable_by(user, parent = nil)
95
+
96
+ end
97
+
98
+ # This method checks permissions for the :destroy action
99
+ def is_deletable_by(user, parent = nil)
100
+
101
+ end
102
+ end
103
+ </pre>
104
+
105
+ h4. View Helpers
106
+
107
+ There are five view helpers also included in RESTful_ACL: @#indexable@, @#creatable@, @#readable@, @#updatable@, and @#deletable@. These enable you to do nifty things like:
108
+ <pre>
109
+ <%= link_to ‘Foo Index’, foos_path if indexable %>
110
+ <%= link_to 'Edit Foo', edit_foo_path(@foo) if updatable(@foo) %>
111
+ <%= link_to 'Create Foo', new_foo_path if creatable %>
112
+ <%= link_to 'View Foo', foo_path(@foo) if readable(@foo) %>
113
+ <%= link_to 'Delete Foo', foo_path(@foo) if deletable(@foo), :method => :destroy %>
114
+ </pre>
115
+
116
+ h3. Huh? Here's an example
117
+
118
+ 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:
119
+
120
+ <pre>
121
+ class Issue < ActiveRecord::Base
122
+ logical_parent :project
123
+
124
+ belongs_to :author
125
+ belongs_to :project
126
+
127
+ def self.is_indexable_by(user, parent = nil)
128
+ user.projects.include?(parent)
129
+ end
130
+
131
+ def self.is_creatable_by(user, parent = nil)
132
+ user.projects.include?(parent)
133
+ end
134
+
135
+ def is_updatable_by(user, parent = nil)
136
+ user == author && parent.is_active?
137
+ end
138
+
139
+ def is_deletable_by(user, parent = nil)
140
+ user == author
141
+ end
142
+
143
+ def is_readable_by(user, parent = nil)
144
+ user.projects.include?(parent)
145
+ end
146
+ end
147
+ </pre>
148
+
149
+ h3. Admins RULE!
150
+
151
+ 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.
152
+
153
+ h3. How to Test
154
+
155
+ I normally do something along these lines in RSpec:
156
+ <pre>
157
+ describe "Issue" do
158
+ before do
159
+ @project = mock_model(Project)
160
+ @author = mock_model(User, :projects => [@project])
161
+
162
+ @issue = Issue.factory_girl(:issue, :author => @author, :project => @project)
163
+ end
164
+
165
+ it "should be modifiable by the author when the Project is active" do
166
+ @project.stub!(:is_active? => true)
167
+ @issue.is_updatable_by(@author, @project).should be_true
168
+ end
169
+
170
+ it "should be deletable by the author" do
171
+ @issue.is_deletable_by(@author, @project).should be_true
172
+ end
173
+
174
+ it "should be readable by those assigned to the Project" do
175
+ Issue.is_readable_by(@author, @project).should be_true
176
+ end
177
+
178
+ it "should be creatable by those assigned to the Project" do
179
+ Issue.is_creatable_by(@author, @project).should be_true
180
+ end
181
+ end
182
+ </pre>
183
+
184
+ h3. Caveats
185
+
186
+ RESTful_ACL doesn't work with nested singleton resources. Wha? Yeah. Those are things in routes.rb like:
187
+
188
+ <pre>
189
+ # Note the singular forms in 'user.resource :profile'
190
+ map.resources :users do |user|
191
+ user.resource :profile
192
+ end
193
+ </pre>
194
+
195
+ 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.
196
+
197
+ h3. Help
198
+
199
+ Add a ticket to "RESTful_ACL's Lighthouse Account":http://mdarby.lighthouseapp.com/projects/28698-restful_acl/overview
200
+
201
+ h3. About the Author
202
+
203
+ 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
204
+
205
+ Feel free to check out my "blog":http://blog.matt-darby.com or "recommend me":http://www.workingwithrails.com/person/10908-matt-darby
@@ -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"
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,104 @@
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
+ p_index = bits.index(parent_klass.pluralize)
77
+ if p_index
78
+ parent_id = bits.at(p_index + 1)
79
+ parent_klass.classify.constantize.find(parent_id)
80
+ else
81
+ nil
82
+ end
83
+ end
84
+
85
+ def administrator?
86
+ current_user.respond_to?("is_admin?") && current_user.is_admin?
87
+ end
88
+
89
+ def permission_denied
90
+ logger.info("[RESTful_ACL] Permission denied to %s at %s for %s" %
91
+ [(logged_in? ? current_user.login : 'guest'), Time.now, request.request_uri])
92
+
93
+ redirect_to denied_url
94
+ end
95
+
96
+ def routing_error
97
+ logger.info("[RESTful_ACL] Routing error by %s at %s for %s" %
98
+ [(logged_in? ? current_user.login : 'guest'), Time.now, request.request_uri])
99
+
100
+ redirect_to error_url
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,67 @@
1
+ module RestfulAclHelper
2
+ # Adrian: sometimes, we want links to create a resource in a view that belongs to a different controller than the resource's
3
+ # controller. E.g. on a company page, I may want a link to create a new user at that company, in which case I can now use
4
+ # if creatable(User), or if indexable(User)
5
+
6
+ def indexable(specified_klass = nil)
7
+ return true if admin_enabled
8
+ (specified_klass || klass).is_indexable_by(current_user, parent_obj)
9
+ end
10
+
11
+
12
+ def creatable(specified_klass = nil)
13
+ return true if admin_enabled
14
+ (specified_klass || klass).is_creatable_by(current_user, parent_obj)
15
+ end
16
+ alias_method :createable, :creatable
17
+
18
+
19
+ def updatable(object)
20
+ return true if admin_enabled
21
+
22
+ parent = object.get_mom rescue nil
23
+ object.is_updatable_by(current_user, parent)
24
+ end
25
+ alias_method :updateable, :updatable
26
+
27
+
28
+ def deletable(object)
29
+ return true if admin_enabled
30
+
31
+ parent = object.get_mom rescue nil
32
+ object.is_deletable_by(current_user, parent)
33
+ end
34
+ alias_method :deleteable, :deletable
35
+
36
+
37
+ def readable(object)
38
+ return true if admin_enabled
39
+
40
+ parent = object.get_mom rescue nil
41
+ object.is_readable_by(current_user, parent)
42
+ end
43
+
44
+
45
+ private
46
+
47
+ def klass
48
+ params[:controller].classify.constantize
49
+ end
50
+
51
+ def parent_obj
52
+ parent_klass.find(parent_id) rescue nil
53
+ end
54
+
55
+ def parent_klass
56
+ klass.mom.to_s.classify.constantize
57
+ end
58
+
59
+ def parent_id
60
+ params["#{klass.mom.to_s}_id"]
61
+ end
62
+
63
+ def admin_enabled
64
+ current_user.respond_to?("is_admin?") && current_user.is_admin?
65
+ end
66
+
67
+ 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
@@ -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"
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: adriand-restful_acl
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.7
5
+ platform: ruby
6
+ authors:
7
+ - Matt Darby
8
+ - Adrian Duyzer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-04-16 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: A Rails gem that provides fine grained access control to RESTful resources in a Rails 2.0+ application.
18
+ email: adrianduyzer@gmail.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - MIT-LICENSE
27
+ - README.textile
28
+ - Rakefile
29
+ - init.rb
30
+ - install.rb
31
+ - lib/restful_acl_controller.rb
32
+ - lib/restful_acl_helper.rb
33
+ - lib/restful_acl_model.rb
34
+ - rails/init.rb
35
+ - uninstall.rb
36
+ has_rdoc: false
37
+ homepage: http://github.com/adriand/restful_acl
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: Object-level access control
62
+ test_files: []
63
+