rodauth-rails 1.1.0 → 1.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.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.
|