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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/rails_base/rails_base_query_checker.js +36 -0
  3. data/app/controllers/rails_base/admin_controller.rb +54 -9
  4. data/app/controllers/rails_base/mfa/evaluation_controller.rb +59 -0
  5. data/app/controllers/rails_base/mfa/register/sms_controller.rb +45 -0
  6. data/app/controllers/rails_base/mfa/register/totp_controller.rb +42 -0
  7. data/app/controllers/rails_base/mfa/validate/sms_controller.rb +83 -0
  8. data/app/controllers/rails_base/mfa/validate/totp_controller.rb +35 -0
  9. data/app/controllers/rails_base/secondary_authentication_controller.rb +40 -96
  10. data/app/controllers/rails_base/user_settings_controller.rb +11 -1
  11. data/app/controllers/rails_base/users/registrations_controller.rb +1 -1
  12. data/app/controllers/rails_base/users/sessions_controller.rb +16 -13
  13. data/app/controllers/rails_base_application_controller.rb +96 -1
  14. data/app/jobs/twilio_job.rb +1 -1
  15. data/app/mailers/rails_base/email_verification_mailer.rb +6 -4
  16. data/app/mailers/rails_base/event_mailer.rb +4 -2
  17. data/app/mailers/rails_base/mailer_kwarg_inject.rb +31 -0
  18. data/app/models/rails_base/user_constants.rb +6 -3
  19. data/app/models/rails_base/user_helper/totp/backup_method_options.rb +33 -0
  20. data/app/models/rails_base/user_helper/totp/class_options.rb +35 -0
  21. data/app/models/rails_base/user_helper/totp/consume_method_options.rb +60 -0
  22. data/app/models/rails_base/user_helper/totp.rb +41 -0
  23. data/app/models/user.rb +28 -13
  24. data/app/services/rails_base/authentication/constants.rb +1 -1
  25. data/app/services/rails_base/authentication/decision_twofa_type.rb +61 -30
  26. data/app/services/rails_base/authentication/send_forgot_password.rb +0 -1
  27. data/app/services/rails_base/authentication/single_sign_on_send.rb +1 -1
  28. data/app/services/rails_base/authentication/sso_verify_email.rb +3 -1
  29. data/app/services/rails_base/authentication/update_phone_send_verification.rb +2 -2
  30. data/app/services/rails_base/authentication/verify_forgot_password.rb +8 -11
  31. data/app/services/rails_base/mfa/decision.rb +70 -0
  32. data/app/services/rails_base/mfa/encrypt_token.rb +34 -0
  33. data/app/services/rails_base/mfa/sms/remove.rb +35 -0
  34. data/app/services/rails_base/{authentication/send_login_mfa_to_user.rb → mfa/sms/send.rb} +19 -13
  35. data/app/services/rails_base/mfa/sms/validate.rb +105 -0
  36. data/app/services/rails_base/mfa/strategy/base.rb +44 -0
  37. data/app/services/rails_base/mfa/strategy/every_request.rb +14 -0
  38. data/app/services/rails_base/mfa/strategy/skip_every_request.rb +14 -0
  39. data/app/services/rails_base/mfa/strategy/time_based.rb +24 -0
  40. data/app/services/rails_base/mfa/totp/helper.rb +21 -0
  41. data/app/services/rails_base/mfa/totp/otp_metadata.rb +19 -0
  42. data/app/services/rails_base/mfa/totp/remove.rb +40 -0
  43. data/app/services/rails_base/mfa/totp/validate_code.rb +52 -0
  44. data/app/services/rails_base/mfa/totp/validate_temporary_code.rb +37 -0
  45. data/app/services/rails_base/mfa.rb +18 -0
  46. data/app/services/rails_base/name_change.rb +3 -3
  47. data/app/views/layouts/rails_base/application.html.erb +22 -6
  48. data/app/views/rails_base/devise/passwords/new.html.erb +1 -1
  49. data/app/views/rails_base/mfa/_switch_mfa_type.html.erb +17 -0
  50. data/app/views/rails_base/mfa/validate/sms/sms_event_input.html.erb +2 -0
  51. data/app/views/rails_base/mfa/validate/totp/totp_event_input.html.erb +1 -0
  52. data/app/views/rails_base/secondary_authentication/reset_password_input.html.erb +4 -0
  53. data/app/views/rails_base/shared/_enable_mfa_auth_modal.html.erb +1 -1
  54. data/app/views/rails_base/shared/_logged_in_header.html.erb +1 -25
  55. data/app/views/rails_base/shared/_modify_mfa_auth_modal.html.erb +102 -3
  56. data/app/views/rails_base/shared/mfa/sms/_login_input.html.erb +13 -0
  57. data/app/views/rails_base/shared/mfa/totp/_login_input.html.erb +22 -0
  58. data/app/views/rails_base/shared/totp/_add_authenticator.html.erb +76 -0
  59. data/app/views/rails_base/shared/totp/_add_authenticator_modal.html.erb +25 -0
  60. data/app/views/rails_base/shared/totp/_confirm_code.html.erb +31 -0
  61. data/app/views/rails_base/shared/totp/_confirm_code_ajax.html.erb +3 -0
  62. data/app/views/rails_base/shared/totp/_confirm_code_rest.html.erb +5 -0
  63. data/app/views/rails_base/shared/totp/_remove_authenticator_modal.html.erb +50 -0
  64. data/app/views/rails_base/user_settings/index.html.erb +84 -1
  65. data/config/initializers/admin_action_helper.rb +44 -8
  66. data/config/routes.rb +42 -7
  67. data/db/migrate/20240808013706_add_totp_to_users.rb +9 -0
  68. data/db/migrate/20240825012724_reconfigure_mfa_variable_names.rb +10 -0
  69. data/lib/rails_base/admin/action_helper.rb +0 -1
  70. data/lib/rails_base/admin/default_index_tile.rb +3 -3
  71. data/lib/rails_base/config.rb +26 -22
  72. data/lib/rails_base/configuration/admin.rb +5 -5
  73. data/lib/rails_base/configuration/base.rb +1 -0
  74. data/lib/rails_base/configuration/mfa.rb +27 -60
  75. data/lib/rails_base/configuration/totp.rb +82 -0
  76. data/lib/rails_base/configuration/twilio.rb +85 -0
  77. data/lib/rails_base/mfa_event.rb +186 -0
  78. data/lib/rails_base/version.rb +3 -3
  79. data/lib/rails_base.rb +1 -0
  80. data/lib/twilio_helper.rb +3 -3
  81. metadata +129 -64
  82. data/app/controllers/rails_base/mfa_auth_controller.rb +0 -50
  83. data/app/services/rails_base/authentication/mfa_set_encrypt_token.rb +0 -32
  84. data/app/services/rails_base/authentication/mfa_validator.rb +0 -88
  85. data/app/views/rails_base/mfa_auth/mfa_code.html.erb +0 -11
  86. 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: 7be240ad162cad85b3a70e4163f99eea08a0eec55d8ce97ab6a9e83f59d6559a
