rodauth-rails 0.11.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +316 -218
  4. data/lib/generators/rodauth/templates/app/lib/rodauth_app.rb +8 -4
  5. data/lib/generators/rodauth/templates/app/models/account.rb +1 -0
  6. data/lib/generators/rodauth/templates/app/views/rodauth/_email_auth_request_form.html.erb +1 -1
  7. data/lib/generators/rodauth/templates/app/views/rodauth/_field.html.erb +2 -2
  8. data/lib/generators/rodauth/templates/app/views/rodauth/_field_error.html.erb +2 -2
  9. data/lib/generators/rodauth/templates/app/views/rodauth/_global_logout_field.html.erb +2 -2
  10. data/lib/generators/rodauth/templates/app/views/rodauth/_login_confirm_field.html.erb +3 -3
  11. data/lib/generators/rodauth/templates/app/views/rodauth/_login_display.html.erb +3 -3
  12. data/lib/generators/rodauth/templates/app/views/rodauth/_login_field.html.erb +3 -3
  13. data/lib/generators/rodauth/templates/app/views/rodauth/_login_form.html.erb +3 -3
  14. data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_footer.html.erb +2 -2
  15. data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_header.html.erb +2 -2
  16. data/lib/generators/rodauth/templates/app/views/rodauth/_login_hidden_field.html.erb +1 -1
  17. data/lib/generators/rodauth/templates/app/views/rodauth/_new_password_field.html.erb +3 -3
  18. data/lib/generators/rodauth/templates/app/views/rodauth/_otp_auth_code_field.html.erb +3 -3
  19. data/lib/generators/rodauth/templates/app/views/rodauth/_password_confirm_field.html.erb +3 -3
  20. data/lib/generators/rodauth/templates/app/views/rodauth/_password_field.html.erb +3 -3
  21. data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_code_field.html.erb +3 -3
  22. data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_codes_form.html.erb +4 -4
  23. data/lib/generators/rodauth/templates/app/views/rodauth/_sms_code_field.html.erb +3 -3
  24. data/lib/generators/rodauth/templates/app/views/rodauth/_sms_phone_field.html.erb +3 -3
  25. data/lib/generators/rodauth/templates/app/views/rodauth/_submit.html.erb +1 -1
  26. data/lib/generators/rodauth/templates/app/views/rodauth/add_recovery_codes.html.erb +2 -2
  27. data/lib/generators/rodauth/templates/app/views/rodauth/change_login.html.erb +3 -3
  28. data/lib/generators/rodauth/templates/app/views/rodauth/change_password.html.erb +3 -3
  29. data/lib/generators/rodauth/templates/app/views/rodauth/close_account.html.erb +2 -2
  30. data/lib/generators/rodauth/templates/app/views/rodauth/confirm_password.html.erb +1 -1
  31. data/lib/generators/rodauth/templates/app/views/rodauth/create_account.html.erb +4 -4
  32. data/lib/generators/rodauth/templates/app/views/rodauth/email_auth.html.erb +1 -1
  33. data/lib/generators/rodauth/templates/app/views/rodauth/logout.html.erb +2 -2
  34. data/lib/generators/rodauth/templates/app/views/rodauth/multi_phase_login.html.erb +1 -1
  35. data/lib/generators/rodauth/templates/app/views/rodauth/otp_auth.html.erb +1 -1
  36. data/lib/generators/rodauth/templates/app/views/rodauth/otp_disable.html.erb +2 -2
  37. data/lib/generators/rodauth/templates/app/views/rodauth/otp_setup.html.erb +9 -9
  38. data/lib/generators/rodauth/templates/app/views/rodauth/recovery_auth.html.erb +1 -1
  39. data/lib/generators/rodauth/templates/app/views/rodauth/remember.html.erb +5 -5
  40. data/lib/generators/rodauth/templates/app/views/rodauth/reset_password.html.erb +2 -2
  41. data/lib/generators/rodauth/templates/app/views/rodauth/reset_password_request.html.erb +2 -2
  42. data/lib/generators/rodauth/templates/app/views/rodauth/sms_auth.html.erb +1 -1
  43. data/lib/generators/rodauth/templates/app/views/rodauth/sms_confirm.html.erb +1 -1
  44. data/lib/generators/rodauth/templates/app/views/rodauth/sms_disable.html.erb +2 -2
  45. data/lib/generators/rodauth/templates/app/views/rodauth/sms_request.html.erb +1 -1
  46. data/lib/generators/rodauth/templates/app/views/rodauth/sms_setup.html.erb +2 -2
  47. data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_auth.html.erb +1 -1
  48. data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb +2 -2
  49. data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_manage.html.erb +6 -6
  50. data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account.html.erb +2 -2
  51. data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account_request.html.erb +1 -1
  52. data/lib/generators/rodauth/templates/app/views/rodauth/verify_account.html.erb +3 -3
  53. data/lib/generators/rodauth/templates/app/views/rodauth/verify_account_resend.html.erb +2 -2
  54. data/lib/generators/rodauth/templates/app/views/rodauth/verify_login_change.html.erb +1 -1
  55. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_auth.html.erb +7 -7
  56. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb +6 -6
  57. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb +7 -7
  58. data/lib/generators/rodauth/views_generator.rb +26 -4
  59. data/lib/rodauth/rails.rb +32 -13
  60. data/lib/rodauth/rails/auth.rb +9 -12
  61. data/lib/rodauth/rails/feature.rb +19 -230
  62. data/lib/rodauth/rails/feature/base.rb +62 -0
  63. data/lib/rodauth/rails/feature/callbacks.rb +65 -0
  64. data/lib/rodauth/rails/feature/csrf.rb +65 -0
  65. data/lib/rodauth/rails/feature/email.rb +30 -0
  66. data/lib/rodauth/rails/feature/instrumentation.rb +71 -0
  67. data/lib/rodauth/rails/feature/internal_request.rb +50 -0
  68. data/lib/rodauth/rails/feature/render.rb +48 -0
  69. data/lib/rodauth/rails/model.rb +101 -0
  70. data/lib/rodauth/rails/model/associations.rb +195 -0
  71. data/lib/rodauth/rails/railtie.rb +0 -5
  72. data/lib/rodauth/rails/tasks.rake +5 -5
  73. data/lib/rodauth/rails/version.rb +1 -1
  74. data/rodauth-rails.gemspec +4 -1
  75. metadata +56 -6
  76. data/lib/rodauth/rails/log_subscriber.rb +0 -34
