salted_login_generator 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -58,7 +58,7 @@ class SaltedLoginGenerator < LocalizationGenerator #Rails::Generator::NamedBase
58
58
  attr_accessor :controller_class_name
59
59
 
60
60
  def login_views
61
- %w(welcome login logout signup forgot_password change_password)
61
+ %w(welcome login logout edit signup forgot_password change_password)
62
62
  end
63
63
 
64
64
  def partial_views
@@ -66,6 +66,6 @@ class SaltedLoginGenerator < LocalizationGenerator #Rails::Generator::NamedBase
66
66
  end
67
67
 
68
68
  def notify_views
69
- %w(signup forgot_password change_password)
69
+ %w(signup forgot_password change_password delete pending_delete)
70
70
  end
71
71
  end
data/templates/README CHANGED
@@ -8,7 +8,7 @@ this:
8
8
 
9
9
  class ApplicationController < ActionController::Base
10
10
  include <%= class_name %>System
11
- helper: <%= class_name %>
11
+ helper: <%= singular_name %>
12
12
  before_filter :login_required
13
13
 
14
14
  After you have done the modifications the the ApplicationController and its
@@ -132,5 +132,6 @@ You can find more help at http://wiki.rubyonrails.com/rails/show/SaltedLoginGene
132
132
 
133
133
  == Changelog
134
134
 
135
+ 1.0.6 Proper delete support and bug fixes
135
136
  1.0.5 Lots of fixes and changes (see rubyforge.org/salted-login)
136
137
  1.0.0 First gem release
@@ -1,6 +1,5 @@
1
1
  <div class="<%= singular_name %>_edit">
2
2
  <%%= form_input :hidden_field, 'form', :value => 'edit' %>
3
- <%%= form_input :hidden_field, 'id' %>
4
3
 
5
4
  <table>
6
5
  <%%= form_input changeable(<%= singular_name %>, "firstname"), "firstname" %>
@@ -1,6 +1,5 @@
1
1
  <div class="<%= singular_name %>_password">
2
2
  <%%= form_input :hidden_field, 'form', :value => 'change_password' %>
3
- <%%= form_input :hidden_field, 'id' %>
4
3
 
5
4
  <table>
6
5
  <%%= form_input :password_field, "password", :size => 30 %>
@@ -96,7 +96,6 @@ class <%= class_name %>Controller < ApplicationController
96
96
  generate_filled_in
97
97
  if @params['<%= singular_name %>']['form']
98
98
  form = @params['<%= singular_name %>'].delete('form')
99
- oid = @params['<%= singular_name %>'].delete('id')
100
99
  begin
101
100
  case form
102
101
  when "edit"
@@ -106,6 +105,8 @@ class <%= class_name %>Controller < ApplicationController
106
105
  @<%= singular_name %>.save
107
106
  when "change_password"
108
107
  change_password
108
+ when "delete"
109
+ delete
109
110
  else
110
111
  raise "unknown edit action"
111
112
  end
@@ -114,11 +115,35 @@ class <%= class_name %>Controller < ApplicationController
114
115
  end
115
116
 
116
117
  def delete
117
- if @params['id']
118
- <%= singular_name %> = <%= class_name %>.find(@params['id'])
119
- <%= singular_name %>.destroy()
118
+ @<%= singular_name %> = @session['<%= singular_name %>']
119
+ begin
120
+ if <%= class_name %>System::CONFIG[:delayed_delete]
121
+ <%= class_name %>.transaction(@<%= singular_name %>) do
122
+ key = @<%= singular_name %>.set_delete_after
123
+ url = url_for(:action => 'restore_deleted')
124
+ url += "?<%= singular_name %>[id]=#{@<%= singular_name %>.id}&key=#{key}"
125
+ <%= class_name %>Notify.deliver_pending_delete(@<%= singular_name %>, url)
126
+ end
127
+ else
128
+ destroy(@<%= singular_name %>)
129
+ end
130
+ logout
131
+ redirect_to :action => 'login'
132
+ rescue
133
+ flash.now['message'] = l(:<%= singular_name %>_delete_email_error, "#{@<%= singular_name %>['email']}")
134
+ redirect_back_or_default :action => 'welcome'
135
+ end
136
+ end
137
+
138
+ def restore_deleted
139
+ @<%= singular_name %> = @session['<%= singular_name %>']
140
+ @<%= singular_name %>.deleted = 0
141
+ if not @<%= singular_name %>.save
142
+ flash.now['notice'] = l(:<%= singular_name %>_restore_deleted_error, "#{@<%= singular_name %>['login']}")
143
+ redirect_to :action => 'login'
144
+ else
145
+ redirect_to :action => 'welcome'
120
146
  end
