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 +1 -1
- data/lib/role_based_authorization.rb +2 -0
- data/lib/role_based_authorization/class_additions.rb +66 -0
- data/lib/role_based_authorization/role_based_authorization.rb +36 -148
- data/lib/role_based_authorization/rule.rb +43 -0
- data/role_based_authorization.gemspec +5 -3
- metadata +4 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.12
|
@@ -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
|
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(
|
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
|
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
|
-
|
130
|
-
|
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
|
150
|
-
#
|
151
|
-
|
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
|
163
|
-
controller =
|
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
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
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
|
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.
|
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-
|
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.
|
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-
|
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
|