model_security_generator 0.0.1 → 0.0.2

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.
Files changed (40) hide show
  1. data/{README → templates/README} +0 -0
  2. data/{USAGE → templates/USAGE} +0 -0
  3. data/templates/{user_controller.rb → controllers/user_controller.rb} +32 -27
  4. data/templates/{schema.sql → db/demo.sql} +0 -0
  5. data/templates/{users.sql → db/users.sql} +0 -0
  6. data/templates/{modal_helper.rb → helpers/modal_helper.rb} +0 -0
  7. data/templates/{model_security_helper.rb → helpers/model_security_helper.rb} +0 -0
  8. data/templates/{modal.rb → lib/modal.rb} +0 -0
  9. data/templates/{model_security.rb → lib/model_security.rb} +33 -6
  10. data/templates/{once.rb → lib/once.rb} +0 -0
  11. data/templates/{user_support.rb → lib/user_support.rb} +9 -0
  12. data/templates/{mailer_forgot_password.rhtml → mailer/forgot_password.rhtml} +0 -0
  13. data/templates/mailer/new_user.rhtml +13 -0
  14. data/templates/{user.rb → models/user.rb} +17 -20
  15. data/templates/{user_mailer.rb → models/user_mailer.rb} +4 -3
  16. data/templates/{mock_mailer.rb → test/mock_mailer.rb} +0 -0
  17. data/templates/{mock_time.rb → test/mock_time.rb} +0 -0
  18. data/templates/{user_controller_test.rb → test/user_controller_test.rb} +0 -0
  19. data/templates/{user_test.rb → test/user_test.rb} +0 -0
  20. data/templates/{users.yml → test/users.yml} +0 -0
  21. data/templates/{_view_form.rhtml → views/_form.rhtml} +0 -0
  22. data/templates/{view_activate.rhtml → views/activate.rhtml} +0 -0
  23. data/templates/views/admin_created.rhtml +3 -0
  24. data/templates/views/created.rhtml +4 -0
  25. data/templates/{view_edit.rhtml → views/edit.rhtml} +0 -0
  26. data/templates/{view_forgot_password_done.rhtml → views/forgot_password_done.rhtml} +0 -0
  27. data/templates/{view_list.rhtml → views/list.rhtml} +0 -0
  28. data/templates/{view_login.rhtml → views/login.rhtml} +0 -0
  29. data/templates/{view_login_admin.rhtml → views/login_admin.rhtml} +0 -0
  30. data/templates/{view_logout.rhtml → views/logout.rhtml} +0 -0
  31. data/templates/{view_new.rhtml → views/new.rhtml} +0 -0
  32. data/templates/{view_show.rhtml → views/show.rhtml} +0 -0
  33. data/templates/{view_success.rhtml → views/success.rhtml} +0 -0
  34. metadata +37 -40
  35. data/model_security_generator.rb +0 -75
  36. data/templates/mailer_new_user.rhtml +0 -10
  37. data/templates/scaffold.css +0 -74
  38. data/templates/scaffold.rhtml +0 -11
  39. data/templates/standard.css +0 -7
  40. data/templates/standard.rhtml +0 -16
File without changes
File without changes
@@ -80,7 +80,7 @@ public
80
80
  render :action => 'forgot_password_done'
81
81
  else
82
82
  flash['notice'] = "Can't find a user with email #{@params['email']}."
83
- @user = User.new
83
+ @user = User.new
84
84
  end
85
85
  end
86
86
  end
@@ -121,27 +121,17 @@ public
121
121
  alias login_admin login
122
122
 
123
123
  # Log out the current user, attempt HTTP authentication to log in a new
124
- # user. The real reason we put up HTTP authentication here is to make
125
- # the browser forget the old authentication data. Otherwise, the browser
126
- # keeps sending it!
124
+ # user. The session information skip_user_setup=true tells the server to
125
+ # generate a new HTTP authentication request and ignore the current HTTP
126
+ # authentication data.
127
+ # We have to request new HTTP authentication here to make the browser
128
+ # forget the old authentication data. Otherwise, the browser keeps sending
129
+ # it!
127
130
  def logout