121
- redirect_to :action => 'login'
122
147
  end
123
148
 
124
149
  def welcome
@@ -126,6 +151,12 @@ class <%= class_name %>Controller < ApplicationController
126
151
 
127
152
  protected
128
153
 
154
+ def destroy(user)
155
+ <%= class_name %>Notify.deliver_delete(<%= singular_name %>)
156
+ flash['notice'] = l(:<%= singular_name %>_delete_finished, "#{<%= singular_name %>['login']}")
157
+ <%= singular_name %>.destroy()
158
+ end
159
+
129
160
  def protect?(action)
130
161
  if ['login', 'signup', 'forgot_password'].include?(action)
131
162
  return false
@@ -48,9 +48,9 @@ class <%= class_name %>ControllerTest < Test::Unit::TestCase
48
48
  assert_equal 0, <%= singular_name %>.verified
49
49
 
50
50
  # First past the expiration.
51
- Time.advance_one_day = true
51
+ Time.advance_by_days = 1
52
52
  get :welcome, "<%= singular_name %>"=> { "id" => "#{<%= singular_name %>.id}" }, "key" => "#{key}"
53
- Time.advance_one_day = false
53
+ Time.advance_by_days = 0
54
54
  <%= singular_name %> = <%= class_name %>.find_by_email("newbob@test.com")
55
55
  assert_equal 0, <%= singular_name %>.verified
56
56
 
@@ -83,6 +83,61 @@ class <%= class_name %>ControllerTest < Test::Unit::TestCase
83
83
  assert false
84
84
  end
85
85
  end
86
+
87
+ def test_edit
88
+ post :login, "<%= singular_name %>" => { "login" => "bob", "password" => "atest" }
89
+ assert_session_has "<%= singular_name %>"
90
+
91
+ post :edit, "<%= singular_name %>" => { "firstname" => "Bob", "form" => "edit" }
92
+ assert_equal @response.session['<%= singular_name %>'].firstname, "Bob"
93
+
94
+ post :edit, "<%= singular_name %>" => { "firstname" => "", "form" => "edit" }
95
+ assert_equal @response.session['<%= singular_name %>'].firstname, ""
96
+
97
+ get :logout
98
+ end
99
+
100
+ def test_delete
101
+ ActionMailer::Base.deliveries = []
102
+
103
+ # Immediate delete
104
+ post :login, "<%= singular_name %>" => { "login" => "deletebob1", "password" => "alongtest" }
105
+ assert_session_has "<%= singular_name %>"
106
+
107
+ <%= class_name %>System::CONFIG[:delayed_delete] = false
108
+ post :edit, "<%= singular_name %>" => { "form" => "delete" }
109
+ assert_equal 1, ActionMailer::Base.deliveries.size
110
+
111
+ assert_session_has_no "<%= singular_name %>"
112
+ post :login, "<%= singular_name %>" => { "login" => "deletebob1", "password" => "alongtest" }
113
+ assert_session_has_no "<%= singular_name %>"
114
+
115
+ # Now try delayed delete
116
+ ActionMailer::Base.deliveries = []
117
+
118
+ post :login, "<%= singular_name %>" => { "login" => "deletebob2", "password" => "alongtest" }
119
+ assert_session_has "<%= singular_name %>"
120
+
121
+ <%= class_name %>System::CONFIG[:delayed_delete] = true
122
+ post :edit, "<%= singular_name %>" => { "form" => "delete" }
123
+ assert_equal 1, ActionMailer::Base.deliveries.size
124
+ mail = ActionMailer::Base.deliveries[0]
125
+ mail.encoded =~ /user\[id\]=(.*?)&key=(.*?)"/
126
+ id = $1
127
+ key = $2
128
+ post :restore_deleted, "<%= singular_name %>" => { "id" => "#{id}" }, "key" => "badkey"
129
+ assert_session_has_no "<%= singular_name %>"
130
+
131
+ # Advance the time past the delete date
132
+ Time.advance_by_days = <%= class_name %>System::CONFIG[:delayed_delete_days]
133
+ post :restore_deleted, "<%= singular_name %>" => { "id" => "#{id}" }, "key" => "#{key}"
134
+ assert_session_has_no "<%= singular_name %>"
135
+ Time.advance_by_days = 0
136
+
137
+ post :restore_deleted, "<%= singular_name %>" => { "id" => "#{id}" }, "key" => "#{key}"
138
+ assert_session_has "<%= singular_name %>"
139
+ get :logout
140
+ end
86
141
 
