role_based_authorization 0.1.11 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.11
1
+ 0.1.12
@@ -1,2 +1,4 @@
1
1
  require 'role_based_authorization/authorization_logger.rb'
2
+ require 'role_based_authorization/class_additions'
3
+ require 'role_based_authorization/rule.rb'
2
4
  require 'role_based_authorization/role_based_authorization.rb'
@@ -0,0 +1,66 @@
1
+ module RoleBasedAuthorization
2
+ # Defines the class methods that are to be added to the application controller
3
+ module ClassAdditions
4
+ # Returns the set of rules defined on this controller
5
+ def role_auth_rules
6
+ @@rules||={}
7
+ @@rules
8
+ end
9
+
10
+ # Returns true if one of the given rules matches the
11
+ # given options. rules must be an hash with a list of rules for
12
+ # each action
13
+ def find_matching_rule rules, options
14
+ user,actions,ids = *options.values_at(:user, :actions, :ids)
15
+
16
+ return actions.find do |action|
17
+ AUTHORIZATION_LOGGER.debug('current action: %s' % [action])
18
+ action = action.to_sym
19
+ rules_for_action = rules[action]
20
+ rules_for_action && rules_for_action.find { |rule| rule.match(user, ids) }
21
+ end
22
+ end
23
+
24
+
25
+ # Defines the DSL for the authorization system. The syntax is:
26
+ # permit :actions => [list of actions],
27
+ # :to => [list of roles],
28
+ # :if => lambda_expression,
29
+ # :object_id => object_id_symbol
30
+ #
31
+ # you can add any number of these in anyone of your controller.
32
+ #
33
+ # options:
34
+ #
35
+ # <b>:to</b>::
36
+ # the list of roles interested by this rule. The actual contents of this list depends on what your application defines to be a role. If you use integers, it could be a vector like [1,4] or as [ROOT, ADMIN], where ROOT and ADMIN are symbolic costants containing the corresponding integer values. You can specify all roles by specifying :all in place of the role list.
37
+ #
38
+ # <b>:actions</b>::
39
+ # the list of actions that are permitted to the mentioned roles. Actions are actual method names of the current controller and can be given as symbols or as strings. For instance ['index', 'show'] is equivalent to [:index, :show]. You can grant access to all actions by specifying :all instead of the action list.
40
+ #
41
+ # <b>:if</b>::
42
+ # a lambda expression that verifies additional conditions. The lambda expression takes two arguments: the current user and the id of an object. For instance you may want to verify that "normal" users could only modify objects that they own. You can say that by specifying:
43
+ # permit :actions => [:edit, :update],
44
+ # :to => [NORMAL],
45
+ # :if => lambda { |user, obj_id| TheObject.find(obj_id).owner == user }
46
+ #
47
+ # <b>:object_id</b>::
48
+ # normally the object id passed to the lambda expression of the :if option is retrieved from the params hash using :id (i.e. normally obj_id = params[:id]), you can specify other identifiers using this option, e.g.:
49
+ # :object_id => :product_id
50
+ # specifies that :product_id should be used instead of :id.
51
+
52
+ def permit options
53
+ options[:controller] ||= controller_name
54
+ controller = options[:controller]
55
+ actions = [*options[:actions]] # create an array if options[:actions] is not already an array
56
+
57
+ role_auth_rules[controller] ||= {}
58
+
59
+ actions.each do |action|
60
+ action = action.to_sym # this allows for both symbols and strings to be used for action names
61
+ role_auth_rules[controller][action] ||= []
62
+ role_auth_rules[controller][action] << RoleBasedAuthorization::Rule.new(options[:to], options[:if], options[:object_id])
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,180 +1,68 @@
1
+
1
2
  module RoleBasedAuthorization
2
3
  # AuthorizationLogger instance that is used throughout the plugin for logging
3
4
  # events.
4
5
  AUTHORIZATION_LOGGER = AuthorizationLogger.new(File.join(RAILS_ROOT,'log','authorization.log'))
5
-
6
- module ClassMethods; end
7
-
6
+
8
7
  # Fires when the module is included into the controllers. It adds all class methods
