rodauth-rails 0.17.0 → 1.0.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -0
  3. data/README.md +224 -548
  4. data/lib/generators/rodauth/install_generator.rb +40 -35
  5. data/lib/generators/rodauth/migration/base.erb +8 -2
  6. data/lib/generators/rodauth/migration_generator.rb +9 -2
  7. data/lib/generators/rodauth/templates/INSTRUCTIONS +40 -0
  8. data/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb +36 -19
  9. data/lib/generators/rodauth/templates/app/misc/rodauth_app.rb +38 -0
  10. data/lib/generators/rodauth/templates/app/{lib/rodauth_app.rb → misc/rodauth_main.rb} +10 -53
  11. data/lib/generators/rodauth/templates/app/views/rodauth/_email_auth_request_form.html.erb +7 -4
  12. data/lib/generators/rodauth/templates/app/views/rodauth/_login_form.html.erb +26 -9
  13. data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_footer.html.erb +7 -6
  14. data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_header.html.erb +3 -3
  15. data/lib/generators/rodauth/templates/app/views/rodauth/add_recovery_codes.html.erb +7 -5
  16. data/lib/generators/rodauth/templates/app/views/rodauth/change_login.html.erb +29 -6
  17. data/lib/generators/rodauth/templates/app/views/rodauth/change_password.html.erb +29 -6
  18. data/lib/generators/rodauth/templates/app/views/rodauth/close_account.html.erb +15 -4
  19. data/lib/generators/rodauth/templates/app/views/rodauth/confirm_password.html.erb +13 -4
  20. data/lib/generators/rodauth/templates/app/views/rodauth/create_account.html.erb +37 -7
  21. data/lib/generators/rodauth/templates/app/views/rodauth/email_auth.html.erb +7 -3
  22. data/lib/generators/rodauth/templates/app/views/rodauth/login.html.erb +5 -3
  23. data/lib/generators/rodauth/templates/app/views/rodauth/logout.html.erb +16 -4
  24. data/lib/generators/rodauth/templates/app/views/rodauth/multi_phase_login.html.erb +5 -3
  25. data/lib/generators/rodauth/templates/app/views/rodauth/otp_auth.html.erb +17 -4
  26. data/lib/generators/rodauth/templates/app/views/rodauth/otp_disable.html.erb +15 -4
  27. data/lib/generators/rodauth/templates/app/views/rodauth/otp_setup.html.erb +30 -10
  28. data/lib/generators/rodauth/templates/app/views/rodauth/recovery_auth.html.erb +13 -4
  29. data/lib/generators/rodauth/templates/app/views/rodauth/recovery_codes.html.erb +15 -1
  30. data/lib/generators/rodauth/templates/app/views/rodauth/remember.html.erb +14 -9
  31. data/lib/generators/rodauth/templates/app/views/rodauth/reset_password.html.erb +21 -5
  32. data/lib/generators/rodauth/templates/app/views/rodauth/reset_password_request.html.erb +19 -9
  33. data/lib/generators/rodauth/templates/app/views/rodauth/sms_auth.html.erb +17 -4
  34. data/lib/generators/rodauth/templates/app/views/rodauth/sms_confirm.html.erb +17 -4
  35. data/lib/generators/rodauth/templates/app/views/rodauth/sms_disable.html.erb +15 -4
  36. data/lib/generators/rodauth/templates/app/views/rodauth/sms_request.html.erb +7 -3
  37. data/lib/generators/rodauth/templates/app/views/rodauth/sms_setup.html.erb +25 -5
  38. data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_auth.html.erb +5 -3
  39. data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb +15 -4
  40. data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_manage.html.erb +17 -15
  41. data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account.html.erb +17 -5
  42. data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account_request.html.erb +11 -5
  43. data/lib/generators/rodauth/templates/app/views/rodauth/verify_account.html.erb +23 -5
  44. data/lib/generators/rodauth/templates/app/views/rodauth/verify_account_resend.html.erb +19 -9
  45. data/lib/generators/rodauth/templates/app/views/rodauth/verify_login_change.html.erb +7 -3
  46. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_auth.html.erb +13 -9
  47. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb +21 -9
  48. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb +21 -9
  49. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/email_auth.text.erb +1 -1
  50. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password.text.erb +1 -1
  51. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/unlock_account.text.erb +1 -1
  52. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_account.text.erb +1 -1
  53. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb +3 -3
  54. data/lib/generators/rodauth/views_generator.rb +55 -93
  55. data/lib/rodauth/rails/app.rb +5 -4
  56. data/lib/rodauth/rails/auth.rb +1 -16
  57. data/lib/rodauth/rails/controller_methods.rb +1 -1
  58. data/lib/rodauth/rails/feature/csrf.rb +15 -4
  59. data/lib/rodauth/rails/feature/internal_request.rb +22 -20
  60. data/lib/rodauth/rails/feature/render.rb +9 -1
  61. data/lib/rodauth/rails/railtie.rb +4 -2
  62. data/lib/rodauth/rails/tasks.rake +2 -2
  63. data/lib/rodauth/rails/version.rb +1 -1
  64. data/lib/rodauth/rails.rb +16 -20
  65. data/rodauth-rails.gemspec +2 -2
  66. metadata +9 -23
  67. data/lib/generators/rodauth/templates/app/views/rodauth/_field.html.erb +0 -10
  68. data/lib/generators/rodauth/templates/app/views/rodauth/_field_error.html.erb +0 -3
  69. data/lib/generators/rodauth/templates/app/views/rodauth/_global_logout_field.html.erb +0 -6
  70. data/lib/generators/rodauth/templates/app/views/rodauth/_login_confirm_field.html.erb +0 -4
  71. data/lib/generators/rodauth/templates/app/views/rodauth/_login_display.html.erb +0 -4
  72. data/lib/generators/rodauth/templates/app/views/rodauth/_login_field.html.erb +0 -4
  73. data/lib/generators/rodauth/templates/app/views/rodauth/_login_hidden_field.html.erb +0 -1
  74. data/lib/generators/rodauth/templates/app/views/rodauth/_new_password_field.html.erb +0 -4
  75. data/lib/generators/rodauth/templates/app/views/rodauth/_otp_auth_code_field.html.erb +0 -8
  76. data/lib/generators/rodauth/templates/app/views/rodauth/_password_confirm_field.html.erb +0 -4
  77. data/lib/generators/rodauth/templates/app/views/rodauth/_password_field.html.erb +0 -4
  78. data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_code_field.html.erb +0 -4
  79. data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_codes_form.html.erb +0 -6
  80. data/lib/generators/rodauth/templates/app/views/rodauth/_sms_code_field.html.erb +0 -8
  81. data/lib/generators/rodauth/templates/app/views/rodauth/_sms_phone_field.html.erb +0 -8
  82. data/lib/generators/rodauth/templates/app/views/rodauth/_submit.html.erb +0 -3