@@ -1,5 +1,5 @@
1
- <%%= form_tag rodauth.sms_setup_path, method: :post do %>
2
- <%%= render "password_field" if rodauth.two_factor_modifications_require_password? %>
1
+ <%%= form_tag <%= rodauth %>.sms_setup_path, method: :post do %>
2
+ <%%= render "password_field" if <%= rodauth %>.two_factor_modifications_require_password? %>
3
3
  <%%= render "sms_phone_field" %>
4
4
  <%%= render "submit", value: "Setup SMS Backup Number" %>
5
5
  <%% end %>
@@ -1,5 +1,5 @@
1
1
  <ul>
2
- <%% rodauth.two_factor_auth_links.sort.each do |_, link, text| %>
2
+ <%% <%= rodauth %>.two_factor_auth_links.sort.each do |_, link, text| %>
3
3
  <li><%%= link_to text, link %></li>
4
4
  <%% end %>
5
5
  </ul>
@@ -1,4 +1,4 @@
1
- <%%= form_tag rodauth.two_factor_disable_path, method: :post do %>
2
- <%%= render "password_field" if rodauth.two_factor_modifications_require_password? %>
1
+ <%%= form_tag <%= rodauth %>.two_factor_disable_path, method: :post do %>
2
+ <%%= render "password_field" if <%= rodauth %>.two_factor_modifications_require_password? %>
3
3
  <%%= render "submit", value: "Remove All Multifactor Authentication Methods" %>
4
4
  <%% end %>
