rodauth-rails 1.1.0 โ†’ 1.2.2

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: 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.