@@ -1,9 +1,19 @@
1
- <%%= form_tag <%= rodauth %>.verify_account_resend_path, method: :post do %>
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] %>
4
- <%%= render "login_hidden_field" %>
5
- <%% else %>
6
- <%%= render "login_field" %>
7
- <%% end %>
8
- <%%= render "submit", value: "Send Verification Login Again" %>
9
- <%% end %>
1
+ <% content_for :title, rodauth.resend_verify_account_page_title %>
2
+
3
+ <%= form_with url: rodauth.verify_account_resend_path, method: :post, data: { turbo: false } do |form| %>
4
+ <%== rodauth.verify_account_resend_explanatory_text %>
5
+
6
+ <% if params[rodauth.login_param] %>
7
+ <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
8
+ <% else %>
9
+ <div class="form-group mb-3">
10
+ <%= form.label "login", rodauth.login_label, class: "form-label" %>
11
+ <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.login_param)}", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
12
+ <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "invalid-feedback", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <div class="form-group mb-3">
17
+ <%= form.submit rodauth.verify_account_resend_button, class: "btn btn-primary" %>
18
+ </div>
19
+ <% end %>
@@ -1,3 +1,7 @@
1
- <%%= form_tag <%= rodauth %>.verify_login_change_path, method: :post do %>
2
- <%%= render "submit", value: "Verify Login Change" %>
3
- <%% end %>
1
+ <% content_for :title, rodauth.verify_login_change_page_title %>
2
+
3
+ <%= form_with url: rodauth.verify_login_change_path, method: :post, data: { turbo: false } do |form| %>
4
+ <div class="form-group mb-3">
5
+ <%= form.submit rodauth.verify_login_change_button, class: "btn btn-primary" %>
6
+ </div>
7
+ <% end %>
@@ -1,13 +1,17 @@
1
- <%% cred = <%= rodauth %>.webauth_credential_options_for_get %>
1
+ <% content_for :title, rodauth.webauthn_auth_page_title %>
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
+ <% cred = rodauth.webauth_credential_options_for_get %>
4
+
5
+ <%= form_with url: rodauth.webauthn_auth_form_path, method: :post, id: "webauthn-auth-form", data: { credential_options: cred.as_json.to_json, turbo: false } do |form| %>
6
+ <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
7
+ <%= form.hidden_field rodauth.webauthn_auth_challenge_param, value: cred.challenge %>
8
+ <%= form.hidden_field rodauth.webauthn_auth_challenge_hmac_param, value: rodauth.compute_hmac(cred.challenge) %>
9
+ <%= form.text_field rodauth.webauthn_auth_param, value: "", id: "webauthn-auth", aria: { hidden: "true" } %>
8
10
  <div id="webauthn-auth-button">