@@ -1,22 +1,22 @@
1
- <%% if rodauth.two_factor_setup_links.any? %>
1
+ <%% if <%= rodauth %>.two_factor_setup_links.any? %>
2
2
  <h2>Setup Multifactor Authentication</h2>
3
3
 
4
4
  <ul>
5
- <%% rodauth.two_factor_setup_links.sort.each do |_, link, text| %>
5
+ <%% <%= rodauth %>.two_factor_setup_links.sort.each do |_, link, text| %>
6
6
  <li><%%= link_to text, link %></li>
7
7
  <%% end %>
8
8
  </ul>
9
9
  <%% end %>
10
10
 
11
- <%% if rodauth.two_factor_remove_links.any? %>
11
+ <%% if <%= rodauth %>.two_factor_remove_links.any? %>
12
12
  <h2>Remove Multifactor Authentication</h2>
13
13
 
14
14
  <ul>
15
- <%% rodauth.two_factor_remove_links.sort.each do |_, link, text| %>
15
+ <%% <%= rodauth %>.two_factor_remove_links.sort.each do |_, link, text| %>
16
16
  <li><%%= link_to text, link %></li>
17
17
  <%% end %>
18
- <%% if rodauth.two_factor_remove_links.length > 1 %>
19
- <li><%%= link_to "Remove All Multifactor Authentication Methods", rodauth.two_factor_disable_path %></li>
18
+ <%% if <%= rodauth %>.two_factor_remove_links.length > 1 %>
19
+ <li><%%= link_to "Remove All Multifactor Authentication Methods", <%= rodauth %>.two_factor_disable_path %></li>
20
20
  <%% end %>
21
21
  </ul>
22
22
  <%% end %>
@@ -1,5 +1,5 @@
1
- <%%= form_tag rodauth.unlock_account_path, method: :post do %>
1
+ <%%= form_tag <%= rodauth %>.unlock_account_path, method: :post do %>
2
2
  <p>This account is currently locked out. You can unlock the account:</p>
3
- <%%= render "password_field" if rodauth.unlock_account_requires_password? %>
3
+ <%%= render "password_field" if <%= rodauth %>.unlock_account_requires_password? %>
4
4
  <%%= render "submit", value: "Unlock Account" %>
5
5
  <%% end %>
@@ -1,4 +1,4 @@
1
- <%%= form_tag rodauth.unlock_account_request_path, method: :post do %>
1
+ <%%= form_tag <%= rodauth %>.unlock_account_request_path, method: :post do %>
2
2
  <p>This account is currently locked out. You can request that the account be unlocked:</p>
3
3
  <%%= render "login_hidden_field" %>
4
4
  <%%= render "submit", value: "Request Account Unlock" %>
@@ -1,5 +1,5 @@
1
- <%%= form_tag rodauth.verify_account_path, method: :post do %>
2
- <%%= render "password_field" if rodauth.verify_account_set_password? %>
3
- <%%= render "password_confirm_field" if rodauth.verify_account_set_password? && rodauth.require_password_confirmation? %>
1
+ <%%= form_tag <%= rodauth %>.verify_account_path, method: :post do %>
2
+ <%%= render "password_field" if <%= rodauth %>.verify_account_set_password? %>
3
+ <%%= render "password_confirm_field" if <%= rodauth %>.verify_account_set_password? && <%= rodauth %>.require_password_confirmation? %>
4
4
  <%%= render "submit", value: "Verify Account" %>
5
5
  <%% end %>
@@ -1,6 +1,6 @@
1
- <%%= form_tag rodauth.verify_account_resend_path, method: :post do %>
1
+ <%%= form_tag <%= rodauth %>.verify_account_resend_path, method: :post do %>
2
2
  <p>If you no longer have the email to verify the account, you can request that it be resent to you:</p>
3
- <%% if params[rodauth.login_param] %>
3
+ <%% if params[<%= rodauth %>.login_param] %>
4
4
  <%%= render "login_hidden_field" %>
5
5
  <%% else %>
6
6
  <%%= render "login_field" %>
