rodauth-rails 1.1.0 โ†’ 1.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8643f8a912963b78be7d03a815b813688304f4e4b2777a1fdade807f79a4c712
4
- data.tar.gz: 31aadb16115fc826750b7d160dcb572ae9c24255d99a7c640abef5fd157bd319
3
+ metadata.gz: 402cbf2f62d93eae97353a3aed436ce742b41421e880176649a7271c5516b39c
4
+ data.tar.gz: ca035e7a60c54b4e1b6f2a42ea6405f43811146141c30c5a6d14fd7c0600669e
5
5
  SHA512:
6
- metadata.gz: dd27d717b3a01f0b5f43e346c0cd839e2703ee0db44f7503eb6ce80f7cffd4493f4ed9e208db474af56af536f675444170f16a6d47435e1f4ce2af38a7487916
7
- data.tar.gz: bbac5b7492751e76886a5bcd275af78aada1026d2cd95ddee732817e8d7a5a154f559e5c27ce08606d444a1ffd4640f8522744bd13939f6491288d57c986fea0
6
+ metadata.gz: 888fd37f380d6f4896b544374c6681445dbb0e90d692dd7c0239aa043c9d6c0cb2aaafa9b47f271cd6cc70ad3a6c88693c988901aed1e5bc0f77ed5c92cb4ab1
7
+ data.tar.gz: 48fe23bfbd3d78c3378ab47ecf92ffd7f87fe768f996764a71b8534a2cab8731ebc080998b03ea9a78f1fecd949548782ca8e38dc17c56fa2606ea11f7a7fbe9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 1.2.2 (2022-02-22)
2
+
3
+ * Fix flash messages not being preserved through consecutive redirects (@janko)
4
+
5
+ ## 1.2.1 (2022-02-19)
6
+
7
+ * Change `accounts.status` column type from string to integer (@zhongsheng)
8
+
9
+ ## 1.2.0 (2022-02-11)
10
+
11
+ * Work around Active Record 4.2 not supporting procs for literal SQL column default (@janko)
12
+
13
+ * Avoid re-fetching the account in `#current_account` when it has already been fetched by Rodauth (@janko)
14
+
15
+ * Extract `#current_account` helper functionality into `#rails_account` Rodauth method (@janko)
16
+
17
+ * Use default account status values in generated configuration, with enum on `Account` model (@janko)
18
+
1
19
  ## 1.1.0 (2022-01-16)
2
20
 
3
21
  * Automatically route the path prefix in `r.rodauth` if one has been set (@janko)
data/README.md CHANGED
@@ -4,7 +4,7 @@ Provides Rails integration for the [Rodauth] authentication framework.
4
4
 
5
5
  ## Resources
6
6
 
7
- Useful links:
7
+ ๐Ÿ”— Useful links:
8
8
 
