authorizer 0.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.
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2011-07-01
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,17 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/authorizer.rb
7
+ script/console
8
+ script/destroy
9
+ script/generate
10
+ lib/authorizer/admin.rb
11
+ lib/authorizer/application_controller.rb
12
+ lib/authorizer/base.rb
13
+ lib/authorizer/exceptions.rb
14
+ lib/authorizer/object_observer.rb
15
+ lib/authorizer/user_observer.rb
16
+ app/models/object_role.rb
17
+ rails/init.rb
@@ -0,0 +1,7 @@
1
+ Remaining installation steps: (1 and 2 already done!)
2
+
3
+ 3. generate the migration by running "script/generate authorizer_migration"
4
+ 4. run "rake db:migrate" to migrate your database
5
+ 5. Add observers to 'config/environment.rb'
6
+
7
+ config.active_record.observers = "Authorizer::UserObserver", "Authorizer::ObjectObserver"
@@ -0,0 +1,160 @@
1
+ = authorizer
2
+
3
+ * https://github.com/cmdjohnson/authorizer
4
+
5
+ == DESCRIPTION:
6
+
7
+ Authorizer is a gem for Ruby (in conjunction with Rails 2.3) that does authorization for you on a per-object basis. What makes this gem different from e.g. declarative_authorization and cancan is they define one role for the entire application. With Authorizer, you define roles for different users on every Rails object.
8
+
9
+ Let's use a Dropbox analogy.
10
+
11
+ With Dropbox, you can choose which folder you want to share. For instance:
12
+
13
+ Al has a home folder with these subfolders in it:
14
+ - Music (shared with Bob)
15
+ - Pictures (shared with Casper and Bob)
16
+ - News (shared with no-one)
17
+
18
+ This causes Al to have all 3 folders in his Dropbox. Bob has 2 and Casper has only 1 folder called Pictures.
19
+
20
+ In other words, a user has access to a subset of the entire collection of folders. Bob has access to 2 of Al's folders, namely Music and Pictures. But he doesn't even see the News folder, nor can he download files from it.
21
+
22
+ Bob's access to the two folders is both read and write, so let's call that role "admin". Al is the owner of all 3 folders and has a role called "owner". This leads to the following Roles table:
23
+
24
+ folder_name user_name role
25
+ Music Al owner
26
+ Bob admin
27
+ Pictures Al owner
28
+ Bob admin
29
+ Casper admin
30
+ News Al owner
31
+
32
+ Now if we would allow Bob to also access the News folder but only read from it, we could add the role called "reader" to the table:
33
+
34
+ folder_name user_name role
35
+ News Bob reader
36
+
37
+ This is exactly what Authorizer does for your Rails application.
38
+
39
+ == FEATURES/PROBLEMS:
40
+
41
+ Handles authorization for you.
42
+
43
+ == SYNOPSIS:
44
+
45
+ Authorize a user on an object
46
+
47
+ Authorizer::Base.authorize_user( :object => object )
48
+
49
+ => true/false
50
+
51
+ If you want to know if the current user is authorized on an object, use:
52
+
53
+ Authorizer::Base.user_is_authorized?( :object => object)
54
+ => true/false
55
+
56
+ Remove authorization from an object
57
+
58
+ Authorizer::Base.remove_authorization( :object => object )
59
+ => true/false
60
+
61
+ Find all objects that the current user is authorized to use
62
+
63
+ Authorizer::Base.find("Post", :all, { :conditions => { :name => "my_post" } }) # [ #<Post id: 1>, #<Post id: 2> ]
64
+ Authorizer::Base.find("Post", :first) #<Post id: 1>
65
+ Authorizer::Base.find("Post", [ 1, 6 ]) # [ #<Post id: 1>, #<Post id: 6> ]
66
+
67
+ If you are using inherited_resources, you can also use these filters in your controller class:
68
+
69
+ # own created objects so you can access them after creation
70
+ after_filter :own_created_object, :only => :create
71
+ # authorize entire controller
72
+ before_filter :authorize, :only => [ :show, :edit, :update, :destroy ]
73
+
74
+ This obviously works out of the box with resource-oriented controllers, but with anything different you'll have to make your own choices.
75
+
76
+ If you're just getting started with Authorizer but you already have a running app, you can have one user own all objects with this method:
77
+
78
+ Authorizer::Admin.create_brand_new_object_roles(:user => User.first)
79
+
80
+ This method will guess what objects to use by checking for descendants of ActiveRecord::Base.
81
+
82
+ If you just want to do this for the Post and Category classes, use:
83
+
84
+ Authorizer::Admin.create_brand_new_object_roles(:user => User.first, :objects => [ "Post", "Category" ])
85
+
86
+ Authorizer uses ActiveRecord observers to make sure it doesn't make any mess, for instance, when a user is deleted, all of his authorization objects are deleted as well. Should you want more control over this garbage collection process, or if you are a cleanfreak, use this to get rid of any stale authorization objects lying around in your database: (protip: embed into rake task!)
87
+
88
+ Authorizer::Admin.remove_all_unused_authorization_objects
89
+
90
+ == REQUIREMENTS:
91
+
92
+ - Ruby (this gem was tested with 1.8.7)
93
+ - Rails 2.3 (tested with 2.3.11 and 2.3.12)
94
+ - Authlogic (for authentication)
95
+
96
+ Optional:
97
+ - inherited_resources if you want to use the controller filters supplied with this gem. Otherwise, you'll have to check for authorization yourself.
98
+
99
+ == INSTALL:
100
+
101
+ Installation
102
+ ===
103
+
104
+ 1. sudo gem install authorizer
105
+ 2. add "authorizer" to your Gemfile (I hope you've stopped using config.gem already even if you are on Rails 2.3?)
106
+ 3. generate a migration for authorization objects:
107
+
108
+ script/generate migration CreateObjectRoles
109
+
110
+ Paste this code into the newly generated file:
111
+
112
+ def self.up
113
+ create_table :object_roles do |t|
114
+ t.string :klazz_name
115
+ t.integer :object_reference
116
+ t.references :user
117
+ t.string :role
118
+
119
+ t.timestamps
120
+ end
121
+ end
122
+
123
+ def self.down
124
+ drop_table :object_roles
125
+ end
126
+
127
+ 4. run "rake db:migrate" to migrate your database
128
+
129
+ That's it!
130
+
131
+ == DEVELOPERS:
132
+
133
+ Reviews, patches and bug tickets are welcome!
134
+
135
+ {authorizer_project}[https://github.com/cmdjohnson/authorizer_project] is a sample project that shows the usage of the Authorizer gem. It also contains all tests for your testing pleasure.
136
+
137
+ == LICENSE:
138
+
139
+ (The MIT License)
140
+
141
+ Copyright (c) 2011 Commander Johnson <commanderjohnson@gmail.com>
142
+
143
+ Permission is hereby granted, free of charge, to any person obtaining
144
+ a copy of this software and associated documentation files (the
145
+ 'Software'), to deal in the Software without restriction, including
146
+ without limitation the rights to use, copy, modify, merge, publish,
147
+ distribute, sublicense, and/or sell copies of the Software, and to
148
+ permit persons to whom the Software is furnished to do so, subject to
149
+ the following conditions:
150
+
151
+ The above copyright notice and this permission notice shall be
152
+ included in all copies or substantial portions of the Software.
153
+
154
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
155
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
156
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
157
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
158
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
159
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
160
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/authorizer'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'authorizer' do
14
+ self.developer 'CmdJohnson', 'commanderjohnson@gmail.com'
15
+ self.post_install_message = 'PostInstall.txt'
16
+ self.rubyforge_name = self.name
17
+ self.extra_deps = [['options_checker','>= 0.0.1']]
18
+
19
+ end
20
+
21
+ require 'newgem/tasks'
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
23
+
24
+ # TODO - want other tests/tasks run by default? Add them to the list
25
+ # remove_task :default
26
+ # task :default => [:spec, :features]
@@ -0,0 +1,59 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class ObjectRole < ActiveRecord::Base
3
+ ##############################################################################
4
+ # class methods
5
+ ##############################################################################
6
+
7
+ def self.roles
8
+ [ "owner" ]
9
+ end
10
+
11
+ # What ObjectRoles does this object have associated?
12
+ def self.find_all_by_object(object)
13
+ raise "Can only operate on ActiveRecord::Base objects." unless object.is_a?(ActiveRecord::Base)
14
+ raise "Can only operate on saved objects" if object.new_record?
15
+
16
+ klazz_name = object.class.to_s
17
+ object_reference = object.id
18
+
19
+ ObjectRole.find(:all, :conditions => { :klazz_name => klazz_name, :object_reference => object_reference } )
20
+ end
21
+
22
+ ##############################################################################
23
+ # associations
24
+ ##############################################################################
25
+
26
+ belongs_to :user
27
+
28
+ ##############################################################################
29
+ # validations
30
+ ##############################################################################
31
+
32
+ validates_presence_of :klazz_name, :object_reference, :user_id, :role
33
+ validates_numericality_of :object_reference, :only_integer => true
34
+ validates_inclusion_of :role, :in => ObjectRole.roles
35
+
36
+ ##############################################################################
37
+ # constructor
38
+ ##############################################################################
39
+
40
+ ##############################################################################
41
+ # instance methods
42
+ ##############################################################################
43
+
44
+ def description
45
+ "#{self.klazz_name} #{self.object_reference}"
46
+ end
47
+
48
+ def object
49
+ obj = nil
50
+
51
+ begin
52
+ klazz = eval(self.klazz_name)
53
+ obj = klazz.find(self.object_reference)
54
+ rescue
55
+ end
56
+
57
+ obj
58
+ end
59
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+ $:.unshift(File.dirname(__FILE__)) unless
3
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
4
+
5
+ module Authorizer
6
+ VERSION = '0.0.1'
7
+ end
@@ -0,0 +1,112 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Authorizer
3
+ ############################################################################
4
+ # helper class that does administrative functions for you
5
+ ############################################################################
6
+ class Admin < Base
7
+ # not everybody uses "User" as user class name. We do :o -=- :))) ffFF ww ppO :)))
8
+ @@user_class_name = "User"
9
+
10
+ ############################################################################
11
+ # create_brand_new_object_roles
12
+ #
13
+ # For if you've been working without Authorizer and want to start using it.
14
+ # Obviously, if you don't have any ObjectRoles then you'll find yourself blocked out of your own app.
15
+ # This method will assign all objects listed in an array to a certain user.
16
+ # For instance:
17
+ #
18
+ # user = User.find(1)
19
+ # objects = [ "Post", "Category" ]
20
+ # Authorizer::Admin.create_brand_new_object_roles( :user => user, :objects => objects )
21
+ #
22
+ # If objects is not specified, Admin will look for all descendants of ActiveRecord::Base
23
+ # and exclude the ObjectRole and User classes.
24
+ #
25
+ # Authorizer::Admin.create_brand_new_object_roles( :user => user )
26
+ ############################################################################
27
+
28
+ def self.create_brand_new_object_roles(options = {})
29
+ OptionsChecker.check(options, [ :user ])
30
+
31
+ objects = gather_direct_descendants_of_activerecord_base || options[:objects]
32
+
33
+ ret = false
34
+
35
+ raise "objects must be an Array" unless objects.is_a?(Array)
36
+
37
+ # Nothing to do ..
38
+ return ret if objects.blank?
39
+
40
+ begin
41
+ user_id = options[:user].id
42
+ rescue
43
+ end
44
+
45
+ unless user_id.nil?
46
+ for object in objects
47
+ evaled_klazz = nil
48
+
49
+ begin
50
+ evaled_klazz = eval(object)
51
+ rescue
52
+ end
53
+
54
+ unless evaled_klazz.nil?
55
+ # One is enough to return exit status OK.
56
+ ret = true
57
+ # Let's find all. This is the same as Post.all
58
+ if evaled_klazz.is_a?(Class)
59
+ collection = evaled_klazz.all
60
+
61
+ # Go
62
+ unless collection.blank?
63
+ for coll_ in collection
64
+ ObjectRole.create!( :klazz_name => object, :object_reference => coll_.id, :user_id => user_id, :role => "owner" )
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ ret
73
+ end
74
+
75
+ ############################################################################
76
+ # remove_all_unused_authorization_objects
77
+ #############################################################################
78
+ # Remove all stale (non-object) authorization objects.
79
+ ############################################################################
80
+
81
+ def self.remove_all_unused_authorization_objects options = {}
82
+ # no options
83
+ # ___
84
+ # Let's iterate all ObjectRoles
85
+ for object_role in ObjectRole.all
86
+ object_role.destroy if object_role.object.nil?
87
+ end
88
+ end
89
+
90
+ protected
91
+
92
+ ############################################################################
93
+ # gather_direct_descendants_of_activerecord_base
94
+ ############################################################################
95
+
96
+ def self.gather_direct_descendants_of_activerecord_base
97
+ ret = []
98
+
99
+ classes = ActiveRecord::Base.send(:subclasses)
100
+
101
+ # Go through it twice to determine direct descendants
102
+ for klazz in classes
103
+ # Push the class name, not the class object itself.
104
+ klazz_name = klazz.to_s
105
+ # May never use the ObjectRole or User class.
106
+ ret.push klazz_name if !klazz_name.eql?("ObjectRole") && !klazz_name.eql?(@@user_class_name)
107
+ end
108
+
109
+ ret
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,65 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # Use this file to add a couple of helper methods to ApplicationController
3
+
4
+ ################################################################################
5
+ # Addendum to ApplicationController
6
+ ################################################################################
7
+ # These methods are heavily dependent on InheritedResources, more specifically the 'resource' method.
8
+ #
9
+ # Otherwise there would be no predefined way of peeking into a controller's resource object.
10
+ ################################################################################
11
+
12
+ # for user_not_authorized
13
+ require 'authorizer/exceptions'
14
+
15
+ class ApplicationController < ActionController::Base
16
+ helper_method :own_created_object, :authorize
17
+
18
+ private
19
+
20
+ ##############################################################################
21
+ # authorizer
22
+ ##############################################################################
23
+
24
+ def own_created_object
25
+ ret = true # always return true otherwise the filter chain would be blocked.
26
+
27
+ begin
28
+ r = resource
29
+ rescue
30
+ end
31
+
32
+ unless r.nil?
33
+ # only if this objet was successfully created will we do this.
34
+ unless r.new_record?
35
+ Authorizer::Base.authorize_user( :object => r )
36
+ end
37
+ end
38
+
39
+ ret
40
+ end
41
+
42
+ def authorize
43
+ ret = false # return false by default, effectively using a whitelist method.
44
+
45
+ begin
46
+ r = resource
47
+ rescue
48
+ end
49
+
50
+ unless r.nil?
51
+ auth = Authorizer::Base.user_is_authorized?( :object => r )
52
+
53
+ if auth.eql?(false)
54
+ raise Authorizer::UserNotAuthorized.new("You are not authorized to access this resource.")
55
+ end
56
+ end
57
+
58
+ ret
59
+ end
60
+
61
+ ##############################################################################
62
+ # end authorizer
63
+ ##############################################################################
64
+ end
65
+
@@ -0,0 +1,320 @@
1
+ # -*- encoding : utf-8 -*-
2
+ ################################################################################
3
+ # Authorizer
4
+ #
5
+ # Authorizer is a Ruby class that authorizes using the ObjectRole record.
6
+ ################################################################################
7
+
8
+ # for user_not_authorized
9
+ require 'authorizer/exceptions'
10
+ require 'authorizer/application_controller'
11
+
12
+ module Authorizer
13
+ class Base < ApplicationController
14
+ ############################################################################
15
+ # authorize_user
16
+ #
17
+ # If no user is specified, authorizes the current user.
18
+ # If no role is specified, "owner" is used as role.
19
+ ############################################################################
20
+
21
+ def self.authorize_user(options)
22
+ OptionsChecker.check(options, [ :object ])
23
+
24
+ ret = false
25
+
26
+ object = options[:object]
27
+ role = options[:role] || "owner"
28
+ user = options[:user] || get_current_user
29
+
30
+ return false if basic_check_fails?(options)
31
+
32
+ check_user(user)
33
+ # Checks done. Let's go.
34
+
35
+ or_ = find_object_role(object, user)
36
+
37
+ # This time, we want it to be nil.
38
+ if or_.nil? && !user.nil?
39
+ klazz_name = object.class.to_s
40
+ object_reference = object.id
41
+
42
+ ObjectRole.create!( :klazz_name => klazz_name, :object_reference => object_reference, :user => user, :role => role )
43
+ Rails.logger.debug("Authorizer: created authorization on #{object} for current_user with ID #{user.id} witih role #{role}")
44
+ ret = true
45
+ end
46
+
47
+ ret
48
+ end
49
+
50
+ ############################################################################
51
+ # user_is_authorized?
52
+ #
53
+ # If no user is specified, current_user is used.
54
+ ############################################################################
55
+
56
+ def self.user_is_authorized? options
57
+ OptionsChecker.check(options, [ :object ])
58
+
59
+ ret = false
60
+
61
+ check = basic_check_fails?(options)
62
+ return ret if check
63
+
64
+ object = options[:object]
65
+ user = options[:user] || get_current_user
66
+
67
+ # Checks
68
+ check_user(user)
69
+ # Checks done. Let's go.
70
+
71
+ or_ = find_object_role(object, user)
72
+
73
+ # Congratulations, you've been Authorized.
74
+ unless or_.nil?
75
+ ret = true
76
+ end
77
+
78
+ if ret
79
+ Rails.logger.debug("Authorizer: authorized current_user with ID #{user.id} to access #{or_.description} because of role #{or_.role}") unless user.nil? || or_.nil?
80
+ else
81
+ Rails.logger.debug("Authorizer: authorization failed for current_user with ID #{user.id} to access #{object.to_s}") unless user.nil? || object.nil?
82
+ end
83
+
84
+ ret
85
+ end
86
+
87
+ ############################################################################
88
+ # remove_authorization
89
+ ############################################################################
90
+ # Remove authorization a user has on a certain object.
91
+ ############################################################################
92
+
93
+ def self.remove_authorization options = {}
94
+ OptionsChecker.check(options, [ :object ])
95
+
96
+ ret = false
97
+
98
+ return ret if basic_check_fails?(options)
99
+
100
+ object = options[:object]
101
+ user = options[:user] || get_current_user
102
+
103
+ # Check
104
+ check_user(user)
105
+ # Checks done. Let's go.
106
+
107
+ or_ = find_object_role(object, user)
108
+
109
+ unless or_.nil?
110
+ Rails.logger.debug("Authorizer: removed authorization for user ID #{user.id} on #{or_.description}")
111
+
112
+ or_.destroy
113
+
114
+ ret = true
115
+ end
116
+
117
+ ret
118
+ end
119
+
120
+ ############################################################################
121
+ # find
122
+ ############################################################################
123
+ # Out of the collection of all Posts, return the subset that belongs to the current user.
124
+ # External method that maps to the internal_find which is the generic find method.
125
+ #
126
+ # Arguments:
127
+ # - class_name: which class to use, e.g. "Post"
128
+ # - what: will be passed on to the ActiveRecord find function (e.g. Post.find(what))
129
+ # - find_options: will also be passed on (e.g. Post.find(what, find_options))
130
+ # - authorizer_options: options for authorizer, e.g. { :user => @user }
131
+ ############################################################################
132
+
133
+ def self.find(class_name, what, find_options = {}, authorizer_options = {})
134
+ options = { :class_name => class_name, :what => what, :find_options => find_options }
135
+ my_options = authorizer_options.merge(options) # options overrides user-specified options.
136
+
137
+ internal_find(my_options)
138
+ end
139
+
140
+ ############################################################################
141
+ # is_authorized?
142
+ #
143
+ # Checks if the corresponding role.eql?("owner")
144
+ ############################################################################
145
+
146
+ def self.is_authorized? object
147
+ user_is_authorized? :object => object
148
+ end
149
+
150
+ ############################################################################
151
+ # create_ownership
152
+ #
153
+ # ObjectRole.create!( :klazz_name => object.class.to_s, :object_reference => object.id, :user => current_user, :role => "owner" )
154
+ ############################################################################
155
+
156
+ def self.create_ownership object
157
+ ret = false
158
+
159
+ return ret if basic_check_fails?(object)
160
+
161
+ ret = authorize_user( :object => object )
162
+
163
+ ret
164
+ end
165
+
166
+ protected
167
+
168
+ ############################################################################
169
+ # get_current_user
170
+ ############################################################################
171
+ # helper method to not be dependent on the current_user method
172
+ ############################################################################
173
+
174
+ def self.get_current_user
175
+ ret = nil
176
+
177
+ begin
178
+ session = UserSession.find
179
+ ret = session.user
180
+ rescue
181
+ end
182
+
183
+ ret
184
+ end
185
+
186
+ ############################################################################
187
+ # internal_find
188
+ ############################################################################
189
+ # Extract some info from ObjectRole objects and then pass the info through
190
+ # to the ActiveRecord finder.
191
+ ############################################################################
192
+
193
+ def self.internal_find(options = {})
194
+ # Options
195
+ OptionsChecker.check(options, [ :what, :class_name ])
196
+
197
+ # assign
198
+ class_name = options[:class_name]
199
+ what = options[:what]
200
+ find_options = options[:find_options] || {}
201
+ user = options[:user] || get_current_user
202
+
203
+ # We don't do the what checks anymore, ActiveRecord::Base.find does that for us now.
204
+ #what_checks = [ :all, :first, :last, :id ]
205
+ #raise "What must be one of #{what_checks.inspect}" unless what_checks.include?(what)
206
+
207
+ # Check userrrrrrrrrrrr --- =====================- ---= ===-=- *&((28 @((8
208
+ check_user(user)
209
+ # rrrr
210
+ ret = nil
211
+ # Checks
212
+ # Checks done. Let's go.
213
+ # Get the real klazz
214
+ klazz = nil
215
+ # Check it
216
+ begin
217
+ klazz = eval(class_name)
218
+ rescue
219
+ end
220
+ # oooo ooo ooo ___ --- === __- --_- ++_+_ =--- +- =+=-=- =-= <--- ice beam!
221
+ unless klazz.nil?
222
+ # now we know klazz really exists.
223
+ # let's find the object_role objects that match the user and klaz.
224
+ # Get the object_role objects
225
+ object_roles_conditions = { :klazz_name => class_name, :user_id => user.id }
226
+ object_roles = ObjectRole.find(:all, :conditions => object_roles_conditions )
227
+ # Get a list of IDs. These are objects that are owned by the current_user
228
+ object_role_ids = object_roles.collect { |or_| or_.object_reference } # [ 1, 1, 1, 1 ]
229
+ # Make it at least an array if object_role_ids returns nil
230
+ object_role_ids ||= []
231
+ # Try to emulate find as good as we can
232
+ # so don't skip this, try to always pass it on.
233
+ unless object_roles.nil?
234
+ # Prepare find_options
235
+ leading_find_options = {} # insert conventions here if needed
236
+ my_find_options = find_options.merge(leading_find_options)
237
+ # If the user passed an Array we should filter it with the list of available (authorized) objects.
238
+ #
239
+ # http://www.ruby-doc.org/core/classes/Array.html
240
+ # &
241
+ # Set Intersection—Returns a new array containing elements common to the two arrays, with no duplicates.
242
+ safe_what = what
243
+ if what.is_a?(Array)
244
+ safe_what = what & object_role_ids
245
+ end
246
+ # The big show. Let's call out F I N D !!!!!!
247
+ # INF FINFD FIWI FFIND IF FIND FIND FIND FIND FIND FIND FIND FIND
248
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
249
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
250
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
251
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
252
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
253
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
254
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
255
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
256
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
257
+ # FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND FIND
258
+ if safe_what.eql?(:all)
259
+ ret = klazz.find(:all, my_find_options)
260
+ elsif safe_what.eql?(:first)
261
+ ret = klazz.find(object_role_ids.first, my_find_options)
262
+ elsif safe_what.eql?(:last)
263
+ ret = klazz.find(object_role_ids.last, my_find_options)
264
+ else
265
+ ret = klazz.find(safe_what, my_find_options)
266
+ end
267
+ # SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT????
268
+ # SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT????
269
+ # SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT????
270
+ # SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT????
271
+ # SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT????
272
+ # SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT????
273
+ # SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT????
274
+ # SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT???? SAFE WHAT????
275
+ end
276
+ end
277
+
278
+ ret
279
+ end
280
+
281
+ def self.find_object_role(object, user)
282
+ return nil if object.nil? || user.nil?
283
+
284
+ # Check
285
+ check_user(user)
286
+ # Checks done. Let's go.
287
+
288
+ klazz_name = object.class.to_s
289
+ object_reference = object.id
290
+
291
+ unless user.nil?
292
+ or_ = ObjectRole.first( :conditions => { :klazz_name => klazz_name, :object_reference => object_reference, :user_id => user.id } )
293
+ end
294
+
295
+ or_
296
+ end
297
+
298
+ def self.basic_check_fails?(options)
299
+ ret = false
300
+
301
+ unless options[:object].nil?
302
+ if !options[:object].is_a?(ActiveRecord::Base) || options[:object].new_record?
303
+ raise "object must be subclass of ActiveRecord::Base and must also be saved."
304
+ end
305
+ end
306
+
307
+ ret
308
+ end
309
+
310
+ def self.check_user(user)
311
+ ret = true
312
+
313
+ raise "User cannot be nil" if user.nil?
314
+ raise "User must inherit from ActiveRecord::Base" unless user.is_a?(ActiveRecord::Base)
315
+ raise "User must be saved" if user.new_record?
316
+
317
+ ret
318
+ end
319
+ end
320
+ end
@@ -0,0 +1,6 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Authorizer
3
+ class UserNotAuthorized < Exception
4
+
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Authorizer
3
+ # Observes users and deleted any associated ObjectRole objects when the user gets deleted.
4
+ class ObjectObserver < ActiveRecord::Observer
5
+ # Observe this.
6
+ observe ActiveRecord::Base
7
+
8
+ # W DONT DO DIZ
9
+ # let's use before_destroy instead of after_destroy. More chance it will still have an ID >:)))))))))) :') :DDDDDDDDDDDDDDDDDDDDDDD
10
+ # W DONT DO DIZ
11
+ def after_destroy(object)
12
+ return nil if object.is_a?(User) # Users are covered by the other observer class.
13
+ # Find all ObjectRole records that point to this object.
14
+ object_roles = ObjectRole.find_all_by_object(object)
15
+ # Walk through 'em
16
+ for object_role in object_roles
17
+ object_role.destroy
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Authorizer
3
+ # Observes users and deleted any associated ObjectRole objects when the user gets deleted.
4
+ class UserObserver < ActiveRecord::Observer
5
+ # Observe this.
6
+ observe :user
7
+
8
+ # W DONT DO DIZ
9
+ # let's use before_destroy instead of after_destroy. More chance it will still have an ID >:)))))))))) :') :DDDDDDDDDDDDDDDDDDDDDDD
10
+ # W DONT DO DIZ
11
+ def after_destroy(user)
12
+ # Default
13
+ object_roles = []
14
+ # Find all ObjectRole records that point to this user's ID.
15
+ begin
16
+ object_roles = ObjectRole.find_all_by_user_id(user.id)
17
+ rescue
18
+ end
19
+ # Walk through 'em
20
+ # Not executed if anything happens (array.size = 0)
21
+ for object_role in object_roles
22
+ object_role.destroy
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+ ################################################################################
3
+ # init.rb
4
+ #
5
+ # This file will load the Observers we need to prevent the database from becoming clogged with stale authorization objects.
6
+ ################################################################################
7
+
8
+ config.after_initialize do
9
+ ActiveRecord::Base.observers << Authorizer::UserObserver
10
+ ActiveRecord::Base.observers << Authorizer::ObjectObserver
11
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/authorizer.rb'}"
9
+ puts "Loading authorizer gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: authorizer
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - CmdJohnson
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-26 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: options_checker
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 29
30
+ segments:
31
+ - 0
32
+ - 0
33
+ - 1
34
+ version: 0.0.1
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: hoe
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 47
46
+ segments:
47
+ - 2
48
+ - 8
49
+ - 0
50
+ version: 2.8.0
51
+ type: :development
52
+ version_requirements: *id002
53
+ description: |-
54
+ Authorizer is a gem for Ruby (in conjunction with Rails 2.3) that does authorization for you on a per-object basis. What makes this gem different from e.g. declarative_authorization and cancan is they define one role for the entire application. With Authorizer, you define roles for different users on every Rails object.
55
+
56
+ Let's use a Dropbox analogy.
57
+
58
+ With Dropbox, you can choose which folder you want to share. For instance:
59
+
60
+ Al has a home folder with these subfolders in it:
61
+ - Music (shared with Bob)
62
+ - Pictures (shared with Casper and Bob)
63
+ - News (shared with no-one)
64
+
65
+ This causes Al to have all 3 folders in his Dropbox. Bob has 2 and Casper has only 1 folder called Pictures.
66
+
67
+ In other words, a user has access to a subset of the entire collection of folders. Bob has access to 2 of Al's folders, namely Music and Pictures. But he doesn't even see the News folder, nor can he download files from it.
68
+
69
+ Bob's access to the two folders is both read and write, so let's call that role "admin". Al is the owner of all 3 folders and has a role called "owner". This leads to the following Roles table:
70
+
71
+ folder_name user_name role
72
+ Music Al owner
73
+ Bob admin
74
+ Pictures Al owner
75
+ Bob admin
76
+ Casper admin
77
+ News Al owner
78
+
79
+ Now if we would allow Bob to also access the News folder but only read from it, we could add the role called "reader" to the table:
80
+
81
+ folder_name user_name role
82
+ News Bob reader
83
+
84
+ This is exactly what Authorizer does for your Rails application.
85
+ email:
86
+ - commanderjohnson@gmail.com
87
+ executables: []
88
+
89
+ extensions: []
90
+
91
+ extra_rdoc_files:
92
+ - History.txt
93
+ - Manifest.txt
94
+ - PostInstall.txt
95
+ files:
96
+ - History.txt
97
+ - Manifest.txt
98
+ - PostInstall.txt
99
+ - README.rdoc
100
+ - Rakefile
101
+ - lib/authorizer.rb
102
+ - script/console
103
+ - script/destroy
104
+ - script/generate
105
+ - lib/authorizer/admin.rb
106
+ - lib/authorizer/application_controller.rb
107
+ - lib/authorizer/base.rb
108
+ - lib/authorizer/exceptions.rb
109
+ - lib/authorizer/object_observer.rb
110
+ - lib/authorizer/user_observer.rb
111
+ - app/models/object_role.rb
112
+ - rails/init.rb
113
+ has_rdoc: true
114
+ homepage: https://github.com/cmdjohnson/authorizer
115
+ licenses: []
116
+
117
+ post_install_message: PostInstall.txt
118
+ rdoc_options:
119
+ - --main
120
+ - README.rdoc
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ hash: 3
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ hash: 3
138
+ segments:
139
+ - 0
140
+ version: "0"
141
+ requirements: []
142
+
143
+ rubyforge_project: authorizer
144
+ rubygems_version: 1.3.7
145
+ signing_key:
146
+ specification_version: 3
147
+ summary: Authorizer is a gem for Ruby (in conjunction with Rails 2.3) that does authorization for you on a per-object basis
148
+ test_files: []
149
+