128
- if User.current and @session[:new_login] != true
129
- @session[:new_login] = true
130
- User.sign_off
131
- session[:user_id] = nil
132
- @response.headers["Status"] = "Unauthorized"
133
- @response.headers["WWW-Authenticate"] = \
134
- "Basic realm=\"#{@request.domain(2)}\""
135
- @user = User.new
136
- render :action => 'login', :status => 401
137
- else
138
- @session[:new_login] = false
139
- if User.current
140
- redirect_to :action => 'success'
141
- else
142
- redirect_to :action => 'login'
143
- end
144
- end
131
+ User.sign_off
132
+ session[:user_id] = nil
133
+ session[:skip_user_setup] = true
134
+ redirect_to :action => 'login'
145
135
  end
146
136
 
147
137
  # Create a new user.
@@ -150,14 +140,29 @@ public
150
140
  when :get
151
141
  @user = User.new
152
142
  when :post
153
- @user = User.new(@params['user'])
154
- # FIX: Save may fail. Create the email before the record is saved,
155
- # deliver it afterward.
156
- url = url_for(:controller => 'user', :action => 'activate', :id => @user.id, :token => @user.new_token)
157
- UserMailer.deliver_new_user(@user, url)
143
+ p = @params['user']
144
+ @user = User.new(p)
158
145
  if @user.save
159
146
  flash['notice'] = 'User created.'
160
- redirect_to :action => 'success'
147
+ # Promote the very first user to be an administrator.
148
+ if @user.id == 1
149
+ @user.admin = 1
150
+ @user.activated = 1
151
+ @user.save
152
+ User.sign_on_by_session(1)
153
+ render :file => 'app/views/user/admin_created.rhtml'
154
+ # Mail the user instructions on how to activate their account.
155
+ else
156
+ url_params = {
157
+ :controller => 'user',
158
+ :action => 'activate',
159
+ :id => @user.id,
160
+ :token => @user.new_token
161
+ }
162
+ url = url_for(url_params)
163
+ UserMailer.deliver_new_user(p, url, @user.token_expiry)
164
+ render :file => 'app/views/user/created.rhtml'
165
+ end
161
166
  else
162
167
  flash['notice'] = 'Creation of new user failed.'
163
168
  end
File without changes
File without changes
File without changes
@@ -153,7 +153,6 @@ end
153
153
  module ModelSecurity
154
154
  include ModelSecurity::BothClassAndInstanceMethods
155
155
 
156
- private
157
156
  # This does the permission test for readable? or writable?.
158
157
  def test_permission(permission, attribute)
159
158
  if (d = self.class.read_inheritable_attribute(permission))
@@ -162,6 +161,7 @@ private
162
161
  return true
163
162
  end
164
163
  end
164
+ private
165
165
 
166
166
  # Responsible for raising an exception when an unpermitted security
167
167
  # access is attempted. *permission* is :let_read or :let_write.
@@ -200,8 +200,13 @@ public
200
200
  def self.append_features(base)
201
201
  super
202
202
  base.extend(ClassMethods)
203
+
204
+ # Define default permissions for attributes like :id that are used by
205
+ # Rails.
206
+ base.default_permissions
203
207
  end
204
208
 
209
+
205
210
  # Return true if a read of *attribute* is permitted.
206
211
  # *attribute* should be a symbol, and should be the
207
212
  # name of a database field for this model.
@@ -242,11 +247,6 @@ end
242
247
  # Uses a Rails-internal inheritable-attribute mechanism so that this data in a
243
248
  # derived class survives modification of similar data in its base class.
244
249
  #
245
- # I'm not sure that write_inheritable_attribute() is the right way to go, either
246
- # here or in the way that validators are declared. Why not declare the data
247
- # independently in each subclass and then use *super* to traverse the
248
- # inheritance graph when accessing it?
249
- #
250
250
  module ModelSecurity::ClassMethods
251
251
  private
252
252
  include ModelSecurity::BothClassAndInstanceMethods
@@ -286,6 +286,33 @@ private
286
286
  end
287
287
 
288
288
  public
