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,130 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Rodauth
|
|
4
|
+
def self.create_database_authentication_functions(db, opts={})
|
|
5
|
+
table_name = opts[:table_name] || :account_password_hashes
|
|
6
|
+
get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
|
|
7
|
+
valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
|
|
8
|
+
case db.database_type
|
|
9
|
+
when :postgres
|
|
10
|
+
db.run <<END
|
|
11
|
+
CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id int8) RETURNS text AS $$
|
|
12
|
+
DECLARE salt text;
|
|
13
|
+
BEGIN
|
|
14
|
+
SELECT substr(password_hash, 0, 30) INTO salt
|
|
15
|
+
FROM #{table_name}
|
|
16
|
+
WHERE acct_id = id;
|
|
17
|
+
RETURN salt;
|
|
18
|
+
END;
|
|
19
|
+
$$ LANGUAGE plpgsql
|
|
20
|
+
SECURITY DEFINER
|
|
21
|
+
SET search_path = public, pg_temp;
|
|
22
|
+
END
|
|
23
|
+
|
|
24
|
+
db.run <<END
|
|
25
|
+
CREATE OR REPLACE FUNCTION #{valid_hash_name}(acct_id int8, hash text) RETURNS boolean AS $$
|
|
26
|
+
DECLARE valid boolean;
|
|
27
|
+
BEGIN
|
|
28
|
+
SELECT password_hash = hash INTO valid
|
|
29
|
+
FROM #{table_name}
|
|
30
|
+
WHERE acct_id = id;
|
|
31
|
+
RETURN valid;
|
|
32
|
+
END;
|
|
33
|
+
$$ LANGUAGE plpgsql
|
|
34
|
+
SECURITY DEFINER
|
|
35
|
+
SET search_path = public, pg_temp;
|
|
36
|
+
END
|
|
37
|
+
when :mysql
|
|
38
|
+
db.run <<END
|
|
39
|
+
CREATE FUNCTION #{get_salt_name}(acct_id int8) RETURNS varchar(255)
|
|
40
|
+
SQL SECURITY DEFINER
|
|
41
|
+
READS SQL DATA
|
|
42
|
+
BEGIN
|
|
43
|
+
DECLARE salt varchar(255);
|
|
44
|
+
DECLARE csr CURSOR FOR
|
|
45
|
+
SELECT substr(password_hash, 1, 30)
|
|
46
|
+
FROM #{table_name}
|
|
47
|
+
WHERE acct_id = id;
|
|
48
|
+
OPEN csr;
|
|
49
|
+
FETCH csr INTO salt;
|
|
50
|
+
CLOSE csr;
|
|
51
|
+
RETURN salt;
|
|
52
|
+
END;
|
|
53
|
+
END
|
|
54
|
+
|
|
55
|
+
db.run <<END
|
|
56
|
+
CREATE FUNCTION #{valid_hash_name}(acct_id int8, hash varchar(255)) RETURNS tinyint(1)
|
|
57
|
+
SQL SECURITY DEFINER
|
|
58
|
+
READS SQL DATA
|
|
59
|
+
BEGIN
|
|
60
|
+
DECLARE valid tinyint(1);
|
|
61
|
+
DECLARE csr CURSOR FOR
|
|
62
|
+
SELECT password_hash = hash
|
|
63
|
+
FROM #{table_name}
|
|
64
|
+
WHERE acct_id = id;
|
|
65
|
+
OPEN csr;
|
|
66
|
+
FETCH csr INTO valid;
|
|
67
|
+
CLOSE csr;
|
|
68
|
+
RETURN valid;
|
|
69
|
+
END;
|
|
70
|
+
END
|
|
71
|
+
when :mssql
|
|
72
|
+
db.run <<END
|
|
73
|
+
CREATE FUNCTION #{get_salt_name}(@account_id bigint) RETURNS nvarchar(255)
|
|
74
|
+
WITH EXECUTE AS OWNER
|
|
75
|
+
AS
|
|
76
|
+
BEGIN
|
|
77
|
+
DECLARE @salt nvarchar(255);
|
|
78
|
+
SELECT @salt = substring(password_hash, 0, 30)
|
|
79
|
+
FROM #{table_name}
|
|
80
|
+
WHERE id = @account_id;
|
|
81
|
+
RETURN @salt;
|
|
82
|
+
END;
|
|
83
|
+
END
|
|
84
|
+
|
|
85
|
+
db.run <<END
|
|
86
|
+
CREATE FUNCTION #{valid_hash_name}(@account_id bigint, @hash nvarchar(255)) RETURNS bit
|
|
87
|
+
WITH EXECUTE AS OWNER
|
|
88
|
+
AS
|
|
89
|
+
BEGIN
|
|
90
|
+
DECLARE @valid bit;
|
|
91
|
+
DECLARE @ph nvarchar(255);
|
|
92
|
+
SELECT @ph = password_hash
|
|
93
|
+
FROM #{table_name}
|
|
94
|
+
WHERE id = @account_id;
|
|
95
|
+
IF(@hash = @ph)
|
|
96
|
+
SET @valid = 1;
|
|
97
|
+
ELSE
|
|
98
|
+
SET @valid = 0
|
|
99
|
+
RETURN @valid;
|
|
100
|
+
END;
|
|
101
|
+
END
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.drop_database_authentication_functions(db)
|
|
106
|
+
case db.database_type
|
|
107
|
+
when :postgres
|
|
108
|
+
db.run "DROP FUNCTION rodauth_get_salt(int8)"
|
|
109
|
+
db.run "DROP FUNCTION rodauth_valid_password_hash(int8, text)"
|
|
110
|
+
when :mysql, :mssql
|
|
111
|
+
db.run "DROP FUNCTION rodauth_get_salt"
|
|
112
|
+
db.run "DROP FUNCTION rodauth_valid_password_hash"
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.create_database_previous_password_check_functions(db)
|
|
117
|
+
create_database_authentication_functions(db, :table_name=>:account_previous_password_hashes, :get_salt_name=>:rodauth_get_previous_salt, :valid_hash_name=>:rodauth_previous_password_hash_match)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.drop_database_previous_password_check_functions(db)
|
|
121
|
+
case db.database_type
|
|
122
|
+
when :postgres
|
|
123
|
+
db.run "DROP FUNCTION rodauth_get_previous_salt(int8)"
|
|
124
|
+
db.run "DROP FUNCTION rodauth_previous_password_hash_match(int8, text)"
|
|
125
|
+
when :mysql, :mssql
|
|
126
|
+
db.run "DROP FUNCTION rodauth_get_previous_salt"
|
|
127
|
+
db.run "DROP FUNCTION rodauth_previous_password_hash_match"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
describe 'Rodauth account expiration feature' do
|
|
4
|
+
it "should force account expiration after x number of days" do
|
|
5
|
+
rodauth do
|
|
6
|
+
enable :login, :logout, :account_expiration
|
|
7
|
+
end
|
|
8
|
+
roda do |r|
|
|
9
|
+
r.rodauth
|
|
10
|
+
r.root{view :content=>rodauth.logged_in? ? "Logged In#{rodauth.last_account_login_at.strftime('%m%d%y')}" : "Not Logged"}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
now = Time.now
|
|
14
|
+
2.times do
|
|
15
|
+
login
|
|
16
|
+
page.body.must_include "Logged In#{now.strftime('%m%d%y')}"
|
|
17
|
+
|
|
18
|
+
logout
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
DB[:account_activity_times].update(:last_login_at => Time.now - 181*86400)
|
|
22
|
+
|
|
23
|
+
2.times do
|
|
24
|
+
login
|
|
25
|
+
page.body.must_include 'Not Logged'
|
|
26
|
+
page.find('#error_flash').text.must_equal "You cannot log into this account as it has expired"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should use last activity time if configured" do
|
|
31
|
+
rodauth do
|
|
32
|
+
enable :login, :logout, :account_expiration
|
|
33
|
+
expire_account_on_last_activity? true
|
|
34
|
+
account_expiration_error_flash{"Account expired on #{account_expired_at.strftime('%m%d%y')}"}
|
|
35
|
+
end
|
|
36
|
+
roda do |r|
|
|
37
|
+
r.is("a"){view :content=>"Logged In#{rodauth.last_account_activity_at.strftime('%m%d%y')}"}
|
|
38
|
+
rodauth.update_last_activity
|
|
39
|
+
r.rodauth
|
|
40
|
+
r.root{view :content=>rodauth.logged_in? ? "Logged In#{rodauth.last_account_activity_at.strftime('%m%d%y')}" : 'Not Logged'}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
now = Time.now
|
|
44
|
+
login
|
|
45
|
+
page.body.must_include "Logged In#{now.strftime('%m%d%y')}"
|
|
46
|
+
|
|
47
|
+
DB[:account_activity_times].count.must_equal 1
|
|
48
|
+
DB[:account_activity_times].delete
|
|
49
|
+
|
|
50
|
+
visit '/'
|
|
51
|
+
DB[:account_activity_times].count.must_equal 1
|
|
52
|
+
|
|
53
|
+
t1 = now - 179*86400
|
|
54
|
+
DB[:account_activity_times].update(:last_activity_at => t1)
|
|
55
|
+
visit '/a'
|
|
56
|
+
page.body.must_include "Logged In#{t1.strftime('%m%d%y')}"
|
|
57
|
+
|
|
58
|
+
logout
|
|
59
|
+
|
|
60
|
+
t2 = now - 181*86400
|
|
61
|
+
DB[:account_activity_times].update(:last_activity_at => t2).must_equal 1
|
|
62
|
+
|
|
63
|
+
login
|
|
64
|
+
page.body.must_include 'Not Logged'
|
|
65
|
+
page.find('#error_flash').text.must_equal "Account expired on #{now.strftime('%m%d%y')}"
|
|
66
|
+
|
|
67
|
+
DB[:account_activity_times].update(:expired_at=>t1).must_equal 1
|
|
68
|
+
|
|
69
|
+
login
|
|
70
|
+
page.body.must_include 'Not Logged'
|
|
71
|
+
page.find('#error_flash').text.must_equal "Account expired on #{t1.strftime('%m%d%y')}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should remove account activity data when closing accounts" do
|
|
75
|
+
rodauth do
|
|
76
|
+
enable :login, :close_account, :account_expiration
|
|
77
|
+
close_account_requires_password? false
|
|
78
|
+
end
|
|
79
|
+
roda do |r|
|
|
80
|
+
r.rodauth
|
|
81
|
+
r.root{view :content=>rodauth.logged_in? ? "Logged In#{rodauth.last_account_login_at.strftime('%m%d%y')}" : "Not Logged"}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
login
|
|
85
|
+
DB[:account_activity_times].count.must_equal 1
|
|
86
|
+
visit '/close-account'
|
|
87
|
+
click_button 'Close Account'
|
|
88
|
+
DB[:account_activity_times].count.must_equal 0
|
|
89
|
+
end
|
|
90
|
+
end
|
data/spec/all.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Dir['./spec/*_spec.rb'].each{|f| require f}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
describe 'Rodauth change_login feature' do
|
|
4
|
+
it "should support changing logins for accounts" do
|
|
5
|
+
DB[:accounts].insert(:email=>'foo2@example.com')
|
|
6
|
+
require_password = false
|
|
7
|
+
require_email = true
|
|
8
|
+
|
|
9
|
+
rodauth do
|
|
10
|
+
enable :login, :logout, :change_login
|
|
11
|
+
change_login_requires_password?{require_password}
|
|
12
|
+
require_email_address_logins?{require_email}
|
|
13
|
+
end
|
|
14
|
+
roda do |r|
|
|
15
|
+
r.rodauth
|
|
16
|
+
r.root{view :content=>""}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
login
|
|
20
|
+
page.current_path.must_equal '/'
|
|
21
|
+
|
|
22
|
+
visit '/change-login'
|
|
23
|
+
page.title.must_equal 'Change Login'
|
|
24
|
+
|
|
25
|
+
fill_in 'Login', :with=>'foobar'
|
|
26
|
+
fill_in 'Confirm Login', :with=>'foobar'
|
|
27
|
+
click_button 'Change Login'
|
|
28
|
+
page.find('#error_flash').text.must_equal "There was an error changing your login"
|
|
29
|
+
page.html.must_include("invalid login, not a valid email address")
|
|
30
|
+
page.current_path.must_equal '/change-login'
|
|
31
|
+
|
|
32
|
+
require_email = false
|
|
33
|
+
|
|
34
|
+
fill_in 'Login', :with=>'fb'
|
|
35
|
+
fill_in 'Confirm Login', :with=>'fb'
|
|
36
|
+
click_button 'Change Login'
|
|
37
|
+
page.find('#error_flash').text.must_equal "There was an error changing your login"
|
|
38
|
+
page.html.must_include("invalid login, minimum 3 characters")
|
|
39
|
+
page.current_path.must_equal '/change-login'
|
|
40
|
+
|
|
41
|
+
fill_in 'Login', :with=>'foo@example.com'
|
|
42
|
+
fill_in 'Confirm Login', :with=>'foo2@example.com'
|
|
43
|
+
click_button 'Change Login'
|
|
44
|
+
page.find('#error_flash').text.must_equal "There was an error changing your login"
|
|
45
|
+
page.html.must_include("logins do not match")
|
|
46
|
+
page.current_path.must_equal '/change-login'
|
|
47
|
+
|
|
48
|
+
fill_in 'Login', :with=>'foo2@example.com'
|
|
49
|
+
click_button 'Change Login'
|
|
50
|
+
page.find('#error_flash').text.must_equal "There was an error changing your login"
|
|
51
|
+
page.html.must_include("invalid login, already an account with this login")
|
|
52
|
+
page.current_path.must_equal '/change-login'
|
|
53
|
+
|
|
54
|
+
fill_in 'Login', :with=>'foo@example.com'
|
|
55
|
+
fill_in 'Confirm Login', :with=>'foo@example.com'
|
|
56
|
+
click_button 'Change Login'
|
|
57
|
+
page.find('#error_flash').text.must_equal "There was an error changing your login"
|
|
58
|
+
page.html.must_include("invalid login, same as current login")
|
|
59
|
+
page.current_path.must_equal '/change-login'
|
|
60
|
+
|
|
61
|
+
fill_in 'Login', :with=>'foo3@example.com'
|
|
62
|
+
fill_in 'Confirm Login', :with=>'foo3@example.com'
|
|
63
|
+
click_button 'Change Login'
|
|
64
|
+
page.find('#notice_flash').text.must_equal "Your login has been changed"
|
|
65
|
+
page.current_path.must_equal '/'
|
|
66
|
+
|
|
67
|
+
logout
|
|
68
|
+
login(:login=>'foo3@example.com')
|
|
69
|
+
page.current_path.must_equal '/'
|
|
70
|
+
|
|
71
|
+
require_password = true
|
|
72
|
+
visit '/change-login'
|
|
73
|
+
fill_in 'Password', :with=>'012345678'
|
|
74
|
+
fill_in 'Login', :with=>'foo4@example.com'
|
|
75
|
+
fill_in 'Confirm Login', :with=>'foo4@example.com'
|
|
76
|
+
click_button 'Change Login'
|
|
77
|
+
page.find('#error_flash').text.must_equal "There was an error changing your login"
|
|
78
|
+
page.html.must_include("invalid password")
|
|
79
|
+
page.current_path.must_equal '/change-login'
|
|
80
|
+
|
|
81
|
+
fill_in 'Password', :with=>'0123456789'
|
|
82
|
+
click_button 'Change Login'
|
|
83
|
+
page.find('#notice_flash').text.must_equal "Your login has been changed"
|
|
84
|
+
page.current_path.must_equal '/'
|
|
85
|
+
|
|
86
|
+
logout
|
|
87
|
+
login(:login=>'foo4@example.com')
|
|
88
|
+
page.current_path.must_equal '/'
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should support changing logins for accounts with login confirmation" do
|
|
92
|
+
rodauth do
|
|
93
|
+
enable :login, :change_login
|
|
94
|
+
change_login_requires_password? false
|
|
95
|
+
require_login_confirmation? false
|
|
96
|
+
end
|
|
97
|
+
roda do |r|
|
|
98
|
+
r.rodauth
|
|
99
|
+
r.root{view :content=>""}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
login
|
|
103
|
+
visit '/change-login'
|
|
104
|
+
fill_in 'Login', :with=>'foo3@example.com'
|
|
105
|
+
click_button 'Change Login'
|
|
106
|
+
page.find('#notice_flash').text.must_equal "Your login has been changed"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "should support changing logins via jwt" do
|
|
110
|
+
DB[:accounts].insert(:email=>'foo2@example.com')
|
|
111
|
+
require_password = false
|
|
112
|
+
|
|
113
|
+
rodauth do
|
|
114
|
+
enable :login, :logout, :change_login
|
|
115
|
+
change_login_requires_password?{require_password}
|
|
116
|
+
end
|
|
117
|
+
roda(:jwt) do |r|
|
|
118
|
+
r.rodauth
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
json_login
|
|
122
|
+
|
|
123
|
+
res = json_request('/change-login', :login=>'foobar', "login-confirm"=>'foobar')
|
|
124
|
+
res.must_equal [400, {'error'=>"There was an error changing your login", "field-error"=>["login", "invalid login, not a valid email address"]}]
|
|
125
|
+
|
|
126
|
+
res = json_request('/change-login', :login=>'foo@example.com', "login-confirm"=>'foo2@example.com')
|
|
127
|
+
res.must_equal [400, {'error'=>"There was an error changing your login", "field-error"=>["login", "logins do not match"]}]
|
|
128
|
+
|
|
129
|
+
res = json_request('/change-login', :login=>'foo2@example.com', "login-confirm"=>'foo2@example.com')
|
|
130
|
+
res.must_equal [400, {'error'=>"There was an error changing your login", "field-error"=>["login", "invalid login, already an account with this login"]}]
|
|
131
|
+
|
|
132
|
+
res = json_request('/change-login', :login=>'foo3@example.com', "login-confirm"=>'foo3@example.com')
|
|
133
|
+
res.must_equal [200, {'success'=>"Your login has been changed"}]
|
|
134
|
+
|
|
135
|
+
json_logout
|
|
136
|
+
json_login(:login=>'foo3@example.com')
|
|
137
|
+
|
|
138
|
+
require_password = true
|
|
139
|
+
|
|
140
|
+
res = json_request('/change-login', :login=>'foo4@example.com', "login-confirm"=>'foo4@example.com', :password=>'012345678')
|
|
141
|
+
res.must_equal [400, {'error'=>"There was an error changing your login", "field-error"=>["password", "invalid password"]}]
|
|
142
|
+
|
|
143
|
+
res = json_request('/change-login', :login=>'foo4@example.com', "login-confirm"=>'foo4@example.com', :password=>'0123456789')
|
|
144
|
+
res.must_equal [200, {'success'=>"Your login has been changed"}]
|
|
145
|
+
|
|
146
|
+
json_logout
|
|
147
|
+
json_login(:login=>'foo4@example.com')
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
describe 'Rodauth change_password feature' do
|
|
4
|
+
[false, true].each do |ph|
|
|
5
|
+
it "should support changing passwords for accounts #{'with account_password_hash_column' if ph}" do
|
|
6
|
+
require_password = true
|
|
7
|
+
rodauth do
|
|
8
|
+
enable :login, :logout, :change_password
|
|
9
|
+
account_password_hash_column :ph if ph
|
|
10
|
+
change_password_requires_password?{require_password}
|
|
11
|
+
end
|
|
12
|
+
roda do |r|
|
|
13
|
+
r.rodauth
|
|
14
|
+
r.root{view :content=>""}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
login
|
|
18
|
+
page.current_path.must_equal '/'
|
|
19
|
+
|
|
20
|
+
visit '/change-password'
|
|
21
|
+
page.title.must_equal 'Change Password'
|
|
22
|
+
|
|
23
|
+
fill_in 'Password', :with=>'0123456789'
|
|
24
|
+
fill_in 'New Password', :with=>'0123456'
|
|
25
|
+
fill_in 'Confirm Password', :with=>'0123456789'
|
|
26
|
+
click_button 'Change Password'
|
|
27
|
+
page.html.must_include("passwords do not match")
|
|
28
|
+
page.find('#error_flash').text.must_equal "There was an error changing your password"
|
|
29
|
+
page.current_path.must_equal '/change-password'
|
|
30
|
+
|
|
31
|
+
fill_in 'Password', :with=>'0123456'
|
|
32
|
+
fill_in 'New Password', :with=>'0123456'
|
|
33
|
+
fill_in 'Confirm Password', :with=>'0123456'
|
|
34
|
+
click_button 'Change Password'
|
|
35
|
+
page.find('#error_flash').text.must_equal "There was an error changing your password"
|
|
36
|
+
page.body.must_include 'invalid password'
|
|
37
|
+
page.current_path.must_equal '/change-password'
|
|
38
|
+
|
|
39
|
+
fill_in 'Password', :with=>'0123456789'
|
|
40
|
+
fill_in 'New Password', :with=>'0123456789'
|
|
41
|
+
fill_in 'Confirm Password', :with=>'0123456789'
|
|
42
|
+
click_button 'Change Password'
|
|
43
|
+
page.find('#error_flash').text.must_equal "There was an error changing your password"
|
|
44
|
+
page.body.must_include 'invalid password, same as current password'
|
|
45
|
+
page.current_path.must_equal '/change-password'
|
|
46
|
+
|
|
47
|
+
fill_in 'Password', :with=>'0123456789'
|
|
48
|
+
fill_in 'New Password', :with=>'0123456'
|
|
49
|
+
fill_in 'Confirm Password', :with=>'0123456'
|
|
50
|
+
click_button 'Change Password'
|
|
51
|
+
page.find('#notice_flash').text.must_equal "Your password has been changed"
|
|
52
|
+
page.current_path.must_equal '/'
|
|
53
|
+
|
|
54
|
+
logout
|
|
55
|
+
login
|
|
56
|
+
page.html.must_include("invalid password")
|
|
57
|
+
page.current_path.must_equal '/login'
|
|
58
|
+
|
|
59
|
+
fill_in 'Password', :with=>'0123456'
|
|
60
|
+
click_button 'Login'
|
|
61
|
+
page.current_path.must_equal '/'
|
|
62
|
+
|
|
63
|
+
require_password = false
|
|
64
|
+
visit '/change-password'
|
|
65
|
+
fill_in 'New Password', :with=>'012345678'
|
|
66
|
+
fill_in 'Confirm Password', :with=>'012345678'
|
|
67
|
+
click_button 'Change Password'
|
|
68
|
+
page.find('#notice_flash').text.must_equal "Your password has been changed"
|
|
69
|
+
page.current_path.must_equal '/'
|
|
70
|
+
|
|
71
|
+
login(:pass=>'012345678')
|
|
72
|
+
page.current_path.must_equal '/'
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should support changing passwords for accounts without confirmation" do
|
|
77
|
+
rodauth do
|
|
78
|
+
enable :login, :change_password
|
|
79
|
+
modifications_require_password? false
|
|
80
|
+
require_password_confirmation? false
|
|
81
|
+
end
|
|
82
|
+
roda do |r|
|
|
83
|
+
r.rodauth
|
|
84
|
+
r.root{view :content=>""}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
login
|
|
88
|
+
visit '/change-password'
|
|
89
|
+
fill_in 'New Password', :with=>'012345678'
|
|
90
|
+
click_button 'Change Password'
|
|
91
|
+
page.find('#notice_flash').text.must_equal "Your password has been changed"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should support setting requirements for passwords" do
|
|
95
|
+
rodauth do
|
|
96
|
+
enable :login, :create_account, :change_password
|
|
97
|
+
create_account_autologin? false
|
|
98
|
+
password_meets_requirements? do |password|
|
|
99
|
+
password =~ /banana/
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
roda do |r|
|
|
103
|
+
r.rodauth
|
|
104
|
+
r.root{view :content=>""}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
visit '/create-account'
|
|
108
|
+
fill_in 'Login', :with=>'foo2@example.com'
|
|
109
|
+
fill_in 'Confirm Login', :with=>'foo2@example.com'
|
|
110
|
+
fill_in 'Password', :with=>'apple'
|
|
111
|
+
fill_in 'Confirm Password', :with=>'apple'
|
|
112
|
+
click_button 'Create Account'
|
|
113
|
+
page.html.must_include("invalid password, does not meet requirements")
|
|
114
|
+
page.find('#error_flash').text.must_equal "There was an error creating your account"
|
|
115
|
+
page.current_path.must_equal '/create-account'
|
|
116
|
+
|
|
117
|
+
fill_in 'Password', :with=>'banana'
|
|
118
|
+
fill_in 'Confirm Password', :with=>'banana'
|
|
119
|
+
click_button 'Create Account'
|
|
120
|
+
|
|
121
|
+
login(:login=>'foo2@example.com', :pass=>'banana')
|
|
122
|
+
|
|
123
|
+
visit '/change-password'
|
|
124
|
+
fill_in 'Password', :with=>'banana'
|
|
125
|
+
fill_in 'New Password', :with=>'apple'
|
|
126
|
+
fill_in 'Confirm Password', :with=>'apple'
|
|
127
|
+
click_button 'Change Password'
|
|
128
|
+
page.html.must_include("invalid password, does not meet requirements")
|
|
129
|
+
page.find('#error_flash').text.must_equal "There was an error changing your password"
|
|
130
|
+
page.current_path.must_equal '/change-password'
|
|
131
|
+
|
|
132
|
+
fill_in 'Password', :with=>'banana'
|
|
133
|
+
fill_in 'New Password', :with=>'my_banana_3'
|
|
134
|
+
fill_in 'Confirm Password', :with=>'my_banana_3'
|
|
135
|
+
click_button 'Change Password'
|
|
136
|
+
page.current_path.must_equal '/'
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "should support changing passwords for accounts via jwt" do
|
|
140
|
+
require_password = true
|
|
141
|
+
rodauth do
|
|
142
|
+
enable :login, :logout, :change_password
|
|
143
|
+
change_password_requires_password?{require_password}
|
|
144
|
+
end
|
|
145
|
+
roda(:jwt) do |r|
|
|
146
|
+
r.rodauth
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
json_login
|
|
150
|
+
|
|
151
|
+
res = json_request('/change-password', :password=>'0123456789', "new-password"=>'0123456', "password-confirm"=>'0123456789')
|
|
152
|
+
res.must_equal [400, {'error'=>"There was an error changing your password", "field-error"=>["new-password", "passwords do not match"]}]
|
|
153
|
+
|
|
154
|
+
res = json_request('/change-password', :password=>'0123456', "new-password"=>'0123456', "password-confirm"=>'0123456')
|
|
155
|
+
res.must_equal [400, {'error'=>"There was an error changing your password", "field-error"=>["password", "invalid password"]}]
|
|
156
|
+
|
|
157
|
+
res = json_request('/change-password', :password=>'0123456789', "new-password"=>'0123456789', "password-confirm"=>'0123456789')
|
|
158
|
+
res.must_equal [400, {'error'=>"There was an error changing your password", "field-error"=>["new-password", "invalid password, same as current password"]}]
|
|
159
|
+
|
|
160
|
+
res = json_request('/change-password', :password=>'0123456789', "new-password"=>'0123456', "password-confirm"=>'0123456')
|
|
161
|
+
res.must_equal [200, {'success'=>"Your password has been changed"}]
|
|
162
|
+
|
|
163
|
+
json_logout
|
|
164
|
+
res = json_login(:no_check=>true)
|
|
165
|
+
res.must_equal [400, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
|
166
|
+
|
|
167
|
+
json_login(:pass=>'0123456')
|
|
168
|
+
|
|
169
|
+
require_password = false
|
|
170
|
+
|
|
171
|
+
res = json_request('/change-password', "new-password"=>'012345678', "password-confirm"=>'012345678')
|
|
172
|
+
res.must_equal [200, {'success'=>"Your password has been changed"}]
|
|
173
|
+
|
|
174
|
+
json_logout
|
|
175
|
+
json_login(:pass=>'012345678')
|
|
176
|
+
end
|
|
177
|
+
end
|