9
- <%%= render "submit", value: "Authenticate Using WebAuthn" %>
11
+ <div class="form-group mb-3">
12
+ <%= form.submit rodauth.webauthn_auth_button, class: "btn btn-primary" %>
13
+ </div>
10
14
  </div>
11
- <%% end %>
15
+ <% end %>
12
16
 
13
- <%%= javascript_include_tag <%= rodauth %>.webauthn_auth_js_path %>
17
+ <%= javascript_include_tag rodauth.webauthn_auth_js_path %>
@@ -1,13 +1,25 @@
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? %>
1
+ <% content_for :title, rodauth.webauthn_remove_page_title %>
2
+
3
+ <%= form_with url: rodauth.webauthn_remove_path, method: :post, id: "webauthn-remove-form", data: { turbo: false } do |form| %>
4
+ <% if rodauth.two_factor_modifications_require_password? %>
5
+ <div class="form-group mb-3">
6
+ <%= form.label "password", rodauth.password_label, class: "form-label" %>
7
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
8
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
9
+ </div>
10
+ <% end %>
11
+
3
12
  <fieldset class="form-group mb-3">
4
- <%% (usage = <%= rodauth %>.account_webauthn_usage).each do |id, last_use| %>
13
+ <% (usage = rodauth.account_webauthn_usage).each do |id, last_use| %>
5
14
  <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 %>
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 %>
15
+ <%= form.radio_button rodauth.webauthn_remove_param, id, id: "webauthn-remove-#{id}", class: "form-check-input #{"is-invalid" if rodauth.field_error(rodauth.webauthn_remove_param)}", aria: ({ invalid: true, describedby: "webauthn_remove_error_message" } if rodauth.field_error(rodauth.webauthn_remove_param)) %>
16
+ <%= form.label "webauthn-remove-#{id}", "Last use: #{last_use}", class: "form-check-label" %>
17
+ <%= content_tag(:span, rodauth.field_error(rodauth.webauthn_remove_param), class: "invalid-feedback", id: "webauthn_remove_error_message") if rodauth.field_error(rodauth.webauthn_remove_param) && id == usage.keys.last %>
9
18
  </div>
10
- <%% end %>
19
+ <% end %>
11
20
  </fieldset>