4
- data.tar.gz: 8357b2607d6eae820b73be0e794ad6cab80912d7cf02d6524f4ba95d47626caf
3
+ metadata.gz: ac8c98816130bae4d552e2b82ecf75c5a3b8f6441936bb70d9663f07a1ee4c2f
4
+ data.tar.gz: f9df9901477573b0e827019a141996cec8c2192e940d334d91da624a8c69dc07
5
5
  SHA512:
6
- metadata.gz: 11ff5f552ba5e901555fdfd4df622a0c3bccef9e0e1f169fcfd50778143814ba7796483c2876121c67885ffc7dc9145c5ebc12954f47016497924a53c5c43d0a
7
- data.tar.gz: 2ae800d156442306b2f34374c1e0c5ab417e233c57c1aa341c516da5df110c2af3c0227dcee5112fb28ba51ed549b8cd598b0f15e38b77a3bad5c4f27d7bb02d
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.user_settings_path
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
- flash[:alert] = 'You do not have correct permissions to change a users name'
169
- redirect_to RailsBase.url_routes.admin_base_path
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
- flash[:alert] = 'You do not have correct permissions to change a users name'
194
- redirect_to RailsBase.url_routes.admin_base_path
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::Authentication::MfaValidator.call(params)
260
- encrypt = RailsBase::Authentication::MfaSetEncryptToken.call(user: admin_user, purpose: session_reason, expires_at: 1.minute.from_now)
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. Wee are struggling to render the page. Please try again later' }, status: 500
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 :validate_token!, only: [:resend_email, :wait, :confirm_phone_registration]
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 validate_token!(purpose: Authentication::Constants::SSOVE_PURPOSE)
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 validate_token!(purpose: Authentication::Constants::SSOVE_PURPOSE)
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 validate_token!(purpose: Authentication::Constants::SSOVE_PURPOSE)
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
- # POST auth/email/forgot/:data
132
- def forgot_password_with_mfa
133
- return unless validate_token!(purpose: Authentication::Constants::VFP_PURPOSE)
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
- # datum is expired because it was used with #forgot_password method
136
- # we dont care, we just want to ensure the correct user (multiple verification ways)
137
- # -- validate user by datum
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
- unless expired_datum
144
- redirect_to(RailsBase.url_routes.new_user_password_path, alert: I18n.t('authentication.forgot_password_with_mfa.expired_datum'))
145
- return
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
- result = Authentication::MfaValidator.call(params: params, session_mfa_user_id: @token_verifier.user_id, current_user: expired_datum.user)
149
- if result.failure?
150
- redirect_to(RailsBase.url_routes.new_user_password_path, alert: result.message)
151
- return
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 validate_token!(purpose: Authentication::Constants::VFP_PURPOSE)
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: @token_verifier.user_id, flow: :forgot_password)
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