87
142
  def test_signup
88
143
  do_test_signup(true, false)
data/templates/en.yaml CHANGED
@@ -15,12 +15,16 @@ active_record_errors_not_a_number: is not a number
15
15
  <%= singular_name %>_login_failed: Login unsuccessful
16
16
  <%= singular_name %>_signup_succeeded: Signup successful! Please check your registered email account to verify your account registration and continue with the login.
17
17
  <%= singular_name %>_confirmation_email_error: 'Error creating account: confirmation email not sent'
18
- <%= singular_name %>_updated_password: Your updated password has been emailed to %s
18
+ <%= singular_name %>_updated_password: Your updated password has been emailed to %s.
19
19
  <%= singular_name %>_change_password_email_error: Your password could not be changed at this time. Please retry.
20
- <%= singular_name %>_enter_valid_email_address: Please enter a valid email address
21
- <%= singular_name %>_email_address_not_found: We could not find a user with the email address %s
22
- <%= singular_name %>_forgotten_password_emailed: Instructions on resetting your password have been emailed to %s
23
- <%= singular_name %>_forgotten_password_email_error: Your password could not be emailed to %s
20
+ <%= singular_name %>_enter_valid_email_address: Please enter a valid email address.
21
+ <%= singular_name %>_email_address_not_found: We could not find a user with the email address %s.
22
+ <%= singular_name %>_forgotten_password_emailed: Instructions on resetting your password have been emailed to %s.
23
+ <%= singular_name %>_forgotten_password_email_error: Your password could not be emailed to %s.
24
+ <%= singular_name %>_delete_emailed: Instructions on deleting your account have been emailed to %s.
25
+ <%= singular_name %>_delete_email_error: The delete instructions were not sent. Please try again later.
26
+ <%= singular_name %>_delete_finished: The account for %s was successfully deleted.
27
+ <%= singular_name %>_restore_deleted_error: The account for %s was not restored. Please try the link again.
24
28
  <%= singular_name %>_account_verified: Account verified!
25
29
 
26
30
  # Views
@@ -67,3 +71,4 @@ active_record_errors_not_a_number: is not a number
67
71
  # Edit
68
72
  <%= singular_name %>_edit_head: Edit <%= singular_name %>
69
73
  <%= singular_name %>_change_settings_button: Change settings
74
+ <%= singular_name %>_delete_account_button: Delete account
@@ -26,6 +26,15 @@ module <%= class_name %>System
26
26
  # should NOT include the email field in this array.
27
27
  :changeable_fields => [ 'firstname', 'lastname' ],
28
28
 
29
+ # Set to true to allow delayed deletes (i.e., delete of record
30
+ # doesn't happen immediately after user selects delete account,
31
+ # but rather after some expiration of time to allow this action
32
+ # to be reverted).
33
+ :delayed_delete => false,
34
+
35
+ # Default is one week
36
+ :delayed_delete_days => 7,
37
+
29
38
  # Server environment
30
39
  :server_env => "#{RAILS_ENV}"
31
40
  }
@@ -1,14 +1,14 @@
1
1
  require 'time'
2
2
 