12
- <%%= render "submit", value: "Remove WebAuthn Authenticator" %>
13
- <%% end %>
21
+
22
+ <div class="form-group mb-3">
23
+ <%= form.submit rodauth.webauthn_remove_button, class: "btn btn-primary" %>
24
+ </div>
25
+ <% end %>
@@ -1,13 +1,25 @@
1
- <%% cred = <%= rodauth %>.new_webauthn_credential %>
1
+ <% content_for :title, rodauth.webauthn_setup_page_title %>
2
+
3
+ <% cred = rodauth.new_webauthn_credential %>
4
+
5
+ <%= form_with url: rodauth.webauthn_setup_path, method: :post, id: "webauthn-setup-form", data: { credential_options: cred.as_json.to_json, turbo: false } do |form| %>
6
+ <%= form.hidden_field rodauth.webauthn_setup_challenge_param, value: cred.challenge %>
7
+ <%= form.hidden_field rodauth.webauthn_setup_challenge_hmac_param, value: rodauth.compute_hmac(cred.challenge) %>
8
+ <%= form.text_field rodauth.webauthn_setup_param, value: "", id: "webauthn-setup", aria: { hidden: "true" } %>
9
+
10
+ <% if rodauth.two_factor_modifications_require_password? %>
11
+ <div class="form-group mb-3">
12
+ <%= form.label "password", rodauth.password_label, class: "form-label" %>
13
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "form-control #{"is-invalid" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
14
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "invalid-feedback", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
15
+ </div>
16
+ <% end %>
2
17
 
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
18
  <div id="webauthn-setup-button">
9
- <%%= render "submit", value: "Setup WebAuthn Authentication" %>
19
+ <div class="form-group mb-3">
20
+ <%= form.submit rodauth.webauthn_setup_button, class: "btn btn-primary" %>
21
+ </div>
10
22
  </div>
11
- <%% end %>
23
+ <% end %>
12
24
 
13
- <%%= javascript_include_tag <%= rodauth %>.webauthn_setup_js_path %>
25
+ <%= javascript_include_tag rodauth.webauthn_setup_js_path %>
@@ -1,5 +1,5 @@
1
1
  Someone has requested a login link for the account with this email
2
2
  address. If you did not request a login link, please ignore this
3
3
  message. If you requested a login link, please go to
4
- <%%= @email_link %>
4
+ <%= @email_link %>
5
5
  to login to this account.
@@ -1,5 +1,5 @@
1
1
  Someone has requested a password reset for the account with this email
2
2
  address. If you did not request a password reset, please ignore this
3
3
  message. If you requested a password reset, please go to
4
- <%%= @email_link %>
4
+ <%= @email_link %>
5
5
  to reset the password for the account.
@@ -1,5 +1,5 @@
1
1
  Someone has requested that the account with this email be unlocked.
2
2
  If you did not request the unlocking of this account, please ignore this
3
3
  message. If you requested the unlocking of this account, please go to
4
- <%%= @email_link %>
4
+ <%= @email_link %>
5
5
  to unlock this account.
@@ -1,4 +1,4 @@
1
1
  Someone has created an account with this email address. If you did not create
2
2
  this account, please ignore this message. If you created this account, please go to
3
- <%%= @email_link %>
3
+ <%= @email_link %>
4
4
  to verify the account.
@@ -1,10 +1,10 @@
1
1
  Someone with an account has requested their login be changed to this email address:
2
2
 
3
- Old email: <%%= @old_login %>
3
+ Old email: <%= @old_login %>
4
4
 
5
- New email: <%%= @new_login %>
5
+ New email: <%= @new_login %>
6
6
 
7
7
  If you did not request this login change, please ignore this message. If you
8
8
  requested this login change, please go to
9
- <%%= @email_link %>
9
+ <%= @email_link %>
10
10
  to verify the login change.
@@ -7,12 +7,8 @@ module Rodauth
7
7
  source_root "#{__dir__}/templates"
8
8
  namespace "rodauth:views"
9
9
 