289
+
290
+ # Install default permissions for all of the attributes that Rails defines.
291
+ #
292
+ # Readable:
293
+ # created_at, created_on, type, id, updated_at, updated_on,
294
+ # lock_version, position, parent_id, lft, rgt,
295
+ # table_name + '_count'
296
+ #
297
+ # Writable:
298
+ # updated_at, updated_on, lock_version, position, parent_id, lft, rgt
299
+ #
300
+ # Writable only before the first save of an Active Record:
301
+ # created_at, created_on, type, id
302
+ #
303
+ def default_permissions
304
+ let_read :created_at, :created_on, :type, :id, :updated_at, \
305
+ :updated_on, :lock_version, :position, :parent_id, :lft, :rgt, \
306
+ (table_name + '_count').to_sym
307
+
308
+ # These shouldn't change after the first save.
309
+ let_write :created_at, :created_on, :type, :id, :if => :new_record?
310
+
311
+ # These can change.
312
+ let_write :updated_at, :updated_on, :lock_version, :position, :parent_id, \
313
+ :lft, :rgt
314
+ end
315
+
289
316
  # Return true if display of the attribute is permitted. *attribute* is a
290
317
  # symbol, and should match a field in the database schema corresponding to
291
318
  # this model.
File without changes
@@ -62,6 +62,15 @@ module UserSupport
62
62
  # that expects login information.
63
63
  #
64
64
  def user_setup
65
+ # This is used by the logout action to discard the old HTTP authentiction.
66
+ # Logout redirects to login and that generates a new authentication
67
+ # request. That request is the only input that can tell the browser to
68
+ # stop sending the old authentication data with every request!
69
+ if @session[:skip_user_setup] == true
70
+ @session[:skip_user_setup] = false
71
+ return true
72
+ end
73
+
65
74
  user = login = password = nil
66
75
 
67
76
  r = @request.env
@@ -0,0 +1,13 @@
1
+ Dear <%= @params['name'] %>,
2
+
3
+ Your login
4
+ '<%= @params['login' %>'
5
+ has been created, but you must now activate it.
6
+ To do so, please access the following URL before
7
+ <%= @token_expiry %>:
8
+
9
+ <%= @url %>
10
+
11
+ Many Thanks
12
+
13
+ The Management
@@ -110,20 +110,19 @@ private
110
110
  # used as a security test, as security tests can access all attributes
111
111
  # with impunity. Login and name are public information.
112
112
  #
113
- # FIX: Reading "id" is necessary for Rails internals.
114
- # Make it readable by default in the ModelSecurity module.
115
- let_read :admin, :id, :login, :name
113
+ let_read :admin, :login, :name
116
114
 
115
+ # Allow the very first user to be promoted to administrator.
116
+ # Once there's an admin, that user has "let_access :all" and can
117
+ # promote others to administrator.
118
+ let_write :admin, :if => :initial_self_promotion
119
+
117
120
  # If this is a new (never saved) record, or if this record corresponds to
118
- # the currently-logged-in user, allow reading of the email address,
119
- # timestamps and lock_version.
120
- #
121
- # FIX: Lock_version is read by rails internals when a record is saved,
122
- # make it readable by default in the ModelSecurity module.
123
- let_read :created_on, :email, :updated_on, :lock_version, :if => :new_or_me_or_logging_in?
121
+ # the currently-logged-in user, allow reading of the email address.
122
+ let_read :email, :if => :new_or_me_or_logging_in?
124
123
 
125
124
  # These attributes are concerned with login security, and can only be read
126
- # while a user is logging in. We create a pseudo-user for the process of
125
+ # while a user is logging in. We create a pseudo-user for the process of= 1
127
126
  # logging in and a security test :logging_in? that tests for that user.
128
127
  let_read :activated, :cypher, :salt, :token, :token_expiry, \
129
128
  :if => :logging_in?
@@ -132,24 +131,15 @@ private
132
131
 
133
132
  # Only in the case of a new (never saved) record can these fields be written.
134
133
  #
135
- # FIX: Rails internals writes the ID and created_on. Make this test a
136
- # default for :id and :created_on within the ModelSecurity module. Document
137
- # it to the user.
138
- let_write :id, :created_on, :login, :name, :if => :new_record?
134
+ let_write :login, :name, :if => :new_record?
139
135
 
