forked from spree/spree
/
role_requirement_system.rb
145 lines (121 loc) · 5.19 KB
/
role_requirement_system.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# Main module for authentication.
# Include this in ApplicationController to activate RoleRequirement
#
# See RoleSecurityClassMethods for some methods it provides.
module RoleRequirementSystem
def self.included(klass)
klass.send :class_inheritable_array, :role_requirements
klass.send :include, RoleSecurityInstanceMethods
klass.send :extend, RoleSecurityClassMethods
klass.send :helper_method, :url_options_authenticate?
klass.send :role_requirements=, []
end
module RoleSecurityClassMethods
def reset_role_requirements!
self.role_requirements.clear
end
# Add this to the top of your controller to require a role in order to access it.
# Example Usage:
#
# require_role "contractor"
# require_role "admin", :only => :destroy # don't allow contractors to destroy
# require_role "admin", :only => :update, :unless => "current_user.authorized_for_listing?(params[:id]) "
#
# Valid options
#
# * :only - Only require the role for the given actions
# * :except - Require the role for everything but
# * :if - a Proc or a string to evaluate. If it evaluates to true, the role is required.
# * :unless - The inverse of :if
#
def require_role(roles, options = {})
options.assert_valid_keys(:if, :unless,
:for, :only,
:for_all_except, :except
)
# only declare that before filter once
unless (@before_filter_declared||=false)
@before_filter_declared=true
before_filter :check_roles
end
# convert to an array if it isn't already
roles = [roles] unless Array===roles
options[:only] ||= options[:for] if options[:for]
options[:except] ||= options[:for_all_except] if options[:for_all_except]
# convert any actions into symbols
for key in [:only, :except]
if options.has_key?(key)
options[key] = [options[key]] unless Array === options[key]
options[key] = options[key].compact.collect{|v| v.to_sym}
end
end
self.role_requirements||=[]
self.role_requirements << {:roles => roles, :options => options }
end
# This is the core of RoleRequirement. Here is where it discerns if a user can access a controller or not./
def user_authorized_for?(user, params = {}, binding = self.binding)
return true unless Array===self.role_requirements
self.role_requirements.each{| role_requirement|
roles = role_requirement[:roles]
options = role_requirement[:options]
# do the options match the params?
# check the action
if options.has_key?(:only)
next unless options[:only].include?( (params[:action]||"index").to_sym )
end
if options.has_key?(:except)
next if options[:except].include?( (params[:action]||"index").to_sym)
end
if options.has_key?(:if)
# execute the proc. if the procedure returns false, we don't need to authenticate these roles
next unless ( String===options[:if] ? eval(options[:if], binding) : options[:if].call(params) )
end
if options.has_key?(:unless)
# execute the proc. if the procedure returns true, we don't need to authenticate these roles
next if ( String===options[:unless] ? eval(options[:unless], binding) : options[:unless].call(params) )
end
# check to see if they have one of the required roles
passed = false
roles.each { |role|
passed = true if user.has_role?(role)
} unless (user.nil? || user==false)
return false unless passed
}
return true
end
end
module RoleSecurityInstanceMethods
def self.included(klass)
#raise "Because role_requirement extends acts_as_authenticated, You must include AuthenticatedSystem first before including RoleRequirementSystem!" unless klass.included_modules.include?(AuthenticatedSystem)
end
# We want to render something when access is denied, how does this make sense?
#
# def access_denied
# if logged_in?
# render :nothing => true, :status => 401
# return false
# else
# super
# end
# end
def check_roles
return access_denied unless self.class.user_authorized_for?(current_user, params, binding)
true
end
protected
# receives a :controller, :action, and :params. Finds the given controller and runs user_authorized_for? on it.
# This can be called in your views, and is for advanced users only. If you are using :if / :unless eval expressions,
# then this may or may not work (eval strings use the current binding to execute, not the binding of the target
# controller)
def url_options_authenticate?(params = {})
params = params.symbolize_keys
if params[:controller]
# find the controller class
klass = eval("#{params[:controller]}_controller".classify)
else
klass = self.class
end
klass.user_authorized_for?(current_user, params, binding)
end
end
end