model_security_generator 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/templates/controllers/user_controller.rb +4 -4
- data/templates/lib/modal.rb +22 -8
- data/templates/lib/user_support.rb +34 -21
- data/templates/models/user.rb +52 -23
- data/templates/views/login.rhtml +12 -2
- metadata +2 -2
@@ -69,13 +69,14 @@ public
|
|
69
69
|
# If this method is called login_admin (it's an alias), keep trying
|
70
70
|
# until an administrator logs in or the user pushes the "back" button.
|
71
71
|
def login
|
72
|
-
if
|
72
|
+
if flash[:login_succeeded]
|
73
73
|
redirect_back_or_default :action => :success
|
74
74
|
return
|
75
75
|
end
|
76
76
|
|
77
77
|
@user = User.new
|
78
78
|
|
79
|
+
flash[:login_succeeded] = false
|
79
80
|
http_authorize
|
80
81
|
end
|
81
82
|
|
@@ -93,7 +94,7 @@ public
|
|
93
94
|
def logout
|
94
95
|
User.sign_off
|
95
96
|
reset_session
|
96
|
-
|
97
|
+
flash[:skip_user_setup] = true
|
97
98
|
redirect_to :action => 'login'
|
98
99
|
end
|
99
100
|
|
@@ -112,8 +113,7 @@ public
|
|
112
113
|
@user.admin = 1
|
113
114
|
@user.activated = 1
|
114
115
|
@user.save
|
115
|
-
User.
|
116
|
-
session[:user_id] = 1
|
116
|
+
User.current = @user
|
117
117
|
render :action => 'admin_created'
|
118
118
|
# Mail the user instructions on how to activate their account.
|
119
119
|
else
|
data/templates/lib/modal.rb
CHANGED
@@ -30,9 +30,14 @@ module Modal
|
|
30
30
|
def modal_setup
|
31
31
|
# Set modal return.
|
32
32
|
if r = @params['ret']
|
33
|
+
logger.info("modal_setup: Save location #{r.sub(/\.L/, '#L')}")
|
33
34
|
self.return_location = r.sub(/\.L/, '#L')
|
34
35
|
elsif return_location.nil?
|
35
|
-
|
36
|
+
r = @request.env['HTTP_REFERER']
|
37
|
+
if r
|
38
|
+
logger.info("modal_setup: Save HTTP Referer #{r}")
|
39
|
+
self.return_location = @request.env['HTTP_REFERER']
|
40
|
+
end
|
36
41
|
end
|
37
42
|
true
|
38
43
|
end
|
@@ -40,19 +45,22 @@ module Modal
|
|
40
45
|
# Get the location to return to after a modal action. The argument should
|
41
46
|
# be a string containing a URL.
|
42
47
|
def return_location
|
43
|
-
|
48
|
+
session[:return_to]
|
44
49
|
end
|
45
50
|
|
46
51
|
# Set the location to return to after a modal action. The return value should
|
47
52
|
# be a string containing a URL.
|
48
53
|
def return_location= a
|
49
|
-
|
54
|
+
logger.info("return_location= #{a}")
|
55
|
+
session[:return_to] = a
|
50
56
|
end
|
51
57
|
|
52
58
|
# Store the location to return to after a modal action from the request URI.
|
53
59
|
# This is usually called before redirecting to another action.
|
54
60
|
def store_location
|
55
|
-
|
61
|
+
uri = @request.request_uri
|
62
|
+
logger.info("Store location: #{uri}")
|
63
|
+
self.return_location = uri
|
56
64
|
end
|
57
65
|
|
58
66
|
# Redirect to the stored return location. If no stored return location
|
@@ -61,11 +69,17 @@ module Modal
|
|
61
69
|
def redirect_back_or_default(attributes = {}, *method_params)
|
62
70
|
r = return_location
|
63
71
|
if r
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
72
|
+
if r == @request.request_uri
|
73
|
+
logger.info("redirect_back_or_default: BREAKING REDIRECTION LOOP #{r}.")
|
74
|
+
else
|
75
|
+
logger.info("redirect_back_or_default: return to #{r}")
|
76
|
+
self.return_location = nil
|
77
|
+
redirect_to_url r
|
78
|
+
return
|
79
|
+
end
|
68
80
|
end
|
81
|
+
logger.info("redirect_back_or_default: go to default #{attributes.inspect}, #{method_params.inspect}")
|
82
|
+
redirect_to attributes, method_params
|
69
83
|
end
|
70
84
|
|
71
85
|
# Create a URL optionally including an internal anchor. If the +id+ argument
|
@@ -22,23 +22,28 @@ module UserSupport
|
|
22
22
|
end
|
23
23
|
|
24
24
|
|
25
|
-
# FIX: This only works for require_login and require_admin for now, because
|
26
|
-
# I'm not passing the block across invocations.
|
27
|
-
#
|
28
25
|
# This is meant to be used as a before_filter.
|
29
26
|
# A condition that is dependent on the user's login is in the block.
|
30
27
|
# If the condition isn't true, a login panel is put up, and the explanation
|
31
28
|
# that is passed as an argument may (or may not) be presented to the user,
|
32
29
|
# depending on whether we're using HTTP authentication or not.
|
33
30
|
# Once the condition is met, it resumes the action it was protecting.
|
34
|
-
def require_condition(
|
31
|
+
def require_condition(header = nil, message = nil)
|
35
32
|
if yield
|
36
33
|
return true
|
37
34
|
else
|
38
|
-
if controller_name != 'user'
|
35
|
+
if controller_name != 'user' or (action_name != 'login' and action_name != 'login_admin')
|
39
36
|
store_location
|
40
37
|
end
|
41
|
-
|
38
|
+
|
39
|
+
# This test is to avoid writing the flash unnecessarily.
|
40
|
+
# Currently, writing the flash causes the entire session, not just the
|
41
|
+
# variables in question, to be written twice.
|
42
|
+
if header or message
|
43
|
+
flash[:login_header] = header
|
44
|
+
flash[:login_message] = message
|
45
|
+
end
|
46
|
+
redirect_to :controller => 'user', :action => 'login'
|
42
47
|
return false
|
43
48
|
end
|
44
49
|
end
|
@@ -48,7 +53,11 @@ module UserSupport
|
|
48
53
|
# isn't currently logged in. Once the administrator logs in, it resumes
|
49
54
|
# the action it was protecting.
|
50
55
|
def require_admin
|
51
|
-
|
56
|
+
header = "Administrative user required."
|
57
|
+
message = "You must be an administrative user to perform this action. " \
|
58
|
+
+ "If you don't have an administrative login, please use the back button "\
|
59
|
+
+ "of your browser to cancel this action."
|
60
|
+
require_condition(header, message) { admin? }
|
52
61
|
end
|
53
62
|
|
54
63
|
# This is meant to be used as a before_filter. It requires a
|
@@ -56,7 +65,7 @@ module UserSupport
|
|
56
65
|
# logged in. Once a user logs in, it resumes the action it was
|
57
66
|
# protecting.
|
58
67
|
def require_login
|
59
|
-
require_condition("
|
68
|
+
require_condition(nil, "You must be logged in to perform this action.") { User.current }
|
60
69
|
end
|
61
70
|
|
62
71
|
# This is a before filter for the entire application, used to set up the
|
@@ -72,23 +81,30 @@ module UserSupport
|
|
72
81
|
# security tests of ModelSecurity that are based on User, or anything
|
73
82
|
# that expects login information.
|
74
83
|
#
|
84
|
+
# Keep this function in sync with User.current() and User.current=().
|
85
|
+
# It's aware of the way those functions store the user information.
|
86
|
+
#
|
75
87
|
def user_setup
|
76
88
|
# require_* use Modal to return to what they were doing after HTTP
|
77
89
|
# authentication.
|
78
90
|
modal_setup
|
79
91
|
|
92
|
+
# User.current=() needs a thread-global reference to the session.
|
93
|
+
Thread.current[:session] = session
|
94
|
+
logger.info("Session is #{Thread.current[:session]}")
|
95
|
+
|
80
96
|
# This is used by the logout action to discard the old HTTP authentiction.
|
81
97
|
# Logout redirects to login and that generates a new authentication
|
82
98
|
# request. That request is the only input that can tell the browser to
|
83
99
|
# stop sending the old authentication data with every request!
|
84
|
-
if
|
85
|
-
|
100
|
+
if flash[:skip_user_setup] == true
|
101
|
+
flash[:skip_user_setup] = false
|
86
102
|
return true
|
87
103
|
end
|
88
104
|
|
89
|
-
|
90
|
-
|
105
|
+
login = password = nil
|
91
106
|
r = @request.env
|
107
|
+
old_user = user = session[:user]
|
92
108
|
|
93
109
|
# If the request contains an HTTP authentication, decode it.
|
94
110
|
# Don't use it to authenticate the user yet.
|
@@ -102,11 +118,6 @@ module UserSupport
|
|
102
118
|
end
|
103
119
|
end
|
104
120
|
|
105
|
-
# If the user is already logged into the session, get the user record.
|
106
|
-
if (id = @session[:user_id])
|
107
|
-
user = User.sign_on_by_session(id)
|
108
|
-
end
|
109
|
-
|
110
121
|
# If the HTTP authentication is for a different user name, the user wants
|
111
122
|
# to change logins. This can happen if an operation requires an
|
112
123
|
# administrative login and the current user isn't the administrator but
|
@@ -137,11 +148,13 @@ module UserSupport
|
|
137
148
|
user = User.sign_on_by_token(@params[:id], @params['token'])
|
138
149
|
end
|
139
150
|
|
140
|
-
|
141
|
-
|
142
|
-
|
151
|
+
# User.current must always be set with each request. It's backed by a
|
152
|
+
# class-global variable.
|
153
|
+
User.current = user
|
154
|
+
|
155
|
+
if user != old_user
|
156
|
+
flash[:login_succeeded] = true
|
143
157
|
end
|
144
|
-
logger.info("Current user is #{User.current.inspect}.")
|
145
158
|
|
146
159
|
true
|
147
160
|
end
|
data/templates/models/user.rb
CHANGED
@@ -156,6 +156,53 @@ public
|
|
156
156
|
let_write :password_confirmation, :if => :new_or_me?
|
157
157
|
let_write :old_password, :if => :me?
|
158
158
|
|
159
|
+
# Return the user for the current request. It is guaranteed that this is
|
160
|
+
# set for each request in the before_filter for the application.
|
161
|
+
#
|
162
|
+
# This function uses the Ruby Thread class to do thread-local storage,
|
163
|
+
# which will be overkill if the Rails server implementation isn't also
|
164
|
+
# using Ruby threads, but works everywhere.
|
165
|
+
#
|
166
|
+
# User.current(), User.current=(), and UserSupport#user_setup encapsulate
|
167
|
+
# session storage of user information. Only these three functions should
|
168
|
+
# know whether we store the entire User object in the session or only
|
169
|
+
# User#id.
|
170
|
+
#
|
171
|
+
def User.current
|
172
|
+
# This does not refer to the session because the application has set
|
173
|
+
# this from the session in user_setup.
|
174
|
+
Thread.current[:user]
|
175
|
+
end
|
176
|
+
|
177
|
+
# Set the user for the current request. It is guaranteed that this is
|
178
|
+
# set for each request in the before_filter for the application.
|
179
|
+
#
|
180
|
+
# This function uses the Ruby Thread class to do thread-local storage,
|
181
|
+
# which will be overkill if the Rails server implementation isn't also
|
182
|
+
# using Ruby threads, but works everywhere.
|
183
|
+
#
|
184
|
+
# User.current(), User.current=(), and UserSupport#user_setup encapsulate
|
185
|
+
# session storage of user information. Only these three functions should
|
186
|
+
# know whether we store the entire User object in the session or only
|
187
|
+
# User#id.
|
188
|
+
#
|
189
|
+
def User.current=(u)
|
190
|
+
Thread.current[:user] = u
|
191
|
+
|
192
|
+
session = Thread.current[:session]
|
193
|
+
|
194
|
+
if session.nil?
|
195
|
+
message = "Programming error: Please add \"before_filter :user_setup\" to your application controller. See the ModelSecurity documentation."
|
196
|
+
|
197
|
+
raise RuntimeError.new(message)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Don't cause a session store unnecessarily
|
201
|
+
if session[:user] != u
|
202
|
+
session[:user] = u
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
159
206
|
# Change the user's password. Confirm the old password while doing so.
|
160
207
|
def change_password(attributes)
|
161
208
|
@password_is_new = true
|
@@ -172,26 +219,18 @@ public
|
|
172
219
|
self.old_password = attributes['old_password']
|
173
220
|
end
|
174
221
|
|
175
|
-
# Return the currently-logged-in user.
|
176
|
-
def User.current
|
177
|
-
@current_user
|
178
|
-
end
|
179
|
-
|
180
|
-
# Set the currently-logged-in user.
|
181
|
-
def User.current=(u)
|
182
|
-
@current_user = u
|
183
|
-
end
|
184
|
-
|
185
222
|
# Return true if this record corresponds to the currently-logged-in user.
|
186
223
|
# This is used as a security test.
|
187
224
|
def me?
|
188
|
-
|
225
|
+
u = User.current
|
226
|
+
u and u.id == id
|
189
227
|
end
|
190
228
|
|
191
229
|
# Return true if the currently-logged-in user is the administrator.
|
192
230
|
# Class method. This is used as a pseudo-security test by let_display.
|
193
231
|
def User.admin?
|
194
|
-
|
232
|
+
u = User.current
|
233
|
+
return ((u != nil ) and (u.admin.to_i == 1))
|
195
234
|
end
|
196
235
|
|
197
236
|
# Return true if the currently-logged-in user is the administrator.
|
@@ -305,16 +344,6 @@ public
|
|
305
344
|
end
|
306
345
|
end
|
307
346
|
|
308
|
-
# Continue the current login, from the session data.
|
309
|
-
# This should be called by User.setup .
|
310
|
-
def User.sign_on_by_session(user_id)
|
311
|
-
begin
|
312
|
-
return (User.current = User.find(user_id))
|
313
|
-
rescue
|
314
|
-
end
|
315
|
-
return nil
|
316
|
-
end
|
317
|
-
|
318
347
|
# Sign on the user using a security token. Instance method.
|
319
348
|
def sign_on_by_token(t)
|
320
349
|
User.current = User.login_user
|
@@ -323,7 +352,7 @@ public
|
|
323
352
|
self.token_expiry = Time.now
|
324
353
|
self.activated = 1
|
325
354
|
save
|
326
|
-
User
|
355
|
+
User.current = self
|
327
356
|
return self;
|
328
357
|
end
|
329
358
|
return nil
|
data/templates/views/login.rhtml
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
-
<h1>
|
1
|
+
<h1>
|
2
|
+
<% if flash[:login_header] %>
|
3
|
+
<%= flash[:login_header] + '. ' %>
|
4
|
+
<% end %>
|
5
|
+
Please log in.
|
6
|
+
</h1>
|
2
7
|
|
3
|
-
|
8
|
+
<% if flash[:login_message] %>
|
9
|
+
<p>
|
10
|
+
<%= flash[:login_message] %>
|
11
|
+
</p>
|
12
|
+
<% end %>
|
13
|
+
<%= form_tag :action => 'login' %>
|
4
14
|
<p><label for="user_login">User ID</label><br/>
|
5
15
|
<%= text_field 'user', 'login' %></p>
|
6
16
|
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: model_security_generator
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2005-10-
|
6
|
+
version: 0.0.7
|
7
|
+
date: 2005-10-11
|
8
8
|
summary: "[Rails] Model security and authentication generator."
|
9
9
|
require_paths:
|
10
10
|
- "."
|