authorizer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+