140
136
  # Only allow this information to be updated by the user who owns the record,
141
137
  # unless this record is new (has never been saved).
142
- #
143
- # FIX: There should be a default write permission for updated_on and
144
- # lock_version within the ModelSecurity module, as Rails internals
145
- # write them.
146
138
  let_write :cypher, :email, :salt, :if => :new_or_me?
147
139
 
148
140
  # The security token can only be changed if we're the special "login" user.
149
141
  let_write :activated, :token, :token_expiry, :if => :logging_in?
150
142
 
151
- let_write :updated_on, :lock_version, :if => :new_or_me_or_logging_in?
152
-
153
143
  public
154
144
  attr_accessor :password, :password_confirmation, :old_password
155
145
 
@@ -197,6 +187,13 @@ public
197
187
  return User.admin?
198
188
  end
199
189
 
190
+ # Return true if the user's ID is 1 and the user is attempting to promote
191
+ # himself to administrator. This is used to bootstrap the first administrator
192
+ # and for no other purpose.
193
+ def initial_self_promotion?
194
+ return (self == User.current and id == 1 and not admin?)
195
+ end
196
+
200
197
  # Return true if the user is currently logging in. This security test allows
201
198
  # us to designate model fields to be visible *only* while a user is logging
202
199
  # in.
@@ -17,11 +17,12 @@ class UserMailer < ActionMailer::Base
17
17
 
18
18
  # Send a new-user email, providing the user with a URL used to validate
19
19
  # that user's login.
20
- def new_user(user, url)
21
- @body['user'] = user
20
+ def new_user(params, url, token_expiry)
21
+ @body['params'] = params
22
22
  @body['url'] = url
23
+ @body['token_expiry'] = token_expiry
23
24
 
24
- recipients user.email
25
+ recipients params['email']
25
26
  subject 'Your new login is ready'
26
27
  from 'bruce@perens.com'
27
28
 
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,3 @@
1
+ Your login has been created,
2
+ you are an administrator,
3
+ and you are now logged in.
@@ -0,0 +1,4 @@
1
+ Your login has been created.
2
+ An email is being sent to
3
+ <%= @email %>
4
+ with information required to activate the new account.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.8
2
+ 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.1
7
- date: 2005-08-12
6
+ version: 0.0.2
7
+ date: 2005-09-23
8
8
  summary: "[Rails] Model security and authentication generator."
9
9
  require_paths:
10
10
  - "."
11
- email: ''
11
+ email: bruce@perens.com
12
12
  homepage:
13
13
  rubyforge_project:
14
14
  description: Generates Rails code implementing a model security and authentication system for your Rails app.
@@ -28,42 +28,39 @@ authors:
28
28
  - Bruce Perens
29
29
  - Joe Hosteny
30
30
  files:
