rodauth-rails 1.4.0 → 1.4.1

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: 8b5fb22e3d8c84eafedffa3463b4e0ecdf481189b8b48adc2d31005f86251d87
4
- data.tar.gz: 142e229b7c9a9b078f773f99885117268e759df2eb49e5252ce21754dcf61763
3
+ metadata.gz: b4cdb91c64a071bc9cb7895a98e7c07ddc047f1c201b65d255523bda906644dd
4
+ data.tar.gz: 3b318fc66015b00e437b77fe650c5cf8c2af9c843b3b4e1f4890237ae9df261b
5
5
  SHA512:
6
- metadata.gz: b1c30c5b1714a80466ad3775720be50d2f02b8d06d016638e6d1ebfb7515cd6060463b975f9810977fe74eb7330ce12b9c6ff81839e41b2e4044615c606ca7bb
7
- data.tar.gz: 4a532058b27cfc07d2ed234457b880a609c7b35186db976a547be2e83d1c18df7ac72402a9d52155819f2d97edb63b0e7742e908b6cb3ed1b40c2deae18e7e2e
6
+ metadata.gz: ddee77b54a991b2699ed5bc60fd5b1d07cc79fda56be237628f3cac400d848cbab42feaba49bfa27eb63c5fb9709fa64edea1a6b0b2c2b98123df4965d38455f
7
+ data.tar.gz: 2b08a11971c028ac6a45110a2265030ed074bbb2d03a46fff58f6ba673476abdae975032d2589dd2121134998e935dcb1ddb4f72056eefd7caa562a34552cd54
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 1.4.1 (2022-05-08)
2
+
3
+ * Deprecate `Rodauth::Rails::Model` constant (@janko)
4
+
5
+ * Remove `Rodauth::Rails::Auth#associations` in favour of new association registration API (@janko)
6
+
7
+ * Extract model mixin into the rodauth-model gem (@janko)
8
+
1
9
  ## 1.4.0 (2022-05-04)
2
10
 
3
11
  * Move association definitions to `#associations` Rodauth method, allowing external features to extend them (@janko)
data/README.md CHANGED
@@ -484,9 +484,9 @@ end
484
484
 
485
485
  ## Model
486
486
 
487
- The `Rodauth::Rails::Model` mixin can be included into the account model, which
488
- defines a password attribute and associations for tables used by enabled
489
- authentication features.
487
+ The [rodauth-model] gem provides a `Rodauth::Model` mixin that can be included
488
+ into the account model, which defines a password attribute and associations for
489
+ tables used by enabled authentication features.
490
490
 
491
491
  ```rb
492
492
  class Account < ApplicationRecord
@@ -494,11 +494,9 @@ class Account < ApplicationRecord
494
494
  end
495
495
  ```
496
496
 
497
- ### Password attribute
498
-
499
- Regardless of whether you're storing the password hash in a column in the
500
- accounts table, or in a separate table, the `#password` attribute can be used
501
- to set or clear the password hash.
497
+ The password attribute can be used to set or clear the password hash. It
498
+ handles both storing the password hash in a column on the accounts table, or in
499
+ a separate table.
502
500
 
503
501
  ```rb
504
502
  account = Account.create!(email: "user@example.com", password: "secret")
@@ -514,132 +512,14 @@ account.password = nil # clears password hash
514
512
  account.password_hash #=> nil
515
513
  ```
516
514
 
517
- Note that the password attribute doesn't come with validations, making it
518
- unsuitable for forms. It was primarily intended to allow easily creating
519
- accounts in development console and in tests.
520
-
521
- ### Associations
522
-
523
- The `Rodauth::Rails::Model` mixin defines associations for Rodauth tables
524
- associated to the accounts table:
515
+ The associations are defined for tables used by enabled authentication features:
525
516
 
526
517
  ```rb
527
518
  account.remember_key #=> #<Account::RememberKey> (record from `account_remember_keys` table)
528
519
  account.active_session_keys #=> [#<Account::ActiveSessionKey>,...] (records from `account_active_session_keys` table)
529
520
  ```