10
- argument :features, optional: true, type: :array,
11
- desc: "Rodauth features to generate views for (login, create_account, reset_password, verify_account etc.)",
12
- default: %w[login logout create_account verify_account reset_password change_password change_login verify_login_change close_account]
13
-
14
- class_option :features, type: :array,
15
- desc: "[DEPRECATED] Rodauth features to generate views for (login, create_account, reset_password, verify_account etc.)"
10
+ argument :selected_features, optional: true, type: :array,
11
+ desc: "Rodauth features to generate views for (login, create_account, reset_password, verify_account etc.)"
16
12
 
17
13
  class_option :all, aliases: "-a", type: :boolean,
18
14
  desc: "Generates views for all Rodauth features",
@@ -23,99 +19,57 @@ module Rodauth
23
19
  default: nil
24
20
 
25
21
  VIEWS = {
26
- login: %w[
27
- _field _field_error _login_field _login_display _password_field
28
- _submit _login_form _login_form_footer _login_form_header login
29
- multi_phase_login
30
- ],
31
- create_account: %w[
32
- _field _field_error _login_field _login_confirm_field
33
- _password_field _password_confirm_field _submit create_account
34
- ],
35
- logout: %w[
36
- _submit logout
37
- ],
38
- reset_password: %w[
39
- _field _field_error _login_field _login_hidden_field
40
- _password_field _password_confirm_field _submit
41
- reset_password_request reset_password
42
- ],
43
- remember: %w[
44
- _submit remember
45
- ],
46
- change_login: %w[
47
- _field _field_error _login_field _login_confirm_field
48
- _password_field _submit change_login
49
- ],
50
- change_password: %w[
51
- _field _field_error _password_field _new_password_field
52
- _password_confirm_field _submit change_password
53
- ],
54
- close_account: %w[
55
- _field _field_error _password_field _submit close_account
56
- ],
57
- email_auth: %w[
58
- _login_hidden_field _submit _email_auth_request_form email_auth
59
- ],
60
- verify_account: %w[
61
- _field _field_error _login_hidden_field _login_field _submit
62
- verify_account_resend verify_account
63
- ],
64
- verify_login_change: %w[
65
- _submit verify_login_change
66
- ],
67
- lockout: %w[
68
- _login_hidden_field _submit unlock_account_request unlock_account
69
- ],
70
- active_sessions: %w[
71
- _global_logout_field
72
- ],
73
- two_factor_base: %w[
74
- _field _field_error _password_field _submit
75
- two_factor_manage two_factor_auth two_factor_disable
76
- ],
77
- otp: %w[
78
- _field _field_error _otp_auth_code_field _password_field _submit
79
- otp_setup otp_auth otp_disable
80
- ],
81
- sms_codes: %w[
82
- _field _field_error _sms_code_field _sms_phone_field
83
- _password_field _submit
84
- sms_setup sms_confirm sms_auth sms_request sms_disable
85
- ],
86
- recovery_codes: %w[
87
- _field _field_error _recovery_code_field _recovery_codes_form
88
- recovery_codes add_recovery_codes recovery_auth
89
- ],
90
- webauthn: %w[
91
- _field _field_error _login_hidden_field _password_field _submit
92
- webauthn_setup webauthn_auth webauthn_remove
93
- ]
22
+ login: %w[_login_form _login_form_footer _login_form_header login multi_phase_login],
23
+ create_account: %w[create_account],
24
+ logout: %w[logout],
25
+ reset_password: %w[reset_password_request reset_password],
26
+ remember: %w[remember],
27
+ change_login: %w[change_login],
28
+ change_password: %w[change_password],
29
+ close_account: %w[close_account],
30
+ email_auth: %w[_email_auth_request_form email_auth],
31
+ verify_account: %w[verify_account_resend verify_account],
32
+ verify_login_change: %w[verify_login_change],
33
+ lockout: %w[unlock_account_request unlock_account],
34
+ two_factor_base: %w[two_factor_manage two_factor_auth two_factor_disable],
35
+ otp: %w[otp_setup otp_auth otp_disable],
36
+ sms_codes: %w[sms_setup sms_confirm sms_auth sms_request sms_disable],
37
+ recovery_codes: %w[recovery_codes add_recovery_codes recovery_auth],
38
+ webauthn: %w[webauthn_setup webauthn_auth webauthn_remove],
94
39
  }
