salted_login_generator 1.0.5 → 1.0.6

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.
@@ -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