31
- - USAGE
32
- - README
33
- - model_security_generator.rb
34
- - templates/modal.rb
35
- - templates/modal_helper.rb
36
- - templates/model_security.rb
37
- - templates/model_security_helper.rb
38
- - templates/once.rb
39
- - templates/user_support.rb
40
- - templates/user.rb
41
- - templates/user_controller.rb
42
- - templates/user_mailer.rb
43
- - templates/user_controller_test.rb
44
- - templates/user_test.rb
45
- - templates/mock_mailer.rb
46
- - templates/mock_time.rb
47
- - templates/users.yml
48
- - templates/schema.sql
49
- - templates/users.sql
50
- - templates/scaffold.css
51
- - templates/scaffold.rhtml
52
- - templates/standard.css
53
- - templates/standard.rhtml
54
- - templates/_view_form.rhtml
55
- - templates/mailer_forgot_password.rhtml
56
- - templates/mailer_new_user.rhtml
57
- - templates/view_activate.rhtml
58
- - templates/view_edit.rhtml
59
- - templates/view_forgot_password_done.rhtml
60
- - templates/view_list.rhtml
61
- - templates/view_login.rhtml
62
- - templates/view_login_admin.rhtml
63
- - templates/view_logout.rhtml
64
- - templates/view_new.rhtml
65
- - templates/view_show.rhtml
66
- - templates/view_success.rhtml
31
+ - templates/README
32
+ - templates/USAGE
33
+ - templates/controllers/user_controller.rb
34
+ - templates/db/demo.sql
35
+ - templates/db/users.sql
36
+ - templates/helpers/modal_helper.rb
37
+ - templates/helpers/model_security_helper.rb
38
+ - templates/lib/modal.rb
39
+ - templates/lib/model_security.rb
40
+ - templates/lib/once.rb
41
+ - templates/lib/user_support.rb
42
+ - templates/mailer/forgot_password.rhtml
43
+ - templates/mailer/new_user.rhtml
44
+ - templates/models/user.rb
45
+ - templates/models/user_mailer.rb
46
+ - templates/test/mock_mailer.rb
47
+ - templates/test/mock_time.rb
48
+ - templates/test/user_controller_test.rb
49
+ - templates/test/user_test.rb
50
+ - templates/test/users.yml
51
+ - templates/views/_form.rhtml
52
+ - templates/views/activate.rhtml
53
+ - templates/views/admin_created.rhtml
54
+ - templates/views/created.rhtml
55
+ - templates/views/edit.rhtml
56
+ - templates/views/forgot_password_done.rhtml
57
+ - templates/views/list.rhtml
58
+ - templates/views/login.rhtml
59
+ - templates/views/login_admin.rhtml
60
+ - templates/views/logout.rhtml
61
+ - templates/views/new.rhtml
62
+ - templates/views/show.rhtml
63
+ - templates/views/success.rhtml
67
64
  test_files: []
68
65
  rdoc_options: []
69
66
  extra_rdoc_files: []
