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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac8c98816130bae4d552e2b82ecf75c5a3b8f6441936bb70d9663f07a1ee4c2f
|
4
|
+
data.tar.gz: f9df9901477573b0e827019a141996cec8c2192e940d334d91da624a8c69dc07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f868b2bb553e9e1f4fcd4e14abda131b153e38f6b43f1bfdfef2f34b5a3d0b2d75d31a8eec7720d3a8339db74782706f9e0d39e7284256416130601233bb54d7
|
7
|
+
data.tar.gz: bf26e5f0ae48f7674ee614d7a159d352c990ddced01a7ca28ccb3215dae5a6b6f0b9c3e218b05ec4eb34d917da4c6296bdcd825a511ae8b8a7ab037317f854c8
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
function _railsBase_urlParams(param){
|
3
|
+
urlParams = new URLSearchParams(window.location.search);
|
4
|
+
return urlParams.get(param);
|
5
|
+
}
|
6
|
+
|
7
|
+
function _railsBase_goToStandardizedCollapse(q_param, identifier, function_base_name, function_yield){
|
8
|
+
param = _railsBase_urlParams(q_param)
|
9
|
+
if(param==null){
|
10
|
+
return false
|
11
|
+
}
|
12
|
+
|
13
|
+
// Let callee decide if they want to continue
|
14
|
+
if(typeof(function_yield) === "function") {
|
15
|
+
if (function_yield(param) != true) {
|
16
|
+
// Callee does not want to continue
|
17
|
+
return
|
18
|
+
}
|
19
|
+
} else {
|
20
|
+
// No function provided. Since the param was present, we will continue as expected
|
21
|
+
}
|
22
|
+
|
23
|
+
// Scroll to top of provided class
|
24
|
+
$('html, body').animate({
|
25
|
+
scrollTop: $(`${identifier}`).offset().top
|
26
|
+
}, 'slow');
|
27
|
+
|
28
|
+
|
29
|
+
// function name declared for the collapsable options
|
30
|
+
// Toggle it and open it up
|
31
|
+
console.log(`trying to open ${function_base_name}_collapse_toggle()`)
|
32
|
+
eval(`${function_base_name}_collapse_toggle()`)
|
33
|
+
|
34
|
+
return param
|
35
|
+
}
|
36
|
+
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RailsBase
|
2
4
|
class AdminController < RailsBaseApplicationController
|
3
5
|
before_action :authenticate_user!, except: [:sso_retrieve]
|
@@ -9,9 +11,10 @@ module RailsBase
|
|
9
11
|
|
10
12
|
# GET admin
|
11
13
|
def index
|
14
|
+
add_mfa_event_to_session(event: RailsBase::MfaEvent.admin_actions(user: current_user))
|
12
15
|
end
|
13
16
|
|
14
|
-
# GET admin
|
17
|
+
# GET admin/config
|
15
18
|
def show_config
|
16
19
|
unless RailsBase.config.admin.config_page?(current_user)
|
17
20
|
flash[:alert] = 'You do not have correct permissions to view admin config'
|
@@ -36,7 +39,7 @@ module RailsBase
|
|
36
39
|
uses: Authentication::Constants::SSO_SEND_USES,
|
37
40
|
reason: Authentication::Constants::SSO_REASON,
|
38
41
|
expires_at: Authentication::Constants::SSO_EXPIRES.from_now,
|
39
|
-
url_redirect: RailsBase.url_routes.
|
42
|
+
url_redirect: RailsBase.url_routes.authenticated_root_path
|
40
43
|
}
|
41
44
|
|
42
45
|
status = RailsBase::Authentication::SingleSignOnSend.call(local_params)
|
@@ -50,6 +53,7 @@ module RailsBase
|
|
50
53
|
redirect_to RailsBase.url_routes.admin_base_path
|
51
54
|
end
|
52
55
|
|
56
|
+
# TODO: Move this to a different controller
|
53
57
|
#GET auth/sso/:data
|
54
58
|
def sso_retrieve
|
55
59
|
local_params = {
|
@@ -153,6 +157,14 @@ module RailsBase
|
|
153
157
|
|
154
158
|
# POST admin/update
|
155
159
|
def update_attribute
|
160
|
+
unless RailsBase.config.admin.view_admin_page?(current_user)
|
161
|
+
session.clear
|
162
|
+
sign_out(current_user)
|
163
|
+
logger.warn("Unauthorized user has tried to update attributes. Fail Quickly!")
|
164
|
+
render json: { success: false, message: "Unauthorized action. You have been signed out" }, status: 404
|
165
|
+
return
|
166
|
+
end
|
167
|
+
|
156
168
|
update = RailsBase::AdminUpdateAttribute.call(params: params, admin_user: admin_user)
|
157
169
|
if update.success?
|
158
170
|
@_admin_action_struct = RailsBase::AdminStruct.new(update.original_attribute, update.attribute, update.model)
|
@@ -161,12 +173,17 @@ module RailsBase
|
|
161
173
|
@_admin_action_struct = false
|
162
174
|
render json: { success: false, message: update.message }, status: 404
|
163
175
|
end
|
176
|
+
|
177
|
+
session[:mfa_randomized_token] = nil
|
164
178
|
end
|
165
179
|
|
180
|
+
# POST admin/update/name
|
166
181
|
def update_name
|
167
182
|
unless RailsBase.config.admin.name_tile_users?(admin_user)
|
168
|
-
|
169
|
-
|
183
|
+
session.clear
|
184
|
+
sign_out(current_user)
|
185
|
+
logger.warn("Unauthorized user has tried to update attributes. Fail Quickly!")
|
186
|
+
render json: { success: false, message: "Unauthorized action. You have been signed out" }, status: 404
|
170
187
|
return
|
171
188
|
end
|
172
189
|
user = User.find(params[:id])
|
@@ -186,12 +203,17 @@ module RailsBase
|
|
186
203
|
@_admin_action_struct = false
|
187
204
|
render json: { success: false, message: "Failed to change #{user.id} name" }, status: 404
|
188
205
|
end
|
206
|
+
|
207
|
+
session[:mfa_randomized_token] = nil
|
189
208
|
end
|
190
209
|
|
210
|
+
# POST admin/update/email
|
191
211
|
def update_email
|
192
212
|
unless RailsBase.config.admin.email_tile_users?(admin_user)
|
193
|
-
|
194
|
-
|
213
|
+
session.clear
|
214
|
+
sign_out(current_user)
|
215
|
+
logger.warn("Unauthorized user has tried to update emai. Fail Quickly!")
|
216
|
+
render json: { success: false, message: "Unauthorized action. You have been signed out" }, status: 404
|
195
217
|
return
|
196
218
|
end
|
197
219
|
|
@@ -205,9 +227,20 @@ module RailsBase
|
|
205
227
|
@_admin_action_struct = false
|
206
228
|
render json: { success: false, message: result.message }, status: 404
|
207
229
|
end
|
230
|
+
|
231
|
+
session[:mfa_randomized_token] = nil
|
208
232
|
end
|
209
233
|
|
234
|
+
# POST admin/update/phone
|
210
235
|
def update_phone
|
236
|
+
unless RailsBase.config.admin.view_admin_page?(current_user)
|
237
|
+
session.clear
|
238
|
+
sign_out(current_user)
|
239
|
+
logger.warn("Unauthorized user has tried to update phone. Fail Quickly!")
|
240
|
+
render json: { success: false, message: "Unauthorized action. You have been signed out" }, status: 400
|
241
|
+
return
|
242
|
+
end
|
243
|
+
|
211
244
|
begin
|
212
245
|
params[:value] = params[:phone_number].gsub(/\D/,'')
|
213
246
|
rescue
|
@@ -220,6 +253,14 @@ module RailsBase
|
|
220
253
|
|
221
254
|
# POST admin/validate_intent/send
|
222
255
|
def send_2fa
|
256
|
+
unless RailsBase.config.admin.view_admin_page?(current_user)
|
257
|
+
session.clear
|
258
|
+
sign_out(current_user)
|
259
|
+
logger.warn("Unauthorized user has tried to send 2fa. Fail Quickly!")
|
260
|
+
render json: { success: false, message: "Unauthorized action. You have been signed out" }, status: 404
|
261
|
+
return
|
262
|
+
end
|
263
|
+
|
223
264
|
reason = "#{SESSION_REASON_BASE}-#{SecureRandom.uuid}"
|
224
265
|
result = AdminRiskyMfaSend.call(user: admin_user, reason: reason)
|
225
266
|
if result.success?
|
@@ -232,6 +273,8 @@ module RailsBase
|
|
232
273
|
|
233
274
|
# POST admin/validate_intent/verify
|
234
275
|
def verify_2fa
|
276
|
+
return unless validate_mfa_with_event_json!(mfa_event_name: RailsBase::MfaEvent::ADMIN_VERIFY)
|
277
|
+
|
235
278
|
unless modify_id = params[:modify_id]
|
236
279
|
logger.warn("Failed to find #{modify_id} in payload")
|
237
280
|
render json: { success: false, message: 'Hmm. Something fishy happend. Failed to find text to modify' }, status: 404
|
@@ -251,13 +294,14 @@ module RailsBase
|
|
251
294
|
end
|
252
295
|
|
253
296
|
params = {
|
297
|
+
mfa_event: @__rails_base_mfa_event,
|
254
298
|
session_mfa_user_id: admin_user.id,
|
255
299
|
current_user: admin_user,
|
256
300
|
input_reason: session_reason,
|
257
301
|
params: parse_mfa_to_obj
|
258
302
|
}
|
259
|
-
result = RailsBase::
|
260
|
-
encrypt = RailsBase::
|
303
|
+
result = RailsBase::Mfa::Sms::Validate.call(params)
|
304
|
+
encrypt = RailsBase::Mfa::EncryptToken.call(user: admin_user, purpose: session_reason, expires_at: 1.minute.from_now)
|
261
305
|
|
262
306
|
begin
|
263
307
|
html = render_to_string(partial: render_partial, locals: { user: user, modify_id: modify_id })
|
@@ -266,9 +310,10 @@ module RailsBase
|
|
266
310
|
logger.warn("Failed to render html correctly")
|
267
311
|
html = nil
|
268
312
|
end
|
313
|
+
|
269
314
|
if html.nil?
|
270
315
|
logger.warn("Failed to find render html correctly")
|
271
|
-
render json: { success: false, message: 'Apologies.
|
316
|
+
render json: { success: false, message: 'Apologies. We are struggling to render the page. Please try again later' }, status: 500
|
272
317
|
return
|
273
318
|
end
|
274
319
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBase::Mfa
|
4
|
+
class EvaluationController < RailsBaseApplicationController
|
5
|
+
before_action :authenticate_user!, only: [:mfa_evaluate_authenticated]
|
6
|
+
before_action :validate_mfa_with_event!
|
7
|
+
OTP_TEMPLATE = "rails_base/mfa/validate/totp/totp_event_input"
|
8
|
+
SMS_TEMPLATE = "rails_base/mfa/validate/sms/sms_event_input"
|
9
|
+
|
10
|
+
# GET mfa/:event
|
11
|
+
def mfa_with_event
|
12
|
+
user = User.find(@__rails_base_mfa_event.user_id)
|
13
|
+
decision = RailsBase::Mfa::Decision.(user: user)
|
14
|
+
mfa_type = mfa_decision(provided: params[:type], default: decision.mfa_type, allowed: decision.mfa_options)
|
15
|
+
|
16
|
+
if @__rails_base_mfa_event.phone_number
|
17
|
+
phone_number = @__rails_base_mfa_event.phone_number
|
18
|
+
else
|
19
|
+
phone_number = User.find(@__rails_base_mfa_event.user_id).phone_number
|
20
|
+
end
|
21
|
+
|
22
|
+
@masked_phone = User.masked_number(phone_number)
|
23
|
+
@mfa_options = decision.mfa_options.map do |type|
|
24
|
+
next if type == mfa_type
|
25
|
+
|
26
|
+
{
|
27
|
+
text: "Switch MFA to #{type}",
|
28
|
+
** RailsBase::Mfa.mfa_link(mfa_event: @__rails_base_mfa_event.event, mfa: type)
|
29
|
+
}
|
30
|
+
end.compact
|
31
|
+
|
32
|
+
case mfa_type
|
33
|
+
when RailsBase::Mfa::OTP
|
34
|
+
render OTP_TEMPLATE
|
35
|
+
when RailsBase::Mfa::SMS
|
36
|
+
render SMS_TEMPLATE
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def mfa_decision(provided:, default:, allowed:)
|
43
|
+
if Array === @__rails_base_mfa_event.only_mfa
|
44
|
+
logger.warn("MFA Event is forcing one of #{@__rails_base_mfa_event.only_mfa}")
|
45
|
+
return @__rails_base_mfa_event.only_mfa.sample.to_sym
|
46
|
+
end
|
47
|
+
|
48
|
+
# Nothing was provided by the user
|
49
|
+
return default if provided.nil?
|
50
|
+
|
51
|
+
# Provided input is an allowed type for the current user
|
52
|
+
return provided.to_sym if allowed.include?(provided.to_sym)
|
53
|
+
|
54
|
+
flash[:alert] = "Unknown MFA type #{provided}. Using #{default} instead"
|
55
|
+
|
56
|
+
return default
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBase::Mfa::Register
|
4
|
+
class SmsController < RailsBaseApplicationController
|
5
|
+
before_action :authenticate_user!
|
6
|
+
before_action ->() { validate_mfa_with_event!(mfa_event_name: RailsBase::MfaEvent::ENABLE_SMS_EVENT) }, only: [:sms_confirmation]
|
7
|
+
before_action ->() { validate_mfa_with_event!(mfa_event_name: RailsBase::MfaEvent::DISABLE_SMS_EVENT) }, only: [:sms_removal]
|
8
|
+
before_action :json_validate_current_user!, only: [:sms_registration]
|
9
|
+
|
10
|
+
# POST mfa/register/sms
|
11
|
+
def sms_registration
|
12
|
+
result = RailsBase::Authentication::UpdatePhoneSendVerification.call(user: current_user, phone_number: params[:phone_number])
|
13
|
+
if result.failure?
|
14
|
+
render :json => { error: I18n.t('request_response.teapot.fail'), msg: result.message }.to_json, :status => 418
|
15
|
+
return
|
16
|
+
end
|
17
|
+
session[:mfa_randomized_token] = result.mfa_randomized_token
|
18
|
+
render :json => { status: :success, message: I18n.t('request_response.teapot.valid') }
|
19
|
+
end
|
20
|
+
|
21
|
+
# POST mfa/register/sms/validate
|
22
|
+
def sms_confirmation
|
23
|
+
mfa_validity = RailsBase::Mfa::Sms::Validate.call(mfa_event: @__rails_base_mfa_event, current_user: current_user, params: params, session_mfa_user_id: @__rails_base_mfa_event.user_id)
|
24
|
+
if mfa_validity.failure?
|
25
|
+
redirect_to RailsBase.url_routes.user_settings_path, alert: I18n.t('authentication.confirm_phone_registration.fail', message: mfa_validity.message)
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
current_user.update!(mfa_sms_enabled: true)
|
30
|
+
redirect_to RailsBase.url_routes.user_settings_path, notice: "Successfully added SMS as an MFA option on your account"
|
31
|
+
end
|
32
|
+
|
33
|
+
# DELETE mfa/register/sms
|
34
|
+
def sms_removal
|
35
|
+
result = RailsBase::Mfa::Sms::Remove.(mfa_event: @__rails_base_mfa_event, current_user: current_user, session_mfa_user_id: @__rails_base_mfa_event.user_id, password: params[:password], sms_code: params[:sms_code])
|
36
|
+
if result.success?
|
37
|
+
flash[:notice] = "Successfully removed SMS as an MFA option on your account"
|
38
|
+
else
|
39
|
+
flash[:alert] = result.message
|
40
|
+
end
|
41
|
+
|
42
|
+
redirect_to RailsBase.url_routes.user_settings_path
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBase::Mfa::Register
|
4
|
+
class TotpController < RailsBaseApplicationController
|
5
|
+
before_action :authenticate_user!
|
6
|
+
|
7
|
+
# DELETE mfa/register/totp
|
8
|
+
def totp_remove
|
9
|
+
result = RailsBase::Mfa::Totp::Remove.(password: params[:password], user: current_user, otp_code: params[:totp_code])
|
10
|
+
|
11
|
+
if result.success?
|
12
|
+
flash[:notice] = "Successfully Removed TOTP Authentication to #{RailsBase.app_name}"
|
13
|
+
else
|
14
|
+
flash[:alert] = "Something Went Wrong! #{result.message}"
|
15
|
+
end
|
16
|
+
|
17
|
+
redirect_to RailsBase.url_routes.user_settings_path
|
18
|
+
end
|
19
|
+
|
20
|
+
# POST mfa/register/totp
|
21
|
+
def totp_secret
|
22
|
+
result = RailsBase::Mfa::Totp::OtpMetadata.(user: current_user)
|
23
|
+
if result.success?
|
24
|
+
render json: result.metadata
|
25
|
+
else
|
26
|
+
render json: { status: result.message }, status: 400
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# POST mfa/register/totp/validate
|
31
|
+
def totp_validate
|
32
|
+
result = RailsBase::Mfa::Totp::ValidateTemporaryCode.(user: current_user, otp_code: params[:totp_code])
|
33
|
+
if result.success?
|
34
|
+
flash[:notice] = "Successfully added an Authenticator for TOTP to #{RailsBase.app_name}"
|
35
|
+
else
|
36
|
+
flash[:alert] = "Something Went Wrong! Failed to add an Authenticator for TOTP to #{RailsBase.app_name}. Please try again"
|
37
|
+
end
|
38
|
+
|
39
|
+
redirect_to RailsBase.url_routes.user_settings_path
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBase::Mfa::Validate
|
4
|
+
class SmsController < RailsBaseApplicationController
|
5
|
+
before_action :validate_mfa_with_event!, only: [:sms_event_input, :sms_event]
|
6
|
+
|
7
|
+
# POST mfa/validate/sms/:mfa_event/send
|
8
|
+
def sms_event_send
|
9
|
+
if soft_mfa_with_event
|
10
|
+
user = User.find(@__rails_base_mfa_event.user_id)
|
11
|
+
else
|
12
|
+
if request.format.json?
|
13
|
+
render json: { message: @__rails_base_mfa_event_invalid_reason }, status: 400
|
14
|
+
else
|
15
|
+
flash[:alert] = @__rails_base_mfa_event_invalid_reason
|
16
|
+
redirect = @__rails_base_mfa_event&.invalid_redirect || RailsBase.url_routes.new_user_session_path
|
17
|
+
|
18
|
+
redirect_to redirect, email: params.dig(:user,:email)
|
19
|
+
end
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
if request.format.json?
|
24
|
+
# When json, this will always come from an authenticated user
|
25
|
+
# otherwise kick them out now!
|
26
|
+
return unless authenticate_user!
|
27
|
+
|
28
|
+
user = current_user
|
29
|
+
end
|
30
|
+
|
31
|
+
result = RailsBase::Mfa::Sms::Send.call(expires_at: 5.minutes.from_now, phone_number: @__rails_base_mfa_event.phone_number, user: user)
|
32
|
+
|
33
|
+
if result.success?
|
34
|
+
flash[:notice] = msg = "SMS Code succesfully sent. Please check messages"
|
35
|
+
status = 200
|
36
|
+
else
|
37
|
+
flash[:alert] = msg = "Unable to complete Request. #{result.message}"
|
38
|
+
status = 400
|
39
|
+
end
|
40
|
+
|
41
|
+
if request.format.json?
|
42
|
+
render json: { message: msg }, status: status
|
43
|
+
flash.clear
|
44
|
+
else
|
45
|
+
redirect_to RailsBase.url_routes.mfa_with_event_path(mfa_event: @__rails_base_mfa_event.event, type: RailsBase::Mfa::SMS)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# GET mfa/validate/sms/:mfa_event
|
50
|
+
def sms_event_input
|
51
|
+
if @__rails_base_mfa_event.phone_number
|
52
|
+
phone_number = @__rails_base_mfa_event.phone_number
|
53
|
+
else
|
54
|
+
phone_number = User.find(@__rails_base_mfa_event.user_id).phone_number
|
55
|
+
end
|
56
|
+
|
57
|
+
@masked_phone = User.masked_number(phone_number)
|
58
|
+
end
|
59
|
+
|
60
|
+
# POST mfa/validate/sms/:mfa_event
|
61
|
+
def sms_event
|
62
|
+
mfa_validity = RailsBase::Mfa::Sms::Validate.call(mfa_event: @__rails_base_mfa_event, params: params, session_mfa_user_id: @__rails_base_mfa_event.user_id)
|
63
|
+
if mfa_validity.failure?
|
64
|
+
redirect_to(mfa_validity.redirect_url, alert: mfa_validity.message)
|
65
|
+
return
|
66
|
+
end
|
67
|
+
|
68
|
+
mfa_validity.user.set_last_mfa_sms_login!
|
69
|
+
if @__rails_base_mfa_event.sign_in_user
|
70
|
+
logger.info("Logging User in")
|
71
|
+
sign_in(mfa_validity.user)
|
72
|
+
end
|
73
|
+
|
74
|
+
if @__rails_base_mfa_event.set_satiated_on_success
|
75
|
+
logger.info("Satiating MFA Event")
|
76
|
+
@__rails_base_mfa_event.satiated!
|
77
|
+
end
|
78
|
+
|
79
|
+
add_mfa_event_to_session(event: @__rails_base_mfa_event)
|
80
|
+
redirect_to @__rails_base_mfa_event.redirect, notice: @__rails_base_mfa_event.flash_notice
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBase::Mfa::Validate
|
4
|
+
class TotpController < RailsBaseApplicationController
|
5
|
+
before_action :validate_mfa_with_event!
|
6
|
+
|
7
|
+
# GET mfa/validate/totp/:event
|
8
|
+
def totp_event_input; end
|
9
|
+
|
10
|
+
# POST mfa/validate/totp/:event
|
11
|
+
def totp_event
|
12
|
+
user = User.find(@__rails_base_mfa_event.user_id)
|
13
|
+
mfa_validity = ::RailsBase::Mfa::Totp::ValidateCode.(user: user, otp_code: params[:totp_code])
|
14
|
+
if mfa_validity.failure?
|
15
|
+
redirect_to(RailsBase.url_routes.mfa_with_event_path(mfa_event: @__rails_base_mfa_event.event, type: RailsBase::Mfa::OTP), alert: mfa_validity.message)
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
user.set_last_mfa_otp_login!
|
20
|
+
|
21
|
+
if @__rails_base_mfa_event.sign_in_user
|
22
|
+
logger.info("Logging User in")
|
23
|
+
sign_in(mfa_validity.user)
|
24
|
+
end
|
25
|
+
|
26
|
+
if @__rails_base_mfa_event.set_satiated_on_success
|
27
|
+
logger.info("Satiating MFA Event")
|
28
|
+
@__rails_base_mfa_event.satiated!
|
29
|
+
end
|
30
|
+
|
31
|
+
add_mfa_event_to_session(event: @__rails_base_mfa_event)
|
32
|
+
redirect_to @__rails_base_mfa_event.redirect, notice: @__rails_base_mfa_event.flash_notice
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -2,13 +2,11 @@ module RailsBase
|
|
2
2
|
class SecondaryAuthenticationController < RailsBaseApplicationController
|
3
3
|
before_action :authenticate_user!, only: [:remove_phone_mfa, :confirm_phone_registration]
|
4
4
|
|
5
|
-
before_action :
|
6
|
-
|
7
|
-
before_action :json_validate_current_user!, only: [:phone_registration]
|
5
|
+
before_action :validate_mfa_token!, only: [:resend_email, :wait, :confirm_phone_registration]
|
8
6
|
|
9
7
|
# GET auth/wait
|
10
8
|
def static
|
11
|
-
return unless
|
9
|
+
return unless validate_mfa_token!(purpose: Authentication::Constants::SSOVE_PURPOSE)
|
12
10
|
|
13
11
|
if flash[:notice].nil? && flash[:alert].nil?
|
14
12
|
flash[:notice] = Authentication::Constants::STATIC_WAIT_FLASH
|
@@ -18,11 +16,6 @@ module RailsBase
|
|
18
16
|
def remove_me
|
19
17
|
end
|
20
18
|
|
21
|
-
def testing_route
|
22
|
-
Rails.logger.error("This will cause an error to be thrown")
|
23
|
-
raise ArgumentError, 'Boo'
|
24
|
-
end
|
25
|
-
|
26
19
|
# POST auth/resend_email
|
27
20
|
def resend_email
|
28
21
|
user = User.find @token_verifier.user_id
|
@@ -51,7 +44,7 @@ module RailsBase
|
|
51
44
|
|
52
45
|
# GET auth/login
|
53
46
|
def after_email_login_session_new
|
54
|
-
return unless
|
47
|
+
return unless validate_mfa_token!(purpose: Authentication::Constants::SSOVE_PURPOSE)
|
55
48
|
|
56
49
|
@user = User.new
|
57
50
|
if flash[:alert].nil? && flash[:notice].nil?
|
@@ -61,7 +54,7 @@ module RailsBase
|
|
61
54
|
|
62
55
|
# POST auth/login
|
63
56
|
def after_email_login_session_create
|
64
|
-
return unless
|
57
|
+
return unless validate_mfa_token!(purpose: Authentication::Constants::SSOVE_PURPOSE)
|
65
58
|
|
66
59
|
flash[:notice] = nil
|
67
60
|
flash[:alert] = nil
|
@@ -78,37 +71,6 @@ module RailsBase
|
|
78
71
|
redirect_to RailsBase.url_routes.authenticated_root_path
|
79
72
|
end
|
80
73
|
|
81
|
-
# POST auth/phone
|
82
|
-
def phone_registration
|
83
|
-
result = Authentication::UpdatePhoneSendVerification.call(user: current_user, phone_number: params[:phone_number])
|
84
|
-
if result.failure?
|
85
|
-
render :json => { error: I18n.t('request_response.teapot.fail'), msg: result.message }.to_json, :status => 418
|
86
|
-
return
|
87
|
-
end
|
88
|
-
session[:mfa_randomized_token] = result.mfa_randomized_token
|
89
|
-
|
90
|
-
render :json => { status: :success, message: I18n.t('request_response.teapot.valid') }
|
91
|
-
end
|
92
|
-
|
93
|
-
# POST auth/phone/mfa
|
94
|
-
def confirm_phone_registration
|
95
|
-
mfa_validity = Authentication::MfaValidator.call(current_user: current_user, params: params, session_mfa_user_id: @token_verifier.user_id)
|
96
|
-
if mfa_validity.failure?
|
97
|
-
redirect_to RailsBase.url_routes.authenticated_root_path, alert: I18n.t('authentication.confirm_phone_registration.fail', message: mfa_validity.message)
|
98
|
-
return
|
99
|
-
end
|
100
|
-
|
101
|
-
current_user.update!(mfa_enabled: true)
|
102
|
-
|
103
|
-
redirect_to RailsBase.url_routes.authenticated_root_path, notice: I18n.t('authentication.confirm_phone_registration.valid')
|
104
|
-
end
|
105
|
-
|
106
|
-
# DELETE auth/phone/disable
|
107
|
-
def remove_phone_mfa
|
108
|
-
current_user.update!(mfa_enabled: false, last_mfa_login: nil)
|
109
|
-
redirect_to RailsBase.url_routes.authenticated_root_path, notice: I18n.t('authentication.remove_phone_mfa')
|
110
|
-
end
|
111
|
-
|
112
74
|
# GET auth/email/forgot/:data
|
113
75
|
def forgot_password
|
114
76
|
result = Authentication::VerifyForgotPassword.call(data: params[:data])
|
@@ -117,52 +79,52 @@ module RailsBase
|
|
117
79
|
redirect_to result.redirect_url, alert: result.message
|
118
80
|
return
|
119
81
|
end
|
120
|
-
session[:mfa_randomized_token] = result.encrypted_val
|
121
|
-
flash[:notice] =
|
122
|
-
if @mfa_flow = result.mfa_flow
|
123
|
-
I18n.t('authentication.forgot_password.2fa')
|
124
|
-
else
|
125
|
-
I18n.t('authentication.forgot_password.base')
|
126
|
-
end
|
127
|
-
@user = result.user
|
128
|
-
@data = params[:data]
|
129
|
-
end
|
130
82
|
|
131
|
-
|
132
|
-
|
133
|
-
|
83
|
+
event = RailsBase::MfaEvent.forgot_password(user: result.user, data: params[:data])
|
84
|
+
if result.mfa_flow
|
85
|
+
flash[:notice] = "MFA required to reset password"
|
86
|
+
redirect_to(RailsBase.url_routes.mfa_with_event_path(mfa_event: event.event))
|
87
|
+
else
|
88
|
+
# Requirements to continue were satiatet..we can let the user reset their password
|
89
|
+
event.satiated!
|
90
|
+
flash[:notice] = "Datum valid. Reset your password"
|
91
|
+
redirect_to(RailsBase.url_routes.reset_password_input_path(data: params[:data]))
|
92
|
+
end
|
134
93
|
|
135
|
-
#
|
136
|
-
|
137
|
-
|
138
|
-
# -- validate user by short lived token
|
139
|
-
# -- validate user by mfa_token
|
140
|
-
# -- When all match by user and within the lifetime of the short lived token... we b gucci uber super secure/over engineered
|
141
|
-
expired_datum = ShortLivedData.get_by_data(data: params[:data], reason: Authentication::Constants::VFP_REASON)
|
94
|
+
# Upload event to the session as a last step to ensure we capture if it was satiated or not
|
95
|
+
add_mfa_event_to_session(event:)
|
96
|
+
end
|
142
97
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
end
|
98
|
+
# GET auth/password/reset/:data
|
99
|
+
def reset_password_input
|
100
|
+
return unless validate_mfa_with_event!(mfa_event_name: RailsBase::MfaEvent::FORGOT_PASSWORD)
|
147
101
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
102
|
+
if @__rails_base_mfa_event.satiated?
|
103
|
+
@data = params[:data]
|
104
|
+
@user = User.find(@__rails_base_mfa_event.user_id)
|
105
|
+
else
|
106
|
+
logger.error("MFA Event was not satiated. Kicking user back to root")
|
107
|
+
clear_mfa_event_from_session!(event_name: @__rails_base_mfa_event.event)
|
108
|
+
session.clear
|
109
|
+
flash[:alert] = "Unauthorized access"
|
110
|
+
redirect_to(RailsBase.url_routes.unauthenticated_root_path)
|
152
111
|
end
|
153
|
-
|
154
|
-
@mfa_flow = false
|
155
|
-
@data = params[:data]
|
156
|
-
@user = result.user
|
157
|
-
flash[:notice] = I18n.t('authentication.forgot_password_with_mfa.valid_mfa')
|
158
|
-
render :forgot_password
|
159
112
|
end
|
160
113
|
|
161
114
|
# POST auth/email/reset/:data
|
162
115
|
def reset_password
|
163
|
-
return unless
|
116
|
+
return unless validate_mfa_with_event!(mfa_event_name: RailsBase::MfaEvent::FORGOT_PASSWORD)
|
117
|
+
|
118
|
+
unless @__rails_base_mfa_event.satiated?
|
119
|
+
logger.error("MFA Event was not satiated. Kicking user back to root")
|
120
|
+
clear_mfa_event_from_session!(event_name: @__rails_base_mfa_event.event)
|
121
|
+
session.clear
|
122
|
+
flash[:alert] = "Unauthorized access"
|
123
|
+
redirect_to(RailsBase.url_routes.unauthenticated_root_path)
|
124
|
+
return
|
125
|
+
end
|
164
126
|
|
165
|
-
result = Authentication::ModifyPassword.call(password: params[:user][:password], password_confirmation: params[:user][:password_confirmation], data: params[:data], user_id: @
|
127
|
+
result = Authentication::ModifyPassword.call(password: params[:user][:password], password_confirmation: params[:user][:password_confirmation], data: params[:data], user_id: @__rails_base_mfa_event.user_id, flow: :forgot_password)
|
166
128
|
if result.failure?
|
167
129
|
redirect_to RailsBase.url_routes.new_user_password_path, alert: result.message
|
168
130
|
return
|
@@ -202,23 +164,5 @@ module RailsBase
|
|
202
164
|
flash[:notice] = I18n.t('authentication.sso_login.valid')
|
203
165
|
redirect_to url
|
204
166
|
end
|
205
|
-
|
206
|
-
private
|
207
|
-
|
208
|
-
def json_validate_current_user!
|
209
|
-
return if current_user
|
210
|
-
|
211
|
-
render json: { error: "Unauthorized" }.to_json, :status => 401
|
212
|
-
return false
|
213
|
-
end
|
214
|
-
|
215
|
-
def validate_token!(purpose: Authentication::Constants::MSET_PURPOSE)
|
216
|
-
@token_verifier =
|
217
|
-
Authentication::SessionTokenVerifier.call(purpose: purpose, mfa_randomized_token: session[:mfa_randomized_token])
|
218
|
-
return true if @token_verifier.success?
|
219
|
-
|
220
|
-
redirect_to RailsBase.url_routes.new_user_session_path, alert: @token_verifier.message
|
221
|
-
return false
|
222
|
-
end
|
223
167
|
end
|
224
168
|
end
|