@@ -1,3 +1,3 @@
1
- <%%= form_tag rodauth.verify_login_change_path, method: :post do %>
1
+ <%%= form_tag <%= rodauth %>.verify_login_change_path, method: :post do %>
2
2
  <%%= render "submit", value: "Verify Login Change" %>
3
3
  <%% end %>
@@ -1,13 +1,13 @@
1
- <%% cred = rodauth.webauth_credential_options_for_get %>
1
+ <%% cred = <%= rodauth %>.webauth_credential_options_for_get %>
2
2
 
3
- <%%= form_tag rodauth.webauthn_auth_form_path, method: :post, id: "webauthn-auth-form", data: { credential_options: cred.as_json.to_json } do %>
4
- <%%= render "login_hidden_field" if params[rodauth.login_param] %>
5
- <%%= hidden_field_tag rodauth.webauthn_auth_challenge_param, cred.challenge %>
6
- <%%= hidden_field_tag rodauth.webauthn_auth_challenge_hmac_param, rodauth.compute_hmac(cred.challenge) %>
7
- <%%= text_field_tag rodauth.webauthn_auth_param, "", id: "webauthn-auth", aria: { hidden: "true" } %>
3
+ <%%= form_tag <%= rodauth %>.webauthn_auth_form_path, method: :post, id: "webauthn-auth-form", data: { credential_options: cred.as_json.to_json } do %>
4
+ <%%= render "login_hidden_field" if params[<%= rodauth %>.login_param] %>
5
+ <%%= hidden_field_tag <%= rodauth %>.webauthn_auth_challenge_param, cred.challenge %>
6
+ <%%= hidden_field_tag <%= rodauth %>.webauthn_auth_challenge_hmac_param, <%= rodauth %>.compute_hmac(cred.challenge) %>
7
+ <%%= text_field_tag <%= rodauth %>.webauthn_auth_param, "", id: "webauthn-auth", aria: { hidden: "true" } %>
8
8
  <div id="webauthn-auth-button">
9
9
  <%%= render "submit", value: "Authenticate Using WebAuthn" %>
10
10
  </div>
11
11
  <%% end %>
12
12
 
13
- <%%= javascript_include_tag rodauth.webauthn_auth_js_path %>
13
+ <%%= javascript_include_tag <%= rodauth %>.webauthn_auth_js_path %>
@@ -1,11 +1,11 @@
1
- <%%= form_tag rodauth.webauthn_remove_path, method: :post, id: "webauthn-remove-form" do %>
2
- <%%= render "password_field" if rodauth.two_factor_modifications_require_password? %>
3
- <fieldset class="form-group">
4
- <%% (usage = rodauth.account_webauthn_usage).each do |id, last_use| %>
1
+ <%%= form_tag <%= rodauth %>.webauthn_remove_path, method: :post, id: "webauthn-remove-form" do %>
2
+ <%%= render "password_field" if <%= rodauth %>.two_factor_modifications_require_password? %>
3
+ <fieldset class="form-group mb-3">
4
+ <%% (usage = <%= rodauth %>.account_webauthn_usage).each do |id, last_use| %>
5
5
  <div class="form-check">
6
- <%%= render "field", name: rodauth.webauthn_remove_param, id: "webauthn-remove-#{id}", type: :radio, class: "form-check-input", skip_error_message: true, value: id, required: false %>
6
+ <%%= render "field", name: <%= rodauth %>.webauthn_remove_param, id: "webauthn-remove-#{id}", type: :radio, class: "form-check-input", skip_error_message: true, value: id, required: false %>
7
7
  <%%= label_tag "webauthn-remove-#{id}", "Last use: #{last_use}", class: "form-check-label" %>
8
- <%%= render "field_error", name: rodauth.webauthn_remove_param if id == usage.keys.last %>
8
+ <%%= render "field_error", name: <%= rodauth %>.webauthn_remove_param if id == usage.keys.last %>
9
9
  </div>
10
10
  <%% end %>
11
11
  </fieldset>
