model_security_generator 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 User.current and (admin? or action_name != 'login_admin')
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
- session[:skip_user_setup] = true
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.sign_on_by_session(1)
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
@@ -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
- self.return_location = @request.env['HTTP_REFERER']
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
- @session[:return_to]
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
- @session[:return_to] = a
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
- self.return_location = @request.request_uri
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
- redirect_to_url r
65
- self.return_location = nil
66
- else
67
- redirect_to attributes, method_params
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(e)
31
+ def require_condition(header = nil, message = nil)
35
32
  if yield
36
33
  return true
37
34
  else
38
- if controller_name != 'user' and (action_name != 'login' and action_name != 'login_admin')
35
+ if controller_name != 'user' or (action_name != 'login' and action_name != 'login_admin')
39
36
  store_location
40
37
  end
41
- redirect_to :controller => 'user', :action => 'login', :explanation => e
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
- require_condition("Administrative user required.") { admin? }
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("Login required.") { User.current }
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 @session[:skip_user_setup] == true
85
- @session[:skip_user_setup] = false
100
+ if flash[:skip_user_setup] == true
101
+ flash[:skip_user_setup] = false
86
102
  return true
87
103
  end
88
104
 
89
- user = login = password = nil
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
- if user
141
- User.current = user
142
- @session[:user_id] = user.id
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
@@ -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
- User.current and User.current.id == id
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
- return ((current != nil ) and (current.admin.to_i == 1))
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::current = self
355
+ User.current = self
327
356
  return self;
328
357
  end
329
358
  return nil
@@ -1,6 +1,16 @@
1
- <h1>Please login</h1>
1
+ <h1>
2
+ <% if flash[:login_header] %>
3
+ <%= flash[:login_header] + '. ' %>
4
+ <% end %>
5
+ Please log in.
6
+ </h1>
2
7
 
3
- <%= start_form_tag :action => 'login' %>
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.6
7
- date: 2005-10-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
  - "."