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
data/spec/login_spec.rb
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
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 session[:account_id]
|
|
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
|
+
|
|
19
|
+
login(:pass=>'012345678', :visit=>false)
|
|
20
|
+
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
21
|
+
page.html.must_include("invalid password")
|
|
22
|
+
|
|
23
|
+
fill_in 'Password', :with=>'0123456789'
|
|
24
|
+
click_button 'Login'
|
|
25
|
+
page.current_path.must_equal '/'
|
|
26
|
+
page.find('#notice_flash').text.must_equal 'You have been logged in'
|
|
27
|
+
page.html.must_include("Logged In")
|
|
28
|
+
|
|
29
|
+
visit '/logout'
|
|
30
|
+
page.title.must_equal 'Logout'
|
|
31
|
+
|
|
32
|
+
click_button 'Logout'
|
|
33
|
+
page.find('#notice_flash').text.must_equal 'You have been logged out'
|
|
34
|
+
page.current_path.must_equal '/login'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should not allow login to unverified account" do
|
|
38
|
+
rodauth do
|
|
39
|
+
enable :login
|
|
40
|
+
skip_status_checks? false
|
|
41
|
+
end
|
|
42
|
+
roda do |r|
|
|
43
|
+
r.rodauth
|
|
44
|
+
next unless session[:account_id]
|
|
45
|
+
r.root{view :content=>"Logged In"}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
DB[:accounts].update(:status_id=>1)
|
|
49
|
+
login
|
|
50
|
+
page.find('#error_flash').text.must_equal 'There was an error logging in'
|
|
51
|
+
page.html.must_include("unverified account, please verify account before logging in")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should handle overriding login action" do
|
|
55
|
+
rodauth do
|
|
56
|
+
enable :login
|
|
57
|
+
end
|
|
58
|
+
roda do |r|
|
|
59
|
+
r.post 'login' do
|
|
60
|
+
if r['login'] == 'apple' && r['password'] == 'banana'
|
|
61
|
+
session[:user_id] = 'pear'
|
|
62
|
+
r.redirect '/'
|
|
63
|
+
end
|
|
64
|
+
r.redirect '/login'
|
|
65
|
+
end
|
|
66
|
+
r.rodauth
|
|
67
|
+
next unless session[:user_id] == 'pear'
|
|
68
|
+
r.root{"Logged In"}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
login(:login=>'appl', :pass=>'banana')
|
|
72
|
+
page.html.wont_match(/Logged In/)
|
|
73
|
+
|
|
74
|
+
login(:login=>'apple', :pass=>'banan', :visit=>false)
|
|
75
|
+
page.html.wont_match(/Logged In/)
|
|
76
|
+
|
|
77
|
+
login(:login=>'apple', :pass=>'banana', :visit=>false)
|
|
78
|
+
page.current_path.must_equal '/'
|
|
79
|
+
page.html.must_include("Logged In")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "should handle overriding some login attributes" do
|
|
83
|
+
rodauth do
|
|
84
|
+
enable :login
|
|
85
|
+
account_from_login do |login|
|
|
86
|
+
DB[:accounts].first if login == 'apple'
|
|
87
|
+
end
|
|
88
|
+
password_match? do |password|
|
|
89
|
+
password == 'banana'
|
|
90
|
+
end
|
|
91
|
+
update_session do
|
|
92
|
+
session[:user_id] = 'pear'
|
|
93
|
+
end
|
|
94
|
+
no_matching_login_message "no user"
|
|
95
|
+
invalid_password_message "bad password"
|
|
96
|
+
end
|
|
97
|
+
roda do |r|
|
|
98
|
+
r.rodauth
|
|
99
|
+
next unless session[:user_id] == 'pear'
|
|
100
|
+
r.root{"Logged In"}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
login(:login=>'appl', :pass=>'banana')
|
|
104
|
+
page.html.must_include("no user")
|
|
105
|
+
|
|
106
|
+
login(:login=>'apple', :pass=>'banan', :visit=>false)
|
|
107
|
+
page.html.must_include("bad password")
|
|
108
|
+
|
|
109
|
+
fill_in 'Password', :with=>'banana'
|
|
110
|
+
click_button 'Login'
|
|
111
|
+
page.current_path.must_equal '/'
|
|
112
|
+
page.html.must_include("Logged In")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "should handle a prefix and some other login options" do
|
|
116
|
+
rodauth do
|
|
117
|
+
enable :login, :logout
|
|
118
|
+
prefix 'auth'
|
|
119
|
+
session_key :login_email
|
|
120
|
+
account_from_session{DB[:accounts].first(:email=>session_value)}
|
|
121
|
+
account_session_value{account[:email]}
|
|
122
|
+
login_param{request['lp']}
|
|
123
|
+
login_additional_form_tags "<input type='hidden' name='lp' value='l' />"
|
|
124
|
+
password_param 'p'
|
|
125
|
+
login_redirect{"/foo/#{account[:email]}"}
|
|
126
|
+
logout_redirect '/auth/lin'
|
|
127
|
+
login_route 'lin'
|
|
128
|
+
logout_route 'lout'
|
|
129
|
+
end
|
|
130
|
+
no_freeze!
|
|
131
|
+
roda do |r|
|
|
132
|
+
r.on 'auth' do
|
|
133
|
+
r.rodauth
|
|
134
|
+
end
|
|
135
|
+
next unless session[:login_email] =~ /example/
|
|
136
|
+
r.get('foo/:email'){|e| "Logged In: #{e}"}
|
|
137
|
+
end
|
|
138
|
+
app.plugin :render, :views=>'spec/views', :engine=>'str'
|
|
139
|
+
|
|
140
|
+
visit '/auth/lin?lp=l'
|
|
141
|
+
|
|
142
|
+
login(:login=>'foo@example2.com', :visit=>false)
|
|
143
|
+
page.html.must_include("no matching login")
|
|
144
|
+
|
|
145
|
+
login(:pass=>'012345678', :visit=>false)
|
|
146
|
+
page.html.must_include("invalid password")
|
|
147
|
+
|
|
148
|
+
login(:visit=>false)
|
|
149
|
+
page.current_path.must_equal '/foo/foo@example.com'
|
|
150
|
+
page.html.must_include("Logged In: foo@example.com")
|
|
151
|
+
|
|
152
|
+
visit '/auth/lout'
|
|
153
|
+
click_button 'Logout'
|
|
154
|
+
page.current_path.must_equal '/auth/lin'
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "should login and logout via jwt" do
|
|
158
|
+
rodauth do
|
|
159
|
+
enable :login, :logout
|
|
160
|
+
jwt_secret{proc{super()}.must_raise ArgumentError; "1"}
|
|
161
|
+
end
|
|
162
|
+
roda(:jwt) do |r|
|
|
163
|
+
r.rodauth
|
|
164
|
+
rodauth.logged_in? ? '1' : '2'
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
json_request.must_equal [200, 2]
|
|
168
|
+
|
|
169
|
+
visit '/login'
|
|
170
|
+
page.status_code.must_equal 400
|
|
171
|
+
page.body.must_equal "Only JSON format requests are allowed"
|
|
172
|
+
|
|
173
|
+
res = json_request("/login", :method=>'GET')
|
|
174
|
+
res.must_equal [405, {'error'=>'non-POST method used in JSON API'}]
|
|
175
|
+
|
|
176
|
+
res = json_request("/login", :login=>'foo@example2.com', :password=>'0123456789')
|
|
177
|
+
res.must_equal [400, {'error'=>"There was an error logging in", "field-error"=>["login", "no matching login"]}]
|
|
178
|
+
|
|
179
|
+
res = json_request("/login", :login=>'foo@example.com', :password=>'012345678')
|
|
180
|
+
res.must_equal [400, {'error'=>"There was an error logging in", "field-error"=>["password", "invalid password"]}]
|
|
181
|
+
|
|
182
|
+
json_request("/login", :login=>'foo@example.com', :password=>'0123456789').must_equal [200, {"success"=>'You have been logged in'}]
|
|
183
|
+
json_request.must_equal [200, 1]
|
|
184
|
+
|
|
185
|
+
json_request("/logout").must_equal [200, {"success"=>'You have been logged out'}]
|
|
186
|
+
json_request.must_equal [200, 2]
|
|
187
|
+
end
|
|
188
|
+
end
|
data/spec/migrate/001_tables.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
Sequel.migration do
|
|
2
2
|
up do
|
|
3
|
+
extension :date_arithmetic
|
|
4
|
+
|
|
3
5
|
# Used by the account verification and close account features
|
|
4
6
|
create_table(:account_statuses) do
|
|
5
7
|
Integer :id, :primary_key=>true
|
|
@@ -7,39 +9,47 @@ Sequel.migration do
|
|
|
7
9
|
end
|
|
8
10
|
from(:account_statuses).import([:id, :name], [[1, 'Unverified'], [2, 'Verified'], [3, 'Closed']])
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
# and close account features.
|
|
12
|
+
db = self
|
|
12
13
|
create_table(:accounts) do
|
|
13
14
|
primary_key :id, :type=>Bignum
|
|
14
15
|
foreign_key :status_id, :account_statuses, :null=>false, :default=>1
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
23
32
|
end
|
|
24
33
|
|
|
25
34
|
# Used by the password reset feature
|
|
26
35
|
create_table(:account_password_reset_keys) do
|
|
27
36
|
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
28
37
|
String :key, :null=>false
|
|
29
|
-
DateTime :deadline,
|
|
38
|
+
DateTime :deadline, deadline_opts[1]
|
|
30
39
|
end
|
|
31
40
|
|
|
32
41
|
# Used by the account verification feature
|
|
33
42
|
create_table(:account_verification_keys) do
|
|
34
43
|
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
35
44
|
String :key, :null=>false
|
|
45
|
+
DateTime :requested_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
36
46
|
end
|
|
37
47
|
|
|
38
48
|
# Used by the remember me feature
|
|
39
49
|
create_table(:account_remember_keys) do
|
|
40
50
|
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
41
51
|
String :key, :null=>false
|
|
42
|
-
DateTime :deadline,
|
|
52
|
+
DateTime :deadline, deadline_opts[14]
|
|
43
53
|
end
|
|
44
54
|
|
|
45
55
|
# Used by the lockout feature
|
|
@@ -50,15 +60,92 @@ Sequel.migration do
|
|
|
50
60
|
create_table(:account_lockouts) do
|
|
51
61
|
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
52
62
|
String :key, :null=>false
|
|
53
|
-
DateTime :deadline,
|
|
63
|
+
DateTime :deadline, deadline_opts[1]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Used by the password expiration feature
|
|
67
|
+
create_table(:account_password_change_times) do
|
|
68
|
+
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
69
|
+
DateTime :changed_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
54
70
|
end
|
|
55
71
|
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
# Used by the account expiration feature
|
|
73
|
+
create_table(:account_activity_times) do
|
|
74
|
+
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
75
|
+
DateTime :last_activity_at, :null=>false
|
|
76
|
+
DateTime :last_login_at, :null=>false
|
|
77
|
+
DateTime :expired_at
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Used by the single session feature
|
|
81
|
+
create_table(:account_session_keys) do
|
|
82
|
+
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
83
|
+
String :key, :null=>false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Used by the otp feature
|
|
87
|
+
create_table(:account_otp_keys) do
|
|
88
|
+
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
89
|
+
String :key, :null=>false
|
|
90
|
+
Integer :num_failures, :null=>false, :default=>0
|
|
91
|
+
Time :last_use, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Used by the recovery codes feature
|
|
95
|
+
create_table(:account_recovery_codes) do
|
|
96
|
+
foreign_key :id, :accounts, :type=>Bignum
|
|
97
|
+
String :code
|
|
98
|
+
primary_key [:id, :code]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Used by the sms codes feature
|
|
102
|
+
create_table(:account_sms_codes) do
|
|
103
|
+
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
104
|
+
String :phone_number, :null=>false
|
|
105
|
+
Integer :num_failures
|
|
106
|
+
String :code
|
|
107
|
+
DateTime :code_issued_at, :null=>false, :default=>Sequel::CURRENT_TIMESTAMP
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
case database_type
|
|
111
|
+
when :postgres
|
|
112
|
+
user = get{Sequel.lit('current_user')} + '_password'
|
|
113
|
+
run "GRANT REFERENCES ON accounts TO #{user}"
|
|
114
|
+
when :mysql, :mssql
|
|
115
|
+
user = if database_type == :mysql
|
|
116
|
+
get{Sequel.lit('current_user')}.sub(/_password@/, '@')
|
|
117
|
+
else
|
|
118
|
+
get{DB_NAME{}}
|
|
119
|
+
end
|
|
120
|
+
run "GRANT ALL ON account_statuses TO #{user}"
|
|
121
|
+
run "GRANT ALL ON accounts TO #{user}"
|
|
122
|
+
run "GRANT ALL ON account_password_reset_keys TO #{user}"
|
|
123
|
+
run "GRANT ALL ON account_verification_keys TO #{user}"
|
|
124
|
+
run "GRANT ALL ON account_remember_keys TO #{user}"
|
|
125
|
+
run "GRANT ALL ON account_login_failures TO #{user}"
|
|
126
|
+
run "GRANT ALL ON account_lockouts TO #{user}"
|
|
127
|
+
run "GRANT ALL ON account_password_change_times TO #{user}"
|
|
128
|
+
run "GRANT ALL ON account_activity_times TO #{user}"
|
|
129
|
+
run "GRANT ALL ON account_session_keys TO #{user}"
|
|
130
|
+
run "GRANT ALL ON account_otp_keys TO #{user}"
|
|
131
|
+
run "GRANT ALL ON account_recovery_codes TO #{user}"
|
|
132
|
+
run "GRANT ALL ON account_sms_codes TO #{user}"
|
|
133
|
+
end
|
|
59
134
|
end
|
|
60
135
|
|
|
61
136
|
down do
|
|
62
|
-
drop_table(:
|
|
137
|
+
drop_table(:account_sms_codes,
|
|
138
|
+
:account_recovery_codes,
|
|
139
|
+
:account_otp_keys,
|
|
140
|
+
:account_session_keys,
|
|
141
|
+
:account_activity_times,
|
|
142
|
+
:account_password_change_times,
|
|
143
|
+
:account_lockouts,
|
|
144
|
+
:account_login_failures,
|
|
145
|
+
:account_remember_keys,
|
|
146
|
+
:account_verification_keys,
|
|
147
|
+
:account_password_reset_keys,
|
|
148
|
+
:accounts,
|
|
149
|
+
:account_statuses)
|
|
63
150
|
end
|
|
64
151
|
end
|
|
@@ -1,55 +1,73 @@
|
|
|
1
|
+
require 'rodauth/migrations'
|
|
2
|
+
|
|
1
3
|
Sequel.migration do
|
|
2
4
|
up do
|
|
3
|
-
# Used by the login and change password features
|
|
4
5
|
create_table(:account_password_hashes) do
|
|
5
6
|
foreign_key :id, :accounts, :primary_key=>true, :type=>Bignum
|
|
6
7
|
String :password_hash, :null=>false
|
|
7
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{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{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
|
|
8
33
|
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
WHERE account_id = id;
|
|
17
|
-
RETURN salt;
|
|
18
|
-
END;
|
|
19
|
-
$$ LANGUAGE plpgsql
|
|
20
|
-
SECURITY DEFINER
|
|
21
|
-
SET search_path = public, pg_temp;
|
|
22
|
-
END
|
|
23
|
-
|
|
24
|
-
# Function that checks if password hash is valid for given user.
|
|
25
|
-
run <<END
|
|
26
|
-
CREATE OR REPLACE FUNCTION rodauth_valid_password_hash(account_id int8, hash text) RETURNS boolean AS $$
|
|
27
|
-
DECLARE valid boolean;
|
|
28
|
-
BEGIN
|
|
29
|
-
SELECT password_hash = hash INTO valid
|
|
30
|
-
FROM account_password_hashes
|
|
31
|
-
WHERE account_id = id;
|
|
32
|
-
RETURN valid;
|
|
33
|
-
END;
|
|
34
|
-
$$ LANGUAGE plpgsql
|
|
35
|
-
SECURITY DEFINER
|
|
36
|
-
SET search_path = public, pg_temp;
|
|
37
|
-
END
|
|
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)
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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{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{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
|
|
48
66
|
end
|
|
49
67
|
|
|
50
68
|
down do
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
drop_table(:account_password_hashes)
|
|
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)
|
|
54
72
|
end
|
|
55
73
|
end
|