mdarby-restful_acl 1.2.1 → 2.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.
data/README.textile ADDED
@@ -0,0 +1,173 @@
1
+ h1. BIG CHANGES!
2
+
3
+ RESTful_ACL has recently been overhauled (v2.0), please read this carefully for changes!
4
+
5
+ h2. RESTful_ACL
6
+
7
+ 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.
8
+
9
+ h3. What it does
10
+
11
+ 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.
12
+
13
+ h3. Requirements
14
+
15
+ RESTful_ACL requires the super amazing "RESTful_Authentication":https://github.com/technoweenie/restful-authentication plugin.
16
+
17
+ h3. How to Install
18
+
19
+ <pre>sudo gem install mdarby-restful_acl -s http://gems.github.com</pre>
20
+
21
+ And add the gem to your environment.rb file as thus:
22
+ <pre>
23
+ # In environment.rb:
24
+ config.gem "mdarby-restful_acl", :lib => 'restful_acl_controller'
25
+ </pre>
26
+
27
+ RESTful_ACL requires two named routes: "error" and "denied". Add the following to your routes.rb file:
28
+ <pre>
29
+ map.error '/error', :controller => '<some_controller>', :action => '<error_action>'
30
+ map.denied '/denied', :controller => '<some_controller>', :action => '<denied_action>'
31
+ </pre>
32
+
33
+ h3. How to Use
34
+
35
+ h4. Controllers
36
+
37
+ Add this before_filter into any controller that you'd like to restrict access to (or application.rb for your entire app).
38
+ <pre>before_filter :has_permission?</pre>
39
+
40
+ h4. Models
41
+
42
+ Define a parent resource (if one exists) by using the <b>my_mom</b> 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.
43
+
44
+ <pre>
45
+ class Issue < ActiveRecord::Base
46
+ my_mom :some_model_name
47
+
48
+ # This method checks permissions for the :index action
49
+ def self.is_indexable_by(user, parent = nil)
50
+
51
+ end
52
+
53
+ # This method checks permissions for the :create and :new action
54
+ def self.is_creatable_by(user, parent = nil)
55
+
56
+ end
57
+
58
+ # This method checks permissions for the :show action
59
+ def is_readable_by(user, parent = nil)
60
+
61
+ end
62
+
63
+ # This method checks permissions for the :update and :edit action
64
+ def is_updatable_by(user, parent = nil)
65
+
66
+ end
67
+
68
+ # This method checks permissions for the :destroy action
69
+ def is_deletable_by(user, parent = nil)
70
+
71
+ end
72
+
73
+ end
74
+ </pre>
75
+
76
+ H4. View Helpers
77
+
78
+ There are five view helpers also included in RESTful_ACL: #indexable, #creatable, #readable, #updatable, and #deletable. These enable you to do nifty things like:
79
+ <pre>
80
+ <%= link_to ‘Foo Index’, foos_path if indexable %>
81
+ <%= link_to 'Edit Foo', edit_foo_path(@foo) if updatable(@foo) %>
82
+ <%= link_to 'Create Foo', new_foo_path if creatable %>
83
+ <%= link_to 'View Foo', foo_path(@foo) if readable(@foo) %>
84
+ <%= link_to 'Delete Foo', foo_path(@foo) if deletable(@foo), :method => :destroy %>
85
+ </pre>
86
+
87
+ h3. Huh? Here's an example
88
+
89
+ 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:
90
+
91
+ <pre>
92
+ class Issue < ActiveRecord::Base
93
+ my_mom :project
94
+
95
+ belongs_to :author
96
+ belongs_to :project
97
+
98
+ def self.is_indexable_by(user, parent = nil)
99
+ user.projects.include?(parent)
100
+ end
101
+
102
+ def self.is_creatable_by(user, parent = nil)
103
+ user.projects.include?(parent)
104
+ end
105
+
106
+ def is_updatable_by(user, parent = nil)
107
+ user == author && parent.is_active?
108
+ end
109
+
110
+ def is_deletable_by(user, parent = nil)
111
+ user == author
112
+ end
113
+
114
+ def is_readable_by(user, parent = nil)
115
+ user.projects.include?(parent)
116
+ end
117
+ end
118
+ </pre>
119
+
120
+ h3. Admins RULE!
121
+
122
+ 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.
123
+
124
+ h3. How to Test
125
+
126
+ I normally do something along these lines in RSpec:
127
+ <pre>
128
+ describe "Issue" do
129
+ before do
130
+ @project = mock_model(Project)
131
+ @author = mock_model(User, :projects => [@project])
132
+
133
+ @issue = Issue.factory_girl(:issue, :author => @author, :project => @project)
134
+ end
135
+
136
+ it "should be modifiable by the author when the Project is active" do
137
+ @project.stub!(:is_active? => true)
138
+ @issue.is_updatable_by(@author, @project).should be_true
139
+ end
140
+
141
+ it "should be deletable by the author" do
142
+ @issue.is_deletable_by(@author, @project).should be_true
143
+ end
144
+
145
+ it "should be readable by those assigned to the Project" do
146
+ Issue.is_readable_by(@author, @project).should be_true
147
+ end
148
+
149
+ it "should be creatable by those assigned to the Project" do
150
+ Issue.is_creatable_by(@author, @project).should be_true
151
+ end
152
+ end
153
+ </pre>
154
+
155
+ h3. Caveats
156
+
157
+ Polymorphic resources (Comments, Attachments, etc) are a bit iffy for obvious reasons.
158
+ RESTful_ACL doesn't work with nested singleton resources. Wha? Yeah. Those are things in routes.rb like:
159
+
160
+ <pre>
161
+ # Note the singular forms in 'user.resource :profile'
162
+ map.resources :users do |user|
163
+ user.resource :profile
164
+ end
165
+ </pre>
166
+
167
+ 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.
168
+
169
+ h3. About the Author
170
+
171
+ My name is Matt Darby. I'm a 29 year old professional Web Developer and IT Manager. I am the IT Manager and Lead Web Developer at "Dynamix Engineering":http://dynamix-ltd.com and hold a MSCS from "Franklin University":http://franklin.edu in Columbus, OH.
172
+
173
+ Feel free to check out my "blog":http://blog.matt-darby.com or to "recommend me":http://www.workingwithrails.com/recommendation/new/person/10908-matt-darby
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,71 @@
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
+ object = nil
23
+ parent = get_parent_from_request_uri(klass) if klass.has_parent?
24
+ end
25
+
26
+ # Let's let the Model decide what is acceptable
27
+ permission_denied unless case params[:action]
28
+ when "index" then klass.is_indexable_by(current_user, parent)
29
+ when "new", "create" then klass.is_creatable_by(current_user, parent)
30
+ when "show" then object.is_readable_by(current_user, parent)
31
+ when "edit", "update" then object.is_updatable_by(current_user, parent)
32
+ when "destroy" then object.is_deletable_by(current_user, parent)
33
+ else object.is_readable_by(current_user, parent)
34
+ end
35
+
36
+ rescue
37
+ # Failsafe: If any funny business is going on, log and redirect
38
+ routing_error
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def get_parent_from_request_uri(child_klass)
45
+ parent_klass = child_klass.mom.to_s
46
+ bits = request.request_uri.split('/')
47
+ parent_id = bits.at(bits.index(parent_klass.pluralize) + 1)
48
+
49
+ parent_klass.classify.constantize.find(parent_id)
50
+ end
51
+
52
+ def administrator?
53
+ current_user.respond_to?("is_admin?") && current_user.is_admin?
54
+ end
55
+
56
+ def permission_denied
57
+ logger.info("[ACL] Permission denied to %s at %s for %s" %
58
+ [(logged_in? ? current_user.login : 'guest'), Time.now, request.request_uri])
59
+
60
+ redirect_to denied_url
61
+ end
62
+
63
+ def routing_error
64
+ logger.info("[ACL] Routing error by %s at %s for %s" %
65
+ [(logged_in? ? current_user.login : 'guest'), Time.now, request.request_uri])
66
+
67
+ redirect_to error_url
68
+ end
69
+
70
+ end
71
+ end
@@ -1,43 +1,53 @@
1
1
  module RestfulAclHelper
