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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8643f8a912963b78be7d03a815b813688304f4e4b2777a1fdade807f79a4c712
4
- data.tar.gz: 31aadb16115fc826750b7d160dcb572ae9c24255d99a7c640abef5fd157bd319
3
+ metadata.gz: f93b80457e9c6e9ea6083974e05514de8e605b0f9a4015fe765ffc1c4059c5ee
4
+ data.tar.gz: '06877735d144b893b1a7a08f125e34316196679e47038112c6df215e352268c6'
5
5
  SHA512:
6
- metadata.gz: dd27d717b3a01f0b5f43e346c0cd839e2703ee0db44f7503eb6ce80f7cffd4493f4ed9e208db474af56af536f675444170f16a6d47435e1f4ce2af38a7487916
7
- data.tar.gz: bbac5b7492751e76886a5bcd275af78aada1026d2cd95ddee732817e8d7a5a154f559e5c27ce08606d444a1ffd4640f8522744bd13939f6491288d57c986fea0
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
- * [Adding Authentication in Rails with Rodauth](https://janko.io/adding-authentication-in-rails-with-rodauth/)
19
- * [Adding Multifactor Authentication in Rails with Rodauth](https://janko.io/adding-multifactor-authentication-in-rails-with-rodauth/)
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
- Unfortunately, this doesn't work when the database is restored from the schema
1103
- file, in which case the account statuses table will be empty. This happens in
1104
- tests by default, but it's also commonly done in development.
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
- configure do
1126
- # ...
1127
- - account_status_column :status
1128
- - account_unverified_status_value "unverified"
1129
- - account_open_status_value "verified"
1130
- - account_closed_status_value "closed"
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: -> { "CURRENT_TIMESTAMP" }
6
- t.datetime :last_use, null: false, default: -> { "CURRENT_TIMESTAMP" }
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: -> { "CURRENT_TIMESTAMP" }
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: "unverified"
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 ('unverified', 'verified')"
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: -> { "CURRENT_TIMESTAMP" }
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: -> { "CURRENT_TIMESTAMP" }
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: -> { "CURRENT_TIMESTAMP" }
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: -> { "CURRENT_TIMESTAMP" }
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: -> { "CURRENT_TIMESTAMP" }
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: -> { "CURRENT_TIMESTAMP" }
6
- t.datetime :email_last_sent, null: false, default: -> { "CURRENT_TIMESTAMP" }
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: -> { "CURRENT_TIMESTAMP" }
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 a text column.
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
@@ -1,3 +1,4 @@
1
1
  class Account < ApplicationRecord
2
2
  include Rodauth::Rails.model
3
+ enum :status, unverified: 1, verified: 2, closed: 3
3
4
  end
@@ -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
- model = rodauth(name).rails_account_model
17
- id = rodauth(name).session_value
12
+ rodauth(name).rails_account || rodauth(name).login_required
13
+ end
18
14
 
19
- @current_account ||= {}
20
- @current_account[name] ||= fetch_account(model, id) do
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, &not_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
@@ -1,5 +1,5 @@
1
1
  module Rodauth
2
2
  module Rails
3
- VERSION = "1.1.0"
3
+ VERSION = "1.2.0"
4
4
  end
5
5
  end
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.1.0
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-01-16 00:00:00.000000000 Z
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.2.15
271
+ rubygems_version: 3.3.3
272
272
  signing_key:
273
273
  specification_version: 4
274
274
  summary: Provides Rails integration for Rodauth.