9
9
  * [Rodauth documentation](http://rodauth.jeremyevans.net/documentation.html)
10
10
  * [Rails demo](https://github.com/janko/rodauth-demo-rails)
@@ -12,11 +12,15 @@ Useful links:
12
12
  * [OmniAuth guide](https://github.com/janko/rodauth-rails/wiki/OmniAuth)
13
13
  * [Testing guide](https://github.com/janko/rodauth-rails/wiki/Testing)
14
14
 
15
- Articles:
15
+ ๐ŸŽฅ Screencasts:
16
+
17
+ * [Rails Authentication with Rodauth](https://www.youtube.com/watch?v=2hDpNikacf0)
18
+
19
+ ๐Ÿ“š Articles:
16
20
 
17
21
  * [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/)
22
+ * [Rails Authentication with Rodauth](https://janko.io/adding-authentication-in-rails-with-rodauth/)
23
+ * [Multifactor Authentication in Rails with Rodauth](https://janko.io/adding-multifactor-authentication-in-rails-with-rodauth/)
20
24
  * [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
25
 
22
26
  ## Why Rodauth?
@@ -140,16 +144,14 @@ authentication experience, and the forms use [Bootstrap] markup.
140
144
  ### Current account
141
145
 
142
146
  The `#current_account` method is defined in controllers and views, which
143
- returns the model instance of the currently logged in account.
147
+ returns the model instance of the currently logged in account. If the account
148
+ doesn't exist in the database, the session will be cleared.
144
149
 
145
150
  ```rb
146
151
  current_account #=> #<Account id=123 email="user@example.com">
147
152
  current_account.email #=> "user@example.com"
148
153
  ```
149
154
 
150
- If the account doesn't exist in the database, the session will be cleared and
151
- login required.
152
-
153
155
  Pass the configuration name to retrieve accounts belonging to other Rodauth
154
156
  configurations:
155
157
 
@@ -157,6 +159,8 @@ configurations:
157
159
  current_account(:admin)
158
160
  ```
159
161
 
162
+ This just delegates to the `#rails_account` method on the Rodauth object.
163
+
160
164
  #### Custom account model
161
165
 
162
166
  The `#current_account` method will try to infer the account model class from
@@ -248,6 +252,18 @@ Rails.application.routes.draw do
248
252
  end
249
253
  ```
250
254
 
255
+ The current account can be retrieved via the `#rails_account` method:
256
+
257
+ ```rb
258
+ # config/routes.rb
259
+ Rails.application.routes.draw do
260
+ # require user to be admin
261
+ constraints Rodauth::Rails.authenticated { |rodauth| rodauth.rails_account.admin? } do
262
+ # ...
263
+ end
264
+ end
265
+ ```
266
+
251
267
  You can specify the Rodauth configuration by passing the configuration name:
252
268
 
253
269
  ```rb
@@ -1097,16 +1113,15 @@ end
1097
1113
 
1098
1114
  The recommended [Rodauth migration] stores possible account status values in a
1099
1115
  separate table, and creates a foreign key on the accounts table, which ensures
1100
- only a valid status value will be persisted.
1116
+ only a valid status value will be persisted. Unfortunately, this doesn't work
1117
+ when the database is restored from the schema file, in which case the account
1118
+ statuses table will be empty. This happens in tests by default, but it's also
1119
+ not unusual to do it in development.
1101
1120
 
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.
1121
+ To address this, rodauth-rails uses a `status` column without a separate table.
1122
+ If you're worried about invalid status values creeping in, you may use enums
1123
+ instead. Alternatively, you can always go back to the setup recommended by
1124
+ Rodauth.
1110
1125
 
1111
1126
  ```rb
1112
1127
  # in the migration:
@@ -1122,14 +1137,13 @@ create_table :accounts do |t|
1122
1137
  end
1123
1138
  ```
1124
1139
  ```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
1140
+ class RodauthMain < Rodauth::Rails::Auth
1141
+ configure do
1142
+ # ...
1143
+ - account_status_column :status
1144
+ # ...
1145
+ end
1146
+ end
1133
1147
  ```
1134
1148
 
1135
1149
  ### 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.integer :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,8 +8,7 @@ module Rodauth
8
8
  end
9
9
 
10
10
  def self.configure(app)
11
- app.before { request.flash } # load flash
12
- app.after { request.commit_flash } # save flash
11
+ app.after { request.commit_flash }
13
12
  end
14
13
 
15
14
  module InstanceMethods
@@ -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
@@ -40,7 +40,7 @@ module Rodauth
40
40
  begin
41
41
  result = catch(:halt) { yield }
42
42
 
43
- response = ActionDispatch::Response.new *(result || [404, {}, []])
43
+ response = ActionDispatch::Response.new(*(result || [404, {}, []]))
44
44
  payload[:response] = response
45
45
  payload[:status] = response.status
46
46
 
@@ -1,5 +1,5 @@
1
1
  module Rodauth
2
2
  module Rails
3
- VERSION = "1.1.0"
3
+ VERSION = "1.2.2"
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.2
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-22 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.