rodauth 0.10.0 → 1.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG +146 -0
- data/README.rdoc +644 -220
- data/Rakefile +99 -11
- data/doc/account_expiration.rdoc +55 -0
- data/doc/base.rdoc +104 -0
- data/doc/change_login.rdoc +29 -0
- data/doc/change_password.rdoc +26 -0
- data/doc/close_account.rdoc +31 -0
- data/doc/confirm_password.rdoc +22 -0
- data/doc/create_account.rdoc +34 -0
- data/doc/disallow_password_reuse.rdoc +37 -0
- data/doc/email_base.rdoc +19 -0
- data/doc/jwt.rdoc +35 -0
- data/doc/lockout.rdoc +83 -0
- data/doc/login.rdoc +27 -0
- data/doc/login_password_requirements_base.rdoc +50 -0
- data/doc/logout.rdoc +21 -0
- data/doc/otp.rdoc +100 -0
- data/doc/password_complexity.rdoc +50 -0
- data/doc/password_expiration.rdoc +52 -0
- data/doc/password_grace_period.rdoc +10 -0
- data/doc/recovery_codes.rdoc +60 -0
- data/doc/release_notes/1.0.0.txt +443 -0
- data/doc/remember.rdoc +82 -0
- data/doc/reset_password.rdoc +70 -0
- data/doc/session_expiration.rdoc +27 -0
- data/doc/single_session.rdoc +43 -0
- data/doc/sms_codes.rdoc +119 -0
- data/doc/two_factor_base.rdoc +27 -0
- data/doc/verify_account.rdoc +70 -0
- data/doc/verify_account_grace_period.rdoc +15 -0
- data/doc/verify_change_login.rdoc +9 -0
- data/lib/roda/plugins/rodauth.rb +3 -262
- data/lib/rodauth.rb +260 -0
- data/lib/rodauth/features/account_expiration.rb +108 -0
- data/lib/rodauth/features/base.rb +479 -0
- data/lib/rodauth/features/change_login.rb +77 -0
- data/lib/rodauth/features/change_password.rb +66 -0
- data/lib/rodauth/features/close_account.rb +82 -0
- data/lib/rodauth/features/confirm_password.rb +51 -0
- data/lib/rodauth/features/create_account.rb +128 -0
- data/lib/rodauth/features/disallow_password_reuse.rb +82 -0
- data/lib/rodauth/features/email_base.rb +63 -0
- data/lib/rodauth/features/jwt.rb +151 -0
- data/lib/rodauth/features/lockout.rb +262 -0
- data/lib/rodauth/features/login.rb +61 -0
- data/lib/rodauth/features/login_password_requirements_base.rb +123 -0
- data/lib/rodauth/features/logout.rb +37 -0
- data/lib/rodauth/features/otp.rb +338 -0
- data/lib/rodauth/features/password_complexity.rb +89 -0
- data/lib/rodauth/features/password_expiration.rb +111 -0
- data/lib/rodauth/features/password_grace_period.rb +46 -0
- data/lib/rodauth/features/recovery_codes.rb +240 -0
- data/lib/rodauth/features/remember.rb +200 -0
- data/lib/rodauth/features/reset_password.rb +207 -0
- data/lib/rodauth/features/session_expiration.rb +55 -0
- data/lib/rodauth/features/single_session.rb +87 -0
- data/lib/rodauth/features/sms_codes.rb +498 -0
- data/lib/rodauth/features/two_factor_base.rb +135 -0
- data/lib/rodauth/features/verify_account.rb +232 -0
- data/lib/rodauth/features/verify_account_grace_period.rb +76 -0
- data/lib/rodauth/features/verify_change_login.rb +20 -0
- data/lib/rodauth/migrations.rb +130 -0
- data/lib/rodauth/version.rb +9 -0
- data/spec/account_expiration_spec.rb +90 -0
- data/spec/all.rb +1 -0
- data/spec/change_login_spec.rb +149 -0
- data/spec/change_password_spec.rb +177 -0
- data/spec/close_account_spec.rb +162 -0
- data/spec/confirm_password_spec.rb +70 -0
- data/spec/create_account_spec.rb +127 -0
- data/spec/disallow_password_reuse_spec.rb +84 -0
- data/spec/lockout_spec.rb +228 -0
- data/spec/login_spec.rb +188 -0
- data/spec/migrate/001_tables.rb +103 -16
- data/spec/migrate/002_account_password_hash_column.rb +11 -0
- data/spec/migrate_password/001_tables.rb +60 -42
- data/spec/migrate_travis/001_tables.rb +116 -0
- data/spec/password_complexity_spec.rb +108 -0
- data/spec/password_expiration_spec.rb +243 -0
- data/spec/password_grace_period_spec.rb +93 -0
- data/spec/remember_spec.rb +424 -0
- data/spec/reset_password_spec.rb +185 -0
- data/spec/rodauth_spec.rb +57 -980
- data/spec/session_expiration_spec.rb +58 -0
- data/spec/single_session_spec.rb +107 -0
- data/spec/spec_helper.rb +202 -0
- data/spec/two_factor_spec.rb +1310 -0
- data/spec/verify_account_grace_period_spec.rb +135 -0
- data/spec/verify_account_spec.rb +142 -0
- data/spec/verify_change_login_spec.rb +46 -0
- data/spec/views/login.str +2 -2
- data/templates/add-recovery-codes.str +2 -0
- data/templates/button.str +5 -0
- data/templates/change-login.str +5 -18
- data/templates/change-password.str +6 -14
- data/templates/close-account.str +3 -6
- data/templates/confirm-password.str +4 -14
- data/templates/create-account.str +6 -30
- data/templates/login-confirm-field.str +6 -0
- data/templates/login-field.str +6 -0
- data/templates/login.str +5 -19
- data/templates/logout.str +2 -6
- data/templates/otp-auth-code-field.str +6 -0
- data/templates/otp-auth.str +8 -0
- data/templates/otp-disable.str +6 -0
- data/templates/otp-setup.str +21 -0
- data/templates/password-confirm-field.str +6 -0
- data/templates/password-field.str +6 -0
- data/templates/recovery-auth.str +12 -0
- data/templates/recovery-codes.str +6 -0
- data/templates/remember.str +8 -12
- data/templates/reset-password-request.str +2 -2
- data/templates/reset-password.str +4 -18
- data/templates/sms-auth.str +6 -0
- data/templates/sms-code-field.str +6 -0
- data/templates/sms-confirm.str +7 -0
- data/templates/sms-disable.str +7 -0
- data/templates/sms-request.str +5 -0
- data/templates/sms-setup.str +12 -0
- data/templates/unlock-account-request.str +3 -7
- data/templates/unlock-account.str +4 -7
- data/templates/verify-account-resend.str +2 -2
- data/templates/verify-account.str +2 -6
- metadata +191 -29
- data/lib/roda/plugins/rodauth/base.rb +0 -428
- data/lib/roda/plugins/rodauth/change_login.rb +0 -48
- data/lib/roda/plugins/rodauth/change_password.rb +0 -42
- data/lib/roda/plugins/rodauth/close_account.rb +0 -42
- data/lib/roda/plugins/rodauth/create_account.rb +0 -92
- data/lib/roda/plugins/rodauth/lockout.rb +0 -292
- data/lib/roda/plugins/rodauth/login.rb +0 -81
- data/lib/roda/plugins/rodauth/logout.rb +0 -36
- data/lib/roda/plugins/rodauth/remember.rb +0 -226
- data/lib/roda/plugins/rodauth/reset_password.rb +0 -205
- data/lib/roda/plugins/rodauth/verify_account.rb +0 -228
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
describe 'Rodauth close_account feature' do
|
|
4
|
+
it "should support closing accounts when passwords are not required" do
|
|
5
|
+
rodauth do
|
|
6
|
+
enable :login, :close_account
|
|
7
|
+
close_account_requires_password? false
|
|
8
|
+
end
|
|
9
|
+
roda do |r|
|
|
10
|
+
r.rodauth
|
|
11
|
+
r.root{view(:content=>"")}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
login
|
|
15
|
+
page.current_path.must_equal '/'
|
|
16
|
+
|
|
17
|
+
visit '/close-account'
|
|
18
|
+
click_button 'Close Account'
|
|
19
|
+
page.current_path.must_equal '/'
|
|
20
|
+
|
|
21
|
+
DB[:accounts].select_map(:status_id).must_equal [3]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should update account information when closing accounts" do
|
|
25
|
+
statuses = nil
|
|
26
|
+
rodauth do
|
|
27
|
+
enable :login, :close_account
|
|
28
|
+
close_account_requires_password? false
|
|
29
|
+
after_close_account{statuses = [account[:status_id], account_ds.get(:status_id)]}
|
|
30
|
+
end
|
|
31
|
+
roda do |r|
|
|
32
|
+
r.rodauth
|
|
33
|
+
r.root{view(:content=>"")}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
login
|
|
37
|
+
visit '/close-account'
|
|
38
|
+
click_button 'Close Account'
|
|
39
|
+
statuses[0].must_equal 3
|
|
40
|
+
statuses[1].must_equal 3
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should delete accounts when skip_status_checks? is true" do
|
|
44
|
+
rodauth do
|
|
45
|
+
enable :login, :close_account
|
|
46
|
+
close_account_requires_password? false
|
|
47
|
+
skip_status_checks? true
|
|
48
|
+
end
|
|
49
|
+
roda do |r|
|
|
50
|
+
r.rodauth
|
|
51
|
+
r.root{view(:content=>"")}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
login
|
|
55
|
+
page.current_path.must_equal '/'
|
|
56
|
+
|
|
57
|
+
visit '/close-account'
|
|
58
|
+
click_button 'Close Account'
|
|
59
|
+
page.current_path.must_equal '/'
|
|
60
|
+
|
|
61
|
+
DB[:accounts].count.must_equal 0
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "should support closing accounts when passwords are required" do
|
|
65
|
+
rodauth do
|
|
66
|
+
enable :login, :close_account
|
|
67
|
+
end
|
|
68
|
+
roda do |r|
|
|
69
|
+
r.rodauth
|
|
70
|
+
r.root{view(:content=>"")}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
login
|
|
74
|
+
page.current_path.must_equal '/'
|
|
75
|
+
|
|
76
|
+
visit '/close-account'
|
|
77
|
+
fill_in 'Password', :with=>'012345678'
|
|
78
|
+
click_button 'Close Account'
|
|
79
|
+
page.find('#error_flash').text.must_equal "There was an error closing your account"
|
|
80
|
+
page.html.must_include("invalid password")
|
|
81
|
+
DB[:accounts].select_map(:status_id).must_equal [2]
|
|
82
|
+
|
|
83
|
+
fill_in 'Password', :with=>'0123456789'
|
|
84
|
+
click_button 'Close Account'
|
|
85
|
+
page.find('#notice_flash').text.must_equal "Your account has been closed"
|
|
86
|
+
page.current_path.must_equal '/'
|
|
87
|
+
|
|
88
|
+
DB[:accounts].select_map(:status_id).must_equal [3]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should support closing accounts with overrides" do
|
|
92
|
+
rodauth do
|
|
93
|
+
enable :login, :close_account
|
|
94
|
+
close_account do
|
|
95
|
+
account_ds.update(:email => 'foo@bar.com', :status_id=>3)
|
|
96
|
+
end
|
|
97
|
+
close_account_route 'close'
|
|
98
|
+
close_account_redirect '/login'
|
|
99
|
+
end
|
|
100
|
+
roda do |r|
|
|
101
|
+
r.rodauth
|
|
102
|
+
r.root{""}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
login
|
|
106
|
+
page.current_path.must_equal '/'
|
|
107
|
+
|
|
108
|
+
visit '/close'
|
|
109
|
+
page.title.must_equal 'Close Account'
|
|
110
|
+
fill_in 'Password', :with=>'0123456789'
|
|
111
|
+
click_button 'Close Account'
|
|
112
|
+
page.find('#notice_flash').text.must_equal "Your account has been closed"
|
|
113
|
+
page.current_path.must_equal '/login'
|
|
114
|
+
|
|
115
|
+
DB[:accounts].select_map(:status_id).must_equal [3]
|
|
116
|
+
DB[:accounts].select_map(:email).must_equal ['foo@bar.com']
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "should close accounts when account_password_hash_column is set" do
|
|
120
|
+
rodauth do
|
|
121
|
+
enable :create_account, :close_account
|
|
122
|
+
close_account_requires_password? false
|
|
123
|
+
account_password_hash_column :ph
|
|
124
|
+
end
|
|
125
|
+
roda do |r|
|
|
126
|
+
r.rodauth
|
|
127
|
+
r.root{view(:content=>"")}
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
visit '/create-account'
|
|
131
|
+
fill_in 'Login', :with=>'foo2@example.com'
|
|
132
|
+
fill_in 'Confirm Login', :with=>'foo2@example.com'
|
|
133
|
+
fill_in 'Password', :with=>'apple2'
|
|
134
|
+
fill_in 'Confirm Password', :with=>'apple2'
|
|
135
|
+
click_button 'Create Account'
|
|
136
|
+
|
|
137
|
+
visit '/close-account'
|
|
138
|
+
click_button 'Close Account'
|
|
139
|
+
page.current_path.must_equal '/'
|
|
140
|
+
|
|
141
|
+
DB[:accounts].reverse(:id).get(:status_id).must_equal 3
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "should support closing accounts via jwt" do
|
|
145
|
+
rodauth do
|
|
146
|
+
enable :login, :close_account
|
|
147
|
+
end
|
|
148
|
+
roda(:jwt) do |r|
|
|
149
|
+
r.rodauth
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
json_login
|
|
153
|
+
|
|
154
|
+
res = json_request('/close-account', :password=>'0123456')
|
|
155
|
+
res.must_equal [400, {'error'=>"There was an error closing your account", "field-error"=>["password", "invalid password"]}]
|
|
156
|
+
DB[:accounts].select_map(:status_id).must_equal [2]
|
|
157
|
+
|
|
158
|
+
res = json_request('/close-account', :password=>'0123456789')
|
|
159
|
+
res.must_equal [200, {'success'=>"Your account has been closed"}]
|
|
160
|
+
DB[:accounts].select_map(:status_id).must_equal [3]
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
describe 'Rodauth confirm password feature' do
|
|
4
|
+
it "should support confirming passwords" do
|
|
5
|
+
rodauth do
|
|
6
|
+
enable :login, :change_login, :confirm_password, :password_grace_period
|
|
7
|
+
before_change_login_route do
|
|
8
|
+
unless password_recently_entered?
|
|
9
|
+
session[:confirm_password_redirect] = request.path_info
|
|
10
|
+
redirect '/confirm-password'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
roda do |r|
|
|
15
|
+
r.rodauth
|
|
16
|
+
r.get("reset"){session[:last_password_entry] = Time.now.to_i - 400; "a"}
|
|
17
|
+
view :content=>""
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
login
|
|
21
|
+
|
|
22
|
+
visit '/change-login'
|
|
23
|
+
page.title.must_equal 'Change Login'
|
|
24
|
+
|
|
25
|
+
visit '/reset'
|
|
26
|
+
page.body.must_equal 'a'
|
|
27
|
+
|
|
28
|
+
visit '/change-login'
|
|
29
|
+
page.title.must_equal 'Confirm Password'
|
|
30
|
+
fill_in 'Password', :with=>'012345678'
|
|
31
|
+
click_button 'Confirm Password'
|
|
32
|
+
page.find('#error_flash').text.must_equal "There was an error confirming your password"
|
|
33
|
+
page.html.must_include("invalid password")
|
|
34
|
+
|
|
35
|
+
fill_in 'Password', :with=>'0123456789'
|
|
36
|
+
click_button 'Confirm Password'
|
|
37
|
+
page.find('#notice_flash').text.must_equal "Your password has been confirmed"
|
|
38
|
+
|
|
39
|
+
fill_in 'Login', :with=>'foo3@example.com'
|
|
40
|
+
fill_in 'Confirm Login', :with=>'foo3@example.com'
|
|
41
|
+
click_button 'Change Login'
|
|
42
|
+
page.find('#notice_flash').text.must_equal "Your login has been changed"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should support confirming passwords via jwt" do
|
|
46
|
+
rodauth do
|
|
47
|
+
enable :login, :change_password, :confirm_password, :password_grace_period
|
|
48
|
+
end
|
|
49
|
+
roda(:jwt) do |r|
|
|
50
|
+
r.rodauth
|
|
51
|
+
r.post("reset"){rodauth.send(:set_session_value, :last_password_entry, Time.now.to_i - 400); [1]}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
json_login
|
|
55
|
+
|
|
56
|
+
res = json_request('/change-password', "new-password"=>'0123456', "password-confirm"=>'0123456')
|
|
57
|
+
res.must_equal [200, {'success'=>"Your password has been changed"}]
|
|
58
|
+
|
|
59
|
+
json_request('/reset').must_equal [200, [1]]
|
|
60
|
+
|
|
61
|
+
res = json_request('/change-password', "new-password"=>'01234567', "password-confirm"=>'01234567')
|
|
62
|
+
res.must_equal [400, {"field-error"=>["password", "invalid password"], "error"=>"There was an error changing your password"}]
|
|
63
|
+
|
|
64
|
+
res = json_request('/confirm-password', "password"=>'0123456')
|
|
65
|
+
res.must_equal [200, {'success'=>"Your password has been confirmed"}]
|
|
66
|
+
|
|
67
|
+
res = json_request('/change-password', "new-password"=>'01234567', "password-confirm"=>'01234567')
|
|
68
|
+
res.must_equal [200, {'success'=>"Your password has been changed"}]
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
describe 'Rodauth create_account feature' do
|
|
4
|
+
[false, true].each do |ph|
|
|
5
|
+
it "should support creating accounts #{'with account_password_hash_column' if ph}" do
|
|
6
|
+
rodauth do
|
|
7
|
+
enable :login, :create_account
|
|
8
|
+
account_password_hash_column :ph if ph
|
|
9
|
+
create_account_autologin? false
|
|
10
|
+
end
|
|
11
|
+
roda do |r|
|
|
12
|
+
r.rodauth
|
|
13
|
+
r.root{view :content=>""}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
visit '/create-account'
|
|
17
|
+
fill_in 'Login', :with=>'foo@example.com'
|
|
18
|
+
fill_in 'Confirm Login', :with=>'foo@example.com'
|
|
19
|
+
fill_in 'Password', :with=>'0123456789'
|
|
20
|
+
fill_in 'Confirm Password', :with=>'0123456789'
|
|
21
|
+
click_button 'Create Account'
|
|
22
|
+
page.html.must_include("invalid login, already an account with this login")
|
|
23
|
+
page.find('#error_flash').text.must_equal "There was an error creating your account"
|
|
24
|
+
page.current_path.must_equal '/create-account'
|
|
25
|
+
|
|
26
|
+
fill_in 'Login', :with=>'foobar'
|
|
27
|
+
fill_in 'Confirm Login', :with=>'foobar'
|
|
28
|
+
fill_in 'Password', :with=>'0123456789'
|
|
29
|
+
fill_in 'Confirm Password', :with=>'0123456789'
|
|
30
|
+
click_button 'Create Account'
|
|
31
|
+
page.html.must_include("invalid login, not a valid email address")
|
|
32
|
+
page.find('#error_flash').text.must_equal "There was an error creating your account"
|
|
33
|
+
page.current_path.must_equal '/create-account'
|
|
34
|
+
|
|
35
|
+
fill_in 'Login', :with=>'foo@example2.com'
|
|
36
|
+
fill_in 'Password', :with=>'0123456789'
|
|
37
|
+
fill_in 'Confirm Password', :with=>'0123456789'
|
|
38
|
+
click_button 'Create Account'
|
|
39
|
+
page.html.must_include("logins do not match")
|
|
40
|
+
page.find('#error_flash').text.must_equal "There was an error creating your account"
|
|
41
|
+
page.current_path.must_equal '/create-account'
|
|
42
|
+
|
|
43
|
+
fill_in 'Confirm Login', :with=>'foo@example2.com'
|
|
44
|
+
fill_in 'Password', :with=>'0123456789'
|
|
45
|
+
fill_in 'Confirm Password', :with=>'012345678'
|
|
46
|
+
click_button 'Create Account'
|
|
47
|
+
page.html.must_include("passwords do not match")
|
|
48
|
+
page.find('#error_flash').text.must_equal "There was an error creating your account"
|
|
49
|
+
page.current_path.must_equal '/create-account'
|
|
50
|
+
|
|
51
|
+
fill_in 'Password', :with=>'0123456789'
|
|
52
|
+
fill_in 'Confirm Password', :with=>'0123456789'
|
|
53
|
+
click_button 'Create Account'
|
|
54
|
+
page.find('#notice_flash').text.must_equal "Your account has been created"
|
|
55
|
+
page.current_path.must_equal '/'
|
|
56
|
+
|
|
57
|
+
login(:login=>'foo@example2.com')
|
|
58
|
+
page.current_path.must_equal '/'
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should support creating accounts without login/password confirmation" do
|
|
63
|
+
rodauth do
|
|
64
|
+
enable :login, :create_account
|
|
65
|
+
require_login_confirmation? false
|
|
66
|
+
require_password_confirmation? false
|
|
67
|
+
create_account_autologin? false
|
|
68
|
+
end
|
|
69
|
+
roda do |r|
|
|
70
|
+
r.rodauth
|
|
71
|
+
r.root{view :content=>""}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
visit '/create-account'
|
|
75
|
+
fill_in 'Login', :with=>'foo@example2.com'
|
|
76
|
+
fill_in 'Password', :with=>'0123456789'
|
|
77
|
+
click_button 'Create Account'
|
|
78
|
+
page.find('#notice_flash').text.must_equal "Your account has been created"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should support autologin after account creation" do
|
|
82
|
+
rodauth do
|
|
83
|
+
enable :create_account
|
|
84
|
+
end
|
|
85
|
+
roda do |r|
|
|
86
|
+
r.rodauth
|
|
87
|
+
next unless session[:account_id]
|
|
88
|
+
r.root{view :content=>"Logged In: #{DB[:accounts].where(:id=>session[:account_id]).get(:email)}"}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
visit '/create-account'
|
|
92
|
+
fill_in 'Login', :with=>'foo2@example.com'
|
|
93
|
+
fill_in 'Confirm Login', :with=>'foo2@example.com'
|
|
94
|
+
fill_in 'Password', :with=>'apple2'
|
|
95
|
+
fill_in 'Confirm Password', :with=>'apple2'
|
|
96
|
+
click_button 'Create Account'
|
|
97
|
+
page.html.must_include("Logged In: foo2@example.com")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "should support creating accounts via jwt" do
|
|
101
|
+
rodauth do
|
|
102
|
+
enable :login, :create_account
|
|
103
|
+
after_create_account{json_response[:account_id] = account_id}
|
|
104
|
+
create_account_autologin? false
|
|
105
|
+
end
|
|
106
|
+
roda(:jwt) do |r|
|
|
107
|
+
r.rodauth
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
res = json_request('/create-account', :login=>'foo@example.com', "login-confirm"=>'foo@example.com', :password=>'0123456789', "password-confirm"=>'0123456789')
|
|
111
|
+
res.must_equal [400, {'error'=>"There was an error creating your account", "field-error"=>["login", "invalid login, already an account with this login"]}]
|
|
112
|
+
|
|
113
|
+
res = json_request('/create-account', :login=>'foobar', "login-confirm"=>'foobar', :password=>'0123456789', "password-confirm"=>'0123456789')
|
|
114
|
+
res.must_equal [400, {'error'=>"There was an error creating your account", "field-error"=>["login", "invalid login, not a valid email address"]}]
|
|
115
|
+
|
|
116
|
+
res = json_request('/create-account', :login=>'foo@example2.com', "login-confirm"=>'foobar', :password=>'0123456789', "password-confirm"=>'0123456789')
|
|
117
|
+
res.must_equal [400, {'error'=>"There was an error creating your account", "field-error"=>["login", "logins do not match"]}]
|
|
118
|
+
|
|
119
|
+
res = json_request('/create-account', :login=>'foo@example2.com', "login-confirm"=>'foo@example2.com', :password=>'012345678', "password-confirm"=>'0123456789')
|
|
120
|
+
res.must_equal [400, {'error'=>"There was an error creating your account", "field-error"=>["password", "passwords do not match"]}]
|
|
121
|
+
|
|
122
|
+
res = json_request('/create-account', :login=>'foo@example2.com', "login-confirm"=>'foo@example2.com', :password=>'0123456789', "password-confirm"=>'0123456789')
|
|
123
|
+
res.must_equal [200, {'success'=>"Your account has been created", 'account_id'=>DB[:accounts].max(:id)}]
|
|
124
|
+
|
|
125
|
+
json_login(:login=>'foo@example2.com')
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
describe 'Rodauth disallow_password_reuse feature' do
|
|
4
|
+
it "should disallow reuse of passwords" do
|
|
5
|
+
rodauth do
|
|
6
|
+
enable :login, :change_password, :disallow_password_reuse, :close_account
|
|
7
|
+
change_password_requires_password? false
|
|
8
|
+
close_account_requires_password? false
|
|
9
|
+
end
|
|
10
|
+
roda do |r|
|
|
11
|
+
r.rodauth
|
|
12
|
+
r.root{view :content=>""}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
login
|
|
16
|
+
page.current_path.must_equal '/'
|
|
17
|
+
|
|
18
|
+
8.times do |i|
|
|
19
|
+
visit '/change-password'
|
|
20
|
+
fill_in 'New Password', :with=>"password#{i}"
|
|
21
|
+
fill_in 'Confirm Password', :with=>"password#{i}"
|
|
22
|
+
click_button 'Change Password'
|
|
23
|
+
page.find('#notice_flash').text.must_equal "Your password has been changed"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
visit '/change-password'
|
|
27
|
+
|
|
28
|
+
(1..6).each do |i|
|
|
29
|
+
fill_in 'New Password', :with=>"password#{i}"
|
|
30
|
+
fill_in 'Confirm Password', :with=>"password#{i}"
|
|
31
|
+
click_button 'Change Password'
|
|
32
|
+
page.html.must_include("invalid password, does not meet requirements (same as previous password)")
|
|
33
|
+
page.find('#error_flash').text.must_equal "There was an error changing your password"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
fill_in 'New Password', :with=>"password7"
|
|
37
|
+
fill_in 'Confirm Password', :with=>"password7"
|
|
38
|
+
click_button 'Change Password'
|
|
39
|
+
page.html.must_include("invalid password, same as current password")
|
|
40
|
+
|
|
41
|
+
fill_in 'New Password', :with=>'password0'
|
|
42
|
+
fill_in 'Confirm Password', :with=>'password0'
|
|
43
|
+
click_button 'Change Password'
|
|
44
|
+
page.find('#notice_flash').text.must_equal "Your password has been changed"
|
|
45
|
+
|
|
46
|
+
DB[:account_previous_password_hashes].get{count(:id)}.must_equal 7
|
|
47
|
+
visit '/close-account'
|
|
48
|
+
click_button 'Close Account'
|
|
49
|
+
DB[:account_previous_password_hashes].get{count(:id)}.must_equal 0
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should handle create account when account_password_hash_column is true" do
|
|
53
|
+
rodauth do
|
|
54
|
+
enable :login, :create_account, :change_password, :disallow_password_reuse
|
|
55
|
+
account_password_hash_column :ph
|
|
56
|
+
change_password_requires_password? false
|
|
57
|
+
end
|
|
58
|
+
roda do |r|
|
|
59
|
+
r.rodauth
|
|
60
|
+
r.root{view :content=>""}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
visit '/create-account'
|
|
64
|
+
fill_in 'Login', :with=>'bar@example.com'
|
|
65
|
+
fill_in 'Confirm Login', :with=>'bar@example.com'
|
|
66
|
+
fill_in 'Password', :with=>'0123456789'
|
|
67
|
+
fill_in 'Confirm Password', :with=>'0123456789'
|
|
68
|
+
click_button 'Create Account'
|
|
69
|
+
page.current_path.must_equal '/'
|
|
70
|
+
page.find('#notice_flash').text.must_equal "Your account has been created"
|
|
71
|
+
|
|
72
|
+
visit '/change-password'
|
|
73
|
+
fill_in 'New Password', :with=>"012345678"
|
|
74
|
+
fill_in 'Confirm Password', :with=>"012345678"
|
|
75
|
+
click_button 'Change Password'
|
|
76
|
+
page.find('#notice_flash').text.must_equal "Your password has been changed"
|
|
77
|
+
|
|
78
|
+
visit '/change-password'
|
|
79
|
+
fill_in 'New Password', :with=>"0123456789"
|
|
80
|
+
fill_in 'Confirm Password', :with=>"0123456789"
|
|
81
|
+
click_button 'Change Password'
|
|
82
|
+
page.html.must_include("invalid password, does not meet requirements (same as previous password)")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
describe 'Rodauth lockout feature' do
|
|
4
|
+
it "should support account lockouts without autologin on unlock" do
|
|
5
|
+
rodauth do
|
|
6
|
+
enable :lockout
|
|
7
|
+
max_invalid_logins 2
|
|
8
|
+
unlock_account_autologin? false
|
|
9
|
+
end
|
|
10
|
+
roda do |r|
|
|
11
|
+
r.rodauth
|
|
12
|
+
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
login(:pass=>'012345678910')
|
|
16
|
+
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
17
|
+
|
|
18
|
+
login
|
|
19
|
+
page.find('#notice_flash').text.must_equal 'You have been logged in'
|
|
20
|
+
page.body.must_include("Logged In")
|
|
21
|
+
|
|
22
|
+
remove_cookie('rack.session')
|
|
23
|
+
|
|
24
|
+
visit '/login'
|
|
25
|
+
fill_in 'Login', :with=>'foo@example.com'
|
|
26
|
+
2.times do
|
|
27
|
+
fill_in 'Password', :with=>'012345678910'
|
|
28
|
+
click_button 'Login'
|
|
29
|
+
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
fill_in 'Password', :with=>'012345678910'
|
|
33
|
+
click_button 'Login'
|
|
34
|
+
page.find('#error_flash').text.must_equal "This account is currently locked out and cannot be logged in to."
|
|
35
|
+
page.body.must_include("This account is currently locked out")
|
|
36
|
+
click_button 'Request Account Unlock'
|
|
37
|
+
page.find('#notice_flash').text.must_equal 'An email has been sent to you with a link to unlock your account'
|
|
38
|
+
|
|
39
|
+
link = email_link(/(\/unlock-account\?key=.+)$/)
|
|
40
|
+
visit link[0...-1]
|
|
41
|
+
page.find('#error_flash').text.must_equal 'No matching unlock account key'
|
|
42
|
+
|
|
43
|
+
visit link
|
|
44
|
+
click_button 'Unlock Account'
|
|
45
|
+
page.find('#notice_flash').text.must_equal 'Your account has been unlocked'
|
|
46
|
+
page.body.must_include('Not Logged')
|
|
47
|
+
|
|
48
|
+
login
|
|
49
|
+
page.find('#notice_flash').text.must_equal 'You have been logged in'
|
|
50
|
+
page.body.must_include("Logged In")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should support account lockouts with autologin and password required on unlock" do
|
|
54
|
+
rodauth do
|
|
55
|
+
enable :lockout
|
|
56
|
+
unlock_account_requires_password? true
|
|
57
|
+
end
|
|
58
|
+
roda do |r|
|
|
59
|
+
r.rodauth
|
|
60
|
+
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
visit '/login'
|
|
64
|
+
fill_in 'Login', :with=>'foo@example.com'
|
|
65
|
+
100.times do
|
|
66
|
+
fill_in 'Password', :with=>'012345678910'
|
|
67
|
+
click_button 'Login'
|
|
68
|
+
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
fill_in 'Password', :with=>'012345678910'
|
|
72
|
+
click_button 'Login'
|
|
73
|
+
page.find('#error_flash').text.must_equal "This account is currently locked out and cannot be logged in to."
|
|
74
|
+
page.body.must_include("This account is currently locked out")
|
|
75
|
+
click_button 'Request Account Unlock'
|
|
76
|
+
page.find('#notice_flash').text.must_equal 'An email has been sent to you with a link to unlock your account'
|
|
77
|
+
|
|
78
|
+
link = email_link(/(\/unlock-account\?key=.+)$/)
|
|
79
|
+
visit link
|
|
80
|
+
click_button 'Unlock Account'
|
|
81
|
+
|
|
82
|
+
page.find('#error_flash').text.must_equal 'There was an error unlocking your account'
|
|
83
|
+
page.body.must_include('invalid password')
|
|
84
|
+
fill_in 'Password', :with=>'0123456789'
|
|
85
|
+
click_button 'Unlock Account'
|
|
86
|
+
|
|
87
|
+
page.find('#notice_flash').text.must_equal 'Your account has been unlocked'
|
|
88
|
+
page.body.must_include("Logged In")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should autounlock after enough time" do
|
|
92
|
+
rodauth do
|
|
93
|
+
enable :lockout
|
|
94
|
+
max_invalid_logins 2
|
|
95
|
+
end
|
|
96
|
+
roda do |r|
|
|
97
|
+
r.rodauth
|
|
98
|
+
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
visit '/login'
|
|
102
|
+
fill_in 'Login', :with=>'foo@example.com'
|
|
103
|
+
2.times do
|
|
104
|
+
fill_in 'Password', :with=>'012345678910'
|
|
105
|
+
click_button 'Login'
|
|
106
|
+
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
107
|
+
end
|
|
108
|
+
fill_in 'Password', :with=>'012345678910'
|
|
109
|
+
click_button 'Login'
|
|
110
|
+
page.find('#error_flash').text.must_equal "This account is currently locked out and cannot be logged in to."
|
|
111
|
+
page.body.must_include("This account is currently locked out")
|
|
112
|
+
DB[:account_lockouts].update(:deadline=>Date.today - 3)
|
|
113
|
+
|
|
114
|
+
login
|
|
115
|
+
page.find('#notice_flash').text.must_equal 'You have been logged in'
|
|
116
|
+
page.body.must_include("Logged In")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "should clear unlock token when closing account" do
|
|
120
|
+
rodauth do
|
|
121
|
+
enable :lockout, :close_account
|
|
122
|
+
max_invalid_logins 2
|
|
123
|
+
end
|
|
124
|
+
roda do |r|
|
|
125
|
+
r.get('b') do
|
|
126
|
+
session[:account_id] = DB[:accounts].get(:id)
|
|
127
|
+
'b'
|
|
128
|
+
end
|
|
129
|
+
r.rodauth
|
|
130
|
+
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
visit '/login'
|
|
134
|
+
fill_in 'Login', :with=>'foo@example.com'
|
|
135
|
+
3.times do
|
|
136
|
+
fill_in 'Password', :with=>'012345678910'
|
|
137
|
+
click_button 'Login'
|
|
138
|
+
end
|
|
139
|
+
DB[:account_lockouts].count.must_equal 1
|
|
140
|
+
|
|
141
|
+
visit 'b'
|
|
142
|
+
|
|
143
|
+
visit '/close-account'
|
|
144
|
+
fill_in 'Password', :with=>'0123456789'
|
|
145
|
+
click_button 'Close Account'
|
|
146
|
+
DB[:account_lockouts].count.must_equal 0
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "should handle uniqueness errors raised when inserting unlock account token" do
|
|
150
|
+
rodauth do
|
|
151
|
+
enable :lockout
|
|
152
|
+
max_invalid_logins 2
|
|
153
|
+
end
|
|
154
|
+
roda do |r|
|
|
155
|
+
def rodauth.raised_uniqueness_violation(*) super; true; end
|
|
156
|
+
r.rodauth
|
|
157
|
+
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
visit '/login'
|
|
161
|
+
fill_in 'Login', :with=>'foo@example.com'
|
|
162
|
+
fill_in 'Password', :with=>'012345678910'
|
|
163
|
+
click_button 'Login'
|
|
164
|
+
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
165
|
+
|
|
166
|
+
fill_in 'Password', :with=>'012345678910'
|
|
167
|
+
click_button 'Login'
|
|
168
|
+
page.find('#error_flash').text.must_equal "This account is currently locked out and cannot be logged in to."
|
|
169
|
+
page.body.must_include("This account is currently locked out")
|
|
170
|
+
click_button 'Request Account Unlock'
|
|
171
|
+
page.find('#notice_flash').text.must_equal 'An email has been sent to you with a link to unlock your account'
|
|
172
|
+
|
|
173
|
+
link = email_link(/(\/unlock-account\?key=.+)$/)
|
|
174
|
+
visit link
|
|
175
|
+
click_button 'Unlock Account'
|
|
176
|
+
page.find('#notice_flash').text.must_equal 'Your account has been unlocked'
|
|
177
|
+
page.body.must_include("Logged In")
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "should support account lockouts via jwt" do
|
|
181
|
+
rodauth do
|
|
182
|
+
enable :logout, :lockout
|
|
183
|
+
max_invalid_logins 2
|
|
184
|
+
unlock_account_autologin? false
|
|
185
|
+
unlock_account_email_body{unlock_account_email_link}
|
|
186
|
+
end
|
|
187
|
+
roda(:jwt) do |r|
|
|
188
|
+
r.rodauth
|
|
189
|
+
[rodauth.logged_in? ? "Logged In" : "Not Logged"]
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
res = json_request('/unlock-account-request', :login=>'foo@example.com')
|
|
193
|
+
res.must_equal [400, {'error'=>"no matching login"}]
|
|
194
|
+
|
|
195
|
+
res = json_login(:pass=>'1', :no_check=>true)
|
|
196
|
+
res.must_equal [400, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
|
197
|
+
|
|
198
|
+
json_login
|
|
199
|
+
json_logout
|
|
200
|
+
|
|
201
|
+
2.times do
|
|
202
|
+
res = json_login(:pass=>'1', :no_check=>true)
|
|
203
|
+
res.must_equal [400, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
2.times do
|
|
207
|
+
res = json_login(:pass=>'1', :no_check=>true)
|
|
208
|
+
res.must_equal [400, {'error'=>"This account is currently locked out and cannot be logged in to."}]
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
res = json_request('/unlock-account')
|
|
212
|
+
res.must_equal [400, {'error'=>"No matching unlock account key"}]
|
|
213
|
+
|
|
214
|
+
res = json_request('/unlock-account-request', :login=>'foo@example.com')
|
|
215
|
+
res.must_equal [200, {'success'=>"An email has been sent to you with a link to unlock your account"}]
|
|
216
|
+
|
|
217
|
+
link = email_link(/key=.+$/)
|
|
218
|
+
res = json_request('/unlock-account', :key=>link[4...-1])
|
|
219
|
+
res.must_equal [400, {'error'=>"No matching unlock account key"}]
|
|
220
|
+
|
|
221
|
+
res = json_request('/unlock-account', :key=>link[4..-1])
|
|
222
|
+
res.must_equal [200, {'success'=>"Your account has been unlocked"}]
|
|
223
|
+
|
|
224
|
+
res = json_request.must_equal [200, ['Not Logged']]
|
|
225
|
+
|
|
226
|
+
json_login
|
|
227
|
+
end
|
|
228
|
+
end
|