rodauth-rails 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +33 -23
- data/lib/generators/rodauth/migration/active_sessions.erb +2 -2
- data/lib/generators/rodauth/migration/audit_logging.erb +1 -1
- data/lib/generators/rodauth/migration/base.erb +2 -2
- data/lib/generators/rodauth/migration/email_auth.erb +1 -1
- data/lib/generators/rodauth/migration/otp.erb +1 -1
- data/lib/generators/rodauth/migration/password_expiration.erb +1 -1
- data/lib/generators/rodauth/migration/reset_password.erb +1 -1
- data/lib/generators/rodauth/migration/sms_codes.erb +1 -1
- data/lib/generators/rodauth/migration/verify_account.erb +2 -2
- data/lib/generators/rodauth/migration/webauthn.erb +1 -1
- data/lib/generators/rodauth/migration_helpers.rb +8 -0
- data/lib/generators/rodauth/templates/app/misc/rodauth_main.rb +1 -4
- data/lib/generators/rodauth/templates/app/models/account.rb +1 -0
- data/lib/rodauth/rails/controller_methods.rb +4 -29
- data/lib/rodauth/rails/feature/base.rb +21 -0
- data/lib/rodauth/rails/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f93b80457e9c6e9ea6083974e05514de8e605b0f9a4015fe765ffc1c4059c5ee
|
4
|
+
data.tar.gz: '06877735d144b893b1a7a08f125e34316196679e47038112c6df215e352268c6'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f26bf373cb8a64e2e0d5156aff9ca94e468d9eb257dcd2e34f702d43a6506bacdc7a22bd85090ddefddc972ebb25b22f3012bca2f21b84aece970d138159166
|
7
|
+
data.tar.gz: b5811e6ae069e71f7cd8be35b9bd884c463aa709a1be4abc6fd792bf747ba7aabe0d7aa2f3f4f7c43466f153b163912d55957d0fd84d1314b37ac60ca4a5b221
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 1.2.0 (2022-02-11)
|
2
|
+
|
3
|
+
* Work around Active Record 4.2 not supporting procs for literal SQL column default (@janko)
|
4
|
+
|
5
|
+
* Avoid re-fetching the account in `#current_account` when it has already been fetched by Rodauth (@janko)
|
6
|
+
|
7
|
+
* Extract `#current_account` helper functionality into `#rails_account` Rodauth method (@janko)
|
8
|
+
|
9
|
+
* Use default account status values in generated configuration, with enum on `Account` model (@janko)
|
10
|
+
|
1
11
|
## 1.1.0 (2022-01-16)
|
2
12
|
|
3
13
|
* Automatically route the path prefix in `r.rodauth` if one has been set (@janko)
|
data/README.md
CHANGED
@@ -15,8 +15,8 @@ Useful links:
|
|
15
15
|
Articles:
|
16
16
|
|
17
17
|
* [Rodauth: A Refreshing Authentication Solution for Ruby](https://janko.io/rodauth-a-refreshing-authentication-solution-for-ruby/)
|
18
|
-
* [
|
19
|
-
* [
|
18
|
+
* [Rails Authentication with Rodauth](https://janko.io/adding-authentication-in-rails-with-rodauth/)
|
19
|
+
* [Multifactor Authentication in Rails with Rodauth](https://janko.io/adding-multifactor-authentication-in-rails-with-rodauth/)
|
20
20
|
* [How to build an OIDC provider using rodauth-oauth on Rails](https://honeyryderchuck.gitlab.io/httpx/2021/03/15/oidc-provider-on-rails-using-rodauth-oauth.html)
|
21
21
|
|
22
22
|
## Why Rodauth?
|
@@ -140,16 +140,14 @@ authentication experience, and the forms use [Bootstrap] markup.
|
|
140
140
|
### Current account
|
141
141
|
|
142
142
|
The `#current_account` method is defined in controllers and views, which
|
143
|
-
returns the model instance of the currently logged in account.
|
143
|
+
returns the model instance of the currently logged in account. If the account
|
144
|
+
doesn't exist in the database, the session will be cleared.
|
144
145
|
|
145
146
|
```rb
|
146
147
|
current_account #=> #<Account id=123 email="user@example.com">
|
147
148
|
current_account.email #=> "user@example.com"
|
148
149
|
```
|
149
150
|
|
150
|
-
If the account doesn't exist in the database, the session will be cleared and
|
151
|
-
login required.
|
152
|
-
|
153
151
|
Pass the configuration name to retrieve accounts belonging to other Rodauth
|
154
152
|
configurations:
|
155
153
|
|
@@ -157,6 +155,8 @@ configurations:
|
|
157
155
|
current_account(:admin)
|
158
156
|
```
|
159
157
|
|
158
|
+
This just delegates to the `#rails_account` method on the Rodauth object.
|
159
|
+
|
160
160
|
#### Custom account model
|
161
161
|
|
162
162
|
The `#current_account` method will try to infer the account model class from
|
@@ -248,6 +248,18 @@ Rails.application.routes.draw do
|
|
248
248
|
end
|
249
249
|
```
|
250
250
|
|
251
|
+
The current account can be retrieved via the `#rails_account` method:
|
252
|
+
|
253
|
+
```rb
|
254
|
+
# config/routes.rb
|
255
|
+
Rails.application.routes.draw do
|
256
|
+
# require user to be admin
|
257
|
+
constraints Rodauth::Rails.authenticated { |rodauth| rodauth.rails_account.admin? } do
|
258
|
+
# ...
|
259
|
+
end
|
260
|
+
end
|
261
|
+
```
|
262
|
+
|
251
263
|
You can specify the Rodauth configuration by passing the configuration name:
|
252
264
|
|
253
265
|
```rb
|
@@ -1097,16 +1109,15 @@ end
|
|
1097
1109
|
|
1098
1110
|
The recommended [Rodauth migration] stores possible account status values in a
|
1099
1111
|
separate table, and creates a foreign key on the accounts table, which ensures
|
1100
|
-
only a valid status value will be persisted.
|
1112
|
+
only a valid status value will be persisted. Unfortunately, this doesn't work
|
1113
|
+
when the database is restored from the schema file, in which case the account
|
1114
|
+
statuses table will be empty. This happens in tests by default, but it's also
|
1115
|
+
not unusual to do it in development.
|
1101
1116
|
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
To address this, rodauth-rails modifies the setup to store account status text
|
1107
|
-
directly in the accounts table. If you're worried about invalid status values
|
1108
|
-
creeping in, you may use enums instead. Alternatively, you can always go back
|
1109
|
-
to the setup recommended by Rodauth.
|
1117
|
+
To address this, rodauth-rails uses a `status` column without a separate table.
|
1118
|
+
If you're worried about invalid status values creeping in, you may use enums
|
1119
|
+
instead. Alternatively, you can always go back to the setup recommended by
|
1120
|
+
Rodauth.
|
1110
1121
|
|
1111
1122
|
```rb
|
1112
1123
|
# in the migration:
|
@@ -1122,14 +1133,13 @@ create_table :accounts do |t|
|
|
1122
1133
|
end
|
1123
1134
|
```
|
1124
1135
|
```diff
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
end
|
1136
|
+
class RodauthMain < Rodauth::Rails::Auth
|
1137
|
+
configure do
|
1138
|
+
# ...
|
1139
|
+
- account_status_column :status
|
1140
|
+
# ...
|
1141
|
+
end
|
1142
|
+
end
|
1133
1143
|
```
|
1134
1144
|
|
1135
1145
|
### Deadline values
|
@@ -2,6 +2,6 @@
|
|
2
2
|
create_table :account_active_session_keys, primary_key: [:account_id, :session_id] do |t|
|
3
3
|
t.references :account, foreign_key: true<%= primary_key_type(:type) %>
|
4
4
|
t.string :session_id
|
5
|
-
t.datetime :created_at, null: false, default:
|
6
|
-
t.datetime :last_use, null: false, default:
|
5
|
+
t.datetime :created_at, null: false, default: <%= current_timestamp %>
|
6
|
+
t.datetime :last_use, null: false, default: <%= current_timestamp %>
|
7
7
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Used by the audit logging feature
|
2
2
|
create_table :account_authentication_audit_logs<%= primary_key_type %> do |t|
|
3
3
|
t.references :account, foreign_key: true, null: false<%= primary_key_type(:type) %>
|
4
|
-
t.datetime :at, null: false, default:
|
4
|
+
t.datetime :at, null: false, default: <%= current_timestamp %>
|
5
5
|
t.text :message, null: false
|
6
6
|
<% case activerecord_adapter -%>
|
7
7
|
<% when "postgresql" -%>
|
@@ -9,10 +9,10 @@ create_table :accounts<%= primary_key_type %> do |t|
|
|
9
9
|
<% else -%>
|
10
10
|
t.string :email, null: false
|
11
11
|
<% end -%>
|
12
|
-
t.string :status, null: false, default:
|
12
|
+
t.string :status, null: false, default: 1
|
13
13
|
<% case activerecord_adapter -%>
|
14
14
|
<% when "postgresql", "sqlite3" -%>
|
15
|
-
t.index :email, unique: true, where: "status IN (
|
15
|
+
t.index :email, unique: true, where: "status IN (1, 2)"
|
16
16
|
<% else -%>
|
17
17
|
t.index :email, unique: true
|
18
18
|
<% end -%>
|
@@ -3,5 +3,5 @@ create_table :account_email_auth_keys<%= primary_key_type %> do |t|
|
|
3
3
|
t.foreign_key :accounts, column: :id
|
4
4
|
t.string :key, null: false
|
5
5
|
t.datetime :deadline, null: false
|
6
|
-
t.datetime :email_last_sent, null: false, default:
|
6
|
+
t.datetime :email_last_sent, null: false, default: <%= current_timestamp %>
|
7
7
|
end
|
@@ -3,5 +3,5 @@ create_table :account_otp_keys<%= primary_key_type %> do |t|
|
|
3
3
|
t.foreign_key :accounts, column: :id
|
4
4
|
t.string :key, null: false
|
5
5
|
t.integer :num_failures, null: false, default: 0
|
6
|
-
t.datetime :last_use, null: false, default:
|
6
|
+
t.datetime :last_use, null: false, default: <%= current_timestamp %>
|
7
7
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Used by the password expiration feature
|
2
2
|
create_table :account_password_change_times<%= primary_key_type %> do |t|
|
3
3
|
t.foreign_key :accounts, column: :id
|
4
|
-
t.datetime :changed_at, null: false, default:
|
4
|
+
t.datetime :changed_at, null: false, default: <%= current_timestamp %>
|
5
5
|
end
|
@@ -3,5 +3,5 @@ create_table :account_password_reset_keys<%= primary_key_type %> do |t|
|
|
3
3
|
t.foreign_key :accounts, column: :id
|
4
4
|
t.string :key, null: false
|
5
5
|
t.datetime :deadline, null: false
|
6
|
-
t.datetime :email_last_sent, null: false, default:
|
6
|
+
t.datetime :email_last_sent, null: false, default: <%= current_timestamp %>
|
7
7
|
end
|
@@ -4,5 +4,5 @@ create_table :account_sms_codes<%= primary_key_type %> do |t|
|
|
4
4
|
t.string :phone_number, null: false
|
5
5
|
t.integer :num_failures
|
6
6
|
t.string :code
|
7
|
-
t.datetime :code_issued_at, null: false, default:
|
7
|
+
t.datetime :code_issued_at, null: false, default: <%= current_timestamp %>
|
8
8
|
end
|
@@ -2,6 +2,6 @@
|
|
2
2
|
create_table :account_verification_keys<%= primary_key_type %> do |t|
|
3
3
|
t.foreign_key :accounts, column: :id
|
4
4
|
t.string :key, null: false
|
5
|
-
t.datetime :requested_at, null: false, default:
|
6
|
-
t.datetime :email_last_sent, null: false, default:
|
5
|
+
t.datetime :requested_at, null: false, default: <%= current_timestamp %>
|
6
|
+
t.datetime :email_last_sent, null: false, default: <%= current_timestamp %>
|
7
7
|
end
|
@@ -8,5 +8,5 @@ create_table :account_webauthn_keys, primary_key: [:account_id, :webauthn_id] do
|
|
8
8
|
t.string :webauthn_id
|
9
9
|
t.string :public_key, null: false
|
10
10
|
t.integer :sign_count, null: false
|
11
|
-
t.datetime :last_use, null: false, default:
|
11
|
+
t.datetime :last_use, null: false, default: <%= current_timestamp %>
|
12
12
|
end
|
@@ -63,6 +63,14 @@ module Rodauth
|
|
63
63
|
ERB.new(content, 0, "-").result(binding)
|
64
64
|
end
|
65
65
|
end
|
66
|
+
|
67
|
+
def current_timestamp
|
68
|
+
if ActiveRecord.version >= Gem::Version.new("5.0")
|
69
|
+
%(-> { "CURRENT_TIMESTAMP" })
|
70
|
+
else
|
71
|
+
%(OpenStruct.new(quoted_id: "CURRENT_TIMESTAMP"))
|
72
|
+
end
|
73
|
+
end
|
66
74
|
end
|
67
75
|
end
|
68
76
|
end
|
@@ -31,11 +31,8 @@ class RodauthMain < Rodauth::Rails::Auth
|
|
31
31
|
# Specify the controller used for view rendering and CSRF verification.
|
32
32
|
rails_controller { RodauthController }
|
33
33
|
|
34
|
-
# Store account status in
|
34
|
+
# Store account status in an integer column without foreign key constraint.
|
35
35
|
account_status_column :status
|
36
|
-
account_unverified_status_value "unverified"
|
37
|
-
account_open_status_value "verified"
|
38
|
-
account_closed_status_value "closed"
|
39
36
|
|
40
37
|
# Store password hash in a column instead of a separate table.
|
41
38
|
# account_password_hash_column :password_digest
|
@@ -8,41 +8,16 @@ module Rodauth
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
def rodauth(name = nil)
|
12
|
-
request.env.fetch ["rodauth", *name].join(".")
|
13
|
-
end
|
14
|
-
|
15
11
|
def current_account(name = nil)
|
16
|
-
|
17
|
-
|
12
|
+
rodauth(name).rails_account || rodauth(name).login_required
|
13
|
+
end
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
rodauth(name).clear_session
|
22
|
-
rodauth(name).login_required
|
23
|
-
end
|
15
|
+
def rodauth(name = nil)
|
16
|
+
request.env.fetch ["rodauth", *name].join(".")
|
24
17
|
end
|
25
18
|
|
26
19
|
private
|
27
20
|
|
28
|
-
def fetch_account(model, id, ¬_found)
|
29
|
-
if defined?(ActiveRecord::Base) && model < ActiveRecord::Base
|
30
|
-
begin
|
31
|
-
model.find(id)
|
32
|
-
rescue ActiveRecord::RecordNotFound
|
33
|
-
not_found.call
|
34
|
-
end
|
35
|
-
elsif defined?(Sequel::Model) && model < Sequel::Model
|
36
|
-
begin
|
37
|
-
model.with_pk!(id)
|
38
|
-
rescue Sequel::NoMatchingRow
|
39
|
-
not_found.call
|
40
|
-
end
|
41
|
-
else
|
42
|
-
fail Error, "unsupported model type: #{model}"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
21
|
def rodauth_response
|
47
22
|
res = catch(:halt) { return yield }
|
48
23
|
|
@@ -8,6 +8,17 @@ module Rodauth
|
|
8
8
|
feature.auth_cached_method :rails_controller_instance
|
9
9
|
end
|
10
10
|
|
11
|
+
def rails_account
|
12
|
+
account_from_session unless account
|
13
|
+
|
14
|
+
unless account
|
15
|
+
clear_session if logged_in?
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
@rails_account ||= instantiate_rails_account
|
20
|
+
end
|
21
|
+
|
11
22
|
# Reset Rails session to protect from session fixation attacks.
|
12
23
|
def clear_session
|
13
24
|
rails_controller_instance.reset_session
|
@@ -43,6 +54,16 @@ module Rodauth
|
|
43
54
|
|
44
55
|
private
|
45
56
|
|
57
|
+
def instantiate_rails_account
|
58
|
+
if defined?(ActiveRecord::Base) && rails_account_model < ActiveRecord::Base
|
59
|
+
rails_account_model.instantiate(account.stringify_keys)
|
60
|
+
elsif defined?(Sequel::Model) && rails_account_model < Sequel::Model
|
61
|
+
rails_account_model.load(account)
|
62
|
+
else
|
63
|
+
fail Error, "unsupported model type: #{rails_account_model}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
46
67
|
# Instances of the configured controller with current request's env hash.
|
47
68
|
def _rails_controller_instance
|
48
69
|
controller = rails_controller.new
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rodauth-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janko Marohnić
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -268,7 +268,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
268
|
- !ruby/object:Gem::Version
|
269
269
|
version: '0'
|
270
270
|
requirements: []
|
271
|
-
rubygems_version: 3.
|
271
|
+
rubygems_version: 3.3.3
|
272
272
|
signing_key:
|
273
273
|
specification_version: 4
|
274
274
|
summary: Provides Rails integration for Rodauth.
|