rodauth 1.21.0 → 2.2.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 +182 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +211 -79
- data/doc/account_expiration.rdoc +12 -26
- data/doc/active_sessions.rdoc +49 -0
- data/doc/audit_logging.rdoc +44 -0
- data/doc/base.rdoc +75 -128
- data/doc/change_login.rdoc +7 -14
- data/doc/change_password.rdoc +9 -13
- data/doc/change_password_notify.rdoc +2 -2
- data/doc/close_account.rdoc +9 -16
- data/doc/confirm_password.rdoc +12 -5
- data/doc/create_account.rdoc +11 -22
- data/doc/disallow_password_reuse.rdoc +6 -13
- data/doc/email_auth.rdoc +15 -14
- data/doc/email_base.rdoc +6 -15
- data/doc/guides/admin_activation.rdoc +46 -0
- data/doc/guides/already_authenticated.rdoc +10 -0
- data/doc/guides/alternative_login.rdoc +46 -0
- data/doc/guides/create_account_programmatically.rdoc +38 -0
- data/doc/guides/delay_password.rdoc +25 -0
- data/doc/guides/email_only.rdoc +16 -0
- data/doc/guides/i18n.rdoc +26 -0
- data/doc/{internals.rdoc → guides/internals.rdoc} +0 -0
- data/doc/guides/links.rdoc +12 -0
- data/doc/guides/login_return.rdoc +37 -0
- data/doc/guides/password_column.rdoc +25 -0
- data/doc/guides/password_confirmation.rdoc +37 -0
- data/doc/guides/password_requirements.rdoc +30 -0
- data/doc/guides/paths.rdoc +36 -0
- data/doc/guides/query_params.rdoc +9 -0
- data/doc/guides/redirects.rdoc +17 -0
- data/doc/guides/registration_field.rdoc +68 -0
- data/doc/guides/require_mfa.rdoc +30 -0
- data/doc/guides/reset_password_autologin.rdoc +21 -0
- data/doc/guides/status_column.rdoc +28 -0
- data/doc/guides/totp_or_recovery.rdoc +16 -0
- data/doc/http_basic_auth.rdoc +10 -1
- data/doc/jwt.rdoc +22 -22
- data/doc/jwt_cors.rdoc +22 -0
- data/doc/jwt_refresh.rdoc +18 -8
- data/doc/lockout.rdoc +17 -15
- data/doc/login.rdoc +10 -2
- data/doc/login_password_requirements_base.rdoc +15 -37
- data/doc/logout.rdoc +2 -2
- data/doc/otp.rdoc +25 -19
- data/doc/password_complexity.rdoc +10 -26
- data/doc/password_expiration.rdoc +11 -25
- data/doc/password_grace_period.rdoc +16 -2
- data/doc/recovery_codes.rdoc +18 -12
- data/doc/release_notes/1.22.0.txt +11 -0
- data/doc/release_notes/1.23.0.txt +32 -0
- data/doc/release_notes/2.0.0.txt +361 -0
- data/doc/release_notes/2.1.0.txt +31 -0
- data/doc/release_notes/2.2.0.txt +39 -0
- data/doc/remember.rdoc +40 -64
- data/doc/reset_password.rdoc +12 -9
- data/doc/session_expiration.rdoc +1 -0
- data/doc/single_session.rdoc +16 -25
- data/doc/sms_codes.rdoc +24 -14
- data/doc/two_factor_base.rdoc +60 -22
- data/doc/verify_account.rdoc +14 -12
- data/doc/verify_account_grace_period.rdoc +6 -2
- data/doc/verify_login_change.rdoc +9 -8
- data/doc/webauthn.rdoc +115 -0
- data/doc/webauthn_login.rdoc +15 -0
- data/doc/webauthn_verify_account.rdoc +9 -0
- data/javascript/webauthn_auth.js +45 -0
- data/javascript/webauthn_setup.js +35 -0
- data/lib/roda/plugins/rodauth.rb +1 -1
- data/lib/rodauth.rb +36 -28
- data/lib/rodauth/features/account_expiration.rb +5 -5
- data/lib/rodauth/features/active_sessions.rb +158 -0
- data/lib/rodauth/features/audit_logging.rb +98 -0
- data/lib/rodauth/features/base.rb +144 -43
- data/lib/rodauth/features/change_password_notify.rb +2 -2
- data/lib/rodauth/features/confirm_password.rb +40 -2
- data/lib/rodauth/features/create_account.rb +8 -13
- data/lib/rodauth/features/disallow_common_passwords.rb +1 -1
- data/lib/rodauth/features/disallow_password_reuse.rb +1 -1
- data/lib/rodauth/features/email_auth.rb +31 -30
- data/lib/rodauth/features/email_base.rb +9 -4
- data/lib/rodauth/features/http_basic_auth.rb +55 -35
- data/lib/rodauth/features/jwt.rb +63 -16
- data/lib/rodauth/features/jwt_cors.rb +53 -0
- data/lib/rodauth/features/jwt_refresh.rb +32 -9
- data/lib/rodauth/features/lockout.rb +12 -14
- data/lib/rodauth/features/login.rb +54 -10
- data/lib/rodauth/features/login_password_requirements_base.rb +4 -4
- data/lib/rodauth/features/otp.rb +77 -80
- data/lib/rodauth/features/password_complexity.rb +8 -13
- data/lib/rodauth/features/password_expiration.rb +2 -2
- data/lib/rodauth/features/password_grace_period.rb +17 -10
- data/lib/rodauth/features/recovery_codes.rb +49 -53
- data/lib/rodauth/features/remember.rb +11 -27
- data/lib/rodauth/features/reset_password.rb +26 -26
- data/lib/rodauth/features/session_expiration.rb +6 -4
- data/lib/rodauth/features/single_session.rb +8 -6
- data/lib/rodauth/features/sms_codes.rb +62 -72
- data/lib/rodauth/features/two_factor_base.rb +134 -30
- data/lib/rodauth/features/verify_account.rb +29 -21
- data/lib/rodauth/features/verify_account_grace_period.rb +18 -9
- data/lib/rodauth/features/verify_login_change.rb +12 -11
- data/lib/rodauth/features/webauthn.rb +505 -0
- data/lib/rodauth/features/webauthn_login.rb +70 -0
- data/lib/rodauth/features/webauthn_verify_account.rb +46 -0
- data/lib/rodauth/version.rb +2 -2
- data/templates/button.str +1 -3
- data/templates/change-login.str +1 -2
- data/templates/change-password.str +3 -5
- data/templates/close-account.str +2 -2
- data/templates/confirm-password.str +1 -1
- data/templates/create-account.str +1 -1
- data/templates/email-auth-email.str +1 -1
- data/templates/email-auth-request-form.str +2 -3
- data/templates/email-auth.str +1 -1
- data/templates/global-logout-field.str +6 -0
- data/templates/login-confirm-field.str +2 -4
- data/templates/login-display.str +3 -2
- data/templates/login-field.str +2 -4
- data/templates/login-form-footer.str +6 -0
- data/templates/login-form.str +7 -0
- data/templates/login.str +1 -9
- data/templates/logout.str +1 -1
- data/templates/multi-phase-login.str +3 -0
- data/templates/otp-auth-code-field.str +5 -3
- data/templates/otp-auth.str +1 -1
- data/templates/otp-disable.str +1 -1
- data/templates/otp-setup.str +3 -3
- data/templates/password-confirm-field.str +2 -4
- data/templates/password-field.str +2 -4
- data/templates/recovery-auth.str +3 -6
- data/templates/recovery-codes.str +1 -1
- data/templates/remember.str +15 -20
- data/templates/reset-password-email.str +1 -1
- data/templates/reset-password-request.str +3 -3
- data/templates/reset-password.str +1 -2
- data/templates/sms-auth.str +1 -1
- data/templates/sms-code-field.str +5 -3
- data/templates/sms-confirm.str +1 -2
- data/templates/sms-disable.str +1 -2
- data/templates/sms-request.str +1 -1
- data/templates/sms-setup.str +6 -4
- data/templates/two-factor-auth.str +5 -0
- data/templates/two-factor-disable.str +6 -0
- data/templates/two-factor-manage.str +16 -0
- data/templates/unlock-account-email.str +1 -1
- data/templates/unlock-account-request.str +4 -4
- data/templates/unlock-account.str +1 -1
- data/templates/verify-account-email.str +1 -1
- data/templates/verify-account-resend.str +3 -3
- data/templates/verify-account.str +1 -2
- data/templates/verify-login-change-email.str +2 -1
- data/templates/verify-login-change.str +1 -1
- data/templates/webauthn-auth.str +11 -0
- data/templates/webauthn-remove.str +14 -0
- data/templates/webauthn-setup.str +12 -0
- metadata +110 -52
- data/Rakefile +0 -179
- data/doc/verify_change_login.rdoc +0 -11
- data/lib/rodauth/features/verify_change_login.rb +0 -20
- data/spec/account_expiration_spec.rb +0 -225
- data/spec/all.rb +0 -1
- data/spec/change_login_spec.rb +0 -156
- data/spec/change_password_notify_spec.rb +0 -33
- data/spec/change_password_spec.rb +0 -202
- data/spec/close_account_spec.rb +0 -162
- data/spec/confirm_password_spec.rb +0 -70
- data/spec/create_account_spec.rb +0 -127
- data/spec/disallow_common_passwords_spec.rb +0 -93
- data/spec/disallow_password_reuse_spec.rb +0 -179
- data/spec/email_auth_spec.rb +0 -285
- data/spec/http_basic_auth_spec.rb +0 -143
- data/spec/jwt_refresh_spec.rb +0 -256
- data/spec/jwt_spec.rb +0 -235
- data/spec/lockout_spec.rb +0 -250
- data/spec/login_spec.rb +0 -328
- data/spec/migrate/001_tables.rb +0 -184
- data/spec/migrate/002_account_password_hash_column.rb +0 -11
- data/spec/migrate_password/001_tables.rb +0 -73
- data/spec/migrate_travis/001_tables.rb +0 -141
- data/spec/password_complexity_spec.rb +0 -109
- data/spec/password_expiration_spec.rb +0 -244
- data/spec/password_grace_period_spec.rb +0 -93
- data/spec/remember_spec.rb +0 -451
- data/spec/reset_password_spec.rb +0 -229
- data/spec/rodauth_spec.rb +0 -343
- data/spec/session_expiration_spec.rb +0 -58
- data/spec/single_session_spec.rb +0 -127
- data/spec/spec_helper.rb +0 -327
- data/spec/two_factor_spec.rb +0 -1462
- data/spec/update_password_hash_spec.rb +0 -40
- data/spec/verify_account_grace_period_spec.rb +0 -171
- data/spec/verify_account_spec.rb +0 -240
- data/spec/verify_change_login_spec.rb +0 -46
- data/spec/verify_login_change_spec.rb +0 -232
- data/spec/views/layout-other.str +0 -11
- data/spec/views/layout.str +0 -11
- data/spec/views/login.str +0 -21
data/spec/lockout_spec.rb
DELETED
|
@@ -1,250 +0,0 @@
|
|
|
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
|
-
lockouts = []
|
|
6
|
-
rodauth do
|
|
7
|
-
enable :lockout
|
|
8
|
-
max_invalid_logins 2
|
|
9
|
-
unlock_account_autologin? false
|
|
10
|
-
after_account_lockout{lockouts << true}
|
|
11
|
-
end
|
|
12
|
-
roda do |r|
|
|
13
|
-
r.rodauth
|
|
14
|
-
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
login(:pass=>'012345678910')
|
|
18
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
19
|
-
|
|
20
|
-
login
|
|
21
|
-
page.find('#notice_flash').text.must_equal 'You have been logged in'
|
|
22
|
-
page.body.must_include("Logged In")
|
|
23
|
-
|
|
24
|
-
remove_cookie('rack.session')
|
|
25
|
-
|
|
26
|
-
visit '/login'
|
|
27
|
-
fill_in 'Login', :with=>'foo@example.com'
|
|
28
|
-
2.times do
|
|
29
|
-
fill_in 'Password', :with=>'012345678910'
|
|
30
|
-
click_button 'Login'
|
|
31
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
32
|
-
end
|
|
33
|
-
lockouts.must_equal [true]
|
|
34
|
-
|
|
35
|
-
fill_in 'Password', :with=>'012345678910'
|
|
36
|
-
click_button 'Login'
|
|
37
|
-
page.find('#error_flash').text.must_equal "This account is currently locked out and cannot be logged in to."
|
|
38
|
-
page.body.must_include("This account is currently locked out")
|
|
39
|
-
click_button 'Request Account Unlock'
|
|
40
|
-
page.find('#notice_flash').text.must_equal 'An email has been sent to you with a link to unlock your account'
|
|
41
|
-
link = email_link(/(\/unlock-account\?key=.+)$/)
|
|
42
|
-
|
|
43
|
-
visit '/login'
|
|
44
|
-
fill_in 'Login', :with=>'foo@example.com'
|
|
45
|
-
fill_in 'Password', :with=>'012345678910'
|
|
46
|
-
click_button 'Login'
|
|
47
|
-
click_button 'Request Account Unlock'
|
|
48
|
-
email_link(/(\/unlock-account\?key=.+)$/).must_equal link
|
|
49
|
-
|
|
50
|
-
visit link[0...-1]
|
|
51
|
-
page.find('#error_flash').text.must_equal "There was an error unlocking your account: invalid or expired unlock account key"
|
|
52
|
-
|
|
53
|
-
visit link
|
|
54
|
-
click_button 'Unlock Account'
|
|
55
|
-
page.find('#notice_flash').text.must_equal 'Your account has been unlocked'
|
|
56
|
-
page.body.must_include('Not Logged')
|
|
57
|
-
|
|
58
|
-
login
|
|
59
|
-
page.find('#notice_flash').text.must_equal 'You have been logged in'
|
|
60
|
-
page.body.must_include("Logged In")
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
it "should support account lockouts with autologin and password required on unlock" do
|
|
64
|
-
rodauth do
|
|
65
|
-
enable :lockout
|
|
66
|
-
unlock_account_requires_password? true
|
|
67
|
-
account_lockouts_email_last_sent_column :email_last_sent
|
|
68
|
-
end
|
|
69
|
-
roda do |r|
|
|
70
|
-
r.rodauth
|
|
71
|
-
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
visit '/login'
|
|
75
|
-
fill_in 'Login', :with=>'foo@example.com'
|
|
76
|
-
100.times do
|
|
77
|
-
fill_in 'Password', :with=>'012345678910'
|
|
78
|
-
click_button 'Login'
|
|
79
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
fill_in 'Password', :with=>'012345678910'
|
|
83
|
-
click_button 'Login'
|
|
84
|
-
page.find('#error_flash').text.must_equal "This account is currently locked out and cannot be logged in to."
|
|
85
|
-
page.body.must_include("This account is currently locked out")
|
|
86
|
-
click_button 'Request Account Unlock'
|
|
87
|
-
page.find('#notice_flash').text.must_equal 'An email has been sent to you with a link to unlock your account'
|
|
88
|
-
link = email_link(/(\/unlock-account\?key=.+)$/)
|
|
89
|
-
|
|
90
|
-
visit '/login'
|
|
91
|
-
fill_in 'Login', :with=>'foo@example.com'
|
|
92
|
-
fill_in 'Password', :with=>'012345678910'
|
|
93
|
-
click_button 'Login'
|
|
94
|
-
click_button 'Request Account Unlock'
|
|
95
|
-
page.find('#error_flash').text.must_equal "An email has recently been sent to you with a link to unlock the account"
|
|
96
|
-
Mail::TestMailer.deliveries.must_equal []
|
|
97
|
-
|
|
98
|
-
visit link
|
|
99
|
-
click_button 'Unlock Account'
|
|
100
|
-
|
|
101
|
-
page.find('#error_flash').text.must_equal 'There was an error unlocking your account'
|
|
102
|
-
page.body.must_include('invalid password')
|
|
103
|
-
fill_in 'Password', :with=>'0123456789'
|
|
104
|
-
click_button 'Unlock Account'
|
|
105
|
-
|
|
106
|
-
page.find('#notice_flash').text.must_equal 'Your account has been unlocked'
|
|
107
|
-
page.body.must_include("Logged In")
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
it "should autounlock after enough time" do
|
|
111
|
-
rodauth do
|
|
112
|
-
enable :lockout
|
|
113
|
-
max_invalid_logins 2
|
|
114
|
-
end
|
|
115
|
-
roda do |r|
|
|
116
|
-
r.rodauth
|
|
117
|
-
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
visit '/login'
|
|
121
|
-
fill_in 'Login', :with=>'foo@example.com'
|
|
122
|
-
2.times do
|
|
123
|
-
fill_in 'Password', :with=>'012345678910'
|
|
124
|
-
click_button 'Login'
|
|
125
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
126
|
-
end
|
|
127
|
-
fill_in 'Password', :with=>'012345678910'
|
|
128
|
-
click_button 'Login'
|
|
129
|
-
page.find('#error_flash').text.must_equal "This account is currently locked out and cannot be logged in to."
|
|
130
|
-
page.body.must_include("This account is currently locked out")
|
|
131
|
-
DB[:account_lockouts].update(:deadline=>Date.today - 3)
|
|
132
|
-
|
|
133
|
-
login
|
|
134
|
-
page.find('#notice_flash').text.must_equal 'You have been logged in'
|
|
135
|
-
page.body.must_include("Logged In")
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
it "should clear unlock token when closing account" do
|
|
139
|
-
rodauth do
|
|
140
|
-
enable :lockout, :close_account
|
|
141
|
-
max_invalid_logins 2
|
|
142
|
-
end
|
|
143
|
-
roda do |r|
|
|
144
|
-
r.get('b') do
|
|
145
|
-
session[:account_id] = DB[:accounts].get(:id)
|
|
146
|
-
'b'
|
|
147
|
-
end
|
|
148
|
-
r.rodauth
|
|
149
|
-
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
visit '/login'
|
|
153
|
-
fill_in 'Login', :with=>'foo@example.com'
|
|
154
|
-
3.times do
|
|
155
|
-
fill_in 'Password', :with=>'012345678910'
|
|
156
|
-
click_button 'Login'
|
|
157
|
-
end
|
|
158
|
-
DB[:account_lockouts].count.must_equal 1
|
|
159
|
-
|
|
160
|
-
visit 'b'
|
|
161
|
-
|
|
162
|
-
visit '/close-account'
|
|
163
|
-
fill_in 'Password', :with=>'0123456789'
|
|
164
|
-
click_button 'Close Account'
|
|
165
|
-
DB[:account_lockouts].count.must_equal 0
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
it "should handle uniqueness errors raised when inserting unlock account token" do
|
|
169
|
-
lockouts = []
|
|
170
|
-
rodauth do
|
|
171
|
-
enable :lockout
|
|
172
|
-
max_invalid_logins 2
|
|
173
|
-
after_account_lockout{lockouts << true}
|
|
174
|
-
end
|
|
175
|
-
roda do |r|
|
|
176
|
-
def rodauth.raised_uniqueness_violation(*) super; true; end
|
|
177
|
-
r.rodauth
|
|
178
|
-
r.root{view :content=>(rodauth.logged_in? ? "Logged In" : "Not Logged")}
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
visit '/login'
|
|
182
|
-
fill_in 'Login', :with=>'foo@example.com'
|
|
183
|
-
fill_in 'Password', :with=>'012345678910'
|
|
184
|
-
click_button 'Login'
|
|
185
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
186
|
-
|
|
187
|
-
fill_in 'Password', :with=>'012345678910'
|
|
188
|
-
click_button 'Login'
|
|
189
|
-
lockouts.must_equal [true]
|
|
190
|
-
page.find('#error_flash').text.must_equal "This account is currently locked out and cannot be logged in to."
|
|
191
|
-
page.body.must_include("This account is currently locked out")
|
|
192
|
-
click_button 'Request Account Unlock'
|
|
193
|
-
page.find('#notice_flash').text.must_equal 'An email has been sent to you with a link to unlock your account'
|
|
194
|
-
|
|
195
|
-
link = email_link(/(\/unlock-account\?key=.+)$/)
|
|
196
|
-
visit link
|
|
197
|
-
click_button 'Unlock Account'
|
|
198
|
-
page.find('#notice_flash').text.must_equal 'Your account has been unlocked'
|
|
199
|
-
page.body.must_include("Logged In")
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
it "should support account lockouts via jwt" do
|
|
203
|
-
rodauth do
|
|
204
|
-
enable :logout, :lockout
|
|
205
|
-
max_invalid_logins 2
|
|
206
|
-
unlock_account_autologin? false
|
|
207
|
-
unlock_account_email_body{unlock_account_email_link}
|
|
208
|
-
end
|
|
209
|
-
roda(:jwt) do |r|
|
|
210
|
-
r.rodauth
|
|
211
|
-
[rodauth.logged_in? ? "Logged In" : "Not Logged"]
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
res = json_request('/unlock-account-request', :login=>'foo@example.com')
|
|
215
|
-
res.must_equal [401, {'error'=>"No matching login"}]
|
|
216
|
-
|
|
217
|
-
res = json_login(:pass=>'1', :no_check=>true)
|
|
218
|
-
res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
|
219
|
-
|
|
220
|
-
json_login
|
|
221
|
-
json_logout
|
|
222
|
-
|
|
223
|
-
2.times do
|
|
224
|
-
res = json_login(:pass=>'1', :no_check=>true)
|
|
225
|
-
res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
2.times do
|
|
229
|
-
res = json_login(:pass=>'1', :no_check=>true)
|
|
230
|
-
res.must_equal [403, {'error'=>"This account is currently locked out and cannot be logged in to."}]
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
res = json_request('/unlock-account')
|
|
234
|
-
res.must_equal [401, {'error'=>"There was an error unlocking your account: invalid or expired unlock account key"}]
|
|
235
|
-
|
|
236
|
-
res = json_request('/unlock-account-request', :login=>'foo@example.com')
|
|
237
|
-
res.must_equal [200, {'success'=>"An email has been sent to you with a link to unlock your account"}]
|
|
238
|
-
|
|
239
|
-
link = email_link(/key=.+$/)
|
|
240
|
-
res = json_request('/unlock-account', :key=>link[4...-1])
|
|
241
|
-
res.must_equal [401, {'error'=>"There was an error unlocking your account: invalid or expired unlock account key"}]
|
|
242
|
-
|
|
243
|
-
res = json_request('/unlock-account', :key=>link[4..-1])
|
|
244
|
-
res.must_equal [200, {'success'=>"Your account has been unlocked"}]
|
|
245
|
-
|
|
246
|
-
res = json_request.must_equal [200, ['Not Logged']]
|
|
247
|
-
|
|
248
|
-
json_login
|
|
249
|
-
end
|
|
250
|
-
end
|
data/spec/login_spec.rb
DELETED
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
-
|
|
3
|
-
describe 'Rodauth login feature' do
|
|
4
|
-
it "should handle logins and logouts" do
|
|
5
|
-
rodauth{enable :login, :logout}
|
|
6
|
-
roda do |r|
|
|
7
|
-
r.rodauth
|
|
8
|
-
next unless rodauth.logged_in?
|
|
9
|
-
r.root{view :content=>"Logged In"}
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
visit '/login'
|
|
13
|
-
page.title.must_equal 'Login'
|
|
14
|
-
|
|
15
|
-
login(:login=>'foo@example2.com', :visit=>false)
|
|
16
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
17
|
-
page.html.must_include("no matching login")
|
|
18
|
-
page.all('[type=text]').first.value.must_equal 'foo@example2.com'
|
|
19
|
-
|
|
20
|
-
login(:pass=>'012345678', :visit=>false)
|
|
21
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
22
|
-
page.html.must_include("invalid password")
|
|
23
|
-
|
|
24
|
-
fill_in 'Password', :with=>'0123456789'
|
|
25
|
-
click_button 'Login'
|
|
26
|
-
page.current_path.must_equal '/'
|
|
27
|
-
page.find('#notice_flash').text.must_equal 'You have been logged in'
|
|
28
|
-
page.html.must_include("Logged In")
|
|
29
|
-
|
|
30
|
-
visit '/logout'
|
|
31
|
-
page.title.must_equal 'Logout'
|
|
32
|
-
|
|
33
|
-
click_button 'Logout'
|
|
34
|
-
page.find('#notice_flash').text.must_equal 'You have been logged out'
|
|
35
|
-
page.current_path.must_equal '/login'
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
it "should handle multi phase login (email first, then password)" do
|
|
39
|
-
rodauth do
|
|
40
|
-
enable :login, :logout
|
|
41
|
-
use_multi_phase_login? true
|
|
42
|
-
login_input_type 'email'
|
|
43
|
-
input_field_label_suffix ' (Required)'
|
|
44
|
-
input_field_error_class ' bad-input'
|
|
45
|
-
input_field_error_message_class 'err-msg'
|
|
46
|
-
mark_input_fields_as_required? true
|
|
47
|
-
field_attributes do |field|
|
|
48
|
-
if field == 'login'
|
|
49
|
-
'custom_field="custom_value"'
|
|
50
|
-
else
|
|
51
|
-
super(field)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
field_error_attributes do |field|
|
|
55
|
-
if field == 'login'
|
|
56
|
-
'custom_error_field="custom_error_value"'
|
|
57
|
-
else
|
|
58
|
-
super(field)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
formatted_field_error do |field, error|
|
|
62
|
-
if field == 'login'
|
|
63
|
-
super(field, error)
|
|
64
|
-
else
|
|
65
|
-
"<span class='err-msg2'>1#{error}2</span>"
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
roda do |r|
|
|
70
|
-
r.rodauth
|
|
71
|
-
next unless rodauth.logged_in?
|
|
72
|
-
r.root{view :content=>"Logged In"}
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
visit '/login'
|
|
76
|
-
page.title.must_equal 'Login'
|
|
77
|
-
|
|
78
|
-
page.find('[custom_field=custom_value]').value.must_equal ''
|
|
79
|
-
page.all('[custom_error_field=custom_error_value]').must_be_empty
|
|
80
|
-
page.all('input[type=password]').must_be_empty
|
|
81
|
-
fill_in 'Login (Required)', :with=>'foo2@example.com'
|
|
82
|
-
click_button 'Login'
|
|
83
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
84
|
-
page.find('[custom_field=custom_value]').value.must_equal 'foo2@example.com'
|
|
85
|
-
page.find('[custom_error_field=custom_error_value]').value.must_equal 'foo2@example.com'
|
|
86
|
-
page.find('[type=email]').value.must_equal 'foo2@example.com'
|
|
87
|
-
page.find('.bad-input').value.must_equal 'foo2@example.com'
|
|
88
|
-
page.find('.err-msg').text.must_equal 'no matching login'
|
|
89
|
-
|
|
90
|
-
page.all('input[type=password]').must_be_empty
|
|
91
|
-
fill_in 'Login (Required)', :with=>'foo@example.com'
|
|
92
|
-
click_button 'Login'
|
|
93
|
-
page.find('#notice_flash').text.must_equal 'Login recognized, please enter your password'
|
|
94
|
-
|
|
95
|
-
page.all('[custom_field=custom_value]').must_be_empty
|
|
96
|
-
page.all('[custom_error_field=custom_error_value]').must_be_empty
|
|
97
|
-
page.all('[aria-invalid=true]').must_be_empty
|
|
98
|
-
page.all('[aria-describedby]').must_be_empty
|
|
99
|
-
page.find('[required=required]').value.to_s.must_equal ''
|
|
100
|
-
page.all('input[type=text]').must_be_empty
|
|
101
|
-
fill_in 'Password (Required)', :with=>'012345678'
|
|
102
|
-
click_button 'Login'
|
|
103
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
104
|
-
page.find('[aria-invalid=true]').value.to_s.must_equal ''
|
|
105
|
-
page.find('[aria-describedby=password_error_message]').value.to_s.must_equal ''
|
|
106
|
-
page.all('[custom_error_field=custom_error_value]').must_be_empty
|
|
107
|
-
page.find('.err-msg2').text.must_equal '1invalid password2'
|
|
108
|
-
|
|
109
|
-
page.all('input[type=text]').must_be_empty
|
|
110
|
-
fill_in 'Password (Required)', :with=>'0123456789'
|
|
111
|
-
click_button 'Login'
|
|
112
|
-
page.current_path.must_equal '/'
|
|
113
|
-
page.find('#notice_flash').text.must_equal 'You have been logged in'
|
|
114
|
-
page.html.must_include("Logged In")
|
|
115
|
-
|
|
116
|
-
visit '/logout'
|
|
117
|
-
page.title.must_equal 'Logout'
|
|
118
|
-
|
|
119
|
-
click_button 'Logout'
|
|
120
|
-
page.find('#notice_flash').text.must_equal 'You have been logged out'
|
|
121
|
-
page.current_path.must_equal '/login'
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
it "should not allow login to unverified account" do
|
|
125
|
-
rodauth do
|
|
126
|
-
enable :login
|
|
127
|
-
skip_status_checks? false
|
|
128
|
-
end
|
|
129
|
-
roda do |r|
|
|
130
|
-
r.rodauth
|
|
131
|
-
next unless rodauth.logged_in?
|
|
132
|
-
r.root{view :content=>"Logged In"}
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
DB[:accounts].update(:status_id=>1)
|
|
136
|
-
login
|
|
137
|
-
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
138
|
-
page.html.must_include("unverified account, please verify account before logging in")
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
it "should handle overriding login action" do
|
|
142
|
-
rodauth do
|
|
143
|
-
enable :login
|
|
144
|
-
end
|
|
145
|
-
roda do |r|
|
|
146
|
-
r.post 'login' do
|
|
147
|
-
if r.params['login'] == 'apple' && r.params['password'] == 'banana'
|
|
148
|
-
session['user_id'] = 'pear'
|
|
149
|
-
r.redirect '/'
|
|
150
|
-
end
|
|
151
|
-
r.redirect '/login'
|
|
152
|
-
end
|
|
153
|
-
r.rodauth
|
|
154
|
-
next unless session['user_id'] == 'pear'
|
|
155
|
-
r.root{"Logged In"}
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
login(:login=>'appl', :pass=>'banana')
|
|
159
|
-
page.html.wont_match(/Logged In/)
|
|
160
|
-
|
|
161
|
-
login(:login=>'apple', :pass=>'banan', :visit=>false)
|
|
162
|
-
page.html.wont_match(/Logged In/)
|
|
163
|
-
|
|
164
|
-
login(:login=>'apple', :pass=>'banana', :visit=>false)
|
|
165
|
-
page.current_path.must_equal '/'
|
|
166
|
-
page.html.must_include("Logged In")
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
it "should handle overriding some login attributes" do
|
|
170
|
-
rodauth do
|
|
171
|
-
enable :login
|
|
172
|
-
account_from_login do |login|
|
|
173
|
-
DB[:accounts].first if login == 'apple'
|
|
174
|
-
end
|
|
175
|
-
password_match? do |password|
|
|
176
|
-
password == 'banana'
|
|
177
|
-
end
|
|
178
|
-
update_session do
|
|
179
|
-
session['user_id'] = 'pear'
|
|
180
|
-
end
|
|
181
|
-
no_matching_login_message "no user"
|
|
182
|
-
invalid_password_message "bad password"
|
|
183
|
-
end
|
|
184
|
-
roda do |r|
|
|
185
|
-
r.rodauth
|
|
186
|
-
next unless session['user_id'] == 'pear'
|
|
187
|
-
r.root{"Logged In"}
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
login(:login=>'appl', :pass=>'banana')
|
|
191
|
-
page.html.must_include("no user")
|
|
192
|
-
|
|
193
|
-
login(:login=>'apple', :pass=>'banan', :visit=>false)
|
|
194
|
-
page.html.must_include("bad password")
|
|
195
|
-
|
|
196
|
-
fill_in 'Password', :with=>'banana'
|
|
197
|
-
click_button 'Login'
|
|
198
|
-
page.current_path.must_equal '/'
|
|
199
|
-
page.html.must_include("Logged In")
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
it "should handle a prefix and some other login options" do
|
|
203
|
-
rodauth do
|
|
204
|
-
enable :login, :logout
|
|
205
|
-
prefix '/auth'
|
|
206
|
-
session_key 'login_email'
|
|
207
|
-
account_from_session{DB[:accounts].first(:email=>session_value)}
|
|
208
|
-
account_session_value{account[:email]}
|
|
209
|
-
login_param{param('lp')}
|
|
210
|
-
login_additional_form_tags "<input type='hidden' name='lp' value='l' />"
|
|
211
|
-
password_param 'p'
|
|
212
|
-
login_redirect{"/foo/#{account[:email]}"}
|
|
213
|
-
logout_redirect '/auth/lin'
|
|
214
|
-
login_route 'lin'
|
|
215
|
-
logout_route 'lout'
|
|
216
|
-
end
|
|
217
|
-
no_freeze!
|
|
218
|
-
roda do |r|
|
|
219
|
-
r.on 'auth' do
|
|
220
|
-
r.rodauth
|
|
221
|
-
end
|
|
222
|
-
next unless session['login_email'] =~ /example/
|
|
223
|
-
r.get('foo', :email){|e| "Logged In: #{e}"}
|
|
224
|
-
end
|
|
225
|
-
app.plugin :render, :views=>'spec/views', :engine=>'str'
|
|
226
|
-
|
|
227
|
-
visit '/auth/lin?lp=l'
|
|
228
|
-
|
|
229
|
-
login(:login=>'foo@example2.com', :visit=>false)
|
|
230
|
-
page.html.must_include("no matching login")
|
|
231
|
-
|
|
232
|
-
login(:pass=>'012345678', :visit=>false)
|
|
233
|
-
page.html.must_include("invalid password")
|
|
234
|
-
|
|
235
|
-
login(:visit=>false)
|
|
236
|
-
page.current_path.must_equal '/foo/foo@example.com'
|
|
237
|
-
page.html.must_include("Logged In: foo@example.com")
|
|
238
|
-
|
|
239
|
-
visit '/auth/lout'
|
|
240
|
-
click_button 'Logout'
|
|
241
|
-
page.current_path.must_equal '/auth/lin'
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
it "should use correct redirect paths when using prefix" do
|
|
245
|
-
rodauth do
|
|
246
|
-
enable :login, :logout
|
|
247
|
-
prefix '/auth'
|
|
248
|
-
end
|
|
249
|
-
roda do |r|
|
|
250
|
-
r.on 'auth' do
|
|
251
|
-
r.rodauth
|
|
252
|
-
rodauth.require_login
|
|
253
|
-
end
|
|
254
|
-
rodauth.send("#{r.remaining_path[1..-1]}_redirect")
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
visit '/login'
|
|
258
|
-
page.html.must_equal '/'
|
|
259
|
-
visit '/logout'
|
|
260
|
-
page.html.must_equal '/auth/login'
|
|
261
|
-
visit '/require_login'
|
|
262
|
-
page.html.must_equal '/auth/login'
|
|
263
|
-
|
|
264
|
-
visit '/auth'
|
|
265
|
-
page.current_path.must_equal '/auth/login'
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
it "should login and logout via jwt" do
|
|
269
|
-
rodauth do
|
|
270
|
-
enable :login, :logout
|
|
271
|
-
json_response_custom_error_status? false
|
|
272
|
-
jwt_secret{proc{super()}.must_raise ArgumentError; "1"}
|
|
273
|
-
end
|
|
274
|
-
roda(:jwt) do |r|
|
|
275
|
-
r.rodauth
|
|
276
|
-
response['Content-Type'] = 'application/json'
|
|
277
|
-
rodauth.logged_in? ? '1' : '2'
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
json_request.must_equal [200, 2]
|
|
281
|
-
|
|
282
|
-
res = json_request("/login", :login=>'foo@example2.com', :password=>'0123456789')
|
|
283
|
-
res.must_equal [400, {'error'=>"There was an error logging in", "field-error"=>["login", "no matching login"]}]
|
|
284
|
-
|
|
285
|
-
res = json_request("/login", :login=>'foo@example.com', :password=>'012345678')
|
|
286
|
-
res.must_equal [400, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
|
287
|
-
|
|
288
|
-
json_request("/login", :login=>'foo@example.com', :password=>'0123456789').must_equal [200, {"success"=>'You have been logged in'}]
|
|
289
|
-
json_request.must_equal [200, 1]
|
|
290
|
-
|
|
291
|
-
json_request("/logout").must_equal [200, {"success"=>'You have been logged out'}]
|
|
292
|
-
json_request.must_equal [200, 2]
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
it "should login and logout via jwt with custom error statuses" do
|
|
296
|
-
rodauth do
|
|
297
|
-
enable :login, :logout
|
|
298
|
-
end
|
|
299
|
-
roda(:jwt) do |r|
|
|
300
|
-
r.rodauth
|
|
301
|
-
response['Content-Type'] = 'application/json'
|
|
302
|
-
r.post('foo') do
|
|
303
|
-
rodauth.require_login
|
|
304
|
-
'3'
|
|
305
|
-
end
|
|
306
|
-
rodauth.logged_in? ? '1' : '2'
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
json_request.must_equal [200, 2]
|
|
310
|
-
|
|
311
|
-
res = json_request("/foo")
|
|
312
|
-
res.must_equal [401, {"error"=>"Please login to continue"}]
|
|
313
|
-
|
|
314
|
-
res = json_request("/login", :login=>'foo@example2.com', :password=>'0123456789')
|
|
315
|
-
res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["login", "no matching login"]}]
|
|
316
|
-
|
|
317
|
-
res = json_request("/login", :login=>'foo@example.com', :password=>'012345678')
|
|
318
|
-
res.must_equal [401, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
|
319
|
-
|
|
320
|
-
json_request("/login", :login=>'foo@example.com', :password=>'0123456789').must_equal [200, {"success"=>'You have been logged in'}]
|
|
321
|
-
json_request.must_equal [200, 1]
|
|
322
|
-
|
|
323
|
-
res = json_request("/foo").must_equal [200, 3]
|
|
324
|
-
|
|
325
|
-
json_request("/logout").must_equal [200, {"success"=>'You have been logged out'}]
|
|
326
|
-
json_request.must_equal [200, 2]
|
|
327
|
-
end
|
|
328
|
-
end
|