@@ -1,13 +1,13 @@
1
- <%% cred = rodauth.new_webauthn_credential %>
1
+ <%% cred = <%= rodauth %>.new_webauthn_credential %>
2
2
 
3
- <%%= form_tag rodauth.webauthn_setup_path, method: :post, id: "webauthn-setup-form", data: { credential_options: cred.as_json.to_json } do %>
4
- <%%= hidden_field_tag rodauth.webauthn_setup_challenge_param, cred.challenge %>
5
- <%%= hidden_field_tag rodauth.webauthn_setup_challenge_hmac_param, rodauth.compute_hmac(cred.challenge) %>
6
- <%%= text_field_tag rodauth.webauthn_setup_param, "", id: "webauthn-setup", aria: { hidden: "true" } %>
7
- <%%= render "password_field" if rodauth.two_factor_modifications_require_password? %>
3
+ <%%= form_tag <%= rodauth %>.webauthn_setup_path, method: :post, id: "webauthn-setup-form", data: { credential_options: cred.as_json.to_json } do %>
4
+ <%%= hidden_field_tag <%= rodauth %>.webauthn_setup_challenge_param, cred.challenge %>
5
+ <%%= hidden_field_tag <%= rodauth %>.webauthn_setup_challenge_hmac_param, <%= rodauth %>.compute_hmac(cred.challenge) %>
6
+ <%%= text_field_tag <%= rodauth %>.webauthn_setup_param, "", id: "webauthn-setup", aria: { hidden: "true" } %>
7
+ <%%= render "password_field" if <%= rodauth %>.two_factor_modifications_require_password? %>
8
8
  <div id="webauthn-setup-button">
9
9
  <%%= render "submit", value: "Setup WebAuthn Authentication" %>
10
10
  </div>
11
11
  <%% end %>
12
12
 
13
- <%%= javascript_include_tag rodauth.webauthn_setup_js_path %>
13
+ <%%= javascript_include_tag <%= rodauth %>.webauthn_setup_js_path %>
@@ -18,9 +18,9 @@ module Rodauth
18
18
  desc: "Generates views for all Rodauth features",
19
19
  default: false
20
20
 
21
- class_option :directory, aliases: "-d", type: :string,
22
- desc: "The directory under app/views/* into which to create views",
23
- default: "rodauth"
21
+ class_option :name, aliases: "-n", type: :string,
22
+ desc: "The configuration name for which to generate views",
23
+ default: nil
24
24
 
