model_security_generator 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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>