rodauth-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +589 -0
- data/lib/generators/rodauth/install_generator.rb +65 -0
- data/lib/generators/rodauth/mailer_generator.rb +38 -0
- data/lib/generators/rodauth/templates/app/controllers/rodauth_controller.rb +3 -0
- data/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb +37 -0
- data/lib/generators/rodauth/templates/app/models/account.rb +2 -0
- data/lib/generators/rodauth/templates/config/initializers/rodauth.rb +3 -0
- data/lib/generators/rodauth/templates/config/initializers/sequel.rb +13 -0
- data/lib/generators/rodauth/templates/db/migrate/create_rodauth.rb +170 -0
- data/lib/generators/rodauth/templates/lib/rodauth_app.rb +186 -0
- data/lib/generators/rodauth/views_generator.rb +123 -0
- data/lib/rodauth/features/rails.rb +1 -0
- data/lib/rodauth/rails/app/flash.rb +50 -0
- data/lib/rodauth/rails/app.rb +45 -0
- data/lib/rodauth/rails/controller_methods.rb +20 -0
- data/lib/rodauth/rails/feature.rb +118 -0
- data/lib/rodauth/rails/middleware.rb +21 -0
- data/lib/rodauth/rails/railtie.rb +18 -0
- data/lib/rodauth/rails.rb +33 -0
- data/lib/rodauth-rails.rb +1 -0
- data/rodauth-rails.gemspec +22 -0
- metadata +147 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
require "rails/generators/base"
|
2
|
+
require "rodauth/version"
|
3
|
+
|
4
|
+
module Rodauth
|
5
|
+
module Rails
|
6
|
+
module Generators
|
7
|
+
class MailerGenerator < ::Rails::Generators::Base
|
8
|
+
source_root "#{__dir__}/templates"
|
9
|
+
namespace "rodauth:mailer"
|
10
|
+
|
11
|
+
VIEWS = %w[
|
12
|
+
email_auth
|
13
|
+
password_changed
|
14
|
+
reset_password
|
15
|
+
unlock_account
|
16
|
+
verify_account
|
17
|
+
verify_login_change
|
18
|
+
]
|
19
|
+
|
20
|
+
class_option :name,
|
21
|
+
desc: "The name for the mailer and the views directory",
|
22
|
+
default: "rodauth"
|
23
|
+
|
24
|
+
def copy_mailer
|
25
|
+
template "app/mailers/rodauth_mailer.rb",
|
26
|
+
"app/mailers/#{options[:name].underscore}_mailer.rb"
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy_mailer_views
|
30
|
+
VIEWS.each do |view|
|
31
|
+
template "app/views/rodauth_mailer/#{view}.text.erb",
|
32
|
+
"app/views/#{options[:name].underscore}_mailer/#{view}.text.erb"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class <%= options[:name].camelize %>Mailer < ApplicationMailer
|
2
|
+
def verify_account(recipient, email_link)
|
3
|
+
@email_link = email_link
|
4
|
+
|
5
|
+
mail to: recipient
|
6
|
+
end
|
7
|
+
|
8
|
+
def reset_password(recipient, email_link)
|
9
|
+
@email_link = email_link
|
10
|
+
|
11
|
+
mail to: recipient
|
12
|
+
end
|
13
|
+
|
14
|
+
def verify_login_change(recipient, old_login, new_login, email_link)
|
15
|
+
@old_login = old_login
|
16
|
+
@new_login = new_login
|
17
|
+
@email_link = email_link
|
18
|
+
|
19
|
+
mail to: recipient
|
20
|
+
end
|
21
|
+
|
22
|
+
def password_changed(recipient)
|
23
|
+
mail to: recipient
|
24
|
+
end
|
25
|
+
|
26
|
+
# def email_auth(recipient, email_link)
|
27
|
+
# @email_link = email_link
|
28
|
+
|
29
|
+
# mail to: recipient
|
30
|
+
# end
|
31
|
+
|
32
|
+
# def unlock_account(recipient, email_link)
|
33
|
+
# @email_link = email_link
|
34
|
+
|
35
|
+
# mail to: recipient
|
36
|
+
# end
|
37
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "sequel/core"
|
2
|
+
|
3
|
+
# initialize the appropriate Sequel adapter without creating a connection
|
4
|
+
<% case adapter -%>
|
5
|
+
<% when "postgresql" -%>
|
6
|
+
DB = Sequel.postgres(test: false)
|
7
|
+
<% when "mysql2" -%>
|
8
|
+
DB = Sequel.mysql2(test: false)
|
9
|
+
<% when "sqlite3" -%>
|
10
|
+
DB = Sequel.sqlite(test: false)
|
11
|
+
<% end -%>
|
12
|
+
# have Sequel use ActiveRecord's connection for database interaction
|
13
|
+
DB.extension :activerecord_connection
|
@@ -0,0 +1,170 @@
|
|
1
|
+
class CreateRodauth < ActiveRecord::Migration<%= migration_version %>
|
2
|
+
def change
|
3
|
+
<% if adapter == "postgresql" -%>
|
4
|
+
enable_extension "citext"
|
5
|
+
|
6
|
+
<% end -%>
|
7
|
+
create_table :accounts do |t|
|
8
|
+
<% case adapter -%>
|
9
|
+
<% when "postgresql" -%>
|
10
|
+
t.citext :email, null: false, index: { unique: true, where: "status IN ('verified', 'unverified')" }
|
11
|
+
<% else -%>
|
12
|
+
t.string :email, null: false, index: { unique: true }
|
13
|
+
<% end -%>
|
14
|
+
t.string :status, null: false, default: "verified"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Used if storing password hashes in a separate table (default)
|
18
|
+
create_table :account_password_hashes do |t|
|
19
|
+
t.foreign_key :accounts, column: :id
|
20
|
+
t.string :password_hash, null: false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Used by the password reset feature
|
24
|
+
create_table :account_password_reset_keys do |t|
|
25
|
+
t.foreign_key :accounts, column: :id
|
26
|
+
t.string :key, null: false
|
27
|
+
t.datetime :deadline, null: false
|
28
|
+
t.datetime :email_last_sent, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Used by the account verification feature
|
32
|
+
create_table :account_verification_keys do |t|
|
33
|
+
t.foreign_key :accounts, column: :id
|
34
|
+
t.string :key, null: false
|
35
|
+
t.datetime :requested_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
36
|
+
t.datetime :email_last_sent, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Used by the verify login change feature
|
40
|
+
create_table :account_login_change_keys do |t|
|
41
|
+
t.foreign_key :accounts, column: :id
|
42
|
+
t.string :key, null: false
|
43
|
+
t.string :login, null: false
|
44
|
+
t.datetime :deadline, null: false
|
45
|
+
end
|
46
|
+
|
47
|
+
# Used by the remember me feature
|
48
|
+
create_table :account_remember_keys do |t|
|
49
|
+
t.foreign_key :accounts, column: :id
|
50
|
+
t.string :key, null: false
|
51
|
+
t.datetime :deadline, null: false
|
52
|
+
end
|
53
|
+
|
54
|
+
# # Used by the audit logging feature
|
55
|
+
# create_table :account_authentication_audit_logs do |t|
|
56
|
+
# t.references :account, null: false
|
57
|
+
# t.datetime :at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
58
|
+
# t.text :message, null: false
|
59
|
+
<% case adapter -%>
|
60
|
+
<% when "postgresql" -%>
|
61
|
+
# t.jsonb :metadata
|
62
|
+
<% when "sqlite3", "mysql2" -%>
|
63
|
+
# t.json :metadata
|
64
|
+
<% else -%>
|
65
|
+
# t.string :metadata
|
66
|
+
<% end -%>
|
67
|
+
# t.index [:account_id, :at], name: "audit_account_at_idx"
|
68
|
+
# t.index :at, name: "audit_at_idx"
|
69
|
+
# end
|
70
|
+
|
71
|
+
# # Used by the jwt refresh feature
|
72
|
+
# create_table :account_jwt_refresh_keys do |t|
|
73
|
+
# t.references :account, null: false
|
74
|
+
# t.string :key, null: false
|
75
|
+
# t.datetime :deadline, null: false
|
76
|
+
# t.index :account_id, name: "account_jwt_rk_account_id_idx"
|
77
|
+
# end
|
78
|
+
|
79
|
+
# # Used by the disallow_password_reuse feature
|
80
|
+
# create_table :account_previous_password_hashes do |t|
|
81
|
+
# t.references :account
|
82
|
+
# t.string :password_hash, null: false
|
83
|
+
# end
|
84
|
+
|
85
|
+
# # Used by the lockout feature
|
86
|
+
# create_table :account_login_failures do |t|
|
87
|
+
# t.foreign_key :accounts, column: :id
|
88
|
+
# t.integer :number, null: false, default: 1
|
89
|
+
# end
|
90
|
+
# create_table :account_lockouts do |t|
|
91
|
+
# t.foreign_key :accounts, column: :id
|
92
|
+
# t.string :key, null: false
|
93
|
+
# t.datetime :deadline, null: false
|
94
|
+
# t.datetime :email_last_sent
|
95
|
+
# end
|
96
|
+
|
97
|
+
# # Used by the email auth feature
|
98
|
+
# create_table :account_email_auth_keys do |t|
|
99
|
+
# t.foreign_key :accounts, column: :id
|
100
|
+
# t.string :key, null: false
|
101
|
+
# t.datetime :deadline, null: false
|
102
|
+
# t.datetime :email_last_sent, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
103
|
+
# end
|
104
|
+
|
105
|
+
# # Used by the password expiration feature
|
106
|
+
# create_table :account_password_change_times do |t|
|
107
|
+
# t.foreign_key :accounts, column: :id
|
108
|
+
# t.datetime :changed_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
109
|
+
# end
|
110
|
+
|
111
|
+
# # Used by the account expiration feature
|
112
|
+
# create_table :account_activity_times do |t|
|
113
|
+
# t.foreign_key :accounts, column: :id
|
114
|
+
# t.datetime :last_activity_at, null: false
|
115
|
+
# t.datetime :last_login_at, null: false
|
116
|
+
# t.datetime :expired_at
|
117
|
+
# end
|
118
|
+
|
119
|
+
# # Used by the single session feature
|
120
|
+
# create_table :account_session_keys do |t|
|
121
|
+
# t.foreign_key :accounts, column: :id
|
122
|
+
# t.string :key, null: false
|
123
|
+
# end
|
124
|
+
|
125
|
+
# # Used by the active sessions feature
|
126
|
+
# create_table :account_active_session_keys, primary_key: [:account_id, :session_id] do |t|
|
127
|
+
# t.references :account
|
128
|
+
# t.string :session_id
|
129
|
+
# t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
130
|
+
# t.datetime :last_use, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
131
|
+
# end
|
132
|
+
|
133
|
+
# # Used by the webauthn feature
|
134
|
+
# create_table :account_webauthn_user_ids do |t|
|
135
|
+
# t.foreign_key :accounts, column: :id
|
136
|
+
# t.string :webauthn_id, null: false
|
137
|
+
# end
|
138
|
+
# create_table :account_webauthn_keys, primary_key: [:account_id, :webauthn_id] do |t|
|
139
|
+
# t.references :account
|
140
|
+
# t.string :webauthn_id
|
141
|
+
# t.string :public_key, null: false
|
142
|
+
# t.integer :sign_count, null: false
|
143
|
+
# t.datetime :last_use, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
144
|
+
# end
|
145
|
+
|
146
|
+
# # Used by the otp feature
|
147
|
+
# create_table :account_otp_keys do |t|
|
148
|
+
# t.foreign_key :accounts, column: :id
|
149
|
+
# t.string :key, null: false
|
150
|
+
# t.integer :num_failures, null: false, default: 0
|
151
|
+
# t.datetime :last_use, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
152
|
+
# end
|
153
|
+
|
154
|
+
# # Used by the recovery codes feature
|
155
|
+
# create_table :account_recovery_codes, primary_key: [:id, :code] do |t|
|
156
|
+
# t.integer :id
|
157
|
+
# t.foreign_key :accounts, column: :id
|
158
|
+
# t.string :code
|
159
|
+
# end
|
160
|
+
|
161
|
+
# # Used by the sms codes feature
|
162
|
+
# create_table :account_sms_codes do |t|
|
163
|
+
# t.foreign_key :accounts, column: :id
|
164
|
+
# t.string :phone_number, null: false
|
165
|
+
# t.integer :num_failures
|
166
|
+
# t.string :code
|
167
|
+
# t.datetime :code_issued_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
168
|
+
# end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
class RodauthApp < Rodauth::Rails::App
|
2
|
+
configure do
|
3
|
+
# List of authentication features that are loaded.
|
4
|
+
enable :create_account, :verify_account, :verify_account_grace_period,
|
5
|
+
:login, :remember, :logout,
|
6
|
+
:reset_password, :change_password, :change_password_notify,
|
7
|
+
:change_login, :verify_login_change,
|
8
|
+
:close_account
|
9
|
+
|
10
|
+
# See the Rodauth documentation for the list of available config options:
|
11
|
+
# http://rodauth.jeremyevans.net/documentation.html
|
12
|
+
|
13
|
+
# ==> General
|
14
|
+
# Specify the controller used for view rendering and CSRF verification.
|
15
|
+
rails_controller { RodauthController }
|
16
|
+
|
17
|
+
# Store account status in a text column.
|
18
|
+
account_status_column :status
|
19
|
+
account_unverified_status_value "unverified"
|
20
|
+
account_open_status_value "verified"
|
21
|
+
account_closed_status_value "closed"
|
22
|
+
|
23
|
+
# Store password hash in a column instead of a separate table.
|
24
|
+
# account_password_hash_column :password_digest
|
25
|
+
|
26
|
+
# Set password when creating account instead of when verifying.
|
27
|
+
verify_account_set_password? false
|
28
|
+
|
29
|
+
# Redirect back to originally requested location after authentication.
|
30
|
+
# login_return_to_requested_location? true
|
31
|
+
# two_factor_auth_return_to_requested_location? true # if using MFA
|
32
|
+
|
33
|
+
# Autologin the user after they have reset their password.
|
34
|
+
# reset_password_autologin? true
|
35
|
+
|
36
|
+
# Delete the account record when the user has closed their account.
|
37
|
+
# delete_account_on_close? true
|
38
|
+
|
39
|
+
# Redirect to the app from login and registration pages if already logged in.
|
40
|
+
# already_logged_in { redirect login_redirect }
|
41
|
+
|
42
|
+
# ==> Emails
|
43
|
+
# Uncomment the lines below once you've imported mailer views.
|
44
|
+
# send_reset_password_email do
|
45
|
+
# RodauthMailer.reset_password(email_to, password_reset_email_link).deliver_now
|
46
|
+
# end
|
47
|
+
# send_verify_account_email do
|
48
|
+
# RodauthMailer.verify_account(email_to, verify_account_email_link).deliver_now
|
49
|
+
# end
|
50
|
+
# send_verify_login_change_email do |login|
|
51
|
+
# RodauthMailer.verify_login_change(login, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_email_link).deliver_now
|
52
|
+
# end
|
53
|
+
# send_password_changed_email do
|
54
|
+
# RodauthMailer.password_changed(email_to).deliver_now
|
55
|
+
# end
|
56
|
+
# # send_email_auth_email do
|
57
|
+
# # RodauthMailer.email_auth(email_to, email_auth_email_link).deliver_now
|
58
|
+
# # end
|
59
|
+
# # send_unlock_account_email do
|
60
|
+
<% if Rodauth::MAJOR == 1 -%>
|
61
|
+
# # @unlock_account_key_value = get_unlock_account_key
|
62
|
+
<% end -%>
|
63
|
+
# # RodauthMailer.unlock_account(email_to, unlock_account_email_link).deliver_now
|
64
|
+
# # end
|
65
|
+
|
66
|
+
# In the meantime you can tweak settings for emails created by Rodauth
|
67
|
+
# email_subject_prefix "[MyApp] "
|
68
|
+
# email_from "noreply@myapp.com"
|
69
|
+
# send_email(&:deliver_later)
|
70
|
+
# reset_password_email_body { "Click here to reset your password: #{reset_password_email_link}" }
|
71
|
+
|
72
|
+
# ==> Flash
|
73
|
+
# Match flash keys with ones already used in the Rails app.
|
74
|
+
# flash_notice_key :success # default is :notice
|
75
|
+
# flash_error_key :error # default is :alert
|
76
|
+
|
77
|
+
# Override default flash messages.
|
78
|
+
# create_account_notice_flash "Your account has been created. Please verify your account by visiting the confirmation link sent to your email address."
|
79
|
+
# login_error_flash "Login is required for accessing this page"
|
80
|
+
# login_notice_flash nil
|
81
|
+
|
82
|
+
# ==> Validation
|
83
|
+
# Override default validation error messages.
|
84
|
+
# no_matching_login_message "user with this email address doesn't exist"
|
85
|
+
# already_an_account_with_this_login_message "user with this email address already exists"
|
86
|
+
# password_too_short_message { "needs to have at least #{password_minimum_length} characters" }
|
87
|
+
# login_does_not_meet_requirements_message { "invalid email#{", #{login_requirement_message}" if login_requirement_message}" }
|
88
|
+
|
89
|
+
# Change minimum number of password characters required when creating an account.
|
90
|
+
# password_minimum_length 8
|
91
|
+
|
92
|
+
# ==> Remember Feature
|
93
|
+
# Remember all logged in users.
|
94
|
+
after_login { remember_login }
|
95
|
+
|
96
|
+
# Or only remember users that have ticked a "Remember Me" checkbox on login.
|
97
|
+
# after_login { remember_login if param_or_nil("remember") }
|
98
|
+
|
99
|
+
# Extend user's remember period when remembered via a cookie
|
100
|
+
extend_remember_deadline? true
|
101
|
+
|
102
|
+
# Consider remembered users to be multifactor-authenticated (if using MFA).
|
103
|
+
# after_load_memory { two_factor_update_session("totp") if two_factor_authentication_setup? }
|
104
|
+
|
105
|
+
# ==> Hooks
|
106
|
+
# Validate custom fields in the create account form.
|
107
|
+
# before_create_account do
|
108
|
+
# throw_error_status(422, "name", "must be present") if param("name").empty?
|
109
|
+
# end
|
110
|
+
|
111
|
+
# Perform additional actions after the account is created.
|
112
|
+
# after_create_account do
|
113
|
+
# Profile.create!(account_id: account[:id], name: param("name"))
|
114
|
+
# end
|
115
|
+
|
116
|
+
# Do additional cleanup after the account is closed.
|
117
|
+
# after_close_account do
|
118
|
+
# Profile.find_by!(account_id: account[:id]).destroy
|
119
|
+
# end
|
120
|
+
|
121
|
+
# ==> Redirects
|
122
|
+
# Redirect to home page after logout.
|
123
|
+
logout_redirect "/"
|
124
|
+
|
125
|
+
# Redirect to wherever login redirects to after account verification.
|
126
|
+
verify_account_redirect { login_redirect }
|
127
|
+
|
128
|
+
# Redirect to login page after password reset.
|
129
|
+
reset_password_redirect { login_path }
|
130
|
+
|
131
|
+
# ==> Deadlines
|
132
|
+
# Change default deadlines for some actions.
|
133
|
+
# verify_account_grace_period 3.days
|
134
|
+
# reset_password_deadline_interval Hash[hours: 6]
|
135
|
+
# verify_login_change_deadline_interval Hash[days: 2]
|
136
|
+
# remember_deadline_interval Hash[days: 30]
|
137
|
+
|
138
|
+
# ==> Extending
|
139
|
+
# Define any additional methods you want for the Rodauth object.
|
140
|
+
# auth_class_eval do
|
141
|
+
# def my_send_email(name, *args)
|
142
|
+
# AuthenticationMailer.public_send(name, *args).deliver_later
|
143
|
+
# end
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# Then use the new custom method in configuration blocks.
|
147
|
+
# send_password_reset_email do
|
148
|
+
# my_send_email(:password_reset, email_to, password_reset_email_link)
|
149
|
+
# end
|
150
|
+
end
|
151
|
+
|
152
|
+
# ==> Multiple configurations
|
153
|
+
# configure(:admin) do
|
154
|
+
# enable :http_basic_auth
|
155
|
+
#
|
156
|
+
# prefix "/admin"
|
157
|
+
# session_key :admin_id
|
158
|
+
# end
|
159
|
+
|
160
|
+
route do |r|
|
161
|
+
rodauth.load_memory # autologin remembered users
|
162
|
+
|
163
|
+
r.rodauth # route rodauth requests
|
164
|
+
|
165
|
+
# ==> Authenticating Requests
|
166
|
+
# Call `rodauth.require_authentication` for requests that you want to
|
167
|
+
# require authentication for. Some examples:
|
168
|
+
#
|
169
|
+
# next if r.path.start_with?("/docs") # skip authentication for documentation pages
|
170
|
+
# next if session[:admin] # skip authentication for admins
|
171
|
+
#
|
172
|
+
# # authenticate /dashboard/* and /account/* requests
|
173
|
+
# if r.path.start_with?("/dashboard") || r.path.start_with?("/account")
|
174
|
+
# rodauth.require_authentication
|
175
|
+
# end
|
176
|
+
|
177
|
+
# ==> Multiple configurations
|
178
|
+
# r.on "admin" do
|
179
|
+
# r.rodauth(:admin)
|
180
|
+
#
|
181
|
+
# unless rodauth(:admin).logged_in?
|
182
|
+
# rodauth(:admin).require_http_basic_auth
|
183
|
+
# end
|
184
|
+
# end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require "rails/generators/base"
|
2
|
+
require "rodauth/version"
|
3
|
+
|
4
|
+
module Rodauth
|
5
|
+
module Rails
|
6
|
+
module Generators
|
7
|
+
class ViewsGenerator < ::Rails::Generators::Base
|
8
|
+
source_root "#{__dir__}/templates"
|
9
|
+
namespace "rodauth:views"
|
10
|
+
|
11
|
+
VIEWS = {
|
12
|
+
login: %w[
|
13
|
+
_field _field_error _login_field _login_display _password_field
|
14
|
+
_submit _login_form _login_form_footer _login_form_header login
|
15
|
+
multi_phase_login
|
16
|
+
],
|
17
|
+
create_account: %w[
|
18
|
+
_field _field_error _login_field _login_confirm_field
|
19
|
+
_password_field _password_confirm_field _submit create_account
|
20
|
+
],
|
21
|
+
logout: %w[
|
22
|
+
_submit logout
|
23
|
+
],
|
24
|
+
reset_password: %w[
|
25
|
+
_field _field_error _login_field _login_hidden_field
|
26
|
+
_password_field _password_confirm_field _submit
|
27
|
+
reset_password_request reset_password
|
28
|
+
],
|
29
|
+
remember: %w[
|
30
|
+
_submit remember
|
31
|
+
],
|
32
|
+
change_login: %w[
|
33
|
+
_field _field_error _login_field _login_confirm_field
|
34
|
+
_password_field _submit change_login
|
35
|
+
],
|
36
|
+
change_password: %w[
|
37
|
+
_field _field_error _password_field _new_password_field
|
38
|
+
_password_confirm_field _submit change_password
|
39
|
+
],
|
40
|
+
close_account: %w[
|
41
|
+
_field _field_error _password_field _submit close_account
|
42
|
+
],
|
43
|
+
email_auth: %w[
|
44
|
+
_login_hidden_field _submit _email_auth_request_form email_auth
|
45
|
+
],
|
46
|
+
verify_account: %w[
|
47
|
+
_field _field_error _login_hidden_field _login_field _submit
|
48
|
+
verify_account_resend verify_account
|
49
|
+
],
|
50
|
+
lockout: %w[
|
51
|
+
_login_hidden_field _submit unlock_account_request unlock_account
|
52
|
+
],
|
53
|
+
active_sessions: %w[
|
54
|
+
_global_logout_field
|
55
|
+
],
|
56
|
+
two_factor_base: %w[
|
57
|
+
_field _field_error _password_field _submit
|
58
|
+
two_factor_manage two_factor_auth two_factor_disable
|
59
|
+
],
|
60
|
+
otp: %w[
|
61
|
+
_field _field_error _otp_auth_code_field _password_field _submit
|
62
|
+
otp_setup otp_auth otp_disable
|
63
|
+
],
|
64
|
+
sms_codes: %w[
|
65
|
+
_field _field_error _sms_code_field _sms_phone_field
|
66
|
+
_password_field _submit
|
67
|
+
sms_setup sms_confirm sms_auth sms_request sms_disable
|
68
|
+
],
|
69
|
+
recovery_codes: %w[
|
70
|
+
_field _field_error _recovery_code_field _recovery_codes_form
|
71
|
+
recovery_codes add_recovery_codes recovery_auth
|
72
|
+
],
|
73
|
+
webauthn: %w[
|
74
|
+
_field _field_error _login_hidden_field _password_field _submit
|
75
|
+
webauthn_setup webauthn_auth webauthn_remove
|
76
|
+
]
|
77
|
+
}
|
78
|
+
|
79
|
+
DEPENDENCIES = {
|
80
|
+
active_sessions: :logout,
|
81
|
+
otp: :two_factor_base,
|
82
|
+
sms_codes: :two_factor_base,
|
83
|
+
recovery_codes: :two_factor_base,
|
84
|
+
webauthn: :two_factor_base,
|
85
|
+
}
|
86
|
+
|
87
|
+
class_option :features, type: :array,
|
88
|
+
desc: "Rodauth features to generate views for (login, create_account, reset_password, verify_account etc.)",
|
89
|
+
default: %w[login logout create_account verify_account reset_password change_password change_login verify_login_change close_account]
|
90
|
+
|
91
|
+
class_option :all, aliases: "-a", type: :boolean,
|
92
|
+
desc: "Generates views for all Rodauth features",
|
93
|
+
default: false
|
94
|
+
|
95
|
+
class_option :directory, aliases: "-d", type: :string,
|
96
|
+
desc: "The directory under app/views/* into which to create views",
|
97
|
+
default: "rodauth"
|
98
|
+
|
99
|
+
def create_views
|
100
|
+
features = options[:all] ? VIEWS.keys : options[:features].map(&:to_sym)
|
101
|
+
|
102
|
+
views = features.inject([]) do |list, feature|
|
103
|
+
list |= VIEWS[feature] || []
|
104
|
+
list |= VIEWS[DEPENDENCIES[feature]] || []
|
105
|
+
end
|
106
|
+
|
107
|
+
if Rodauth::MAJOR == 1
|
108
|
+
views -= %w[
|
109
|
+
multi_phase_login _global_logout_field
|
110
|
+
two_factor_manage two_factor_auth two_factor_disable
|
111
|
+
webauthn_setup webauthn_auth webauthn_remove
|
112
|
+
]
|
113
|
+
end
|
114
|
+
|
115
|
+
views.each do |view|
|
116
|
+
template "app/views/rodauth/#{view}.html.erb",
|
117
|
+
"app/views/#{options[:directory].underscore}/#{view}.html.erb"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "rodauth/rails/feature"
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Rodauth
|
2
|
+
module Rails
|
3
|
+
class App
|
4
|
+
# Sets up Rails' flash integration.
|
5
|
+
module Flash
|
6
|
+
def self.load_dependencies(app)
|
7
|
+
app.plugin :hooks
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configure(app)
|
11
|
+
app.before { request.flash } # load flash
|
12
|
+
app.after { request.commit_flash } # save flash
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods
|
16
|
+
def flash
|
17
|
+
request.flash
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module RequestMethods
|
22
|
+
# If the redirect would bubble up outside of the Roda app, the after
|
23
|
+
# hook would never get called, so we make sure to commit the flash.
|
24
|
+
def redirect(*)
|
25
|
+
commit_flash
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def flash
|
30
|
+
rails_request.flash
|
31
|
+
end
|
32
|
+
|
33
|
+
def commit_flash
|
34
|
+
if ActionPack.version >= Gem::Version.new("5.0.0")
|
35
|
+
rails_request.commit_flash
|
36
|
+
else
|
37
|
+
# ActionPack 4.2 automatically commits flash
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def rails_request
|
44
|
+
ActionDispatch::Request.new(env)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "roda"
|
2
|
+
|
3
|
+
module Rodauth
|
4
|
+
module Rails
|
5
|
+
# The superclass for creating a Rodauth middleware.
|
6
|
+
class App < Roda
|
7
|
+
require "rodauth/rails/app/flash"
|
8
|
+
|
9
|
+
plugin :middleware
|
10
|
+
plugin :hooks
|
11
|
+
plugin :render, layout: false
|
12
|
+
|
13
|
+
plugin Flash
|
14
|
+
|
15
|
+
def self.configure(name = nil, **options, &block)
|
16
|
+
plugin :rodauth, name: name, csrf: false, flash: false, **options do
|
17
|
+
# load the Rails integration
|
18
|
+
enable :rails
|
19
|
+
|
20
|
+
# database functions are more complex to set up, so disable them by default
|
21
|
+
use_database_authentication_functions? false
|
22
|
+
|
23
|
+
# avoid having to set deadline values in column default values
|
24
|
+
set_deadline_values? true
|
25
|
+
|
26
|
+
# use HMACs for additional security
|
27
|
+
hmac_secret { ::Rails.application.secrets.secret_key_base }
|
28
|
+
|
29
|
+
# evaluate user configuration
|
30
|
+
instance_exec(&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
before do
|
35
|
+
(opts[:rodauths] || {}).each do |name, _|
|
36
|
+
if name
|
37
|
+
env["rodauth.#{name}"] = rodauth(name)
|
38
|
+
else
|
39
|
+
env["rodauth"] = rodauth
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|