9
- # defined in the ClassMethods sub-module and the authorize_action? and if_authorized?
8
+ # defined in the ClassAdditions sub-module and the authorize_action? and if_authorized?
10
9
  # instance methods.
11
10
  def self.included(klass)
12
- klass.extend(ClassMethods)
11
+ klass.extend(ClassAdditions)
13
12
 
14
13
  klass.class_eval do
15
14
  helper_method :authorize_action?
16
15
  helper_method :if_authorized?
17
16
  end
18
17
  end
19
-
20
- # Defines the class methods that are to be added to the application controller
21
- module ClassMethods
22
- # Returns the set of rules defined on this controller
23
- def role_auth_rules
24
- @@rules||={}
25
- @@rules
26
- end
27
-
28
- # Defines the DSL for the authorization system. The syntax is:
29
- # permit :actions => [list of actions],
30
- # :to => [list of roles],
31
- # :if => lambda_expression,
32
- # :object_id => object_id_symbol
33
- #
34
- # you can add any number of these in anyone of your controller.
35
- #
36
- # options:
37
- #
38
- # <b>:to</b>::
39
- # the list of roles interested by this rule. The actual contents of this list depends on what your application defines to be a role. If you use integers, it could be a vector like [1,4] or as [ROOT, ADMIN], where ROOT and ADMIN are symbolic costants containing the corresponding integer values. You can specify all roles by specifying :all in place of the role list.
40
- #
41
- # <b>:actions</b>::
42
- # the list of actions that are permitted to the mentioned roles. Actions are actual method names of the current controller and can be given as symbols or as strings. For instance ['index', 'show'] is equivalent to [:index, :show]. You can grant access to all actions by specifying :all instead of the action list.
43
- #
44
- # <b>:if</b>::
45
- # a lambda expression that verifies additional conditions. The lambda expression takes two arguments: the current user and the id of an object. For instance you may want to verify that "normal" users could only modify objects that they own. You can say that by specifying:
46
- # permit :actions => [:edit, :update],
47
- # :to => [NORMAL],
48
- # :if => lambda { |user, obj_id| TheObject.find(obj_id).owner == user }
49
- #
50
- # <b>:object_id</b>::
51
- # normally the object id passed to the lambda expression of the :if option is retrieved from the params hash using :id (i.e. normally obj_id = params[:id]), you can specify other identifiers using this option, e.g.:
52
- # :object_id => :product_id
53
- # specifies that :product_id should be used instead of :id.
54
-
55
- def permit options
56
- options[:controller] ||= controller_name
57
- controller = options[:controller]
58
- actions = [*options[:actions]] # create an array if options[:actions] is not already an array
59
18
 
60
- role_auth_rules[controller] ||= {}
61
-
62
- actions.each do |action|
63
- action = action.to_sym # this allows for both symbols and strings to be used for action names
64
- role_auth_rules[controller][action] ||= []
65
- role_auth_rules[controller][action] << RoleBasedAuthorization::Rule.new(options[:to], options[:if], options[:object_id])
66
- end
67
- end
68
- end
69
-
70
-
71
- # Model an authorization rule. A rule is a triplet: <roles, cond, object_id>
72
- # a rule match if the user role is in roles and cond (if not nil) is satisfied when objects
73
- # are retrieved using object_id.
74
- class Rule
75
- # rule initialization. roles is mandatory, cond is optional, object_id defaults
76
- # to :id if nil.
77
- def initialize roles, cond, object_id
78
- roles = [roles] unless roles.respond_to? :each
79
-
80
- @roles = roles
81
- @cond = cond
82
- @object_id = object_id || :id
83
- end
84
-
85
- # return true if this rule matches the given user and objects
86
- def match(user, objects)
87
- AUTHORIZATION_LOGGER.debug('trying '+self.inspect)
88
-
89
- matching = @roles.include?(:all)
90
-
91
- # checking for right role (no need to check them if already matching)
92
- matching = !@roles.find { |role| !user.nil? && role == user.role }.nil? if !matching
93
-
94
- if @cond.nil?
95
- return matching
96
- else
97
- # to have a proper match, also the condition must hold
98
- return matching && @cond.call(user,objects[@object_id])
99
- end
100
- end
101
-
102
- # string representation for this rule
103
- def inspect
104
- str = "rule(#{self.object_id}): allow roles [" + @roles.join(',') + "]"
105
- str += " (only under condition object_id will be retrieved using '#{@object_id}')" if @cond
106
-
107
- str
108
- end
109
- end
110
-
111
-
112
19
  # Returns true if one of the rules defined for this controller matches