@@ -1,75 +0,0 @@
1
- class ModelSecurityGenerator < Rails::Generator::NamedBase
2
- def manifest
3
- record do |m|
4
- # Check for class naming collisions.
5
- m.class_collisions class_path, "User", "UserController", "UserMailer", "UserSupport", "Modal", "ModalHelper", "ModelSecurity", "ModelSecurityHelper"
6
-
7
- # Libraries
8
- m.file "modal.rb", "lib/modal.rb"
9
- m.file "model_security.rb", "lib/model_security.rb"
10
- m.file "once.rb", "lib/once.rb"
11
- m.file "user_support.rb", "lib/user_support.rb"
12
-
13
- # Helpers
14
- m.file "modal_helper.rb", "app/helpers/modal_helper.rb"
15
- m.file "model_security_helper.rb", "app/helpers/model_security_helper.rb"
16
-
17
- # User
18
- m.file "user.rb", File.join("app/models", "user.rb")
19
- m.file "user_controller.rb", File.join("app/controllers", "user_controller.rb")
20
-
21
- # User mailer
22
- m.file "user_mailer.rb", File.join("app/models", "user_mailer.rb")
23
-
24
- # Testing related stuff
25
- m.file "user_controller_test.rb", "test/functional/user_controller_test.rb"
26
- m.file "user_test.rb", "test/unit/user_test.rb"
27
- m.file "mock_mailer.rb", "test/mocks/test/user_mailer.rb"
28
- m.file "mock_time.rb", "test/mocks/test/time.rb"
29
- m.file "users.yml", "test/fixtures/users.yml"
30
-
31
- # Schemas, configuration and miscellaneous
32
- m.file "schema.sql", "db/schema.sql"
33
- m.file "users.sql", "db/users.sql"
34
-
35
- # Layout and stylesheet.
36
- m.file "scaffold.rhtml", "app/views/layouts/scaffold.rhtml"
37
- m.file "scaffold.css", "public/stylesheets/scaffold.css"
38
- m.file "standard.rhtml", "app/views/layouts/standard.rhtml"
39
- m.file "standard.css", "public/stylesheets/standard.css"
40
-
41
- # Views
42
- m.directory File.join("app/views", "user", file_name)
43
- user_views.each do |action|
44
- m.file "view_#{action}.rhtml",
45
- File.join("app/views", "user", file_name, "#{action}.rhtml")
46
- end
47
-
48
- # Partials
49
- m.directory File.join("app/views", "user", file_name)
50
- partial_views.each do |action|
51
- m.file "_view_#{action}.rhtml",
52
- File.join("app/views", "user", file_name, "_#{action}.rhtml")
53
- end
54
-
55
- # Mailer
56
- m.directory File.join("app/views", "user_mailer")
57
- mailer_views.each do |action|
58
- m.file "mailer_#{action}.rhtml",
59
- File.join("app/views", "user_mailer", "#{action}.rhtml")
60
- end
61
- end
62
- end
63
-
64
- def user_views
65
- %w(activate edit forgot_password_done list login login_admin logout new show success)
66
- end
67
-
68
- def partial_views
69
- %w(form)
70
- end
71
-
72
- def mailer_views
73
- %w(forgot_password new_user)
74
- end
75
- end
@@ -1,10 +0,0 @@
1
- Dear <%= @user.name %>,
2
-
3
- Your login '<%= @user.login %>' has been created, but you must now activate it.
4
- To do so, please access the following URL before <%= @user.token_expiry %>:
5
-
6
- <%= @url %>
7
-
8
- Many Thanks
9
-
10
- The Management
@@ -1,74 +0,0 @@
1
- body { background-color: #fff; color: #333; }
2
-
3
- body, p, ol, ul, td {
4
- font-family: verdana, arial, helvetica, sans-serif;
5
- font-size: 13px;
6
- line-height: 18px;
7
- }
8
-
9
- pre {
10
- background-color: #eee;
11
- padding: 10px;
12
- font-size: 11px;
13
- }
14
-
15
- a { color: #000; }
16
- a:visited { color: #666; }
17
- a:hover { color: #fff; background-color:#000; }
18
-
19
- .fieldWithErrors {
20
- padding: 2px;
21
- background-color: red;
22
- display: table;
23
- }
24
-
25
- #ErrorExplanation {
26
- width: 400px;
27
- border: 2px solid 'red';
28
- padding: 7px;
29
- padding-bottom: 12px;
30
- margin-bottom: 20px;
31
- background-color: #f0f0f0;
32
- }
33
-
34
- #ErrorExplanation h2 {
35
- text-align: left;
36
- font-weight: bold;
37
- padding: 5px 5px 5px 15px;
38
- font-size: 12px;
39
- margin: -7px;
40
- background-color: #c00;
41
- color: #fff;
42
- }
43
-
44
- #ErrorExplanation p {
45
- color: #333;
46
- margin-bottom: 0;
47
- padding: 5px;
48
- }
49
-
50
- #ErrorExplanation ul li {
51
- font-size: 12px;
52
- list-style: square;
53
- }
54
-
55
- div.uploadStatus {
56
- margin: 5px;
57
- }
58
-
59
- div.progressBar {
60
- margin: 5px;
61
- }
62
-
63
- div.progressBar div.border {
64
- background-color: #fff;
65
- border: 1px solid grey;
66
- width: 100%;
67
- }
68
-
69
- div.progressBar div.background {
70
- background-color: #333;
71
- height: 18px;
72
- width: 0%;
73
- }
74
-
@@ -1,11 +0,0 @@
1
- <html>
2
- <head>
3
- <title>: <%= controller.action_name %></title>
4
- <%= stylesheet_link_tag 'scaffold' %>
5
- </head>
6
- <body>
7
-
8
- <%= @content_for_layout %>
9
-
10
- </body>
11
- </html>
@@ -1,7 +0,0 @@
1
- .flash {
2
- text-align: center;
3
- background-color: #C0C0FF;
4
- border: medium double #FF0000;
5
- margin: 4px;
6
- }
7
-
@@ -1,16 +0,0 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
- <head>
4
- <link href="/stylesheets/standard.css" rel="stylesheet" type="text/css" />
5
- </head>
6
- <body class="page">
7
- <% if flash['notice'] %>
8
- <center>
9
- <div class="flash">
10
- <%=h flash['notice'] %>
11
- </div>
12
- </center>
13
- <% end %>
14
- <%= @content_for_layout %>
15
- </body>
16
- </html>