530
521
 
531
- You can also reference the associated models directly:
532
-
533
- ```rb
534
- # model referencing the `account_authentication_audit_logs` table
535
- Account::AuthenticationAuditLog.where(message: "login").group(:account_id)
536
- ```
537
-
538
- The associated models define the inverse `belongs_to :account` association:
539
-
540
- ```rb
541
- Account::ActiveSessionKey.includes(:account).map(&:account)
542
- ```
543
-
544
- Here is an example of using associations to create a method that returns
545
- whether the account has multifactor authentication enabled:
546
-
547
- ```rb
548
- class Account < ApplicationRecord
549
- include Rodauth::Rails.model
550
-
551
- def mfa_enabled?
552
- otp_key || (sms_code && sms_code.num_failures.nil?) || recovery_codes.any?
553
- end
554
- end
555
- ```
556
-
557
- Here is another example of creating a query scope that selects accounts with
558
- multifactor authentication enabled:
559
-
560
- ```rb
561
- class Account < ApplicationRecord
562
- include Rodauth::Rails.model
563
-
564
- scope :otp_setup, -> { where(otp_key: OtpKey.all) }
565
- scope :sms_codes_setup, -> { where(sms_code: SmsCode.where(num_failures: nil)) }
566
- scope :recovery_codes_setup, -> { where(recovery_codes: RecoveryCode.all) }
567
- scope :mfa_enabled, -> { merge(otp_setup.or(sms_codes_setup).or(recovery_codes_setup)) }
568
- end
569
- ```
570
-
571
- #### Association reference
572
-
573
- Below is a list of all associations defined depending on the features loaded:
574
-
575
- | Feature | Association | Type | Model | Table (default) |
576
- | :------ | :---------- | :--- | :---- | :---- |
577
- | account_expiration | `:activity_time` | `has_one` | `ActivityTime` | `account_activity_times` |
578
- | active_sessions | `:active_session_keys` | `has_many` | `ActiveSessionKey` | `account_active_session_keys` |
579
- | audit_logging | `:authentication_audit_logs` | `has_many` | `AuthenticationAuditLog` | `account_authentication_audit_logs` |
580
- | disallow_password_reuse | `:previous_password_hashes` | `has_many` | `PreviousPasswordHash` | `account_previous_password_hashes` |
581
- | email_auth | `:email_auth_key` | `has_one` | `EmailAuthKey` | `account_email_auth_keys` |
582
- | jwt_refresh | `:jwt_refresh_keys` | `has_many` | `JwtRefreshKey` | `account_jwt_refresh_keys` |
583
- | lockout | `:lockout` | `has_one` | `Lockout` | `account_lockouts` |
584
- | lockout | `:login_failure` | `has_one` | `LoginFailure` | `account_login_failures` |
585
- | otp | `:otp_key` | `has_one` | `OtpKey` | `account_otp_keys` |
586
- | password_expiration | `:password_change_time` | `has_one` | `PasswordChangeTime` | `account_password_change_times` |
587
- | recovery_codes | `:recovery_codes` | `has_many` | `RecoveryCode` | `account_recovery_codes` |
588
- | remember | `:remember_key` | `has_one` | `RememberKey` | `account_remember_keys` |
589
- | reset_password | `:password_reset_key` | `has_one` | `PasswordResetKey` | `account_password_reset_keys` |
590
- | single_session | `:session_key` | `has_one` | `SessionKey` | `account_session_keys` |
591
- | sms_codes | `:sms_code` | `has_one` | `SmsCode` | `account_sms_codes` |
592
- | verify_account | `:verification_key` | `has_one` | `VerificationKey` | `account_verification_keys` |
593
- | verify_login_change | `:login_change_key` | `has_one` | `LoginChangeKey` | `account_login_change_keys` |
594
- | webauthn | `:webauthn_keys` | `has_many` | `WebauthnKey` | `account_webauthn_keys` |
595
- | webauthn | `:webauthn_user_id` | `has_one` | `WebauthnUserId` | `account_webauthn_user_ids` |
596
-
597
- Note that some Rodauth tables use composite primary keys, which Active Record
598
- doesn't support out of the box. For associations to work properly, you might
599
- need to add the [composite_primary_keys] gem to your Gemfile.
600
-
601
- #### Association options
602
-
603
- By default, all associations except for audit logs have `dependent: :destroy`
604
- set, to allow for easy deletion of account records in the console. You can use
605
- `:association_options` to modify global or per-association options:
606
-
607
- ```rb
608
- # don't auto-delete associations when account model is deleted
609
- Rodauth::Rails.model(association_options: { dependent: nil })
610
-
611
- # require authentication audit logs to be eager loaded before retrieval
612
- Rodauth::Rails.model(association_options: -> (name) {
613
- { strict_loading: true } if name == :authentication_audit_logs
614
- })
615
- ```
616
-
617
- #### Extending Associations
618
-
619
- External features can extend the list of associations with their own
620
- definitions, which the model mixin will pick up and declare the new associations
621
- on the model.
622
-
623
- ```rb
624
- # lib/rodauth/features/foo.rb
625
- module Rodauth
626
- Feature.define(:foo, :Foo) do
627
- auth_value_method :foo_table, :account_foos
628
- auth_value_method :foo_id_column, :id
629
-
630
- def associations
631
- list = super
632
- list << {
633
- name: :foo, # will define `Account::Foo` model
634
- type: :one, # or :many
635
- table: foo_table,
636
- foreign_key: foo_id_column
637
- }
638
- list
639
- end
640
- end
641
- end
642
- ```
522
+ See the [rodauth-model] documentation for more details.
643
523
 
