rodauth-rails 0.11.0 → 0.15.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 +40 -0
- data/README.md +316 -218
- data/lib/generators/rodauth/templates/app/lib/rodauth_app.rb +8 -4
- data/lib/generators/rodauth/templates/app/models/account.rb +1 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/_email_auth_request_form.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_field.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/_field_error.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/_global_logout_field.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_confirm_field.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_display.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_field.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_form.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_footer.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_header.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_hidden_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_new_password_field.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_otp_auth_code_field.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_password_confirm_field.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_password_field.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_code_field.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_codes_form.html.erb +4 -4
- data/lib/generators/rodauth/templates/app/views/rodauth/_sms_code_field.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_sms_phone_field.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_submit.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/add_recovery_codes.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/change_login.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/change_password.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/close_account.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/confirm_password.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/create_account.html.erb +4 -4
- data/lib/generators/rodauth/templates/app/views/rodauth/email_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/logout.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/multi_phase_login.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/otp_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/otp_disable.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/otp_setup.html.erb +9 -9
- data/lib/generators/rodauth/templates/app/views/rodauth/recovery_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/remember.html.erb +5 -5
- data/lib/generators/rodauth/templates/app/views/rodauth/reset_password.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/reset_password_request.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_confirm.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_disable.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_request.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_setup.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_manage.html.erb +6 -6
- data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account_request.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/verify_account.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/verify_account_resend.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/verify_login_change.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_auth.html.erb +7 -7
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb +6 -6
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb +7 -7
- data/lib/generators/rodauth/views_generator.rb +26 -4
- data/lib/rodauth/rails.rb +32 -13
- data/lib/rodauth/rails/auth.rb +9 -12
- data/lib/rodauth/rails/feature.rb +19 -230
- data/lib/rodauth/rails/feature/base.rb +62 -0
- data/lib/rodauth/rails/feature/callbacks.rb +65 -0
- data/lib/rodauth/rails/feature/csrf.rb +65 -0
- data/lib/rodauth/rails/feature/email.rb +30 -0
- data/lib/rodauth/rails/feature/instrumentation.rb +71 -0
- data/lib/rodauth/rails/feature/internal_request.rb +50 -0
- data/lib/rodauth/rails/feature/render.rb +48 -0
- data/lib/rodauth/rails/model.rb +101 -0
- data/lib/rodauth/rails/model/associations.rb +195 -0
- data/lib/rodauth/rails/railtie.rb +0 -5
- data/lib/rodauth/rails/tasks.rake +5 -5
- data/lib/rodauth/rails/version.rb +1 -1
- data/rodauth-rails.gemspec +4 -1
- metadata +56 -6
- data/lib/rodauth/rails/log_subscriber.rb +0 -34
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rodauth
|
2
|
+
module Rails
|
3
|
+
module Feature
|
4
|
+
module Base
|
5
|
+
def self.included(feature)
|
6
|
+
feature.auth_methods :rails_controller
|
7
|
+
feature.auth_cached_method :rails_controller_instance
|
8
|
+
end
|
9
|
+
|
10
|
+
# Reset Rails session to protect from session fixation attacks.
|
11
|
+
def clear_session
|
12
|
+
rails_controller_instance.reset_session
|
13
|
+
end
|
14
|
+
|
15
|
+
# Default the flash error key to Rails' default :alert.
|
16
|
+
def flash_error_key
|
17
|
+
:alert
|
18
|
+
end
|
19
|
+
|
20
|
+
# Evaluates the block in context of a Rodauth controller instance.
|
21
|
+
def rails_controller_eval(&block)
|
22
|
+
rails_controller_instance.instance_exec(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def rails_controller
|
26
|
+
if only_json? && Rodauth::Rails.api_only?
|
27
|
+
ActionController::API
|
28
|
+
else
|
29
|
+
ActionController::Base
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
delegate :rails_routes, :rails_request, to: :scope
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Instances of the configured controller with current request's env hash.
|
38
|
+
def _rails_controller_instance
|
39
|
+
controller = rails_controller.new
|
40
|
+
prepare_rails_controller(controller, rails_request)
|
41
|
+
controller
|
42
|
+
end
|
43
|
+
|
44
|
+
if ActionPack.version >= Gem::Version.new("5.0")
|
45
|
+
def prepare_rails_controller(controller, rails_request)
|
46
|
+
controller.set_request! rails_request
|
47
|
+
controller.set_response! rails_controller.make_response!(rails_request)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
def prepare_rails_controller(controller, rails_request)
|
51
|
+
controller.send(:set_response!, rails_request)
|
52
|
+
controller.instance_variable_set(:@_request, rails_request)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def rails_api_controller?
|
57
|
+
defined?(ActionController::API) && rails_controller <= ActionController::API
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Rodauth
|
2
|
+
module Rails
|
3
|
+
module Feature
|
4
|
+
module Callbacks
|
5
|
+
private
|
6
|
+
|
7
|
+
def _around_rodauth
|
8
|
+
rails_controller_around { super }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Runs controller callbacks and rescue handlers around Rodauth actions.
|
12
|
+
def rails_controller_around
|
13
|
+
result = nil
|
14
|
+
|
15
|
+
rails_controller_rescue do
|
16
|
+
rails_controller_callbacks do
|
17
|
+
result = catch(:halt) { yield }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
result = handle_rails_controller_response(result)
|
22
|
+
|
23
|
+
throw :halt, result if result
|
24
|
+
end
|
25
|
+
|
26
|
+
# Runs any #(before|around|after)_action controller callbacks.
|
27
|
+
def rails_controller_callbacks(&block)
|
28
|
+
rails_controller_instance.run_callbacks(:process_action, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Runs any registered #rescue_from controller handlers.
|
32
|
+
def rails_controller_rescue
|
33
|
+
yield
|
34
|
+
rescue Exception => exception
|
35
|
+
rails_controller_instance.rescue_with_handler(exception) || raise
|
36
|
+
|
37
|
+
unless rails_controller_instance.performed?
|
38
|
+
raise Rodauth::Rails::Error, "rescue_from handler didn't write any response"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Handles controller rendering a response or setting response headers.
|
43
|
+
def handle_rails_controller_response(result)
|
44
|
+
if rails_controller_instance.performed?
|
45
|
+
rails_controller_response
|
46
|
+
elsif result
|
47
|
+
result[1].merge!(rails_controller_instance.response.headers)
|
48
|
+
result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns Roda response from controller response if set.
|
53
|
+
def rails_controller_response
|
54
|
+
controller_response = rails_controller_instance.response
|
55
|
+
|
56
|
+
response.status = controller_response.status
|
57
|
+
response.headers.merge! controller_response.headers
|
58
|
+
response.write controller_response.body
|
59
|
+
|
60
|
+
response.finish
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Rodauth
|
2
|
+
module Rails
|
3
|
+
module Feature
|
4
|
+
module Csrf
|
5
|
+
def self.included(feature)
|
6
|
+
feature.auth_methods(
|
7
|
+
:rails_csrf_tag,
|
8
|
+
:rails_csrf_param,
|
9
|
+
:rails_csrf_token,
|
10
|
+
:rails_check_csrf!,
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Render Rails CSRF tags in Rodauth templates.
|
15
|
+
def csrf_tag(*)
|
16
|
+
rails_csrf_tag
|
17
|
+
end
|
18
|
+
|
19
|
+
# Verify Rails' authenticity token.
|
20
|
+
def check_csrf
|
21
|
+
rails_check_csrf!
|
22
|
+
end
|
23
|
+
|
24
|
+
# Have Rodauth call #check_csrf automatically.
|
25
|
+
def check_csrf?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def rails_controller_callbacks
|
32
|
+
return super if rails_api_controller?
|
33
|
+
|
34
|
+
# don't verify CSRF token as part of callbacks, Rodauth will do that
|
35
|
+
rails_controller_instance.allow_forgery_protection = false
|
36
|
+
super do
|
37
|
+
# turn the setting back to default so that form tags generate CSRF tags
|
38
|
+
rails_controller_instance.allow_forgery_protection = rails_controller.allow_forgery_protection
|
39
|
+
yield
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Calls the controller to verify the authenticity token.
|
44
|
+
def rails_check_csrf!
|
45
|
+
rails_controller_instance.send(:verify_authenticity_token)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Hidden tag with Rails CSRF token inserted into Rodauth templates.
|
49
|
+
def rails_csrf_tag
|
50
|
+
%(<input type="hidden" name="#{rails_csrf_param}" value="#{rails_csrf_token}">)
|
51
|
+
end
|
52
|
+
|
53
|
+
# The request parameter under which to send the Rails CSRF token.
|
54
|
+
def rails_csrf_param
|
55
|
+
rails_controller.request_forgery_protection_token
|
56
|
+
end
|
57
|
+
|
58
|
+
# The Rails CSRF token value inserted into Rodauth templates.
|
59
|
+
def rails_csrf_token
|
60
|
+
rails_controller_instance.send(:form_authenticity_token)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rodauth
|
2
|
+
module Rails
|
3
|
+
module Feature
|
4
|
+
module Email
|
5
|
+
def self.included(feature)
|
6
|
+
feature.depends :email_base
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# Create emails with ActionMailer which uses configured delivery method.
|
12
|
+
def create_email_to(to, subject, body)
|
13
|
+
Mailer.create_email(to: to, from: email_from, subject: "#{email_subject_prefix}#{subject}", body: body)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Delivers the given email.
|
17
|
+
def send_email(email)
|
18
|
+
email.deliver_now
|
19
|
+
end
|
20
|
+
|
21
|
+
# ActionMailer subclass for correct email delivering.
|
22
|
+
class Mailer < ActionMailer::Base
|
23
|
+
def create_email(**options)
|
24
|
+
mail(**options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Rodauth
|
2
|
+
module Rails
|
3
|
+
module Feature
|
4
|
+
module Instrumentation
|
5
|
+
private
|
6
|
+
|
7
|
+
def _around_rodauth
|
8
|
+
rails_instrument_request { super }
|
9
|
+
end
|
10
|
+
|
11
|
+
def redirect(*)
|
12
|
+
rails_instrument_redirection { super }
|
13
|
+
end
|
14
|
+
|
15
|
+
def rails_render(*)
|
16
|
+
render_output = nil
|
17
|
+
rails_controller_instance.view_runtime = rails_controller_instance.send(:cleanup_view_runtime) do
|
18
|
+
Benchmark.ms { render_output = super }
|
19
|
+
end
|
20
|
+
render_output
|
21
|
+
end
|
22
|
+
|
23
|
+
def rails_instrument_request
|
24
|
+
request = rails_request
|
25
|
+
|
26
|
+
raw_payload = {
|
27
|
+
controller: scope.class.superclass.name,
|
28
|
+
action: "call",
|
29
|
+
request: request,
|
30
|
+
params: request.filtered_parameters,
|
31
|
+
headers: request.headers,
|
32
|
+
format: request.format.ref,
|
33
|
+
method: request.request_method,
|
34
|
+
path: request.fullpath
|
35
|
+
}
|
36
|
+
|
37
|
+
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
|
38
|
+
|
39
|
+
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
40
|
+
begin
|
41
|
+
result = catch(:halt) { yield }
|
42
|
+
|
43
|
+
response = ActionDispatch::Response.new *(result || [404, {}, []])
|
44
|
+
payload[:response] = response
|
45
|
+
payload[:status] = response.status
|
46
|
+
|
47
|
+
throw :halt, result if result
|
48
|
+
rescue => error
|
49
|
+
payload[:status] = ActionDispatch::ExceptionWrapper.status_code_for_exception(error.class.name)
|
50
|
+
raise
|
51
|
+
ensure
|
52
|
+
rails_controller_eval { append_info_to_payload(payload) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def rails_instrument_redirection
|
58
|
+
ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: rails_request) do |payload|
|
59
|
+
result = catch(:halt) { yield }
|
60
|
+
|
61
|
+
response = ActionDispatch::Response.new(*result)
|
62
|
+
payload[:status] = response.status
|
63
|
+
payload[:location] = response.filtered_location
|
64
|
+
|
65
|
+
throw :halt, result
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Rodauth
|
2
|
+
module Rails
|
3
|
+
module Feature
|
4
|
+
module InternalRequest
|
5
|
+
def post_configure
|
6
|
+
super
|
7
|
+
return unless internal_request?
|
8
|
+
|
9
|
+
self.class.define_singleton_method(:internal_request) do |route, opts = {}, &blk|
|
10
|
+
url_options = ::Rails.application.config.action_mailer.default_url_options || {}
|
11
|
+
|
12
|
+
scheme = url_options[:protocol]
|
13
|
+
port = url_options[:port]
|
14
|
+
port||= Rack::Request::DEFAULT_PORTS[scheme] if Rack.release < "2"
|
15
|
+
host = url_options[:host]
|
16
|
+
host_with_port = host && port ? "#{host}:#{port}" : host
|
17
|
+
|
18
|
+
env = {
|
19
|
+
"HTTP_HOST" => host_with_port,
|
20
|
+
"rack.url_scheme" => scheme,
|
21
|
+
"SERVER_NAME" => host,
|
22
|
+
"SERVER_PORT" => port,
|
23
|
+
}.compact
|
24
|
+
|
25
|
+
opts = opts.merge(env: env) { |k, v1, v2| v2.merge(v1) }
|
26
|
+
|
27
|
+
super(route, opts, &blk)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def rails_controller_around
|
34
|
+
return yield if internal_request?
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def rails_instrument_request
|
39
|
+
return yield if internal_request?
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def rails_instrument_redirection
|
44
|
+
return yield if internal_request?
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Rodauth
|
2
|
+
module Rails
|
3
|
+
module Feature
|
4
|
+
module Render
|
5
|
+
def self.included(feature)
|
6
|
+
feature.auth_methods :rails_render
|
7
|
+
end
|
8
|
+
|
9
|
+
# Renders templates with layout. First tries to render a user-defined
|
10
|
+
# template, otherwise falls back to Rodauth's template.
|
11
|
+
def view(page, *)
|
12
|
+
rails_render(action: page.tr("-", "_"), layout: true) ||
|
13
|
+
rails_render(html: super.html_safe, layout: true)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Renders templates without layout. First tries to render a user-defined
|
17
|
+
# template or partial, otherwise falls back to Rodauth's template.
|
18
|
+
def render(page)
|
19
|
+
rails_render(partial: page.tr("-", "_"), layout: false) ||
|
20
|
+
rails_render(action: page.tr("-", "_"), layout: false) ||
|
21
|
+
super.html_safe
|
22
|
+
end
|
23
|
+
|
24
|
+
def button(*)
|
25
|
+
super.html_safe
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Calls the Rails renderer, returning nil if a template is missing.
|
31
|
+
def rails_render(*args)
|
32
|
+
return if rails_api_controller?
|
33
|
+
|
34
|
+
rails_controller_instance.render_to_string(*args)
|
35
|
+
rescue ActionView::MissingTemplate
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Only look up template formats that the current request is accepting.
|
40
|
+
def _rails_controller_instance
|
41
|
+
controller = super
|
42
|
+
controller.formats = rails_request.formats.map(&:ref).compact
|
43
|
+
controller
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Rodauth
|
2
|
+
module Rails
|
3
|
+
class Model < Module
|
4
|
+
require "rodauth/rails/model/associations"
|
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
|
+
feature_associations.each do |association|
|
50
|
+
define_association(model, **association)
|
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
|
+
model.public_send type, name, scope,
|
78
|
+
class_name: associated_model.name,
|
79
|
+
foreign_key: foreign_key,
|
80
|
+
dependent: :destroy,
|
81
|
+
inverse_of: :account,
|
82
|
+
**options,
|
83
|
+
**association_options(name)
|
84
|
+
end
|
85
|
+
|
86
|
+
def feature_associations
|
87
|
+
Rodauth::Rails::Model::Associations.call(rodauth)
|
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
|
100
|
+
end
|
101
|
+
end
|