authorization-san 1.0.1

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/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ (c) 2009 Fingertips, Manfred Stienstra <m.stienstra@fngtps.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,21 @@
1
+ = Authorization-San
2
+
3
+ Authorization-san allows you to specify access policies in your controllers. The plugin assumes a number of things about the application.
4
+
5
+ * If a user has authenticated with the application, it's stored in <tt>@authenticated</tt>. The method of authentication doesn't matter. It also doesn't matter what you put in @authenticated, as long as it's truthy.
6
+ * <tt>@authenticated</tt> has either a <tt>role</tt> attribute or a number of methods to query for the role: <tt>admin?</tt>, <tt>editor?</tt>, <tt>guest?</tt>. When the <tt>@authenticated</tt> object doesn't have role methods you can't use role based authentication rules, but the rest still works.
7
+
8
+ == What does it look like?
9
+
10
+ class BooksController < ActionController::Base
11
+ # Visitors can see list of books and book pages
12
+ allow_access :all, :only => [:index, :show]
13
+ # An editor can create new books, but…
14
+ allow_access :editor, :only => [:new, :create]
15
+ # …she can only update her own books.
16
+ allow_access(:editor, :only => [:edit, :update]) { @book = @authenticated.books.find(params[:id]) }
17
+ # Admin users can do it all.
18
+ allow_access :admin
19
+ end
20
+
21
+ The best place to start learning more is the <tt>examples</tt> directory in the source.
@@ -0,0 +1,11 @@
1
+ # The administrations controller is nested under organizations (ie. /organizations/3214/administrations)
2
+ class PagesController < ApplicationController
3
+ # The following rule only allows @authenticated if @authenticated.organization.id == params[:organization_id].
4
+ # Roughly translated this means that the authenticated user can only access resources belonging to its own
5
+ # organization.
6
+ allow_access :authenticated, :scope => :organization
7
+
8
+ def index
9
+ @administrations = @authenticated.organization.administrations
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ class ApplicationController < ActionController::Base
2
+ # You have to specify where you want these actions to appear in your filter chain. Make sure you :block_access
3
+ # before any sensitive processing occurs.
4
+ before_filter :find_authenticated, :block_access
5
+
6
+ private
7
+
8
+ # Find the authenticated user
9
+ def find_authenticated
10
+ @authenticated = authenticate_with_http_basic { |username, password| User.authenticate(username, password) }
11
+ end
12
+
13
+ # Access was forbidden to client requesting the resource. React to that appropriately. Note that this reply is very
14
+ # bare bones and you might want to return more elaborate responses in a real application.
15
+ def access_forbidden
16
+ if @authenticated.nil?
17
+ request_http_basic_authentication "Accounting"
18
+ else
19
+ head :forbidden
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ class ApplicationController < ActionController::Base
2
+ before_filter :find_authenticated, :block_access
3
+
4
+ private
5
+
6
+ # Find the authenticated user, cookie based authentication for browser users and HTTP Basic Authentication for
7
+ # API users. Note that this does not allow you to get HTML resources when logged in through Basic Auth.
8
+ def find_authenticated
9
+ respond_to do |format|
10
+ format.html do
11
+ @authenticated = Person.find_by_id session[:authenticated_id] unless session[:authenticated_id].nil?
12
+ end
13
+ format.xml do
14
+ @authenticated = authenticate_with_http_basic { |username, password| User.authenticate(username, password) }
15
+ end
16
+ end
17
+ end
18
+
19
+ # Access was forbidden to client requesting the resource. React to that appropriately. Note that this reply is very
20
+ # bare bones and you might want to return more elaborate responses in a real application.
21
+ def access_forbidden
22
+ unless @authenticated
23
+ # The user is not authenticated; ask for credentials
24
+ respond_to do |format|
25
+ format.html { redirect_to login_url }
26
+ format.xml { request_http_basic_authentication "Accounting" }
27
+ end
28
+ else
29
+ # The user is authentication but unauthorized for this resource
30
+ head :forbidden
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,6 @@
1
+ class AuthenticatedController < ApplicationController
2
+ # Authenticated users can access all actions
3
+ allow_access :authenticated
4
+
5
+ def index; end
6
+ end
@@ -0,0 +1,28 @@
1
+ # The pages controller is a nest resource under users (ie. /users/12/pages)
2
+ class PagesController < ApplicationController
3
+ # A user may only access her own index
4
+ allow_access(:authenticated, :only => :index) { @authenticated == @user }
5
+ # A user may only access her own pages
6
+ allow_access(:authenticated, :only => :show) { @authenticated == @page.user}
7
+
8
+ # Always find the user the pages are nested under before applying the rules
9
+ prepend_before_filter :find_user
10
+ # Find the page before applying the rules when the show action is called
11
+ prepend_before_filter :find_page, :only => :show
12
+
13
+ def index
14
+ @pages = @user.pages
15
+ end
16
+
17
+ def show; end
18
+
19
+ private
20
+
21
+ def find_user
22
+ @user = User.find params[:user_id]
23
+ end
24
+
25
+ def find_page
26
+ @page = Page.find params[:id]
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ # The pages controller is nested under users (ie. /users/12/pages)
2
+ class PagesController < ApplicationController
3
+ # Users can only reach pages nested under their user_id. Note that this doesn't define the complete access policy,
4
+ # some of the authorization is still done in the actions. See pages_controller_with_full_policy.rb for an example
5
+ # of specifying everything in access rules.
6
+ allow_access(:authenticated) { @authenticated.to_param == params[:user_id].to_param }
7
+
8
+ before_filter :find_user
9
+
10
+ def index
11
+ @pages = @user.pages
12
+ end
13
+
14
+ def show
15
+ @page = @user.pages.find params[:id]
16
+ rescue ActiveRecord::RecordNotFound
17
+ head :forbidden
18
+ end
19
+
20
+ private
21
+
22
+ def find_user
23
+ @user = User.find params[:user_id]
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ class PublicController < ApplicationController
2
+ # Everyone can access all actions
3
+ allow_access
4
+
5
+ def index; end
6
+ end
@@ -0,0 +1,27 @@
1
+ class UsersController < ApplicationController
2
+ # The default is to deny all access. Every rule creates a 'hole' in this policy. You can specify multiple rules
3
+ # per role if you want.
4
+
5
+ # The 'admin' role (@authenticated.role) has access to all the actions.
6
+ allow_access :admin
7
+ # The 'editor' role has access to the index and show action.
8
+ allow_access :editor, :only => [:index, :show]
9
+ # The 'user' role has access to the index, show, edit and update role only if the resource he's editing is the same
10
+ # as the user resource.
11
+ allow_access :user, :only => [:index, :show, :edit, :update], :user_resource => true
12
+ # The 'guest' role has access to the index and show action if the Proc returns true.
13
+ allow_access(:guest, :only => [:index, :show]) { @authenticated.valid_email? }
14
+ # Everyone can access the listing and the index action, the other actions can be accessed when it's not sunday.
15
+ allow_access :only => :listing
16
+ allow_access :only => :index
17
+ allow_access() { Time.now.strftime('%A') != 'Sunday' }
18
+
19
+ def index; end
20
+ def listing; end
21
+ def new; end
22
+ def create; end
23
+ def show; end
24
+ def edit; end
25
+ def update; end
26
+ def destroy; end
27
+ end
@@ -0,0 +1,2 @@
1
+ require 'authorization/allow_access'
2
+ require 'authorization/block_access'
@@ -0,0 +1,78 @@
1
+ module Authorization
2
+ module AllowAccess
3
+ # By default you block all access to the controller with <tt>block_access</tt>, with <tt>allow_access</tt> you
4
+ # specify who can access the actions on the controller under certain conditions. <tt>allow_access</tt> can deal
5
+ # with accounts with and without roles.
6
+ #
7
+ # *Examples*
8
+ #
9
+ # Everyone can access all actions on the controller.
10
+ # allow_access
11
+ # allow_access :all
12
+ # Everyone with the admin role can access the controller.
13
+ # allow_access :admin
14
+ # Everyone with the admin and editor role can access the controller.
15
+ # allow_access :admin, :editor
16
+ # Everyone with the editor role can access the index. show, edit and update actions.
17
+ # allow_access :editor, :only => [:index, :show, :edit, :update]
18
+ # A coordinator can do anything the admin can, except for delete
19
+ # allow_access :coordinator, :except => :destroy
20
+ # Everyone with the admin and editor role can access the show action.
21
+ # allow_access :admin, :editor, :action => :show
22
+ # A guest can view all resources if he has view permissions. The block is evaltuated in the controller's instance.
23
+ # Note that rules are evaluated when <tt>block_access</tt> is run.
24
+ # allow_access(:guest, :only => [:index, :show]) { @authenticated.view_permission? }
25
+ # Specifying a role is optional, if you don't specify a role the rule is added for the default role <tt>:all</tt>.
26
+ # allow_access(:only => :unsubscribe) { @authenticated.subscribed? }
27
+ # Only allow authenticated users, :authenticated is a special role meaning all authenticated users.
28
+ # allow_access :authenticated
29
+ # You need to be authenticated for the secret action
30
+ # allow_access :all, :except => :secret
31
+ # allow_access :authenticated, :only => :secret
32
+ #
33
+ # The following special directives might be a little hard to explain, I will give the equivalent rule with the
34
+ # block access.
35
+ #
36
+ # Imagine we have a user controller, every user has an organization association. The users resource is nested
37
+ # in the organization resource like this.
38
+ # map.resources :organizations { |org| org.resources :users }
39
+ # Now we want the user to edit his own resource (for instance to update the password).
40
+ # allow_access :only => [:index, :show, :edit, :update], :user_resource => true
41
+ # allow_access(:only => [:index, :show, :edit, :update]) do
42
+ # @authenticated.id == params[:id].to_i
43
+ # end
44
+ # We could also specify that a user can edit everything in his own organization.
45
+ # allow_access :only => [:index, :show, :edit, :update], :scope => :organization
46
+ # allow_access(:only => [:index, :show, :edit, :update]) do
47
+ # @authenticated.organization.id == params[:organization_id].to_i
48
+ # end
49
+ def allow_access(*args, &block)
50
+ unless self.respond_to?(:access_allowed_for)
51
+ self.class_inheritable_accessor(:access_allowed_for)
52
+ send(:protected, :access_allowed_for, :access_allowed_for=)
53
+ end
54
+ self.access_allowed_for ||= HashWithIndifferentAccess.new
55
+ if args.first.kind_of?(Hash) || args.empty?
56
+ self.access_allowed_for[:all] ||= []
57
+ self.access_allowed_for[:all] << {
58
+ :directives => args.first || {},
59
+ :block => block
60
+ }
61
+ else
62
+ directives = args.extract_options!
63
+ roles = args.flatten
64
+ if roles.delete(:authenticated) or roles.delete('authenticated')
65
+ roles = [:all] if roles.empty?
66
+ directives[:authenticated] = true
67
+ end
68
+ roles.each do |role|
69
+ self.access_allowed_for[role.to_s] ||= []
70
+ self.access_allowed_for[role.to_s] << {
71
+ :directives => directives,
72
+ :block => block
73
+ }
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,133 @@
1
+ module Authorization
2
+ module BlockAccess
3
+ protected
4
+
5
+ # Block access to all actions in the controller, designed to be used as a <tt>before_filter</tt>.
6
+ # class ApplicationController < ActionController::Base
7
+ # before_filter :block_access
8
+ # end
9
+ def block_access
10
+ die_if_undefined
11
+ unless @authenticated.nil?
12
+ # Find the user's roles
13
+ roles = []
14
+ roles << @authenticated.role if @authenticated.respond_to?(:role)
15
+ access_allowed_for.keys.each do |role|
16
+ roles << role.to_s if @authenticated.respond_to?("#{role}?") and @authenticated.send("#{role}?")
17
+ end
18
+ # Check if any of the roles give her access
19
+ roles.each do |role|
20
+ return true if access_allowed?(params, role, @authenticated)
21
+ end
22
+ end
23
+ return true if access_allowed?(params, :all, @authenticated)
24
+ access_forbidden
25
+ end
26
+
27
+ # Checks if access is allowed for the params, role and authenticated user.
28
+ # access_allowed?({:action => :show, :id => 1}, :admin, @authenticated)
29
+ def access_allowed?(params, role, authenticated=nil)
30
+ die_if_undefined
31
+ if (rules = access_allowed_for[role]).nil?
32
+ logger.debug(" \e[31mCan't find rules for `#{role}'\e[0m")
33
+ return false
34
+ end
35
+ !rules.detect do |rule|
36
+ if !action_allowed_by_rule?(rule, params, role) or !resource_allowed_by_rule?(rule, params, role, authenticated) or !block_allowed_by_rule?(rule)
37
+ logger.debug(" \e[31mAccess DENIED by RULE #{rule.inspect} FOR `#{role}'\e[0m")
38
+ false
39
+ else
40
+ logger.debug(" \e[32mAccess GRANTED by RULE #{rule.inspect} FOR `#{role}'\e[0m")
41
+ true
42
+ end
43
+ end.nil?
44
+ end
45
+
46
+ # <tt>access_forbidden</tt> is called by <tt>block_access</tt> when access is forbidden. This method does
47
+ # nothing by default. Make sure you return <tt>false</tt> from the method if you want to halt the filter
48
+ # chain.
49
+ def access_forbidden
50
+ false
51
+ end
52
+
53
+ # Checks if a certain action can be accessed by the role.
54
+ # If you want to check for <tt>action_allowed?</tt>, <tt>resource_allowed?</tt> and <tt>block_allowed?</tt>
55
+ # use <tt>access_allowed?</tt>.
56
+ # action_allowed?({:action => :show, :id => 1}, :editor)
57
+ def action_allowed?(params, role=:all)
58
+ die_if_undefined
59
+ return false if (rules = access_allowed_for[role]).nil?
60
+ !rules.detect { |rule| action_allowed_by_rule?(rule, params, role) }.nil?
61
+ end
62
+
63
+ def action_allowed_by_rule?(rule, params, role) #:nodoc:
64
+ return false if (action = params[:action]).nil?
65
+ directives = rule[:directives]
66
+ return false if directives[:only].kind_of?(Array) and !directives[:only].include?(action.to_sym)
67
+ return false if directives[:only].kind_of?(Symbol) and directives[:only] != action.to_sym
68
+ return false if directives[:except].kind_of?(Array) and directives[:except].include?(action.to_sym)
69
+ return false if directives[:except].kind_of?(Symbol) and directives[:except] == action.to_sym
70
+ true
71
+ end
72
+
73
+ # Checks if the resource indicated by the params can be accessed by user.
74
+ # If you want to check for <tt>action_allowed?</tt>, <tt>resource_allowed?</tt> and <tt>block_allowed?</tt>
75
+ # use <tt>access_allowed?</tt>.
76
+ # resource_allowed?({:id => 1, :organization_id => 12}, :guest, @authenticated)
77
+ def resource_allowed?(params, role=:all, user=nil)
78
+ user ||= @authenticated
79
+ die_if_undefined
80
+ return false if (rules = access_allowed_for[role]).nil?
81
+ !rules.detect { |rule| resource_allowed_by_rule?(rule, params, role, user) }.nil?
82
+ end
83
+
84
+ def resource_allowed_by_rule?(rule, params, role, user) #:nodoc:
85
+ directives = rule[:directives]
86
+ if directives[:authenticated]
87
+ return false unless user
88
+ end
89
+ begin
90
+ if directives[:user_resource]
91
+ return false if params[:id].nil? or user.id.nil?
92
+ return false if params[:id].to_i != user.id.to_i
93
+ end
94
+ rescue NoMethodError
95
+ end
96
+ begin
97
+ if scope = directives[:scope]
98
+ assoc_id = params["#{scope}_id"].to_i
99
+ begin
100
+ object_id = user.send(scope).id.to_i
101
+ rescue NoMethodError
102
+ return false
103
+ end
104
+ return false if assoc_id.nil? or object_id.nil?
105
+ return false if assoc_id != object_id
106
+ end
107
+ rescue NoMethodError
108
+ end
109
+ true
110
+ end
111
+
112
+ # Checks if the blocks associated with the rules doesn't stop the user from acessing the resource.
113
+ # If you want to check for <tt>action_allowed?</tt>, <tt>resource_allowed?</tt> and <tt>block_allowed?</tt>
114
+ # use <tt>access_allowed?</tt>.
115
+ # block_allowed?(:guest)
116
+ def block_allowed?(role)
117
+ die_if_undefined
118
+ return false if (rules = access_allowed_for[role]).nil?
119
+ !rules.detect { |rule| block_allowed_by_rule?(rule) }.nil?
120
+ end
121
+
122
+ def block_allowed_by_rule?(rule) #:nodoc:
123
+ return false if !rule[:block].nil? and !rule[:block].bind(self).call
124
+ true
125
+ end
126
+
127
+ def die_if_undefined #:nodoc:
128
+ if !self.respond_to?(:access_allowed_for) or access_allowed_for.nil?
129
+ raise ArgumentError, "Please specify access control using `allow_access' in the controller"
130
+ end
131
+ end
132
+ end
133
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'authorization'
2
+
3
+ ActionController::Base.send(:include, Authorization::BlockAccess)
4
+ ActionController::Base.send(:extend, Authorization::AllowAccess)
@@ -0,0 +1,167 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ require 'controllers/all'
4
+ require 'models/resource'
5
+
6
+ class BehaviourTest < ActionController::TestCase
7
+ test "access is denied for nonexistant actions without an access rule" do
8
+ tests UsersController, :authenticated => Resource.new(:role => :tester, :id => 1)
9
+ get :unknown, :id => 1
10
+ assert_response :forbidden
11
+ end
12
+
13
+ test "roles are properly checked" do
14
+ tests UsersController, :authenticated => Resource.new
15
+ {
16
+ [:admin, :index] => :ok,
17
+ [:admin, :show] => :ok,
18
+ [:admin, :guest] => :ok,
19
+ [:admin, :listing] => :ok,
20
+ [:admin, :react] => :ok,
21
+ [:editor, :index] => :ok,
22
+ [:editor, :guest] => :forbidden,
23
+ [:editor, :listing] => :ok,
24
+ [:editor, :react] => :ok,
25
+ [:guest, :index] => :forbidden,
26
+ [:guest, :guest] => :ok,
27
+ [:guest, :listing] => :ok,
28
+ [:guest, :react] => :ok,
29
+ [:user, :listing] => :ok,
30
+ [:user, :react] => :ok,
31
+ [:user, :index] => :forbidden,
32
+ }.each do |(role, action), status|
33
+ @controller.authenticated.role = role
34
+ get action
35
+ assert_response status
36
+ end
37
+ end
38
+
39
+ test "authenticated is allowed to access its own resource" do
40
+ tests UsersController, :authenticated => Resource.new(:role => :tester, :id => 1)
41
+ get :show, :id => 1
42
+ assert_response :ok
43
+ end
44
+
45
+ test "authenticated is not allowed to access other users" do
46
+ tests UsersController, :authenticated => Resource.new(:role => :tester, :id => 1)
47
+ get :show, :id => 2
48
+ assert_response :forbidden
49
+ end
50
+
51
+ test "authenticated is allowed to access within the defined scope" do
52
+ tests UsersController, :authenticated => Resource.new(:role => :reader, :organization => Resource.new(:id => 1))
53
+ get :show, :organization_id => 1
54
+ assert_response :success
55
+ end
56
+
57
+ test "authenticated is not allowed to access outside of the defined scope" do
58
+ tests UsersController, :authenticated => Resource.new(:role => :tester, :id => 1)
59
+ get :show, :organization_id => 2
60
+ assert_response :forbidden
61
+ end
62
+ test "rule without restrictions opens up the whole controller" do
63
+ tests PublicController
64
+ get :index
65
+ assert_response :ok
66
+ end
67
+
68
+ test "rule with special role :authenticated allows when @authenticated is truthy" do
69
+ tests AuthenticatedController, :authenticated => true
70
+ get :index
71
+ assert_response :ok
72
+ end
73
+
74
+ test "rule with special role :authenticated disallows when @authenticated is not truthy" do
75
+ tests AuthenticatedController, :authenticated => false
76
+ get :index
77
+ assert_response :forbidden
78
+ end
79
+
80
+ test "rule with broken block should raise an exception when evaluated" do
81
+ tests BrokenBlockController
82
+ assert_raises(NoMethodError) do
83
+ get :index
84
+ end
85
+ end
86
+
87
+ test "rule with block should only be evaluated when the action matches" do
88
+ tests BrokenBlockController
89
+ assert_nothing_raised do
90
+ get :show
91
+ end
92
+ end
93
+
94
+ test "rule with block should only be evaluated when the role matches" do
95
+ tests BrokenBlockController, :authenticated => Resource.new(:role => :admin)
96
+ assert_nothing_raised do
97
+ get :show
98
+ end
99
+ end
100
+
101
+ test "rule with block should only be evaluated when the special role matches" do
102
+ tests BrokenBlockController, :authenticated => true
103
+ assert_nothing_raised do
104
+ get :show
105
+ end
106
+ end
107
+
108
+ test "rule with multiple roles" do
109
+ tests MultipleRolesController, :authenticated => Resource.new
110
+ {
111
+ [:a, :index] => :ok,
112
+ [:b, :index] => :ok,
113
+ [:c, :index] => :ok,
114
+ [:d, :index] => :ok,
115
+ [:e, :index] => :ok,
116
+ [:f, :index] => :ok,
117
+ [:e, :show] => :forbidden,
118
+ [:f, :show] => :forbidden,
119
+ [:g, :index] => :forbidden,
120
+ [:h, :index] => :forbidden,
121
+ [:g, :show] => :ok,
122
+ [:h, :show] => :ok,
123
+ }.each do |(role, action), status|
124
+ @controller.authenticated.role = role
125
+ get action
126
+ assert_response status
127
+ end
128
+ end
129
+
130
+ test "rule with special role, user resource and action restriction, should disallow unauthenticated" do
131
+ tests ComplicatedController
132
+ get :show, :id => 1
133
+ assert_response :forbidden
134
+ end
135
+
136
+ test "rule with special role, user resource and action restriction, should disallow incorrect user" do
137
+ tests ComplicatedController, :authenticated => Resource.new(:id => 2)
138
+ get :show, :id => 1
139
+ assert_response :forbidden
140
+ end
141
+
142
+ test "rule with special role, user resource and action restriction, should allow correct user" do
143
+ tests ComplicatedController, :authenticated => Resource.new(:id => 1)
144
+ get :show, :id => 1
145
+ assert_response :ok
146
+ end
147
+
148
+ test "controller with rule about special role, user resource and action restriction, should allow open actions" do
149
+ tests ComplicatedController
150
+ get :index
151
+ assert_response :ok
152
+ end
153
+
154
+ private
155
+
156
+ def tests(controller, options={})
157
+ @request = ActionController::TestRequest.new
158
+ @response = ActionController::TestResponse.new
159
+ @controller ||= controller.new rescue nil
160
+
161
+ @controller.request = @request
162
+ @controller.params = {}
163
+ @controller.send(:initialize_current_url)
164
+
165
+ @controller.authenticated = options[:authenticated]
166
+ end
167
+ end
@@ -0,0 +1,252 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ require 'models/resource'
4
+
5
+ class MethodsTest < ActiveSupport::TestCase
6
+ include Authorization::BlockAccess
7
+ attr_accessor :params, :access_allowed_for
8
+
9
+ def logger
10
+ @logger ||= Logger.new('/dev/null')
11
+ end
12
+
13
+ def do_false
14
+ false
15
+ end
16
+
17
+ def do_true
18
+ true
19
+ end
20
+
21
+ def test_action_allowed
22
+ @access_allowed_for = {
23
+ :admin => [{
24
+ :directives => {}
25
+ }],
26
+ :editor => [{
27
+ :directives => {:only => :index}
28
+ }],
29
+ :complex => [
30
+ {:directives => {:only => :index}},
31
+ {:directives => {:only => :show}}
32
+ ],
33
+ :all => [{
34
+ :directives => {:only => :listing}
35
+ }]
36
+ }
37
+ assert_action_allowed({
38
+ [:admin, :index] => true,
39
+ [:admin, :show] => true,
40
+ [:admin, :unknown] => true,
41
+ [:editor, :unknown] => false,
42
+ [:editor, :index] => true,
43
+ [:all, :index] => false,
44
+ [:all, :unknown] => false,
45
+ [:all, :listing] => true,
46
+ [:complex, :index] => true,
47
+ [:complex, :show] => true,
48
+ [:complex, :unknown] => false
49
+ })
50
+ end
51
+
52
+ def test_action_allowed_open
53
+ @access_allowed_for = {:all => [{:directives => {}}] }
54
+ assert_action_allowed({
55
+ [:admin, :index] => false,
56
+ [:all, :show] => true,
57
+ [:unknown, :show] => false
58
+ })
59
+ end
60
+
61
+ def test_action_allowed_closed
62
+ @access_allowed_for = {}
63
+ assert_action_allowed({
64
+ [:admin, :index] => false,
65
+ [:all, :show] => false,
66
+ [:show, :unknown] => false
67
+ })
68
+ end
69
+
70
+ def test_action_allowed_nil
71
+ @access_allowed_for = nil
72
+ params = HashWithIndifferentAccess.new :action => :something
73
+ assert_raises(ArgumentError) { action_allowed?(params, :something) }
74
+ end
75
+
76
+ def test_resource_allowed_user_resource
77
+ @access_allowed_for = {
78
+ :user => [{
79
+ :directives => {:only => [:index, :show], :user_resource => true}
80
+ }]
81
+ }
82
+ assert_resource_allowed({
83
+ [{}, :admin, {}] => false,
84
+ [{:id => 1}, :admin, {:id => 1}] => false,
85
+ [{}, :admin, {:id => 1}] => false,
86
+ [{:id => 1}, :admin, {}] => false,
87
+ [{}, :user, {}] => false,
88
+ [{:id => 1}, :user, {:id => 1}] => true,
89
+ [{:id => 2}, :user, {:id => 1}] => false,
90
+ [{:id => 1}, :user, {:id => 2}] => false,
91
+ [{}, :user, {:id => 1}] => false,
92
+ [{:id => 1}, :user, {}] => false,
93
+ })
94
+ end
95
+
96
+ def test_resource_allowed_scope
97
+ @access_allowed_for = {
98
+ :user => [{
99
+ :directives => {:only => [:index, :show], :scope => :organization}
100
+ }]
101
+ }
102
+ assert_resource_allowed({
103
+ [{}, :admin, {}] => false,
104
+ [{:organization_id => 1}, :admin, {:organization => Resource.new({:id => 1})}] => false,
105
+ [{}, :admin, {:organization => Resource.new({:id => 1})}] => false,
106
+ [{:organization_id => 1}, :admin, {}] => false,
107
+ [{}, :user, {}] => false,
108
+ [{:organization_id => 1}, :user, {:organization => Resource.new({:id => 1})}] => true,
109
+ [{}, :user, {:organization => Resource.new({:id => 1})}] => false,
110
+ [{:organization_id => 1}, :user, {}] => false,
111
+ [{:organization_id => 2}, :user, {:organization => Resource.new({:id => 1})}] => false,
112
+ [{:organization_id => 1}, :user, {:organization => Resource.new({:id => 2})}] => false,
113
+ })
114
+ end
115
+
116
+ def test_resource_allowed_authenticated
117
+ @access_allowed_for = {
118
+ :all => [{
119
+ :directives => {:authenticated => true}
120
+ }]
121
+ }
122
+ assert !resource_allowed?({}, :admin, nil)
123
+ assert !resource_allowed?({}, :admin, true)
124
+ assert resource_allowed?({}, :all, true)
125
+ assert resource_allowed?({:action => :edit}, :all, true)
126
+ end
127
+
128
+ def test_block_allowed
129
+ @access_allowed_for = {
130
+ :admin => [{:block => MethodsTest.instance_method(:do_true)}],
131
+ :all => [{:block => MethodsTest.instance_method(:do_false)}]
132
+ }
133
+ assert_block_allowed({
134
+ :admin => true,
135
+ :all => false
136
+ })
137
+ end
138
+
139
+ def test_access_forbidden
140
+ assert_equal false, access_forbidden
141
+ end
142
+
143
+ def test_block_access
144
+ @access_allowed_for = {
145
+ :admin => [{
146
+ :directives => {}
147
+ }],
148
+ :editor => [{
149
+ :directives => {:only => :index}
150
+ }],
151
+ :blocked_guest => [{
152
+ :directives => {:only => :index},
153
+ :block => MethodsTest.instance_method(:do_false)
154
+ }],
155
+ :open_guest => [{
156
+ :directives => {:only => :index},
157
+ :block => MethodsTest.instance_method(:do_true)
158
+ }],
159
+ :complex => [
160
+ {:directives => {:only => :index}},
161
+ {:directives => {:only => :show}}
162
+ ],
163
+ :all => [{
164
+ :directives => {:only => :listing}
165
+ }]
166
+ }
167
+ assert_block_access({
168
+ [:admin, :index] => true,
169
+ [:admin, :show] => true,
170
+ [:admin, :unknown] => true,
171
+ [:editor, :unknown] => false,
172
+ [:editor, :index] => true,
173
+ [:blocked_guest, :index] => false,
174
+ [:blocked_guest, :unknown] => false,
175
+ [:open_guest, :index] => true,
176
+ [:open_guest, :unknown] => false,
177
+ [:all, :index] => false,
178
+ [:all, :unknown] => false,
179
+ [:all, :listing] => true,
180
+ [:complex, :index] => true,
181
+ [:complex, :show] => true,
182
+ [:complex, :unknown] => false
183
+ })
184
+ end
185
+
186
+ def test_block_access_closed
187
+ @access_allowed_for = {}
188
+ assert_equal false, block_access
189
+ end
190
+
191
+ def test_block_access_nil
192
+ @access_allowed_for = nil
193
+ assert_raises(ArgumentError) { block_access }
194
+ end
195
+
196
+ def test_block_access_on_object_with_role_and_accessors_defined
197
+ @access_allowed_for = {:special => [{
198
+ :directives => {}
199
+ }]}
200
+ @authenticated = Resource.new :role => 'user', :'special?' => true
201
+ @params = HashWithIndifferentAccess.new :action => :new
202
+ assert !block_access
203
+ end
204
+
205
+ def test_block_access_on_object_with_multiple_block_accessors_defined
206
+ @access_allowed_for = {:special => [{
207
+ :directives => {}
208
+ }]}
209
+ @authenticated = Resource.new :'special?' => true, :'admin?' => true
210
+ @params = HashWithIndifferentAccess.new :action => :new
211
+ assert !block_access
212
+ end
213
+
214
+ def test_block_access_on_object_with_accessor_dined_on_role
215
+ @access_allowed_for = {:user => [{
216
+ :directives => {}
217
+ }]}
218
+ @authenticated = Resource.new :role => 'user', :'special?' => true
219
+ @params = HashWithIndifferentAccess.new :action => :new
220
+ assert !block_access
221
+ end
222
+
223
+ private
224
+
225
+ def assert_action_allowed(h)
226
+ h.each do |pair, value|
227
+ params = HashWithIndifferentAccess.new(:action => pair.last)
228
+ assert_equal value, action_allowed?(params, pair.first), "For #{pair.inspect} => #{value.inspect}"
229
+ end
230
+ end
231
+
232
+ def assert_resource_allowed(h)
233
+ h.each do |triplet, value|
234
+ params = HashWithIndifferentAccess.new(triplet.first)
235
+ assert_equal value, resource_allowed?(params, triplet[1], triplet.last ? Resource.new(triplet.last) : nil), "For #{triplet.inspect} => #{value.inspect}"
236
+ end
237
+ end
238
+
239
+ def assert_block_allowed(h)
240
+ h.each do |role, value|
241
+ assert_equal value, block_allowed?(role)
242
+ end
243
+ end
244
+
245
+ def assert_block_access(h)
246
+ h.each do |pair, value|
247
+ @authenticated = Resource.new :role => pair.first
248
+ @params = {:action => pair.last}
249
+ assert_equal value, block_access, "For #{pair.inspect} => #{value.inspect}"
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ require 'controllers/application_controller'
4
+ require 'controllers/users_controller'
5
+ require 'models/resource'
6
+
7
+ class StructuralTest < ActionController::TestCase
8
+ tests UsersController
9
+
10
+ def setup
11
+ @controller.authenticated = Resource.new(:role => :admin)
12
+ end
13
+
14
+ test "rules should be in place" do
15
+ assert @controller.__send__(:access_allowed_for)
16
+ end
17
+
18
+ test "role accessors should not be public" do
19
+ assert @acontroller.public_methods.grep(/access_allowed_for/).empty?
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ require 'controllers/application_controller'
2
+ require 'controllers/authenticated_controller'
3
+ require 'controllers/broken_block_controller'
4
+ require 'controllers/complicated_controller'
5
+ require 'controllers/public_controller'
6
+ require 'controllers/multiple_roles_controller'
7
+ require 'controllers/users_controller'
@@ -0,0 +1,16 @@
1
+ class ApplicationController < ActionController::Base
2
+ attr_accessor :authenticated
3
+
4
+ before_filter :block_access
5
+
6
+ def access_forbidden
7
+ head :forbidden
8
+ false
9
+ end
10
+
11
+ def logger
12
+ @logger ||= Logger.new('/dev/null')
13
+ end
14
+
15
+ def rescue_action(e) raise e end;
16
+ end
@@ -0,0 +1,7 @@
1
+ class AuthenticatedController < ApplicationController
2
+ allow_access :authenticated
3
+
4
+ def index
5
+ head :ok
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ class BrokenBlockController < ApplicationController
2
+ allow_access(:only => :index) { nil.unknown_method }
3
+ allow_access(:only => :show) { true }
4
+ allow_access(:authenticated, :only => :edit) { @authenticated.unknown_method }
5
+ allow_access(:admin, :only => :edit) { @authenticated.unknown_method }
6
+
7
+ %w(index show edit).each do |name|
8
+ define_method(name) { head :ok }
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ class ComplicatedController < ApplicationController
2
+ allow_access :all, :only => :index
3
+ allow_access :authenticated, :only => [:show, :edit, :update], :user_resource => true
4
+
5
+ %w(index show edit update).each do |name|
6
+ define_method(name) { head :ok }
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ class MultipleRolesController < ApplicationController
2
+ allow_access :a, :b
3
+ allow_access [:c, :d]
4
+ allow_access [:e, :f], :only => :index
5
+ allow_access :g, :h, :only => :show
6
+
7
+ %w(index show).each do |name|
8
+ define_method(name) { head 200 }
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ class PublicController < ApplicationController
2
+ allow_access
3
+
4
+ def index
5
+ head :ok
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ class UsersController < ApplicationController
2
+ allow_access :admin
3
+ allow_access :editor, :only => [:index, :show]
4
+ allow_access(:guest, :only => :guest) { params[:action] == 'guest' }
5
+ allow_access :tester, :only => :show, :user_resource => true
6
+ allow_access :reader, :only => :show, :scope => :organization
7
+ allow_access :only => :listing
8
+ allow_access :only => :react
9
+
10
+ %w(index show guest listing react).each do |name|
11
+ define_method(name) { head :ok }
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ class Resource
2
+ def initialize(hash={})
3
+ @attributes = {}
4
+ hash.each do |k,v|
5
+ self.send("#{k}=", v)
6
+ end
7
+ end
8
+
9
+ def id
10
+ @attributes['id']
11
+ end
12
+
13
+ def id=(value)
14
+ @attributes['id'] = value
15
+ end
16
+
17
+ def method_missing(m, v=nil)
18
+ if m.to_s =~ /(.*)=$/
19
+ @attributes[$1] = v
20
+ else
21
+ if @attributes.has_key?(m.to_s)
22
+ @attributes[m.to_s]
23
+ else
24
+ raise NoMethodError, "We don't know anything about #{m}"
25
+ end
26
+ end
27
+ end
28
+
29
+ alias_method :old_respond_to?, :respond_to?
30
+ def respond_to?(m)
31
+ old_respond_to?(m) or @attributes.has_key?(m.to_s)
32
+ end
33
+ end
@@ -0,0 +1,45 @@
1
+ module AuthorizationSanTest
2
+ module Initializer
3
+ VENDOR_RAILS = File.expand_path('../../../../rails', __FILE__)
4
+ OTHER_RAILS = File.expand_path('../../../rails', __FILE__)
5
+ PLUGIN_ROOT = File.expand_path('../../', __FILE__)
6
+
7
+ def self.rails_directory
8
+ if File.exist?(VENDOR_RAILS)
9
+ VENDOR_RAILS
10
+ elsif File.exist?(OTHER_RAILS)
11
+ OTHER_RAILS
12
+ end
13
+ end
14
+
15
+ def self.load_dependencies
16
+ $stdout.write('Loading Rails from ')
17
+ if rails_directory
18
+ puts rails_directory
19
+ $:.unshift(File.join(rails_directory, 'activesupport', 'lib'))
20
+ $:.unshift(File.join(rails_directory, 'activerecord', 'lib'))
21
+ else
22
+ puts 'rubygems'
23
+ require 'rubygems' rescue LoadError
24
+ end
25
+
26
+ require 'test/unit'
27
+ require 'active_support'
28
+ require 'active_support/test_case'
29
+ require 'action_controller'
30
+ require 'action_controller/test_process'
31
+
32
+ require File.join(PLUGIN_ROOT, 'rails', 'init')
33
+
34
+ $:.unshift(File.join(PLUGIN_ROOT, 'lib'))
35
+ $:.unshift(File.join(PLUGIN_ROOT, 'test'))
36
+ end
37
+
38
+ def self.start
39
+ load_dependencies
40
+ ActionController::Routing::Routes.reload rescue nil
41
+ end
42
+ end
43
+ end
44
+
45
+ AuthorizationSanTest::Initializer.start
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: authorization-san
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Manfred Stienstra
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-29 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A plugin for authorization in a ReSTful application.
17
+ email: manfred@fngtps.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - LICENSE
27
+ - README.rdoc
28
+ - lib/authorization.rb
29
+ - lib/authorization/allow_access.rb
30
+ - lib/authorization/block_access.rb
31
+ - rails/init.rb
32
+ has_rdoc: true
33
+ homepage: http://fingertips.github.com
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --charset=UTF-8
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project:
56
+ rubygems_version: 1.3.5
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: A plugin for authorization in a ReSTful application.
60
+ test_files:
61
+ - test/cases/behaviour_test.rb
62
+ - test/cases/internals_test.rb
63
+ - test/cases/structural_test.rb
64
+ - test/controllers/all.rb
65
+ - test/controllers/application_controller.rb
66
+ - test/controllers/authenticated_controller.rb
67
+ - test/controllers/broken_block_controller.rb
68
+ - test/controllers/complicated_controller.rb
69
+ - test/controllers/multiple_roles_controller.rb
70
+ - test/controllers/public_controller.rb
71
+ - test/controllers/users_controller.rb
72
+ - test/models/resource.rb
73
+ - test/test_helper.rb
74
+ - examples/administrations_controller.rb
75
+ - examples/application.rb
76
+ - examples/application_with_multiple_auth_methods.rb
77
+ - examples/authenticated_controller.rb
78
+ - examples/page_controller_with_full_policy.rb
79
+ - examples/pages_controller.rb
80
+ - examples/public_controller.rb
81
+ - examples/users_controller.rb