3
3
  Time.class_eval {
4
- @@advance_one_day = false
5
- cattr_accessor :advance_one_day
4
+ @@advance_by_days = 0
5
+ cattr_accessor :advance_by_days
6
6
 
7
7
  class << Time
8
8
  alias now_old now
9
9
  def now
10
- if Time.advance_one_day
11
- return Time.at(now_old.to_i + 60 * 60 * 24 + 1)
10
+ if Time.advance_by_days != 0
11
+ return Time.at(now_old.to_i + Time.advance_by_days * 60 * 60 * 24 + 1)
12
12
  else
13
13
  now_old
14
14
  end
data/templates/notify.rb CHANGED
@@ -40,6 +40,31 @@ class <%= class_name %>Notify < ActionMailer::Base
40
40
  @body["app_name"] = <%= class_name %>System::CONFIG[:app_name].to_s
41
41
  end
42
42
 
43
+ def pending_delete(<%= singular_name %>, url=nil)
44
+ setup_email(<%= singular_name %>)
45
+
46
+ # Email header info
47
+ @subject += "Delete <%= singular_name %> notification"
48
+
49
+ # Email body substitutions
50
+ @body["name"] = "#{<%= singular_name %>.firstname} #{<%= singular_name %>.lastname}"
51
+ @body["url"] = url || <%= class_name %>System::CONFIG[:app_url].to_s
52
+ @body["app_name"] = <%= class_name %>System::CONFIG[:app_name].to_s
53
+ @body["days"] = <%= class_name %>System::CONFIG[:delayed_delete_days].to_s
54
+ end
55
+
56
+ def delete(<%= singular_name %>, url=nil)
57
+ setup_email(<%= singular_name %>)
58
+
59
+ # Email header info
60
+ @subject += "Delete <%= singular_name %> notification"
61
+
62
+ # Email body substitutions
63
+ @body["name"] = "#{<%= singular_name %>.firstname} #{<%= singular_name %>.lastname}"
64
+ @body["url"] = url || <%= class_name %>System::CONFIG[:app_url].to_s
65
+ @body["app_name"] = <%= class_name %>System::CONFIG[:app_name].to_s
66
+ end
67
+
43
68
  def setup_email(<%= singular_name %>)
44
69
  @recipients = "#{<%= singular_name %>.email}"
45
70
  @from = <%= class_name %>System::CONFIG[:email_from].to_s
@@ -0,0 +1,5 @@
1
+ Dear <%%= @name %>,
2
+
3
+ At your request, <%%= @app_name %> has permanently deleted your account.
4
+
5
+ <%%= @url %>
@@ -0,0 +1,9 @@
1
+ Dear <%%= @name %>,
2
+
3
+ At your request, <%%= @app_name %> has marked your account for deletion. If it was not at your request, then you should be aware that someone has access to your account and requested this change.
4
+
5
+ The following link is provided for you to restore your deleted account. If you click on this link within the next <%%= @days %> days, your account will not be deleted. Otherwise, simply ignore this email and your account will be permanently deleted after that time.
6
+
7
+ <a href="<%%= @url%>">Click me!</a>
8
+
9
+ <%%= @url %>
@@ -1,4 +1,4 @@
1
- Welcome to logbook, <%%= @name %>.
1
+ Welcome to <%%= @app_name %>, <%%= @name %>.
2
2
 
3
3
  Your login credentials are:
4
4
 
data/templates/user.rb CHANGED
@@ -27,12 +27,14 @@ class <%= class_name %> < ActiveRecord::Base
27
27
  end
28
28
 
29
29
  def self.authenticate(login, pass)
30
- u = find_first(["login = ? AND verified = 1", login])
30
+ u = find_first(["login = ? AND verified = 1 AND deleted = 0", login])
31
31
  return nil if u.nil?
32
32
  find_first(["login = ? AND salted_password = ? AND verified = 1", login, salted_password(u.salt, hashed(pass))])
33
33
  end
34
34
 
35
35
  def self.authenticate_by_token(id, token)
36
+ # Allow logins for deleted accounts, but only via this method (and
37
+ # not the regular authenticate call)
36
38
  u = find_first(["id = ? AND security_token = ?", id, token])
37
39
  return nil if u.nil? or u.token_expired?
38
40
  return nil if false == u.update_expiry
@@ -50,15 +52,25 @@ class <%= class_name %> < ActiveRecord::Base
50
52
  update_without_callbacks
51
53
  end
52
54
 
53
- def generate_security_token
54
- if self.security_token.nil? or self.token_expiry.nil? or
55
+ def generate_security_token(hours = nil)
56
+ if not hours.nil? or self.security_token.nil? or self.token_expiry.nil? or
55
57
  (Time.now.to_i + token_lifetime / 2) >= self.token_expiry.to_i
56
- return new_security_token
58
+ return new_security_token(hours)
57
59
  else
58
60
  return self.security_token
59
61
  end
60
62
  end
61
63
 
64
+ def set_delete_after
65
+ hours = <%= class_name %>System::CONFIG[:delayed_delete_days] * 24
66
+ write_attribute('deleted', 1)
67
+ write_attribute('delete_after', Time.at(Time.now.to_i + hours * 60 * 60))
68
+
69
+ # Generate and return a token here, so that it expires at
70
+ # the same time that the account deletion takes effect.
71
+ return generate_security_token(hours)
72
+ end
73
+
62
74
  def change_password(pass, confirm = nil)
63
75
  self.password = pass
64
76
  self.password_confirmation = confirm.nil? ? pass : confirm
@@ -67,7 +79,7 @@ class <%= class_name %> < ActiveRecord::Base
67
79
 
68
80
  def valid?
69
81
  super
70
- run_validations(:validate_on_virtual) if do_virtual_validations
82
+ run_validations(:validate_on_virtual) if virtual_validations?
71
83
  errors.empty?
72
84
  end
73
85
 
@@ -75,7 +87,7 @@ class <%= class_name %> < ActiveRecord::Base
75
87
 
76
88
  attr_accessor :password, :password_confirmation
77
89
 
78
- def do_virtual_validations
90
+ def virtual_validations?
79
91
  @new_password
80
92
  end
81
93
 
@@ -92,15 +104,19 @@ class <%= class_name %> < ActiveRecord::Base
92
104
  end
93
105
  end
94
106
 
95
- def new_security_token
107
+ def new_security_token(hours = nil)
96
108
  write_attribute('security_token', self.class.hashed(self.salted_password + Time.now.to_i.to_s + rand.to_s))
97
- write_attribute('token_expiry', Time.at(Time.now.to_i + token_lifetime))
109
+ write_attribute('token_expiry', Time.at(Time.now.to_i + token_lifetime(hours)))
98
110
  update_without_callbacks
99
111
  return self.security_token
100
112
  end
101
113
 
102
- def token_lifetime
103
- <%= class_name %>System::CONFIG[:security_token_life_hours] * 60 * 60
114
+ def token_lifetime(hours = nil)
115
+ if hours.nil?
116
+ <%= class_name %>System::CONFIG[:security_token_life_hours] * 60 * 60
117
+ else
118
+ hours * 60 * 60
119
+ end
104
120
  end
105
121
 
106
122
  def self.salted_password(salt, hashed_password)
@@ -12,5 +12,7 @@ CREATE TABLE users (
12
12
  token_expiry <%= @datetime %> default NULL,
13
13
  created_at <%= @datetime %> default NULL,
14
14
  updated_at <%= @datetime %> default NULL,
15
- logged_in_at <%= @datetime %> default NULL
15
+ logged_in_at <%= @datetime %> default NULL,
16
+ deleted INT default 0,
17
+ delete_after <%= @datetime %> default NULL
16
18
  ) <%= @options %>;
data/templates/users.yml CHANGED
@@ -22,4 +22,20 @@ longbob:
22
22
  salted_password: c841391e1d29100a4920de7a8fbb4b0fd180c6c0 # alongtest
23
23
  salt: c068e3671780f16898c0a8295ae8d82cc59713e2
24
24
  email: longbob@test.com
25
+ verified: 1
26
+
27
+ deletebob1:
28
+ id: 1000004
29
+ login: deletebob1
30
+ salted_password: c841391e1d29100a4920de7a8fbb4b0fd180c6c0 # alongtest
31
+ salt: c068e3671780f16898c0a8295ae8d82cc59713e2
32
+ email: deletebob1@test.com
33
+ verified: 1
34
+
35
+ deletebob2:
36
+ id: 1000005
37
+ login: deletebob2
38
+ salted_password: c841391e1d29100a4920de7a8fbb4b0fd180c6c0 # alongtest
39
+ salt: c068e3671780f16898c0a8295ae8d82cc59713e2
40
+ email: deletebob2@test.com
25
41
  verified: 1
@@ -8,5 +8,13 @@
8
8
  <%%= start_form_tag_helper %>
9
9
  <%%= render_partial 'password', :submit => true %>
10
10
  <%%= end_form_tag %>
11
+
12
+ <%%= start_form_tag_helper %>
13
+ <div class="<%= singular_name %>_delete">
14
+ <%%= form_input :hidden_field, 'form', :value => 'delete' %>
15
+
16
+ <%%= form_input :submit_button, 'delete_account' %>
17
+ </div>
18
+ <%%= end_form_tag %>
11
19
  </div>
12
- </div>
20
+ </div>
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.8
3
3
  specification_version: 1
4
4
  name: salted_login_generator
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.0.5
7
- date: 2005-04-19
6
+ version: 1.0.6
7
+ date: 2005-05-04
8
8
  summary: "[Rails] Login generator with salted passwords."
9
9
  require_paths:
10
10
  - "."
@@ -51,6 +51,8 @@ files:
51
51
  - templates/view_change_password.rhtml
52
52
  - templates/notify_signup.rhtml
53
53
  - templates/notify_forgot_password.rhtml
54
+ - templates/notify_pending_delete.rhtml
55
+ - templates/notify_delete.rhtml
54
56
  - templates/notify_change_password.rhtml
55
57
  - templates/mock_notify.rb
56
58
  - templates/mock_time.rb