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.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +146 -0
  3. data/README.rdoc +644 -220
  4. data/Rakefile +99 -11
  5. data/doc/account_expiration.rdoc +55 -0
  6. data/doc/base.rdoc +104 -0
  7. data/doc/change_login.rdoc +29 -0
  8. data/doc/change_password.rdoc +26 -0
  9. data/doc/close_account.rdoc +31 -0
  10. data/doc/confirm_password.rdoc +22 -0
  11. data/doc/create_account.rdoc +34 -0
  12. data/doc/disallow_password_reuse.rdoc +37 -0
  13. data/doc/email_base.rdoc +19 -0
  14. data/doc/jwt.rdoc +35 -0
  15. data/doc/lockout.rdoc +83 -0
  16. data/doc/login.rdoc +27 -0
  17. data/doc/login_password_requirements_base.rdoc +50 -0
  18. data/doc/logout.rdoc +21 -0
  19. data/doc/otp.rdoc +100 -0
  20. data/doc/password_complexity.rdoc +50 -0
  21. data/doc/password_expiration.rdoc +52 -0
  22. data/doc/password_grace_period.rdoc +10 -0
  23. data/doc/recovery_codes.rdoc +60 -0
  24. data/doc/release_notes/1.0.0.txt +443 -0
  25. data/doc/remember.rdoc +82 -0
  26. data/doc/reset_password.rdoc +70 -0
  27. data/doc/session_expiration.rdoc +27 -0
  28. data/doc/single_session.rdoc +43 -0
  29. data/doc/sms_codes.rdoc +119 -0
  30. data/doc/two_factor_base.rdoc +27 -0
  31. data/doc/verify_account.rdoc +70 -0
  32. data/doc/verify_account_grace_period.rdoc +15 -0
  33. data/doc/verify_change_login.rdoc +9 -0
  34. data/lib/roda/plugins/rodauth.rb +3 -262
  35. data/lib/rodauth.rb +260 -0
  36. data/lib/rodauth/features/account_expiration.rb +108 -0
  37. data/lib/rodauth/features/base.rb +479 -0
  38. data/lib/rodauth/features/change_login.rb +77 -0
  39. data/lib/rodauth/features/change_password.rb +66 -0
  40. data/lib/rodauth/features/close_account.rb +82 -0
  41. data/lib/rodauth/features/confirm_password.rb +51 -0
  42. data/lib/rodauth/features/create_account.rb +128 -0
  43. data/lib/rodauth/features/disallow_password_reuse.rb +82 -0
  44. data/lib/rodauth/features/email_base.rb +63 -0
  45. data/lib/rodauth/features/jwt.rb +151 -0
  46. data/lib/rodauth/features/lockout.rb +262 -0
  47. data/lib/rodauth/features/login.rb +61 -0
  48. data/lib/rodauth/features/login_password_requirements_base.rb +123 -0
  49. data/lib/rodauth/features/logout.rb +37 -0
  50. data/lib/rodauth/features/otp.rb +338 -0
  51. data/lib/rodauth/features/password_complexity.rb +89 -0
  52. data/lib/rodauth/features/password_expiration.rb +111 -0
  53. data/lib/rodauth/features/password_grace_period.rb +46 -0
  54. data/lib/rodauth/features/recovery_codes.rb +240 -0
  55. data/lib/rodauth/features/remember.rb +200 -0
  56. data/lib/rodauth/features/reset_password.rb +207 -0
  57. data/lib/rodauth/features/session_expiration.rb +55 -0
  58. data/lib/rodauth/features/single_session.rb +87 -0
  59. data/lib/rodauth/features/sms_codes.rb +498 -0
  60. data/lib/rodauth/features/two_factor_base.rb +135 -0
  61. data/lib/rodauth/features/verify_account.rb +232 -0
  62. data/lib/rodauth/features/verify_account_grace_period.rb +76 -0
  63. data/lib/rodauth/features/verify_change_login.rb +20 -0
  64. data/lib/rodauth/migrations.rb +130 -0
  65. data/lib/rodauth/version.rb +9 -0
  66. data/spec/account_expiration_spec.rb +90 -0
  67. data/spec/all.rb +1 -0
  68. data/spec/change_login_spec.rb +149 -0
  69. data/spec/change_password_spec.rb +177 -0
  70. data/spec/close_account_spec.rb +162 -0
  71. data/spec/confirm_password_spec.rb +70 -0
  72. data/spec/create_account_spec.rb +127 -0
  73. data/spec/disallow_password_reuse_spec.rb +84 -0
  74. data/spec/lockout_spec.rb +228 -0
  75. data/spec/login_spec.rb +188 -0
  76. data/spec/migrate/001_tables.rb +103 -16
  77. data/spec/migrate/002_account_password_hash_column.rb +11 -0
  78. data/spec/migrate_password/001_tables.rb +60 -42
  79. data/spec/migrate_travis/001_tables.rb +116 -0
  80. data/spec/password_complexity_spec.rb +108 -0
  81. data/spec/password_expiration_spec.rb +243 -0
  82. data/spec/password_grace_period_spec.rb +93 -0
  83. data/spec/remember_spec.rb +424 -0
  84. data/spec/reset_password_spec.rb +185 -0
  85. data/spec/rodauth_spec.rb +57 -980
  86. data/spec/session_expiration_spec.rb +58 -0
  87. data/spec/single_session_spec.rb +107 -0
  88. data/spec/spec_helper.rb +202 -0
  89. data/spec/two_factor_spec.rb +1310 -0
  90. data/spec/verify_account_grace_period_spec.rb +135 -0
  91. data/spec/verify_account_spec.rb +142 -0
  92. data/spec/verify_change_login_spec.rb +46 -0
  93. data/spec/views/login.str +2 -2
  94. data/templates/add-recovery-codes.str +2 -0
  95. data/templates/button.str +5 -0
  96. data/templates/change-login.str +5 -18
  97. data/templates/change-password.str +6 -14
  98. data/templates/close-account.str +3 -6
  99. data/templates/confirm-password.str +4 -14
  100. data/templates/create-account.str +6 -30
  101. data/templates/login-confirm-field.str +6 -0
  102. data/templates/login-field.str +6 -0
  103. data/templates/login.str +5 -19
  104. data/templates/logout.str +2 -6
  105. data/templates/otp-auth-code-field.str +6 -0
  106. data/templates/otp-auth.str +8 -0
  107. data/templates/otp-disable.str +6 -0
  108. data/templates/otp-setup.str +21 -0
  109. data/templates/password-confirm-field.str +6 -0
  110. data/templates/password-field.str +6 -0
  111. data/templates/recovery-auth.str +12 -0
  112. data/templates/recovery-codes.str +6 -0
  113. data/templates/remember.str +8 -12
  114. data/templates/reset-password-request.str +2 -2
  115. data/templates/reset-password.str +4 -18
  116. data/templates/sms-auth.str +6 -0
  117. data/templates/sms-code-field.str +6 -0
  118. data/templates/sms-confirm.str +7 -0
  119. data/templates/sms-disable.str +7 -0
  120. data/templates/sms-request.str +5 -0
  121. data/templates/sms-setup.str +12 -0
  122. data/templates/unlock-account-request.str +3 -7
  123. data/templates/unlock-account.str +4 -7
  124. data/templates/verify-account-resend.str +2 -2
  125. data/templates/verify-account.str +2 -6
  126. metadata +191 -29
  127. data/lib/roda/plugins/rodauth/base.rb +0 -428
  128. data/lib/roda/plugins/rodauth/change_login.rb +0 -48
  129. data/lib/roda/plugins/rodauth/change_password.rb +0 -42
  130. data/lib/roda/plugins/rodauth/close_account.rb +0 -42
  131. data/lib/roda/plugins/rodauth/create_account.rb +0 -92
  132. data/lib/roda/plugins/rodauth/lockout.rb +0 -292
  133. data/lib/roda/plugins/rodauth/login.rb +0 -81
  134. data/lib/roda/plugins/rodauth/logout.rb +0 -36
  135. data/lib/roda/plugins/rodauth/remember.rb +0 -226
  136. data/lib/roda/plugins/rodauth/reset_password.rb +0 -205
  137. 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,9 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Rodauth
4
+ VERSION = '1.0.0'.freeze
5
+
6
+ def self.version
7
+ VERSION
8
+ end
9
+ 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
@@ -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