95
40
 
96
41
  DEPENDENCIES = {
97
- active_sessions: :logout,
98
- otp: :two_factor_base,
99
- sms_codes: :two_factor_base,
100
- recovery_codes: :two_factor_base,
101
- webauthn: :two_factor_base,
42
+ otp: :two_factor_base,
43
+ sms_codes: :two_factor_base,
44
+ recovery_codes: :two_factor_base,
45
+ webauthn: :two_factor_base,
102
46
  }
103
47
 
104
48
  def create_views
105
- if options[:all]
106
- features = VIEWS.keys
107
- else
108
- features = (options[:features] || self.features).map(&:to_sym)
109
- end
110
-
111
49
  views = features.inject([]) do |list, feature|
112
50
  list |= VIEWS[feature] || []
113
51
  list |= VIEWS[DEPENDENCIES[feature]] || []
114
52
  end
115
53
 
116
54
  views.each do |view|
117
- template "app/views/rodauth/#{view}.html.erb",
118
- "app/views/#{directory}/#{view}.html.erb"
55
+ copy_file "app/views/rodauth/#{view}.html.erb", "app/views/#{directory}/#{view}.html.erb" do |content|
56
+ content = content.gsub("rodauth.", "rodauth(:#{configuration_name}).") if configuration_name
57
+ content = content.gsub("rodauth/", "#{directory}/")
58
+ content = form_helpers_compatibility(content) if ActionView.version < Gem::Version.new("5.1")
59
+ content
60
+ end
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def features
67
+ if options[:all]
68
+ VIEWS.keys
69
+ elsif selected_features
70
+ selected_features.map(&:to_sym)
71
+ else
72
+ rodauth_configuration.features
119
73
  end
120
74
  end
121
75
 
@@ -127,19 +81,27 @@ module Rodauth
127
81
  controller.controller_path
128
82
  end
129
83
 