2
+ def indexable
3
+ return true if admin_enabled
4
+
5
+ parent = klass.mom rescue nil
6
+ klass.is_indexable_by(current_user, parent)
7
+ end
2
8
 
3
9
  def creatable
4
10
  return true if admin_enabled
5
-
6
- klass.is_creatable_by(current_user)
11
+
12
+ parent = klass.mom rescue nil
13
+ klass.is_creatable_by(current_user, parent)
7
14
  end
8
15
  alias_method :createable, :creatable
9
-
10
-
16
+
17
+
11
18
  def updatable(object)
12
19
  return true if admin_enabled
13
-
14
- object.is_updatable_by(current_user)
20
+
21
+ parent = object.get_mom rescue nil
22
+ object.is_updatable_by(current_user, parent)
15
23
  end
16
24
  alias_method :updateable, :updatable
17
-
18
-
25
+
26
+
19
27
  def deletable(object)
20
28
  return true if admin_enabled
21
-
22
- object.is_deletable_by(current_user)
29
+
30
+ parent = object.get_mom rescue nil
31
+ object.is_deletable_by(current_user, parent)
23
32
  end
24
33
  alias_method :deleteable, :deletable
25
-
26
-
27
- def readable(object = nil)
34
+
35
+
36
+ def readable(object)
28
37
  return true if admin_enabled