644
524
  ## Multiple configurations
645
525
 
@@ -1304,8 +1184,8 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
1304
1184
  [account_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/account_expiration_rdoc.html
1305
1185
  [simple_ldap_authenticator]: https://github.com/jeremyevans/simple_ldap_authenticator
1306
1186
  [internal_request]: http://rodauth.jeremyevans.net/rdoc/files/doc/internal_request_rdoc.html
1307
- [composite_primary_keys]: https://github.com/composite-primary-keys/composite_primary_keys
1308
1187
  [path_class_methods]: https://rodauth.jeremyevans.net/rdoc/files/doc/path_class_methods_rdoc.html
1309
1188
  [account types]: https://github.com/janko/rodauth-rails/wiki/Account-Types
1310
1189
  [custom mailer worker]: https://github.com/janko/rodauth-rails/wiki/Custom-Mailer-Worker
1311
1190
  [Turbo]: https://turbo.hotwired.dev/
1191
+ [rodauth-model]: https://github.com/janko/rodauth-model
@@ -11,7 +11,6 @@ module Rodauth
11
11
  require "rodauth/rails/feature/email"
12
12
  require "rodauth/rails/feature/instrumentation"
13
13
  require "rodauth/rails/feature/internal_request"
14
- require "rodauth/rails/feature/associations"
15
14
 
16
15
  include Rodauth::Rails::Feature::Base
17
16
  include Rodauth::Rails::Feature::Callbacks
@@ -20,6 +19,5 @@ module Rodauth
20
19
  include Rodauth::Rails::Feature::Email
21
20
  include Rodauth::Rails::Feature::Instrumentation
22
21
  include Rodauth::Rails::Feature::InternalRequest
23
- include Rodauth::Rails::Feature::Associations
24
22
  end
25
23
  end
@@ -1,101 +1,6 @@
1
1
  module Rodauth
2
2
  module Rails
3
- class Model < Module
4
- ASSOCIATION_TYPES = { one: :has_one, many: :has_many }
5
-
6
- def initialize(auth_class, association_options: {})
7
- @auth_class = auth_class
8
- @association_options = association_options
9
-
10
- define_methods
11
- end
12
-
13
- def included(model)
14
- fail Rodauth::Rails::Error, "must be an Active Record model" unless model < ActiveRecord::Base
15
-
16
- define_associations(model)
17
- end
18
-
19
- private
20
-
21
- def define_methods
22
- rodauth = @auth_class.allocate.freeze
23
-
24
- attr_reader :password
25
-
26
- define_method(:password=) do |password|
27
- @password = password
28
- password_hash = rodauth.send(:password_hash, password) if password
29
- set_password_hash(password_hash)
30
- end
31
-
32
- define_method(:set_password_hash) do |password_hash|
33
- if rodauth.account_password_hash_column
34
- public_send(:"#{rodauth.account_password_hash_column}=", password_hash)
35
- else
36
- if password_hash
37
- record = self.password_hash || build_password_hash
38
- record.public_send(:"#{rodauth.password_hash_column}=", password_hash)
39
- else
40
- self.password_hash&.mark_for_destruction
41
- end
42
- end
43
- end
44
- end
45
-
46
- def define_associations(model)
47
- define_password_hash_association(model) unless rodauth.account_password_hash_column
48
-
49
- rodauth.associations.each do |association|
50
- define_association(model, **association, type: ASSOCIATION_TYPES.fetch(association[:type]))
51
- end
52
- end
53
-
54
- def define_password_hash_association(model)
55
- password_hash_id_column = rodauth.password_hash_id_column
56
- scope = -> { select(password_hash_id_column) } if rodauth.send(:use_database_authentication_functions?)
57
-
58
- define_association model,
59
- type: :has_one,
60
- name: :password_hash,
61
- table: rodauth.password_hash_table,
62
- foreign_key: password_hash_id_column,
63
- scope: scope,
64
- autosave: true
65
- end
66
-
67
- def define_association(model, type:, name:, table:, foreign_key:, scope: nil, **options)
68
- associated_model = Class.new(model.superclass)
69
- associated_model.table_name = table
70
- associated_model.belongs_to :account,
71
- class_name: model.name,
72
- foreign_key: foreign_key,
73
- inverse_of: name
74
-
75
- model.const_set(name.to_s.singularize.camelize, associated_model)
76
-
77
- unless name == :authentication_audit_logs
78
- dependent = type == :has_many ? :delete_all : :delete
79
- end
80
-
81
- model.public_send type, name, scope,
82
- class_name: associated_model.name,
83
- foreign_key: foreign_key,
84
- dependent: dependent,
85
- inverse_of: :account,
86
- **options,
87
- **association_options(name)
88
- end
89
-
90
- def association_options(name)
91
- options = @association_options
92
- options = options.call(name) if options.respond_to?(:call)
93
- options || {}
94
- end
95
-
96
- def rodauth
97
- @auth_class.allocate
98
- end
99
- end
3
+ Model = Rodauth::Model
4
+ deprecate_constant :Model
100
5
  end
101
6
  end
@@ -1,5 +1,5 @@
1
1
  module Rodauth
2
2
  module Rails
3
- VERSION = "1.4.0"
3
+ VERSION = "1.4.1"
4
4
  end
5
5
  end
data/lib/rodauth/rails.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "rodauth/rails/version"
2
2
  require "rodauth/rails/railtie"
3
+ require "rodauth/model"
3
4
 
4
5
  module Rodauth
5
6
  module Rails
@@ -43,7 +44,7 @@ module Rodauth
43
44
  end
44
45
 
45
46
  def model(name = nil, **options)
46
- Rodauth::Rails::Model.new(app.rodauth!(name), **options)
47
+ Rodauth::Model.new(app.rodauth!(name), **options)
47
48
  end
48
49
 
49
50
  # routing constraint that requires authentication
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.add_dependency "rodauth", "~> 2.23"
21
21
  spec.add_dependency "roda", "~> 3.55"
22
22
  spec.add_dependency "sequel-activerecord_connection", "~> 1.1"
23
+ spec.add_dependency "rodauth-model", "~> 0.1"
23
24
  spec.add_dependency "tilt"
24
25
  spec.add_dependency "bcrypt"
25
26
 
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.0
4
+ version: 1.4.1
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-05-04 00:00:00.000000000 Z
11
+ date: 2022-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -72,6 +72,20 @@ dependencies:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: '1.1'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rodauth-model
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.1'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.1'
75
89
  - !ruby/object:Gem::Dependency
76
90
  name: tilt
77
91
  requirement: !ruby/object:Gem::Requirement
@@ -264,7 +278,6 @@ files:
264
278
  - lib/rodauth/rails/auth.rb
265
279
  - lib/rodauth/rails/controller_methods.rb
266
280
  - lib/rodauth/rails/feature.rb
267
- - lib/rodauth/rails/feature/associations.rb
268
281
  - lib/rodauth/rails/feature/base.rb
269
282
  - lib/rodauth/rails/feature/callbacks.rb
270
283
  - lib/rodauth/rails/feature/csrf.rb
@@ -1,54 +0,0 @@
1
- module Rodauth
2
- module Rails
3
- module Feature
4
- module Associations
5
- def associations
6
- list = []
7
-
8
- features.each do |feature|
9
- case feature
10
- when :remember
11
- list << { name: :remember_key, type: :one, table: remember_table, foreign_key: remember_id_column }
12
- when :verify_account
13
- list << { name: :verification_key, type: :one, table: verify_account_table, foreign_key: verify_account_id_column }
14
- when :reset_password
15
- list << { name: :password_reset_key, type: :one, table: reset_password_table, foreign_key: reset_password_id_column }
16
- when :verify_login_change
17
- list << { name: :login_change_key, type: :one, table: verify_login_change_table, foreign_key: verify_login_change_id_column }
18
- when :lockout
19
- list << { name: :lockout, type: :one, table: account_lockouts_table, foreign_key: account_lockouts_id_column }
20
- list << { name: :login_failure, type: :one, table: account_login_failures_table, foreign_key: account_login_failures_id_column }
21
- when :email_auth
22
- list << { name: :email_auth_key, type: :one, table: email_auth_table, foreign_key: email_auth_id_column }
23
- when :account_expiration
24
- list << { name: :activity_time, type: :one, table: account_activity_table, foreign_key: account_activity_id_column }
25
- when :active_sessions
26
- list << { name: :active_session_keys, type: :many, table: active_sessions_table, foreign_key: active_sessions_account_id_column }
27
- when :audit_logging
28
- list << { name: :authentication_audit_logs, type: :many, table: audit_logging_table, foreign_key: audit_logging_account_id_column }
29
- when :disallow_password_reuse
30
- list << { name: :previous_password_hashes, type: :many, table: previous_password_hash_table, foreign_key: previous_password_account_id_column }
31
- when :jwt_refresh
32
- list << { name: :jwt_refresh_keys, type: :many, table: jwt_refresh_token_table, foreign_key: jwt_refresh_token_account_id_column }
33
- when :password_expiration
34
- list << { name: :password_change_time, type: :one, table: password_expiration_table, foreign_key: password_expiration_id_column }
35
- when :single_session
36
- list << { name: :session_key, type: :one, table: single_session_table, foreign_key: single_session_id_column }
37
- when :otp
38
- list << { name: :otp_key, type: :one, table: otp_keys_table, foreign_key: otp_keys_id_column }
39
- when :sms_codes
40
- list << { name: :sms_code, type: :one, table: sms_codes_table, foreign_key: sms_id_column }
41
- when :recovery_codes
42
- list << { name: :recovery_codes, type: :many, table: recovery_codes_table, foreign_key: recovery_codes_id_column }
43
- when :webauthn
44
- list << { name: :webauthn_user_id, type: :one, table: webauthn_user_ids_table, foreign_key: webauthn_user_ids_account_id_column }
45
- list << { name: :webauthn_keys, type: :many, table: webauthn_keys_table, foreign_key: webauthn_keys_account_id_column }
46
- end
47
- end
48
-
49
- list
50
- end
51
- end
52
- end
53
- end
54
- end