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/migrate/001_tables.rb
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
Sequel.migration do
|
|
2
|
-
up do
|
|
3
|
-
extension :date_arithmetic
|
|
4
|
-
|
|
5
|
-
# Used by the account verification and close account features
|
|
6
|
-
create_table(:account_statuses) do
|
|
7
|
-
Integer :id, :primary_key=>true
|
|
8
|
-
String :name, :null=>false, :unique=>true
|
|
9
|
-
end
|
|
10
|
-
from(:account_statuses).import([:id, :name], [[1, 'Unverified'], [2, 'Verified'], [3, 'Closed']])
|
|
11
|
-
|
|
12
|
-
db = self
|
|
13
|
-
create_table(:accounts) do
|
|
14
|
-
primary_key :id, :type=>:Bignum
|
|
15
|
-
foreign_key :status_id, :account_statuses, :null=>false, :default=>1
|
|
16
|
-
if db.database_type == :postgres
|
|
17
|
-
citext :email, :null=>false
|
|
18
|
-
constraint :valid_email, :email=>/^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
|
|
19
|
-
index :email, :unique=>true, :where=>{:status_id=>[1, 2]}
|
|
20
|
-
else
|
|
21
|
-
String :email, :null=>false
|
|
22
|
-
index :email, :unique=>true
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
deadline_opts = proc do |days|
|
|
27
|
-
if database_type == :mysql
|
|
28
|
-
{:null=>false}
|
|
29
|
-
else
|
|
30
|
-
{:null=>false, :default=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :days=>days)}
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Used by the password reset feature
|
|
35
|
-
create_table(:account_password_reset_keys) do
|
|
36
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
37
|
-
String :key, :null=>false
|
|
38
|
-
DateTime :deadline, deadline_opts[1]
|
|
39
|
-
DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Used by the refresh token feature
|
|
43
|
-
create_table(:account_jwt_refresh_keys) do
|
|
44
|
-
primary_key :id, :type=>:Bignum
|
|
45
|
-
foreign_key :account_id, :accounts, :type=>:Bignum
|
|
46
|
-
String :key, :null=>false
|
|
47
|
-
DateTime :deadline, deadline_opts[1]
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Used by the account verification feature
|
|
51
|
-
create_table(:account_verification_keys) do
|
|
52
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
53
|
-
String :key, :null=>false
|
|
54
|
-
DateTime :requested_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
55
|
-
DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Used by the verify login change feature
|
|
59
|
-
create_table(:account_login_change_keys) do
|
|
60
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
61
|
-
String :key, :null=>false
|
|
62
|
-
String :login, :null=>false
|
|
63
|
-
DateTime :deadline, deadline_opts[1]
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Used by the remember me feature
|
|
67
|
-
create_table(:account_remember_keys) do
|
|
68
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
69
|
-
String :key, :null=>false
|
|
70
|
-
DateTime :deadline, deadline_opts[14]
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Used by the lockout feature
|
|
74
|
-
create_table(:account_login_failures) do
|
|
75
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
76
|
-
Integer :number, :null=>false, :default=>1
|
|
77
|
-
end
|
|
78
|
-
create_table(:account_lockouts) do
|
|
79
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
80
|
-
String :key, :null=>false
|
|
81
|
-
DateTime :deadline, deadline_opts[1]
|
|
82
|
-
DateTime :email_last_sent
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Used by the email auth feature
|
|
86
|
-
create_table(:account_email_auth_keys) do
|
|
87
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
88
|
-
String :key, :null=>false
|
|
89
|
-
DateTime :deadline, deadline_opts[1]
|
|
90
|
-
DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Used by the password expiration feature
|
|
94
|
-
create_table(:account_password_change_times) do
|
|
95
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
96
|
-
DateTime :changed_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Used by the account expiration feature
|
|
100
|
-
create_table(:account_activity_times) do
|
|
101
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
102
|
-
DateTime :last_activity_at, :null=>false
|
|
103
|
-
DateTime :last_login_at, :null=>false
|
|
104
|
-
DateTime :expired_at
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Used by the single session feature
|
|
108
|
-
create_table(:account_session_keys) do
|
|
109
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
110
|
-
String :key, :null=>false
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Used by the otp feature
|
|
114
|
-
create_table(:account_otp_keys) do
|
|
115
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
116
|
-
String :key, :null=>false
|
|
117
|
-
Integer :num_failures, :null=>false, :default=>0
|
|
118
|
-
Time :last_use, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Used by the recovery codes feature
|
|
122
|
-
create_table(:account_recovery_codes) do
|
|
123
|
-
foreign_key :id, :accounts, :type=>:Bignum
|
|
124
|
-
String :code
|
|
125
|
-
primary_key [:id, :code]
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Used by the sms codes feature
|
|
129
|
-
create_table(:account_sms_codes) do
|
|
130
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
131
|
-
String :phone_number, :null=>false
|
|
132
|
-
Integer :num_failures
|
|
133
|
-
String :code
|
|
134
|
-
DateTime :code_issued_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
case database_type
|
|
138
|
-
when :postgres
|
|
139
|
-
user = get(Sequel.lit('current_user')) + '_password'
|
|
140
|
-
run "GRANT REFERENCES ON accounts TO #{user}"
|
|
141
|
-
when :mysql, :mssql
|
|
142
|
-
user = if database_type == :mysql
|
|
143
|
-
get(Sequel.lit('current_user')).sub(/_password@/, '@')
|
|
144
|
-
else
|
|
145
|
-
get(Sequel.function(:DB_NAME))
|
|
146
|
-
end
|
|
147
|
-
run "GRANT ALL ON account_statuses TO #{user}"
|
|
148
|
-
run "GRANT ALL ON accounts TO #{user}"
|
|
149
|
-
run "GRANT ALL ON account_password_reset_keys TO #{user}"
|
|
150
|
-
run "GRANT ALL ON account_jwt_refresh_keys TO #{user}"
|
|
151
|
-
run "GRANT ALL ON account_verification_keys TO #{user}"
|
|
152
|
-
run "GRANT ALL ON account_login_change_keys TO #{user}"
|
|
153
|
-
run "GRANT ALL ON account_remember_keys TO #{user}"
|
|
154
|
-
run "GRANT ALL ON account_login_failures TO #{user}"
|
|
155
|
-
run "GRANT ALL ON account_email_auth_keys TO #{user}"
|
|
156
|
-
run "GRANT ALL ON account_lockouts TO #{user}"
|
|
157
|
-
run "GRANT ALL ON account_password_change_times TO #{user}"
|
|
158
|
-
run "GRANT ALL ON account_activity_times TO #{user}"
|
|
159
|
-
run "GRANT ALL ON account_session_keys TO #{user}"
|
|
160
|
-
run "GRANT ALL ON account_otp_keys TO #{user}"
|
|
161
|
-
run "GRANT ALL ON account_recovery_codes TO #{user}"
|
|
162
|
-
run "GRANT ALL ON account_sms_codes TO #{user}"
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
down do
|
|
167
|
-
drop_table(:account_sms_codes,
|
|
168
|
-
:account_recovery_codes,
|
|
169
|
-
:account_otp_keys,
|
|
170
|
-
:account_session_keys,
|
|
171
|
-
:account_activity_times,
|
|
172
|
-
:account_password_change_times,
|
|
173
|
-
:account_email_auth_keys,
|
|
174
|
-
:account_lockouts,
|
|
175
|
-
:account_login_failures,
|
|
176
|
-
:account_remember_keys,
|
|
177
|
-
:account_login_change_keys,
|
|
178
|
-
:account_verification_keys,
|
|
179
|
-
:account_jwt_refresh_keys,
|
|
180
|
-
:account_password_reset_keys,
|
|
181
|
-
:accounts,
|
|
182
|
-
:account_statuses)
|
|
183
|
-
end
|
|
184
|
-
end
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
require 'rodauth/migrations'
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
up do
|
|
5
|
-
create_table(:account_password_hashes) do
|
|
6
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
7
|
-
String :password_hash, :null=>false
|
|
8
|
-
end
|
|
9
|
-
Rodauth.create_database_authentication_functions(self)
|
|
10
|
-
case database_type
|
|
11
|
-
when :postgres
|
|
12
|
-
user = get(Sequel.lit('current_user')).sub(/_password\z/, '')
|
|
13
|
-
run "REVOKE ALL ON account_password_hashes FROM public"
|
|
14
|
-
run "REVOKE ALL ON FUNCTION rodauth_get_salt(int8) FROM public"
|
|
15
|
-
run "REVOKE ALL ON FUNCTION rodauth_valid_password_hash(int8, text) FROM public"
|
|
16
|
-
run "GRANT INSERT, UPDATE, DELETE ON account_password_hashes TO #{user}"
|
|
17
|
-
run "GRANT SELECT(id) ON account_password_hashes TO #{user}"
|
|
18
|
-
run "GRANT EXECUTE ON FUNCTION rodauth_get_salt(int8) TO #{user}"
|
|
19
|
-
run "GRANT EXECUTE ON FUNCTION rodauth_valid_password_hash(int8, text) TO #{user}"
|
|
20
|
-
when :mysql
|
|
21
|
-
user = get(Sequel.lit('current_user')).sub(/_password@/, '@')
|
|
22
|
-
db_name = get(Sequel.function(:database))
|
|
23
|
-
run "GRANT EXECUTE ON #{db_name}.* TO #{user}"
|
|
24
|
-
run "GRANT INSERT, UPDATE, DELETE ON account_password_hashes TO #{user}"
|
|
25
|
-
run "GRANT SELECT (id) ON account_password_hashes TO #{user}"
|
|
26
|
-
when :mssql
|
|
27
|
-
user = get(Sequel.function(:DB_NAME))
|
|
28
|
-
run "GRANT EXECUTE ON rodauth_get_salt TO #{user}"
|
|
29
|
-
run "GRANT EXECUTE ON rodauth_valid_password_hash TO #{user}"
|
|
30
|
-
run "GRANT INSERT, UPDATE, DELETE ON account_password_hashes TO #{user}"
|
|
31
|
-
run "GRANT SELECT ON account_password_hashes(id) TO #{user}"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Used by the disallow_password_reuse feature
|
|
35
|
-
create_table(:account_previous_password_hashes) do
|
|
36
|
-
primary_key :id, :type=>:Bignum
|
|
37
|
-
foreign_key :account_id, :accounts, :type=>:Bignum
|
|
38
|
-
String :password_hash, :null=>false
|
|
39
|
-
end
|
|
40
|
-
Rodauth.create_database_previous_password_check_functions(self)
|
|
41
|
-
|
|
42
|
-
case database_type
|
|
43
|
-
when :postgres
|
|
44
|
-
user = get(Sequel.lit('current_user')).sub(/_password\z/, '')
|
|
45
|
-
run "REVOKE ALL ON account_previous_password_hashes FROM public"
|
|
46
|
-
run "REVOKE ALL ON FUNCTION rodauth_get_previous_salt(int8) FROM public"
|
|
47
|
-
run "REVOKE ALL ON FUNCTION rodauth_previous_password_hash_match(int8, text) FROM public"
|
|
48
|
-
run "GRANT INSERT, UPDATE, DELETE ON account_previous_password_hashes TO #{user}"
|
|
49
|
-
run "GRANT SELECT(id, account_id) ON account_previous_password_hashes TO #{user}"
|
|
50
|
-
run "GRANT USAGE ON account_previous_password_hashes_id_seq TO #{user}"
|
|
51
|
-
run "GRANT EXECUTE ON FUNCTION rodauth_get_previous_salt(int8) TO #{user}"
|
|
52
|
-
run "GRANT EXECUTE ON FUNCTION rodauth_previous_password_hash_match(int8, text) TO #{user}"
|
|
53
|
-
when :mysql
|
|
54
|
-
user = get(Sequel.lit('current_user')).sub(/_password@/, '@')
|
|
55
|
-
db_name = get(Sequel.function(:database))
|
|
56
|
-
run "GRANT EXECUTE ON #{db_name}.* TO #{user}"
|
|
57
|
-
run "GRANT INSERT, UPDATE, DELETE ON account_previous_password_hashes TO #{user}"
|
|
58
|
-
run "GRANT SELECT (id, account_id) ON account_previous_password_hashes TO #{user}"
|
|
59
|
-
when :mssql
|
|
60
|
-
user = get(Sequel.function(:DB_NAME))
|
|
61
|
-
run "GRANT EXECUTE ON rodauth_get_previous_salt TO #{user}"
|
|
62
|
-
run "GRANT EXECUTE ON rodauth_previous_password_hash_match TO #{user}"
|
|
63
|
-
run "GRANT INSERT, UPDATE, DELETE ON account_previous_password_hashes TO #{user}"
|
|
64
|
-
run "GRANT SELECT ON account_previous_password_hashes(id, account_id) TO #{user}"
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
down do
|
|
69
|
-
Rodauth.drop_database_previous_password_check_functions(self)
|
|
70
|
-
Rodauth.drop_database_authentication_functions(self)
|
|
71
|
-
drop_table(:account_previous_password_hashes, :account_password_hashes)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
require 'rodauth/migrations'
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
up do
|
|
5
|
-
extension :date_arithmetic
|
|
6
|
-
|
|
7
|
-
create_table(:account_statuses) do
|
|
8
|
-
Integer :id, :primary_key=>true
|
|
9
|
-
String :name, :null=>false, :unique=>true
|
|
10
|
-
end
|
|
11
|
-
from(:account_statuses).import([:id, :name], [[1, 'Unverified'], [2, 'Verified'], [3, 'Closed']])
|
|
12
|
-
|
|
13
|
-
db = self
|
|
14
|
-
create_table(:accounts) do
|
|
15
|
-
primary_key :id, :type=>:Bignum
|
|
16
|
-
foreign_key :status_id, :account_statuses, :null=>false, :default=>1
|
|
17
|
-
if db.database_type == :postgres
|
|
18
|
-
citext :email, :null=>false
|
|
19
|
-
constraint :valid_email, :email=>/^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
|
|
20
|
-
index :email, :unique=>true, :where=>{:status_id=>[1, 2]}
|
|
21
|
-
else
|
|
22
|
-
String :email, :null=>false
|
|
23
|
-
index :email, :unique=>true
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
String :ph
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
create_table(:account_password_hashes) do
|
|
30
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
31
|
-
String :password_hash, :null=>false
|
|
32
|
-
end
|
|
33
|
-
Rodauth.create_database_authentication_functions(self)
|
|
34
|
-
|
|
35
|
-
deadline_opts = proc do |days|
|
|
36
|
-
if database_type == :mysql
|
|
37
|
-
{:null=>false}
|
|
38
|
-
else
|
|
39
|
-
{:null=>false, :default=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :days=>days)}
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
create_table(:account_password_reset_keys) do
|
|
44
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
45
|
-
String :key, :null=>false
|
|
46
|
-
DateTime :deadline, deadline_opts[1]
|
|
47
|
-
DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Used by the refresh token feature
|
|
51
|
-
create_table(:account_jwt_refresh_keys) do
|
|
52
|
-
primary_key :id, :type=>:Bignum
|
|
53
|
-
foreign_key :account_id, :accounts, :type=>:Bignum
|
|
54
|
-
String :key, :null=>false
|
|
55
|
-
DateTime :deadline, deadline_opts[1]
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
create_table(:account_verification_keys) do
|
|
59
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
60
|
-
String :key, :null=>false
|
|
61
|
-
DateTime :requested_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
62
|
-
DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
create_table(:account_login_change_keys) do
|
|
66
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
67
|
-
String :key, :null=>false
|
|
68
|
-
String :login, :null=>false
|
|
69
|
-
DateTime :deadline, deadline_opts[1]
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
create_table(:account_remember_keys) do
|
|
73
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
74
|
-
String :key, :null=>false
|
|
75
|
-
DateTime :deadline, deadline_opts[14]
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
create_table(:account_email_auth_keys) do
|
|
79
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
80
|
-
String :key, :null=>false
|
|
81
|
-
DateTime :deadline, deadline_opts[1]
|
|
82
|
-
DateTime :email_last_sent, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
create_table(:account_login_failures) do
|
|
86
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
87
|
-
Integer :number, :null=>false, :default=>1
|
|
88
|
-
end
|
|
89
|
-
create_table(:account_lockouts) do
|
|
90
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
91
|
-
String :key, :null=>false
|
|
92
|
-
DateTime :deadline, deadline_opts[1]
|
|
93
|
-
DateTime :email_last_sent
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
create_table(:account_password_change_times) do
|
|
97
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
98
|
-
DateTime :changed_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
create_table(:account_activity_times) do
|
|
102
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
103
|
-
DateTime :last_activity_at, :null=>false
|
|
104
|
-
DateTime :last_login_at, :null=>false
|
|
105
|
-
DateTime :expired_at
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
create_table(:account_session_keys) do
|
|
109
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
110
|
-
String :key, :null=>false
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
create_table(:account_otp_keys) do
|
|
114
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
115
|
-
String :key, :null=>false
|
|
116
|
-
Integer :num_failures, :null=>false, :default=>0
|
|
117
|
-
Time :last_use, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
create_table(:account_recovery_codes) do
|
|
121
|
-
foreign_key :id, :accounts, :type=>:Bignum
|
|
122
|
-
String :code
|
|
123
|
-
primary_key [:id, :code]
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
create_table(:account_sms_codes) do
|
|
127
|
-
foreign_key :id, :accounts, :primary_key=>true, :type=>:Bignum
|
|
128
|
-
String :phone_number, :null=>false
|
|
129
|
-
Integer :num_failures
|
|
130
|
-
String :code
|
|
131
|
-
DateTime :code_issued_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
create_table(:account_previous_password_hashes) do
|
|
135
|
-
primary_key :id, :type=>:Bignum
|
|
136
|
-
foreign_key :account_id, :accounts, :type=>:Bignum
|
|
137
|
-
String :password_hash, :null=>false
|
|
138
|
-
end
|
|
139
|
-
Rodauth.create_database_previous_password_check_functions(self)
|
|
140
|
-
end
|
|
141
|
-
end
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
|
2
|
-
|
|
3
|
-
describe 'Rodauth password complexity feature' do
|
|
4
|
-
it "should do additional password complexity checks" do
|
|
5
|
-
rodauth do
|
|
6
|
-
enable :login, :change_password, :password_complexity
|
|
7
|
-
change_password_requires_password? false
|
|
8
|
-
password_dictionary_file 'spec/words'
|
|
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
|
-
visit '/change-password'
|
|
19
|
-
|
|
20
|
-
bad_passwords = [
|
|
21
|
-
["minimum 6 characters", %w"a1OX"],
|
|
22
|
-
["does not include uppercase letters, lowercase letters, and numbers",
|
|
23
|
-
%w'sdflksdfl sdflks!fl Sdflksdfl dfl1sdfl DFL1SDFL DFL!SDFL'],
|
|
24
|
-
["includes common character sequence",
|
|
25
|
-
%w"Aqwerty12 Aazerty12 HA123ha HA234ha HA345ha HA456ha HA567ha HA678ha HA789ha HA890ha"],
|
|
26
|
-
["contains 3 or more of the same character in a row", %w"Helll0 Hellllll0"],
|
|
27
|
-
["is a word in a dictionary",
|
|
28
|
-
%w"Password1 1Password1 1PaSSword1 1P@$5w0Rd1 2398|3@$+7809 2|!7+1e l4$7$124 N!88|e56"]
|
|
29
|
-
]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
bad_passwords.each do |message, passwords|
|
|
33
|
-
passwords.each do |pass|
|
|
34
|
-
fill_in 'New Password', :with=>pass
|
|
35
|
-
fill_in 'Confirm Password', :with=>pass
|
|
36
|
-
click_button 'Change Password'
|
|
37
|
-
page.html.must_include("invalid password, does not meet requirements (#{message})")
|
|
38
|
-
page.find('#error_flash').text.must_equal "There was an error changing your password"
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
fill_in 'New Password', :with=>'footpassword'
|
|
43
|
-
fill_in 'Confirm Password', :with=>'footpassword'
|
|
44
|
-
click_button 'Change Password'
|
|
45
|
-
page.find('#notice_flash').text.must_equal "Your password has been changed"
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it "should support default dictionary" do
|
|
49
|
-
default_dictionary = '/usr/share/dict/words'
|
|
50
|
-
skip("#{default_dictionary} not present") unless File.file?(default_dictionary)
|
|
51
|
-
pass = File.read(default_dictionary).split.sort_by{|w| w.length}.last
|
|
52
|
-
skip("#{default_dictionary} empty") unless pass
|
|
53
|
-
pass = pass.downcase.gsub(/[^a-z]/, '')
|
|
54
|
-
|
|
55
|
-
rodauth do
|
|
56
|
-
enable :login, :change_password, :password_complexity
|
|
57
|
-
change_password_requires_password? false
|
|
58
|
-
end
|
|
59
|
-
roda do |r|
|
|
60
|
-
r.rodauth
|
|
61
|
-
r.root{view :content=>""}
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
login
|
|
65
|
-
page.current_path.must_equal '/'
|
|
66
|
-
|
|
67
|
-
visit '/change-password'
|
|
68
|
-
fill_in 'New Password', :with=>"135#{pass}135"
|
|
69
|
-
fill_in 'Confirm Password', :with=>"135#{pass}135"
|
|
70
|
-
click_button 'Change Password'
|
|
71
|
-
page.html.must_include("invalid password")
|
|
72
|
-
page.find('#error_flash').text.must_equal "There was an error changing your password"
|
|
73
|
-
|
|
74
|
-
fill_in 'New Password', :with=>'footpassword'
|
|
75
|
-
fill_in 'Confirm Password', :with=>'footpassword'
|
|
76
|
-
click_button 'Change Password'
|
|
77
|
-
page.find('#notice_flash').text.must_equal "Your password has been changed"
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it "should support no dictionary" do
|
|
81
|
-
default_dictionary = '/usr/share/dict/words'
|
|
82
|
-
skip("#{default_dictionary} not present") unless File.file?(default_dictionary)
|
|
83
|
-
|
|
84
|
-
rodauth do
|
|
85
|
-
enable :login, :change_password, :password_complexity
|
|
86
|
-
change_password_requires_password? false
|
|
87
|
-
password_dictionary_file false
|
|
88
|
-
end
|
|
89
|
-
roda do |r|
|
|
90
|
-
r.rodauth
|
|
91
|
-
r.root{view :content=>""}
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
login
|
|
95
|
-
page.current_path.must_equal '/'
|
|
96
|
-
|
|
97
|
-
visit '/change-password'
|
|
98
|
-
fill_in 'New Password', :with=>"password123"
|
|
99
|
-
fill_in 'Confirm Password', :with=>"password123"
|
|
100
|
-
click_button 'Change Password'
|
|
101
|
-
page.html.must_include("invalid password")
|
|
102
|
-
page.find('#error_flash').text.must_equal "There was an error changing your password"
|
|
103
|
-
|
|
104
|
-
fill_in 'New Password', :with=>'Password1'
|
|
105
|
-
fill_in 'Confirm Password', :with=>'Password1'
|
|
106
|
-
click_button 'Change Password'
|
|
107
|
-
page.find('#notice_flash').text.must_equal "Your password has been changed"
|
|
108
|
-
end
|
|
109
|
-
end
|