113
20
  # the given options
114
- def exists_rule_matching_options? user, controllers, actions, ids
21
+ def exists_matching_rule? options
115
22
  rules = self.class.role_auth_rules
116
- AUTHORIZATION_LOGGER.debug("current set of rules: %s" % [rules.inspect])
117
-
118
-
119
- controllers.each do |controller|
120
- if( !controller.blank? && rules[controller].nil? )
121
- # tries to load the controller. Rails automagically loads classes if their name
122
- # is used anywhere. By trying to constantize the name of the controller, we
123
- # force rails to load it.
124
- controller_klass = (controller.to_s+'_controller').camelize.constantize
125
- end
126
23
 
24
+ return options[:controllers].find do |controller|
127
25
  AUTHORIZATION_LOGGER.debug("current controller: %s" % [controller])
26
+
27
+ rules_for_controller = rules[controller]
28
+
29
+ # tries to load the controller. Rails automagically loads classes if their name
30
+ # is used anywhere. By trying to constantize the name of the controller, we
31
+ # force rails to load it. Loading the controller class causes the insertion of the rules defined therein
32
+ # into the object pointed by rules_for_controller.
33
+ (controller.to_s+'_controller').camelize.constantize if( !controller.blank? && rules_for_controller.nil? )
128
34
 
129
- actions.each do |action|
130
- AUTHORIZATION_LOGGER.debug('current action: %s' % [action])
131
-
132
- action = action.to_sym
133
- action_class = action.class
134
- raise "Action should be a symbol -- not a #{action_class.name}!" if action_class != Symbol
135
-
136
- rules_for_this_action = rules[controller] && rules[controller][action]
137
- next if rules_for_this_action.nil?
138
-
139
- return true if rules_for_this_action.find { |rule| rule.match(user, ids) }
140
- end
35
+
36
+ rules_for_controller && self.class.find_matching_rule(rules_for_controller, options)
141
37
  end
142
-
143
- return false
144
38
  end
145
39
 
146
40
  # Main authorization logic. opts is an hash with the following keys
147
41
  # :user, :controller, :action:: self explanatory
148
42
  # :ids:: id to be used to retrieve relevant objects
149
- def authorize_action? opts = {}
150
- # Option handling
151
- user, ids, controller, action = *opts.values_at(:user, :ids, :controller, :action)
152
- user ||= current_user
153
-
154
- if respond_to?(:logged_in?) && !logged_in?
155
- AUTHORIZATION_LOGGER.info("returning false (not logged in)")
156
- return false
157
- end
158
-
159
- ids ||= {}
160
- ids.reverse_merge!( opts.reject { |key,value| key.to_s !~ /(_id\Z)|(\Aid\Z)/ } )
43
+ def do_authorize_action? opts
44
+ # exiting immediately if not logged in
45
+ return false if respond_to?(:logged_in?) && !logged_in?
161
46
 
162
- user = current_user if user.nil? && respond_to?(:current_user)
163
- controller = controller_name if controller.nil? && respond_to?(:controller_name)
47
+ opts.reverse_merge!( :user => current_user, :controller => controller_name, :ids => {} )
48
+ user, controller, action, ids = opts.values_at( :user, :controller, :action, :ids )
49
+ ids.reverse_merge!( opts.reject { |key,value| key.to_s !~ /(_id\Z)|(\Aid\Z)/ } )
164
50
 
