rails_base 0.75.6 → 0.80.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/rails_base/rails_base_query_checker.js +36 -0
- data/app/controllers/rails_base/admin_controller.rb +54 -9
- data/app/controllers/rails_base/mfa/evaluation_controller.rb +59 -0
- data/app/controllers/rails_base/mfa/register/sms_controller.rb +45 -0
- data/app/controllers/rails_base/mfa/register/totp_controller.rb +42 -0
- data/app/controllers/rails_base/mfa/validate/sms_controller.rb +83 -0
- data/app/controllers/rails_base/mfa/validate/totp_controller.rb +35 -0
- data/app/controllers/rails_base/secondary_authentication_controller.rb +40 -96
- data/app/controllers/rails_base/user_settings_controller.rb +11 -1
- data/app/controllers/rails_base/users/registrations_controller.rb +1 -1
- data/app/controllers/rails_base/users/sessions_controller.rb +16 -13
- data/app/controllers/rails_base_application_controller.rb +96 -1
- data/app/jobs/twilio_job.rb +1 -1
- data/app/mailers/rails_base/email_verification_mailer.rb +6 -4
- data/app/mailers/rails_base/event_mailer.rb +4 -2
- data/app/mailers/rails_base/mailer_kwarg_inject.rb +31 -0
- data/app/models/rails_base/user_constants.rb +6 -3
- data/app/models/rails_base/user_helper/totp/backup_method_options.rb +33 -0
- data/app/models/rails_base/user_helper/totp/class_options.rb +35 -0
- data/app/models/rails_base/user_helper/totp/consume_method_options.rb +60 -0
- data/app/models/rails_base/user_helper/totp.rb +41 -0
- data/app/models/user.rb +28 -13
- data/app/services/rails_base/authentication/constants.rb +1 -1
- data/app/services/rails_base/authentication/decision_twofa_type.rb +61 -30
- data/app/services/rails_base/authentication/send_forgot_password.rb +0 -1
- data/app/services/rails_base/authentication/single_sign_on_send.rb +1 -1
- data/app/services/rails_base/authentication/sso_verify_email.rb +3 -1
- data/app/services/rails_base/authentication/update_phone_send_verification.rb +2 -2
- data/app/services/rails_base/authentication/verify_forgot_password.rb +8 -11
- data/app/services/rails_base/mfa/decision.rb +70 -0
- data/app/services/rails_base/mfa/encrypt_token.rb +34 -0
- data/app/services/rails_base/mfa/sms/remove.rb +35 -0
- data/app/services/rails_base/{authentication/send_login_mfa_to_user.rb → mfa/sms/send.rb} +19 -13
- data/app/services/rails_base/mfa/sms/validate.rb +105 -0
- data/app/services/rails_base/mfa/strategy/base.rb +44 -0
- data/app/services/rails_base/mfa/strategy/every_request.rb +14 -0
- data/app/services/rails_base/mfa/strategy/skip_every_request.rb +14 -0
- data/app/services/rails_base/mfa/strategy/time_based.rb +24 -0
- data/app/services/rails_base/mfa/totp/helper.rb +21 -0
- data/app/services/rails_base/mfa/totp/otp_metadata.rb +19 -0
- data/app/services/rails_base/mfa/totp/remove.rb +40 -0
- data/app/services/rails_base/mfa/totp/validate_code.rb +52 -0
- data/app/services/rails_base/mfa/totp/validate_temporary_code.rb +37 -0
- data/app/services/rails_base/mfa.rb +18 -0
- data/app/services/rails_base/name_change.rb +3 -3
- data/app/views/layouts/rails_base/application.html.erb +22 -6
- data/app/views/rails_base/devise/passwords/new.html.erb +1 -1
- data/app/views/rails_base/mfa/_switch_mfa_type.html.erb +17 -0
- data/app/views/rails_base/mfa/validate/sms/sms_event_input.html.erb +2 -0
- data/app/views/rails_base/mfa/validate/totp/totp_event_input.html.erb +1 -0
- data/app/views/rails_base/secondary_authentication/reset_password_input.html.erb +4 -0
- data/app/views/rails_base/shared/_enable_mfa_auth_modal.html.erb +1 -1
- data/app/views/rails_base/shared/_logged_in_header.html.erb +1 -25
- data/app/views/rails_base/shared/_modify_mfa_auth_modal.html.erb +102 -3
- data/app/views/rails_base/shared/mfa/sms/_login_input.html.erb +13 -0
- data/app/views/rails_base/shared/mfa/totp/_login_input.html.erb +22 -0
- data/app/views/rails_base/shared/totp/_add_authenticator.html.erb +76 -0
- data/app/views/rails_base/shared/totp/_add_authenticator_modal.html.erb +25 -0
- data/app/views/rails_base/shared/totp/_confirm_code.html.erb +31 -0
- data/app/views/rails_base/shared/totp/_confirm_code_ajax.html.erb +3 -0
- data/app/views/rails_base/shared/totp/_confirm_code_rest.html.erb +5 -0
- data/app/views/rails_base/shared/totp/_remove_authenticator_modal.html.erb +50 -0
- data/app/views/rails_base/user_settings/index.html.erb +84 -1
- data/config/initializers/admin_action_helper.rb +44 -8
- data/config/routes.rb +42 -7
- data/db/migrate/20240808013706_add_totp_to_users.rb +9 -0
- data/db/migrate/20240825012724_reconfigure_mfa_variable_names.rb +10 -0
- data/lib/rails_base/admin/action_helper.rb +0 -1
- data/lib/rails_base/admin/default_index_tile.rb +3 -3
- data/lib/rails_base/config.rb +26 -22
- data/lib/rails_base/configuration/admin.rb +5 -5
- data/lib/rails_base/configuration/base.rb +1 -0
- data/lib/rails_base/configuration/mfa.rb +27 -60
- data/lib/rails_base/configuration/totp.rb +82 -0
- data/lib/rails_base/configuration/twilio.rb +85 -0
- data/lib/rails_base/mfa_event.rb +186 -0
- data/lib/rails_base/version.rb +3 -3
- data/lib/rails_base.rb +1 -0
- data/lib/twilio_helper.rb +3 -3
- metadata +129 -64
- data/app/controllers/rails_base/mfa_auth_controller.rb +0 -50
- data/app/services/rails_base/authentication/mfa_set_encrypt_token.rb +0 -32
- data/app/services/rails_base/authentication/mfa_validator.rb +0 -88
- data/app/views/rails_base/mfa_auth/mfa_code.html.erb +0 -11
- data/app/views/rails_base/secondary_authentication/forgot_password.html.erb +0 -9
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBase::Mfa::Totp
|
4
|
+
class Remove < RailsBase::ServiceBase
|
5
|
+
include Helper
|
6
|
+
|
7
|
+
delegate :user, to: :context
|
8
|
+
delegate :otp_code, to: :context
|
9
|
+
delegate :password, to: :context
|
10
|
+
|
11
|
+
def call
|
12
|
+
password_result = RailsBase::Authentication::AuthenticateUser.(email: user.email, current_user: user, password: password)
|
13
|
+
|
14
|
+
if password_result.failure?
|
15
|
+
log(level: :debug, msg: "#{lgp} Password validation failed. Unable to continue")
|
16
|
+
context.fail!(message: password_result.message)
|
17
|
+
end
|
18
|
+
|
19
|
+
valid_code = ValidateCode.(user: user, otp_code: otp_code)
|
20
|
+
if valid_code.failure?
|
21
|
+
log(level: :debug, msg: "#{lgp} Code Validation failed.")
|
22
|
+
context.fail!(message: "#{valid_code.message}. Please try again.")
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
user.reset_otp!
|
27
|
+
log(level: :info, msg: "#{lgp} TOTP successfully removed from User Account")
|
28
|
+
rescue => e
|
29
|
+
context.fail!(message: "Yikes! Unknown error occured. TOTP was not removed from the account.")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def validate!
|
34
|
+
raise "Expected user to be a User." unless User === user
|
35
|
+
raise "Expected otp_code to be present" if otp_code.nil?
|
36
|
+
raise "Expected password to be present" if password.nil?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBase::Mfa::Totp
|
4
|
+
class ValidateCode < RailsBase::ServiceBase
|
5
|
+
include Helper
|
6
|
+
|
7
|
+
delegate :user, to: :context
|
8
|
+
delegate :otp_code, to: :context
|
9
|
+
|
10
|
+
def call
|
11
|
+
return if validate_and_consume_otp
|
12
|
+
|
13
|
+
context.fail!(message: "Invalid TOTP code")
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_and_consume_otp
|
17
|
+
if user.consumed_timestep
|
18
|
+
# reconstruct the timestamp of the last consumed timestep
|
19
|
+
after_timestamp = user.consumed_timestep * otp.interval
|
20
|
+
end
|
21
|
+
|
22
|
+
if otp.verify(otp_code.gsub(/\s+/, ""), drift_behind: User.totp_drift_behind, drift_ahead: User.totp_drift_ahead, after: after_timestamp)
|
23
|
+
log(level: :debug, msg: "#{lgp} Correct code provided")
|
24
|
+
return consume_otp!
|
25
|
+
else
|
26
|
+
log(level: :debug, msg: "#{lgp} InValid code provided")
|
27
|
+
end
|
28
|
+
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
# An OTP cannot be used more than once in a given timestep
|
33
|
+
# Storing timestep of last valid OTP is sufficient to satisfy this requirement
|
34
|
+
def consume_otp!
|
35
|
+
timestep = Time.now.utc.to_i / otp.interval
|
36
|
+
if user.consumed_timestep != timestep
|
37
|
+
user.consumed_timestep = timestep
|
38
|
+
log(level: :debug, msg: "#{lgp} Consuming timestep based on code input")
|
39
|
+
return user.save(validate: false)
|
40
|
+
end
|
41
|
+
|
42
|
+
log(level: :debug, msg: "#{lgp} Timestep for code was already consumed. Invalid code")
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate!
|
47
|
+
raise "Expected user to be a User. " unless User === user
|
48
|
+
raise "Expected otp_code to be present" if otp_code.nil?
|
49
|
+
raise "Expected `otp_secret` passed in or `otp_secret` present on User" if secret.nil?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBase::Mfa::Totp
|
4
|
+
class ValidateTemporaryCode < RailsBase::ServiceBase
|
5
|
+
include Helper
|
6
|
+
|
7
|
+
delegate :user, to: :context
|
8
|
+
delegate :otp_code, to: :context
|
9
|
+
|
10
|
+
def call
|
11
|
+
valid_code = ValidateCode.(user: user, otp_code: otp_code, otp_secret: current_secret)
|
12
|
+
if valid_code.failure?
|
13
|
+
log(level: :debug, msg: "#{lgp} Code Validation failed. Will not persist temporary token")
|
14
|
+
context.fail!(message: valid_code.message)
|
15
|
+
end
|
16
|
+
|
17
|
+
log(level: :info, msg: "#{lgp} correctly validated authenticator code. Persisting")
|
18
|
+
user.persist_otp_metadata!
|
19
|
+
if user.otp_backup_codes.empty?
|
20
|
+
backup_codes = user.generate_otp_backup_codes!
|
21
|
+
log(level: :info, msg: "#{lgp} first authenticator added. Generating Backup Codes. Will also return backup codes to user")
|
22
|
+
context.backup_codes = backup_codes
|
23
|
+
else
|
24
|
+
log(level: :warn, msg: "#{lgp} added additional Authenticator. Will NOT provide backup codes")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_secret
|
29
|
+
@current_secret ||= user.reload.otp_metadata(safe: true, use_existing_temp: true)[:secret]
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate!
|
33
|
+
raise "Expected user to be a User. " unless User === user
|
34
|
+
raise "Expected otp_code to be present" if otp_code.nil?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBase::Mfa
|
4
|
+
MFA_DECISIONS = [
|
5
|
+
OTP = :otp,
|
6
|
+
SMS = :sms,
|
7
|
+
NONE = :none
|
8
|
+
]
|
9
|
+
|
10
|
+
def self.mfa_link(mfa:, mfa_event:)
|
11
|
+
case mfa
|
12
|
+
when OTP
|
13
|
+
{ method: :get, link: RailsBase.url_routes.mfa_with_event_path(mfa_event:, type: mfa) }
|
14
|
+
when SMS
|
15
|
+
{ method: :post, link: RailsBase.url_routes.sms_validate_send_event_path(mfa_event:) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -41,9 +41,9 @@ class RailsBase::NameChange < RailsBase::ServiceBase
|
|
41
41
|
return if admin_user_id
|
42
42
|
|
43
43
|
RailsBase::EmailVerificationMailer.event(
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
current_user,
|
45
|
+
"Succesfull name change",
|
46
|
+
"We changed the name on your account from #{original_name} to #{context.name_change}."
|
47
47
|
).deliver_me
|
48
48
|
end
|
49
49
|
|
@@ -83,12 +83,28 @@
|
|
83
83
|
<%= render partial: 'rails_base/shared/logged_out_header'%>
|
84
84
|
<% end %>
|
85
85
|
<% if notice %>
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
86
|
+
<% if session.delete(:add_mfa_button) %>
|
87
|
+
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
88
|
+
<div class="row">
|
89
|
+
<div class="col-md-6">
|
90
|
+
<%= notice %>
|
91
|
+
</div>
|
92
|
+
<div class="col-md-6">
|
93
|
+
<%= link_to "Enable MFA", RailsBase.url_routes.user_settings_path(openmfa: true), method: :get, class: "btn btn-light float-right" %>
|
94
|
+
</div>
|
95
|
+
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
96
|
+
<span aria-hidden="true">×</span>
|
97
|
+
</button>
|
98
|
+
</div>
|
99
|
+
</div>
|
100
|
+
<% else %>
|
101
|
+
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
102
|
+
<%= notice %>
|
103
|
+
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
104
|
+
<span aria-hidden="true">×</span>
|
105
|
+
</button>
|
106
|
+
</div>
|
107
|
+
<% end %>
|
92
108
|
<% end %>
|
93
109
|
<% if alert %>
|
94
110
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<%= f.label :email, class: 'text-center' %><br>
|
11
11
|
<%= f.email_field :email, autofocus: true, autocomplete: "email", placeholder: :email, class: 'form-control'%>
|
12
12
|
<div class="invalid-feedback">
|
13
|
-
|
13
|
+
Valid email required
|
14
14
|
</div>
|
15
15
|
</div>
|
16
16
|
</div>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<% if mfa_options.length > 0 %>
|
2
|
+
<br>
|
3
|
+
<div class="row">
|
4
|
+
<div class="col-md-6 offset-3">
|
5
|
+
<hr>
|
6
|
+
</div>
|
7
|
+
</div>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<% mfa_options.each do |metadata| %>
|
11
|
+
<br>
|
12
|
+
<div class="row">
|
13
|
+
<div class="col-md-6 offset-md-3">
|
14
|
+
<%= link_to metadata[:text], metadata[:link], method: metadata[:method], class: "btn btn-warning btn-block" %>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render partial: 'rails_base/shared/mfa/totp/login_input', locals: { endpoint: RailsBase.url_routes.totp_validate_event_path(mfa_event: @__rails_base_mfa_event.event), type: :rest, mfa_options: @mfa_options } %>
|
@@ -86,7 +86,7 @@
|
|
86
86
|
var data = { 'phone_number': number}
|
87
87
|
$.ajax({
|
88
88
|
type: "POST",
|
89
|
-
url: "<%= RailsBase.url_routes.phone_registration_path%>",
|
89
|
+
url: "<%= RailsBase.url_routes.phone_registration_path %>",
|
90
90
|
headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') },
|
91
91
|
dataType: 'json',
|
92
92
|
data: data,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
2
|
-
<a class="navbar-brand" href="
|
2
|
+
<a class="navbar-brand" href="<%= RailsBase.url_routes.authenticated_root_path %>"><%= RailsBase.config.app.web_title_logged_in(current_user)%></a>
|
3
3
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
4
4
|
<span class="navbar-toggler-icon"></span>
|
5
5
|
</button>
|
@@ -41,22 +41,6 @@
|
|
41
41
|
</button>
|
42
42
|
</div>
|
43
43
|
<div class="modal-body ">
|
44
|
-
<div class='row'>
|
45
|
-
<div class='col'>
|
46
|
-
<% if RailsBase.config.mfa.enable? %>
|
47
|
-
<% if current_user.mfa_enabled %>
|
48
|
-
<button type="button" class="btn btn-block btn_info close-me" data-toggle="modal" data-target="#modifyMfamodal">
|
49
|
-
Modify 2fa Auth
|
50
|
-
</button>
|
51
|
-
<% else %>
|
52
|
-
<button type="button" class="btn btn-block btn_info close-me" data-toggle="modal" data-target="#enableMfamodal">
|
53
|
-
Enable 2fa Auth
|
54
|
-
</button>
|
55
|
-
<% end %>
|
56
|
-
<% end %>
|
57
|
-
</div>
|
58
|
-
</div>
|
59
|
-
</br>
|
60
44
|
<div class='row'>
|
61
45
|
<div class='col'>
|
62
46
|
<a class="btn btn_info btn-block" href="<%=RailsBase.url_routes.user_settings_path %>" role="button">Modify User</a>
|
@@ -88,14 +72,6 @@
|
|
88
72
|
</div>
|
89
73
|
</div>
|
90
74
|
|
91
|
-
<% if RailsBase.config.mfa.enable? %>
|
92
|
-
<% if current_user.mfa_enabled %>
|
93
|
-
<%= render partial: 'rails_base/shared/modify_mfa_auth_modal'%>
|
94
|
-
<% else %>
|
95
|
-
<%= render partial: 'rails_base/shared/enable_mfa_auth_modal'%>
|
96
|
-
<% end %>
|
97
|
-
<% end %>
|
98
|
-
|
99
75
|
<% if @__admin_actions_array && @__admin_actions_array.present? %>
|
100
76
|
<%= render partial: 'rails_base/shared/admin_actions_modal'%>
|
101
77
|
<% end %>
|
@@ -1,4 +1,10 @@
|
|
1
|
-
<%
|
1
|
+
<%
|
2
|
+
code_length = RailsBase::Authentication::Constants::MFA_LENGTH
|
3
|
+
values = [
|
4
|
+
{ name: '#smsRemovalPassword', criteria: { required: true }},
|
5
|
+
{ name: '#smsRemovalCode', criteria: { required: true, pattern: :numeric, min_length: code_length }}
|
6
|
+
]
|
7
|
+
%>
|
2
8
|
|
3
9
|
<div class="modal fade" id="modifyMfamodal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
4
10
|
<div class="modal-dialog modal-lg" role="document">
|
@@ -9,8 +15,49 @@
|
|
9
15
|
<span aria-hidden="true">×</span>
|
10
16
|
</button>
|
11
17
|
</div>
|
12
|
-
<div class="modal-body">
|
13
|
-
|
18
|
+
<div class="modal-body mfaSmsModalBody">
|
19
|
+
<div class="mfaSmsStatusDiv"></div>
|
20
|
+
<%= form_with(url: RailsBase.url_routes.remove_phone_registration_mfa_path, method: :delete) do |form| %>
|
21
|
+
<div id="confirmSmsAuthenticationCode">
|
22
|
+
<div class="smsInput">
|
23
|
+
<div class="input-group input-group-lg">
|
24
|
+
<div class="input-group-prepend">
|
25
|
+
<span class="input-group-text" id="smsInput-Prepend">Password</span>
|
26
|
+
</div>
|
27
|
+
<%= form.password_field :password, id: "smsRemovalPassword", class:"form-control" %>
|
28
|
+
<div class="invalid-feedback">
|
29
|
+
Password required to remove SMS option for MFA
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
<br>
|
34
|
+
<div class="smsInput">
|
35
|
+
<div class="input-group input-group-lg">
|
36
|
+
<div class="input-group-prepend">
|
37
|
+
<span class="input-group-text" id="smsInput-Prepend">SMS Code</span>
|
38
|
+
</div>
|
39
|
+
<%= form.telephone_field :sms_code, id: "smsRemovalCode", class:"form-control", placeholder: "Code delivered via SMS" %>
|
40
|
+
<div class="invalid-feedback">
|
41
|
+
SMS Code is required. At least <%= code_length %> numerics expected
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
<br>
|
46
|
+
<div class="smsSubmit">
|
47
|
+
<div class="row">
|
48
|
+
<div class="col-md-9">
|
49
|
+
<%= form.submit "Confirm SMS MFA Removal", id: "confirmSmsRemovalButton", class: " btn btn_danger btn-block" %>
|
50
|
+
</div>
|
51
|
+
<div class="col-md-3">
|
52
|
+
<button type="button" id="sendSmsButton" class="btn btn_warning btn-block" onclick="sendSMS()">
|
53
|
+
Send SMS
|
54
|
+
</button>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
<% end %>
|
60
|
+
|
14
61
|
</div>
|
15
62
|
<div class="modal-footer">
|
16
63
|
<button type="button" class="mr-auto btn btn_secondary" data-dismiss="modal">Close</button>
|
@@ -18,3 +65,55 @@
|
|
18
65
|
</div>
|
19
66
|
</div>
|
20
67
|
</div>
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
<%= render partial: 'rails_base/shared/custom_form_validation_javascript', locals: { function_name: "mfaSmsRemoval", values: values } %>
|
72
|
+
|
73
|
+
<script type="text/javascript">
|
74
|
+
$(`#confirmSmsRemovalButton`).prop("disabled", true)
|
75
|
+
$(`.mfaSmsModalBody input`).on("keypress", function() {
|
76
|
+
setConfirmButtonStatus()
|
77
|
+
});
|
78
|
+
|
79
|
+
$(`.mfaSmsModalBody input`).change(function() {
|
80
|
+
setConfirmButtonStatus()
|
81
|
+
});
|
82
|
+
|
83
|
+
function setConfirmButtonStatus(){
|
84
|
+
if(mfaSmsRemoval_validation_event()){
|
85
|
+
$(`#confirmSmsRemovalButton`).prop("disabled", false)
|
86
|
+
} else {
|
87
|
+
$(`#confirmSmsRemovalButton`).prop("disabled", true)
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
function setDelay(){
|
92
|
+
$(`#sendSmsButton`).prop("disabled", true)
|
93
|
+
timeoutClock = setTimeout(enableSendSMS, 5_000);
|
94
|
+
}
|
95
|
+
|
96
|
+
function enableSendSMS(){
|
97
|
+
$(`#sendSmsButton`).prop("disabled", false)
|
98
|
+
}
|
99
|
+
|
100
|
+
function sendSMS(){
|
101
|
+
$(`#sendSmsButton`).text("Resend SMS")
|
102
|
+
setDelay();
|
103
|
+
$.ajax({
|
104
|
+
type: "POST",
|
105
|
+
url: "<%= RailsBase.url_routes.sms_validate_send_event_path(mfa_event: RailsBase::MfaEvent::DISABLE_SMS_EVENT) %>",
|
106
|
+
headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') },
|
107
|
+
dataType: 'json',
|
108
|
+
success: function(data) {
|
109
|
+
html_notice = `<p class="alert alert-success">${data.message}</p>`
|
110
|
+
$(`.mfaSmsStatusDiv`).html(html_notice)
|
111
|
+
},
|
112
|
+
error: function(xhr, status, error) {
|
113
|
+
html_notice = `<p class="alert alert-danger">${xhr.responseJSON.message}</p>`
|
114
|
+
$(`.mfaSmsStatusDiv`).html(html_notice)
|
115
|
+
console.log(xhr.responseJSON)
|
116
|
+
}
|
117
|
+
})
|
118
|
+
}
|
119
|
+
</script>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
<div class="row" >
|
3
|
+
<div class="col-md-8 offset-md-2">
|
4
|
+
<%= render partial: 'rails_base/shared/mfa_input_layout', locals: { url: RailsBase.url_routes.sms_validate_event_path(mfa_event: mfa_event.event), size: 30, masked_phone: @masked_phone }%>
|
5
|
+
<br>
|
6
|
+
<div class="text-center">
|
7
|
+
<%= button_to 'Resend MFA', RailsBase.url_routes.sms_validate_send_event_path(mfa_event: mfa_event.event), method: :post, class: 'btn btn_warning', style: 'width: 25%;' %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
|
13
|
+
<%= render partial: 'rails_base/mfa/switch_mfa_type', locals: { mfa_options: @mfa_options } %>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<div class="row text-center">
|
2
|
+
<div class="col-md-10 offset-md-1">
|
3
|
+
<div class="h1"> One Time Password</div>
|
4
|
+
<div class="row">
|
5
|
+
<div class="col">
|
6
|
+
MFA is required on your account before you can proceed. Please use the gnerated code from provided by your Password Manager.
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<hr>
|
11
|
+
|
12
|
+
<div class="row">
|
13
|
+
<div class="col">
|
14
|
+
<div class="totpComfirmation">
|
15
|
+
<%= render partial: 'rails_base/shared/totp/confirm_code', locals: { endpoint: endpoint, type: :rest } %>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<%= render partial: 'rails_base/mfa/switch_mfa_type', locals: { mfa_options: mfa_options } %>
|
@@ -0,0 +1,76 @@
|
|
1
|
+
<div id="totpAddAuthentication">
|
2
|
+
<div class="description">
|
3
|
+
<h4>What is this?</h4>
|
4
|
+
One time Passwords (OTP) are an industry standard for a secondary Authentication (MFA). <%= RailsBase.app_name %> supports One Time Passwords via Time based One Time Password (also referred to as TOTP).
|
5
|
+
<div class="row"><div class="col-6 offset-3">
|
6
|
+
<hr>
|
7
|
+
</div></div>
|
8
|
+
When TOTP is enabled on your account, upon logging in, you will be required to also provided the secondary Authentication facor before accessing your data. This is optional but highly recommended.
|
9
|
+
<div class="row"><div class="col-6 offset-3">
|
10
|
+
<hr>
|
11
|
+
</div></div>
|
12
|
+
<h4>How to use is this?</h4>
|
13
|
+
Using your password manager, you can scan the QR code here to store along with your Username and Password for <%= RailsBase.app_name %>. When logging in, your Password Manager should automatically give you your OTP.
|
14
|
+
</div>
|
15
|
+
<br>
|
16
|
+
<div class="row">
|
17
|
+
<div class="col-lg-6">
|
18
|
+
<div class="qrCode text-center"></div>
|
19
|
+
</div>
|
20
|
+
<div class="col-lg-6">
|
21
|
+
<div class="row otpauthLink" style="height: 50%; margin-top: 12.5%;">
|
22
|
+
<div class="col-12">
|
23
|
+
<a class="btn btn-info btn-block" id="defaultManagerURI" href="" target="_blank">Open Default Password Manager</a>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
<div class="row otpauthLink">
|
27
|
+
<div class="col-12">
|
28
|
+
<button onclick="copyURIToClipboard()" class="btn btn-info btn-block" id="copyPasteURI">Copy To ClipBoard</button>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<br>
|
35
|
+
<div class="row"><div class="col-6 offset-3">
|
36
|
+
<hr>
|
37
|
+
</div></div>
|
38
|
+
<div class="totpValidation">
|
39
|
+
<div class="totpdescription">
|
40
|
+
<h4>Confirm TOTP code</h4>
|
41
|
+
Once you have added the TOTP Athentication to your password manager, please validate the below by entering the code. Once the code is validated, TOTP is enabled on your account.
|
42
|
+
</div>
|
43
|
+
<div class="totpComfirmation">
|
44
|
+
<%= render partial: 'rails_base/shared/totp/confirm_code', locals: { endpoint: endpoint, type: type } %>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
|
49
|
+
|
50
|
+
<script type="text/javascript">
|
51
|
+
var text_to_write;
|
52
|
+
function copyURIToClipboard(){
|
53
|
+
navigator.clipboard.writeText(text_to_write)
|
54
|
+
}
|
55
|
+
|
56
|
+
function retrieveSuccess(data){
|
57
|
+
$(`#totpAddAuthentication .qrCode`).html(data.qr_code)
|
58
|
+
$(`#totpAddAuthentication #defaultManagerURI`).attr(`href`, data.uri)
|
59
|
+
text_to_write = data.uri
|
60
|
+
}
|
61
|
+
|
62
|
+
function retreiveSecret(){
|
63
|
+
$.ajax({
|
64
|
+
type: "POST",
|
65
|
+
url: "<%= RailsBase.url_routes.totp_register_secret_path %>",
|
66
|
+
headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') },
|
67
|
+
dataType: 'json',
|
68
|
+
success: function(data) {
|
69
|
+
retrieveSuccess(data)
|
70
|
+
},
|
71
|
+
error: function(xhr, status, error) {
|
72
|
+
_rails_base_display_alert(`Failed to get event`);
|
73
|
+
}
|
74
|
+
})
|
75
|
+
}
|
76
|
+
</script>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<div class="modal fade" id="totpEnableModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
2
|
+
<div class="modal-dialog modal-lg" role="document">
|
3
|
+
<div class="modal-content">
|
4
|
+
<div class="modal-header">
|
5
|
+
<h3 class="modal-title" id="exampleModalLabel">Add Authenticator Device</h3>
|
6
|
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
7
|
+
<span aria-hidden="true">×</span>
|
8
|
+
</button>
|
9
|
+
</div>
|
10
|
+
<div class="modal-body">
|
11
|
+
<%= render partial: 'rails_base/shared/totp/add_authenticator', locals: { endpoint: endpoint, type: type } %>
|
12
|
+
</div>
|
13
|
+
<div class="modal-footer">
|
14
|
+
<button type="button" class="mr-auto btn btn_secondary" data-dismiss="modal">Close</button>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
<script type="text/javascript">
|
21
|
+
$('#totpEnableModal').on('shown.bs.modal', function (e) {
|
22
|
+
retreiveSecret()
|
23
|
+
})
|
24
|
+
|
25
|
+
</script>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<%
|
2
|
+
case type
|
3
|
+
when :ajax
|
4
|
+
partial = "rails_base/shared/totp/confirm_code_ajax"
|
5
|
+
endpoint = endpoint
|
6
|
+
when :rest
|
7
|
+
partial = "rails_base/shared/totp/confirm_code_rest"
|
8
|
+
endpoint = endpoint
|
9
|
+
else
|
10
|
+
raise "type must be of [:ajax, :rest]"
|
11
|
+
end
|
12
|
+
%>
|
13
|
+
<%= form_with(url: endpoint, method: :post) do |f| %>
|
14
|
+
<div id="confirmTotpAuthenticationCode">
|
15
|
+
<br>
|
16
|
+
<div class="totpInput">
|
17
|
+
<div class="input-group input-group-lg">
|
18
|
+
<div class="input-group-prepend">
|
19
|
+
<span class="input-group-text" id="totpInput-Prepend">OTP</span>
|
20
|
+
</div>
|
21
|
+
<%= f.telephone_field :totp_code, id: "totpInput-Input", class:"form-control", placeholder: "One Time Password Value" %>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
<br>
|
25
|
+
<div class="totpSubmit">
|
26
|
+
<%= render partial: partial, locals: { endpoint: endpoint, form: f } %>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<div class="modal fade" id="totpDisableModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
2
|
+
<div class="modal-dialog modal-lg" role="document">
|
3
|
+
<div class="modal-content">
|
4
|
+
<div class="modal-header">
|
5
|
+
<h3 class="modal-title" id="exampleModalLabel">Remove TOTP as Multi Factor Authenticator</h3>
|
6
|
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
7
|
+
<span aria-hidden="true">×</span>
|
8
|
+
</button>
|
9
|
+
</div>
|
10
|
+
<div class="modal-body">
|
11
|
+
<div class="text-center">
|
12
|
+
Time based One time Password is one of the the most Secure MFA option that <%= RailsBase.app_name %> provides. If you would like to remove this MFA option, please enter your credentials below and confirm. Upon Successful request, TOTP is no longer active on your account
|
13
|
+
</div>
|
14
|
+
<hr>
|
15
|
+
<%= form_with(url: RailsBase.url_routes.totp_register_delete_path, method: :delete) do |form| %>
|
16
|
+
<div id="confirmTotpAuthenticationCode">
|
17
|
+
<div class="totpInput">
|
18
|
+
<div class="input-group input-group-lg">
|
19
|
+
<div class="input-group-prepend">
|
20
|
+
<span class="input-group-text" id="totpInput-Prepend">Password</span>
|
21
|
+
</div>
|
22
|
+
<%= form.password_field :password, id: "totpRemoval-password", class:"form-control" %>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
<br>
|
26
|
+
<div class="totpInput">
|
27
|
+
<div class="input-group input-group-lg">
|
28
|
+
<div class="input-group-prepend">
|
29
|
+
<span class="input-group-text" id="totpInput-Prepend">TOTP</span>
|
30
|
+
</div>
|
31
|
+
<%= form.telephone_field :totp_code, id: "totpRemoval-code", class:"form-control", placeholder: "One Time Password Value" %>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
<br>
|
35
|
+
<div class="totpSubmit">
|
36
|
+
<div class="row">
|
37
|
+
<div class="col-md-6 offset-md-3">
|
38
|
+
<%= form.submit "Confirm TOTP Removal", class: " btn btn_danger btn-block" %>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
<% end %>
|
44
|
+
</div>
|
45
|
+
<div class="modal-footer">
|
46
|
+
<button type="button" class="mr-auto btn btn_secondary" data-dismiss="modal">Close</button>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
</div>
|