25
25
  VIEWS = {
26
26
  login: %w[
@@ -112,9 +112,31 @@ module Rodauth
112
112
 
113
113
  views.each do |view|
114
114
  template "app/views/rodauth/#{view}.html.erb",
115
- "app/views/#{options[:directory].underscore}/#{view}.html.erb"
115
+ "app/views/#{directory}/#{view}.html.erb"
116
116
  end
117
117
  end
118
+
119
+ def directory
120
+ if controller.abstract?
121
+ fail Error, "no controller configured for configuration: #{configuration_name.inspect}"
122
+ end
123
+
124
+ controller.controller_path
125
+ end
126
+
127
+ def rodauth
128
+ "rodauth#{"(:#{configuration_name})" if configuration_name}"
129
+ end
130
+
131
+ def controller
132
+ rodauth = Rodauth::Rails.app.rodauth(configuration_name)
133
+ fail ArgumentError, "unknown rodauth configuration: #{configuration_name.inspect}" unless rodauth
134
+ rodauth.allocate.rails_controller
135
+ end
136
+
137
+ def configuration_name
138
+ options[:name]&.to_sym
139
+ end
118
140
  end
119
141
  end
120
142
  end
data/lib/rodauth/rails.rb CHANGED
@@ -9,28 +9,47 @@ module Rodauth
9
9
  # This allows the developer to avoid loading Rodauth at boot time.
10
10
  autoload :App, "rodauth/rails/app"
11
11
  autoload :Auth, "rodauth/rails/auth"
12
+ autoload :Model, "rodauth/rails/model"
12
13
 
13
14
  @app = nil
14
15
  @middleware = true
15
16
 
17
+ LOCK = Mutex.new
18
+
16
19
  class << self
17
- def rodauth(name = nil)
18
- url_options = ActionMailer::Base.default_url_options
20
+ def rodauth(name = nil, query: nil, form: nil, account: nil, **options)
21
+ auth_class = app.rodauth(name)
22
+
23
+ unless auth_class
24
+ fail ArgumentError, "undefined rodauth configuration: #{name.inspect}"
25
+ end
26
+
27
+ LOCK.synchronize do
28
+ unless auth_class.features.include?(:internal_request)
29
+ auth_class.configure { enable :internal_request }
30
+ warn "Rodauth::Rails.rodauth requires the internal_request feature to be enabled. For now it was enabled automatically, but this behaviour will be removed in version 1.0."
31
+ end
32
+ end
19
33
 
20
- scheme = url_options[:protocol] || "http"
21
- port = url_options[:port]
22
- port ||= Rack::Request::DEFAULT_PORTS[scheme] if Gem::Version.new(Rack.release) < Gem::Version.new("2.0")
23
- host = url_options[:host]
24
- host += ":#{port}" if port
34
+ if query || form
35
+ warn "The :query and :form keyword arguments for Rodauth::Rails.rodauth have been deprecated. Please use the :params argument supported by internal_request feature instead."
36
+ options[:params] = query || form
37
+ end
25
38
 
26
- rack_env = {
27
- "HTTP_HOST" => host,
28
- "rack.url_scheme" => scheme,
29
- }
39
+ if account
40
+ options[:account_id] = account.id
41
+ end
30
42
 
31
- scope = app.new(rack_env)
43
+ instance = auth_class.internal_request_eval(options) do
44
+ @account = account.attributes.symbolize_keys if account
45
+ self
46
+ end
47
+
48
+ instance
49
+ end
32
50
 
33
- scope.rodauth(name)
51
+ def model(name = nil, **options)
52
+ Rodauth::Rails::Model.new(app.rodauth(name), **options)
34
53
  end
35
54
 
36
55
  # routing constraint that requires authentication
@@ -6,20 +6,17 @@ module Rodauth
6
6
  # Base auth class that applies some default configuration and supports
7
7
  # multi-level inheritance.
8
8
  class Auth < Rodauth::Auth
9
- class << self
10
- attr_writer :features
11
- attr_writer :routes
12
- attr_accessor :configuration
13
- end
14
-
15
9
  def self.inherited(auth_class)
16
10
  super
17
- auth_class.roda_class = Rodauth::Rails.app
18
- auth_class.features = features.dup
19
- auth_class.routes = routes.dup
20
- auth_class.route_hash = route_hash.dup
21
- auth_class.configuration = configuration.clone
22
- auth_class.configuration.instance_variable_set(:@auth, auth_class)
11
+ superclass = self
12
+ auth_class.class_eval do
13
+ @roda_class = Rodauth::Rails.app
14
+ @features = superclass.features.clone
15
+ @routes = superclass.routes.clone
16
+ @route_hash = superclass.route_hash.clone
17
+ @configuration = superclass.instance_variable_get(:@configuration).clone
18
+ @configuration.instance_variable_set(:@auth, self)
19
+ end
23
20
  end
24
21
 
25
22
  # apply default configuration
@@ -1,234 +1,23 @@
1
1
  module Rodauth
2
2
  Feature.define(:rails) do
3
- depends :email_base
4
-
5
- # List of overridable methods.
6
- auth_methods(
7
- :rails_render,
8
- :rails_csrf_tag,
9
- :rails_csrf_param,
10
- :rails_csrf_token,
11
- :rails_check_csrf!,
12
- :rails_controller,
13
- )
14
-
15
- auth_cached_method :rails_controller_instance
16
-
17
- # Renders templates with layout. First tries to render a user-defined
18
- # template, otherwise falls back to Rodauth's template.
19
- def view(page, *)
20
- rails_render(action: page.tr("-", "_"), layout: true) ||
21
- rails_render(html: super.html_safe, layout: true)
22
- end
23
-
24
- # Renders templates without layout. First tries to render a user-defined
25
- # template or partial, otherwise falls back to Rodauth's template.
26
- def render(page)
27
- rails_render(partial: page.tr("-", "_"), layout: false) ||
28
- rails_render(action: page.tr("-", "_"), layout: false) ||
29
- super.html_safe
30
- end
31
-
32
- # Render Rails CSRF tags in Rodauth templates.
33
- def csrf_tag(*)
34
- rails_csrf_tag
35
- end
36
-
37
- # Verify Rails' authenticity token.
38
- def check_csrf
39
- rails_check_csrf!
40
- end
41
-
42
- # Have Rodauth call #check_csrf automatically.
43
- def check_csrf?
44
- true
45
- end
46
-
47
- # Reset Rails session to protect from session fixation attacks.
48
- def clear_session
49
- rails_controller_instance.reset_session
50
- end
51
-
52
- # Default the flash error key to Rails' default :alert.
53
- def flash_error_key
54
- :alert
55
- end
56
-
57
- # Evaluates the block in context of a Rodauth controller instance.
58
- def rails_controller_eval(&block)
59
- rails_controller_instance.instance_exec(&block)
60
- end
61
-
62
- def button(*)
63
- super.html_safe
64
- end
65
-
66
- delegate :rails_routes, :rails_request, to: :scope
67
-
68
- private
69
-
70
- # Runs controller callbacks and rescue handlers around Rodauth actions.
71
- def _around_rodauth(&block)
72
- result = nil
73
-
74
- rails_instrument_request do
75
- rails_controller_rescue do
76
- rails_controller_callbacks do
77
- result = catch(:halt) { super(&block) }
78
- end
79
- end
80
-
81
- result = handle_rails_controller_response(result)
82
- end
83
-
84
- throw :halt, result if result
85
- end
86
-
87
- # Handles controller rendering a response or setting response headers.
88
- def handle_rails_controller_response(result)
89
- if rails_controller_instance.performed?
90
- rails_controller_response
91
- elsif result
92
- result[1].merge!(rails_controller_instance.response.headers)
93
- result
94
- end
95
- end
96
-
97
- # Runs any #(before|around|after)_action controller callbacks.
98
- def rails_controller_callbacks
99
- # don't verify CSRF token as part of callbacks, Rodauth will do that
100
- rails_controller_forgery_protection { false }
101
-
102
- rails_controller_instance.run_callbacks(:process_action) do
103
- # turn the setting back to default so that form tags generate CSRF tags
104
- rails_controller_forgery_protection { rails_controller.allow_forgery_protection }
105
-
106
- yield
107
- end
108
- end
109
-
110
- # Runs any registered #rescue_from controller handlers.
111
- def rails_controller_rescue
112
- yield
113
- rescue Exception => exception
114
- rails_controller_instance.rescue_with_handler(exception) || raise
115
-
116
- unless rails_controller_instance.performed?
117
- raise Rodauth::Rails::Error, "rescue_from handler didn't write any response"
118
- end
119
- end
120
-
121
- def rails_instrument_request
122
- ActiveSupport::Notifications.instrument("start_processing.rodauth", rodauth: self)
123
- ActiveSupport::Notifications.instrument("process_request.rodauth", rodauth: self) do |payload|
124
- begin
125
- status, headers, body = yield
126
- payload[:status] = status || 404
127
- payload[:headers] = headers
128
- payload[:body] = body
129
- ensure
130
- rails_controller_instance.send(:append_info_to_payload, payload)
131
- end
132
- end
133
- end
134
-
135
- # Returns Roda response from controller response if set.
136
- def rails_controller_response
137
- controller_response = rails_controller_instance.response
138
-
139
- response.status = controller_response.status
140
- response.headers.merge! controller_response.headers
141
- response.write controller_response.body
142
-
143
- response.finish
144
- end
145
-
146
- # Create emails with ActionMailer which uses configured delivery method.
147
- def create_email_to(to, subject, body)
148
- Mailer.create_email(to: to, from: email_from, subject: "#{email_subject_prefix}#{subject}", body: body)
149
- end
150
-
151
- # Delivers the given email.
152
- def send_email(email)
153
- email.deliver_now
154
- end
155
-
156
- # Calls the Rails renderer, returning nil if a template is missing.
157
- def rails_render(*args)
158
- return if rails_api_controller?
159
-
160
- rails_controller_instance.render_to_string(*args)
161
- rescue ActionView::MissingTemplate
162
- nil
163
- end
164
-
165
- # Calls the controller to verify the authenticity token.
166
- def rails_check_csrf!
167
- rails_controller_instance.send(:verify_authenticity_token)
168
- end
169
-
170
- # Hidden tag with Rails CSRF token inserted into Rodauth templates.
171
- def rails_csrf_tag
172
- %(<input type="hidden" name="#{rails_csrf_param}" value="#{rails_csrf_token}">)
173
- end
174
-
175
- # The request parameter under which to send the Rails CSRF token.
176
- def rails_csrf_param
177
- rails_controller.request_forgery_protection_token
178
- end
179
-
180
- # The Rails CSRF token value inserted into Rodauth templates.
181
- def rails_csrf_token
182
- rails_controller_instance.send(:form_authenticity_token)
183
- end
184
-
185
- # allows/disables forgery protection
186
- def rails_controller_forgery_protection(&value)
187
- return if rails_api_controller?
188
-
189
- rails_controller_instance.allow_forgery_protection = value.call
190
- end
191
-
192
- # Instances of the configured controller with current request's env hash.
193
- def _rails_controller_instance
194
- controller = rails_controller.new
195
- prepare_rails_controller(controller, rails_request)
196
- controller
197
- end
198
-
199
- if ActionPack.version >= Gem::Version.new("5.0")
200
- def prepare_rails_controller(controller, rails_request)
201
- controller.set_request! rails_request
202
- controller.set_response! rails_controller.make_response!(rails_request)
203
- end
204
- else
205
- def prepare_rails_controller(controller, rails_request)
206
- controller.send(:set_response!, rails_request)
207
- controller.instance_variable_set(:@_request, rails_request)
208
- end
209
- end
210
-
211
- def rails_api_controller?
212
- defined?(ActionController::API) && rails_controller <= ActionController::API
213
- end
214
-
215
- def rails_controller
216
- if only_json? && Rodauth::Rails.api_only?
217
- ActionController::API
218
- else
219
- ActionController::Base
220
- end
221
- end
222
-
223
- # ActionMailer subclass for correct email delivering.
224
- class Mailer < ActionMailer::Base
225
- def create_email(**options)
226
- mail(**options)
227
- end
228
- end
3
+ # Assign feature and feature configuration to constants for introspection.
4
+ Rodauth::Rails::Feature = self
5
+ Rodauth::Rails::FeatureConfiguration = self.configuration
6
+
7
+ require "rodauth/rails/feature/base"
8
+ require "rodauth/rails/feature/callbacks"
9
+ require "rodauth/rails/feature/csrf"
10
+ require "rodauth/rails/feature/render"
11
+ require "rodauth/rails/feature/email"
12
+ require "rodauth/rails/feature/instrumentation"
13
+ require "rodauth/rails/feature/internal_request"
14
+
15
+ include Rodauth::Rails::Feature::Base
16
+ include Rodauth::Rails::Feature::Callbacks
17
+ include Rodauth::Rails::Feature::Csrf
18
+ include Rodauth::Rails::Feature::Render
19
+ include Rodauth::Rails::Feature::Email
20
+ include Rodauth::Rails::Feature::Instrumentation
21
+ include Rodauth::Rails::Feature::InternalRequest
229
22
  end
230
-
231
- # Assign feature and feature configuration to constants for introspection.
232
- Rails::Feature = FEATURES[:rails]
233
- Rails::FeatureConfiguration = FEATURES[:rails].configuration
234
23
  end