165
- AUTHORIZATION_LOGGER.info("user %s requested access to method %s:%s using ids:%s" %
166
- [ user && (user.inspect + "(id:#{user.id} role:#{user.role})") || 'none',
167
- controller,
168
- action,
169
- ids.inspect])
51
+ new_options = { :user => user,
52
+ :controllers => [controller,'application'],
53
+ :actions => [:all,action],
54
+ :ids => ids }
55
+
56
+ return exists_matching_rule?( new_options ) != nil
57
+ end
58
+
59
+ # wraps some logging around do_authorize_action?.
60
+ def authorize_action? opts = {}
61
+ AUTHORIZATION_LOGGER.info("access request. options: %s" % [opts.inspect])
62
+ result = do_authorize_action? opts
63
+ AUTHORIZATION_LOGGER.info("returning #{result}")
170
64
 
171
- if exists_rule_matching_options?( user, [controller,'application'], [:all,action] , ids )
172
- AUTHORIZATION_LOGGER.info('returning true (access granted)')
173
- return true
174
- else
175
- AUTHORIZATION_LOGGER.info('returning false (access denied)')
176
- return false
177
- end
65
+ return result
178
66
  end
179
67
 
180
68
 
@@ -0,0 +1,43 @@
1
+ module RoleBasedAuthorization
2
+
3
+ # Model an authorization rule. A rule is a triplet: <roles, cond, object_id>
4
+ # a rule match if the user role is in roles and cond (if not nil) is satisfied when objects
5
+ # are retrieved using object_id.
6
+ class Rule
7
+ # rule initialization. roles is mandatory, cond is optional, object_id defaults
8
+ # to :id if nil.
9
+ def initialize roles, cond, object_id
10
+ roles = [roles] unless roles.respond_to? :each
11
+
12
+ @roles = roles
13
+ @cond = cond
14
+ @object_id = object_id || :id
15
+ end
16
+
17
+ # return true if this rule matches the given user and objects
18
+ def match(user, objects)
19
+ AUTHORIZATION_LOGGER.debug('trying '+self.inspect)
20
+
21
+ matching = @roles.include?(:all)
22
+
23
+ # checking for right role (no need to check them if already matching)
24
+ matching = !@roles.find { |role| !user.nil? && role == user.role }.nil? if !matching
25
+
26
+ if @cond.nil?
27
+ return matching
28
+ else
29
+ # to have a proper match, also the condition must hold
30
+ return matching && @cond.call(user,objects[@object_id])
31
+ end
32
+ end
33
+
34
+ # string representation for this rule
35
+ def inspect
36
+ str = "rule(#{self.object_id}): allow roles [" + @roles.join(',') + "]"
37
+ str += " (only under condition object_id will be retrieved using '#{@object_id}')" if @cond
38
+
39
+ str
40
+ end
41
+ end
42
+
43
+ end
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{role_based_authorization}
8
- s.version = "0.1.11"
8
+ s.version = "0.1.12"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Roberto Esposito"]
12
- s.date = %q{2010-02-17}
12
+ s.date = %q{2010-02-18}
13
13
  s.description = %q{Provides a simple DSL for specifying the authorization logic of your application. Install the gem, add a role attribute to your user model and your almost ready to go.}
14
14
  s.email = %q{boborbt@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -23,7 +23,9 @@ Gem::Specification.new do |s|
23
23
  "VERSION",
24
24
  "lib/role_based_authorization.rb",
25
25
  "lib/role_based_authorization/authorization_logger.rb",
26
+ "lib/role_based_authorization/class_additions.rb",
26
27
  "lib/role_based_authorization/role_based_authorization.rb",
28
+ "lib/role_based_authorization/rule.rb",
27
29
  "rails/init.rb",
28
30
  "role_based_authorization.gemspec",
29
31
  "test/role_based_authorization_test.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: role_based_authorization
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roberto Esposito
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-17 00:00:00 +01:00
12
+ date: 2010-02-18 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -29,7 +29,9 @@ files:
29
29
  - VERSION
30
30
  - lib/role_based_authorization.rb
31
31
  - lib/role_based_authorization/authorization_logger.rb
32
+ - lib/role_based_authorization/class_additions.rb
32
33
  - lib/role_based_authorization/role_based_authorization.rb
34
+ - lib/role_based_authorization/rule.rb
33
35
  - rails/init.rb
34
36
  - role_based_authorization.gemspec
35
37
  - test/role_based_authorization_test.rb