130
- def rodauth
131
- "rodauth#{"(:#{configuration_name})" if configuration_name}"
84
+ def controller
85
+ rodauth_configuration.allocate.rails_controller
132
86
  end
133
87
 
134
- def controller
135
- rodauth = Rodauth::Rails.app.rodauth(configuration_name)
136
- fail ArgumentError, "unknown rodauth configuration: #{configuration_name.inspect}" unless rodauth
137
- rodauth.allocate.rails_controller
88
+ def rodauth_configuration
89
+ Rodauth::Rails.app.rodauth!(configuration_name)
138
90
  end
139
91
 
140
92
  def configuration_name
141
93
  options[:name]&.to_sym
142
94
  end
95
+
96
+ # We need to use the *_tag helpers on versions lower than Rails 5.1.
97
+ def form_helpers_compatibility(content)
98
+ content
99
+ .gsub(/form_with url: (.+) do \|form\|/, 'form_tag \1 do')
100
+ .gsub(/form\.(label|submit)/, '\1_tag')
101
+ .gsub(/form\.(email|password|text|telephone|hidden)_field (\S+), value:/, '\1_field_tag \2,')
102
+ .gsub(/form\.radio_button (\S+), (\S+),/, 'radio_button_tag \1, \2, false,')
103
+ .gsub(/form\.check_box (\S+), (.+) /, 'check_box_tag \1, "t", false, \2 ')
104
+ end
143
105
  end
144
106
  end
145
107
  end
@@ -10,7 +10,6 @@ module Rodauth
10
10
 
11
11
  plugin :hooks
12
12
  plugin :render, layout: false
13
- plugin :pass
14
13
 
15
14
  unless Rodauth::Rails.api_only?
16
15
  require "rodauth/rails/app/flash"
@@ -25,9 +24,7 @@ module Rodauth
25
24
 
26
25
  auth_class ||= Class.new(Rodauth::Rails::Auth)
27
26
 
28
- plugin :rodauth, auth_class: auth_class, name: name, csrf: false, flash: false, json: true, **options do
29
- instance_exec(&block) if block
30
- end
27
+ plugin :rodauth, auth_class: auth_class, name: name, csrf: false, flash: false, json: true, **options, &block
31
28
  end
32
29
 
33
30
  before do
@@ -43,6 +40,10 @@ module Rodauth
43
40
  def rails_request
44
41
  ActionDispatch::Request.new(env)
45
42
  end
43
+
44
+ def self.rodauth!(name)
45
+ rodauth(name) or fail ArgumentError, "unknown rodauth configuration: #{name.inspect}"
46
+ end
46
47
  end
47
48
  end
48
49
  end
@@ -3,23 +3,8 @@ require "rodauth/rails/feature"
3
3
 
4
4
  module Rodauth
5
5
  module Rails
6
- # Base auth class that applies some default configuration and supports
7
- # multi-level inheritance.
6
+ # Base auth class that applies some changes to the default configuration.
8
7
  class Auth < Rodauth::Auth
9
- def self.inherited(subclass)
10
- super
11
- superclass = self
12
- subclass.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
20
- end
21
-
22
- # apply default configuration
23
8
  configure do
24
9
  enable :rails
25
10
 
@@ -32,7 +32,7 @@ module Rodauth
32
32
  rescue ActiveRecord::RecordNotFound
33
33
  not_found.call
34
34
  end
35
- elsif model < Sequel::Model
35
+ elsif defined?(Sequel::Model) && model < Sequel::Model
36
36
  begin
37
37
  model.with_pk!(id)
38
38
  rescue Sequel::NoMatchingRow
@@ -13,23 +13,23 @@ module Rodauth
13
13
 
14
14
  # Render Rails CSRF tags in Rodauth templates.
15
15
  def csrf_tag(*)
16
- rails_csrf_tag
16
+ rails_csrf_tag if rails_controller_csrf?
17
17
  end
18
18
 
19
19
  # Verify Rails' authenticity token.
20
20
  def check_csrf
21
- rails_check_csrf!
21
+ rails_check_csrf! if rails_controller_csrf?
22
22
  end
23
23
 
24
24
  # Have Rodauth call #check_csrf automatically.
25
25
  def check_csrf?
26
- true
26
+ rails_check_csrf? if rails_controller_csrf?
27
27
  end
28
28
 
29
29
  private
30
30
 
31
31
  def rails_controller_callbacks
32
- return super if rails_api_controller?
32
+ return super unless rails_controller_csrf?
33
33
 
34
34
  # don't verify CSRF token as part of callbacks, Rodauth will do that
35
35
  rails_controller_instance.allow_forgery_protection = false
@@ -40,6 +40,12 @@ module Rodauth
40
40
  end
41
41
  end
42
42
 
43
+ # Checks whether ActionController::RequestForgeryProtection is included
44
+ # and that protect_from_forgery was called.
45
+ def rails_check_csrf?
46
+ !!rails_controller_instance.forgery_protection_strategy
47
+ end
48
+
43
49
  # Calls the controller to verify the authenticity token.
44
50
  def rails_check_csrf!
45
51
  rails_controller_instance.send(:verify_authenticity_token)
@@ -59,6 +65,11 @@ module Rodauth
59
65
  def rails_csrf_token
60
66
  rails_controller_instance.send(:form_authenticity_token)
61
67
  end
68
+
69
+ # Checks whether ActionController::RequestForgeryProtection is included.
70
+ def rails_controller_csrf?
71
+ rails_controller.respond_to?(:protect_from_forgery)
72
+ end
62
73
  end
63
74
  end
64
75
  end
@@ -2,30 +2,21 @@ module Rodauth
2
2
  module Rails
3
3
  module Feature
4
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 || {}
5
+ def domain
6
+ return super unless missing_host?
11
7
 
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
8
+ rails_url_options.fetch(:host)
9
+ end
17
10
 
18
- env = {
19
- "HTTP_HOST" => host_with_port,
20
- "rack.url_scheme" => scheme,
21
- "SERVER_NAME" => host,
22
- "SERVER_PORT" => port,
23
- }.compact
11
+ def base_url
12
+ return super unless missing_host? && domain
24
13
 
25
- opts = opts.merge(env: env) { |k, v1, v2| v2.merge(v1) }
14
+ scheme = rails_url_options[:protocol] || "http"
15
+ port = rails_url_options[:port]
26
16
 
27
- super(route, opts, &blk)
28
- end
17
+ url = "#{scheme}://#{domain}"
18
+ url << ":#{port}" if port
19
+ url
29
20
  end
30
21
 
31
22
  private
@@ -44,6 +35,17 @@ module Rodauth
44
35
  return yield if internal_request?
45
36
  super
46
37
  end
38
+
39
+ # Checks whether we're in an internal request and host was not set,
40
+ # or the request doesn't exist such as with path_class_methods feature.
41
+ def missing_host?
42
+ internal_request? && request.host == INVALID_DOMAIN || scope.nil?
43
+ end
44
+
45
+ def rails_url_options
46
+ ::Rails.application.config.action_mailer.default_url_options or
47
+ fail Error, "There is no information to set the URL host from. Please set config.action_mailer.default_url_options in your Rails application, or configure #domain and #base_url in your Rodauth configuration."
48
+ end
47
49
  end
48
50
  end
49
51
  end
@@ -10,7 +10,7 @@ module Rodauth
10
10
  # template, otherwise falls back to Rodauth's template.
11
11
  def view(page, *)
12
12
  rails_render(action: page.tr("-", "_"), layout: true) ||
13
- rails_render(html: super.html_safe, layout: true)
13
+ rails_render(html: super.html_safe, layout: true, formats: :html)
14
14
  end
15
15
 
16
16
  # Renders templates without layout. First tries to render a user-defined
@@ -42,6 +42,14 @@ module Rodauth
42
42
  controller.formats = rails_request.formats.map(&:ref).compact
43
43
  controller
44
44
  end
45
+
46
+ # Not all Rodauth actions are Turbo-compatible (some form submissions
47
+ # render 200 HTML responses), so we disable Turbo on all Rodauth forms.
48
+ def _view(meth, *)
49
+ html = super
50
+ html = html.gsub(/<form(.+)>/, '<form\1 data-turbo="false">') if meth == :view
51
+ html
52
+ end
45
53
  end
46
54
  end
47
55
  end
@@ -6,8 +6,10 @@ require "rails"
6
6
  module Rodauth
7
7
  module Rails
8
8
  class Railtie < ::Rails::Railtie
9
- initializer "rodauth.middleware" do |app|
10
- app.middleware.use Rodauth::Rails::Middleware if Rodauth::Rails.middleware?
9
+ initializer "rodauth.middleware", after: :load_config_initializers do |app|
10
+ if Rodauth::Rails.middleware?
11
+ app.middleware.use Rodauth::Rails::Middleware
12
+ end
11
13
  end
12
14
 
13
15
  initializer "rodauth.controller" do
@@ -5,13 +5,13 @@ namespace :rodauth do
5
5
  puts "Routes handled by #{app}:"
6
6
 
7
7
  app.opts[:rodauths].each do |configuration_name, auth_class|
8
- auth_class.configure { enable :path_class_methods }
8
+ rodauth = auth_class.allocate
9
9
 
10
10
  routes = auth_class.routes.map do |handle_method|
11
11
  path_method = "#{handle_method.to_s.sub(/\Ahandle_/, "")}_path"
12
12
 
13
13
  [
14
- auth_class.public_send(path_method),
14
+ rodauth.public_send(path_method),
15
15
  "rodauth#{configuration_name && "(:#{configuration_name})"}.#{path_method}",
16
16
  ]
17
17
  end
@@ -1,5 +1,5 @@
1
1
  module Rodauth
2
2
  module Rails
3
- VERSION = "0.17.0"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end