29
-
30
- klass.is_readable_by(current_user, object)
31
- end
38
+
39
+ parent = object.get_mom rescue nil
40
+ object.is_readable_by(current_user, parent)
41
+ end
42
+
32
43
 
33
44
  private
34
-
45
+
35
46
  def admin_enabled
36
47
  current_user.respond_to?("is_admin?") && current_user.is_admin?
37
48
  end
38
-
49
+
39
50
  def klass
40
51
  params[:controller].classify.constantize
41
52
  end
42
-
43
- end
53
+ 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 my_mom(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 CHANGED
@@ -1,7 +1,9 @@
1
- require 'restful_acl'
1
+ require 'restful_acl_controller'
2
2
  require 'restful_acl_helper'
3
+ require 'restful_acl_model'
3
4
 
4
- ActionController::Base.send :include, RestfulAcl
5
+ ActionController::Base.send :include, RestfulAclController
5
6
  ActionView::Base.send :include, RestfulAclHelper
7
+ ActiveRecord::Base.send :include, RestfulAclModel
6
8
 
7
9
  RAILS_DEFAULT_LOGGER.debug "** [RESTful_ACL] loaded"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mdarby-restful_acl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: "2.0"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Darby
@@ -9,11 +9,11 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-08 00:00:00 -08:00
12
+ date: 2009-01-15 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
16
- description: A Rails gem that provides fine grained access control to RESTful resources in a Rails 2.0+ project.
16
+ description: A Rails gem that provides fine grained access control to RESTful resources in a Rails 2.0+ application.
17
17
  email: matt@matt-darby.com
18
18
  executables: []
19
19
 
@@ -23,16 +23,17 @@ extra_rdoc_files: []
23
23
 
24
24
  files:
25
25
  - MIT-LICENSE
26
- - README
26
+ - README.textile
27
27
  - Rakefile
28
28
  - init.rb
29
- - lib/restful_acl.rb
29
+ - install.rb
30
+ - lib/restful_acl_controller.rb
30
31
  - lib/restful_acl_helper.rb
32
+ - lib/restful_acl_model.rb
31
33
  - rails/init.rb
32
- - tasks/restful_acl_tasks.rake
33
34
  - uninstall.rb
34
35
  has_rdoc: false
35
- homepage: http://github.com/mdarby/restful_acl/tree/master
36
+ homepage: http://github.com/mdarby/restful_acl
36
37
  post_install_message:
37
38
  rdoc_options: []
38
39
 
data/lib/restful_acl.rb DELETED
@@ -1,54 +0,0 @@
1
- module RestfulAcl
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 current_user.respond_to?("is_admin?") && current_user.is_admin? # Admins rule.
12
-
13
- begin
14
- # Load the Model based on the controller name
15
- klass = self.controller_name.classify.constantize
16
-
17
- # Load the object requested if the param[:id] exists
18
- object = klass.find(params[:id]) unless params[:id].blank?
19
-
20
- # Let's let the Model decide what is acceptable
21
- permission_denied unless case params[:action]
22
- when "index" then klass.is_readable_by(current_user)
23
- when "show" then klass.is_readable_by(current_user, object)
24
- when "edit", "update" then object.is_updatable_by(current_user)
25
- when "new", "create" then klass.is_creatable_by(current_user)
26
- when "destroy" then object.is_deletable_by(current_user)
27
- else klass.is_readable_by(current_user)
28
- end
29
-
30
- rescue
31
- # Failsafe: If any funny business is going on, log and redirect
32
- routing_error
33
- end
34
- end
35
-
36
-
37
- private
38
-
39
- def permission_denied
40
- logger.info("[ACL] Permission denied to %s at %s for %s" %
41
- [(logged_in? ? current_user.login : 'guest'), Time.now, request.request_uri])
42
-
43
- redirect_to denied_url
44
- end
45
-
46
- def routing_error
47
- logger.info("[ACL] Routing error by %s at %s for %s" %
48
- [(logged_in? ? current_user.login : 'guest'), Time.now, request.request_uri])
49
-
50
- redirect_to error_url
51
- end
52
-
53
- end
54
- end
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :restful_acl do
3
- # # Task goes here
4
- # end