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
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsBase
4
+ class MfaEvent
5
+ ENABLE_SMS_EVENT = :sms_enable
6
+ DISABLE_SMS_EVENT = :sms_disable
7
+ FORGOT_PASSWORD = :forgot_password
8
+ ADMIN_VERIFY = :admin_verify
9
+
10
+ class InvalidParameter < ArgumentError; end
11
+
12
+ attr_reader :only_mfa, :phone_number, :set_satiated_on_success, :satiated, :access_count, :flash_notice, :sign_in_user,
13
+ :invalid_redirect, :ttl, :user_id, :event, :description, :death_time, :redirect, :params, :access_count_max
14
+
15
+ def self.admin_actions(user:)
16
+ params = {
17
+ user: user,
18
+ event: ADMIN_VERIFY,
19
+ ttl: 30.seconds,
20
+ redirect: "",
21
+ invalid_redirect: "",
22
+ flash_notice: "",
23
+ }
24
+
25
+ new(**params)
26
+ end
27
+
28
+ def self.login_event(user:)
29
+ params = {
30
+ user: user,
31
+ event: :login,
32
+ ttl: 1.minutes,
33
+ redirect: RailsBase.url_routes.authenticated_root_path,
34
+ invalid_redirect: RailsBase.url_routes.unauthenticated_root_path,
35
+ sign_in_user: true,
36
+ flash_notice: "Welcome #{user.full_name}. You have succesfully signed in"
37
+ }
38
+
39
+ new(**params)
40
+ end
41
+
42
+ # This is a JSON event not html; Can leave redirects/notice empty
43
+ def self.sms_enable(user:)
44
+ params = {
45
+ user: user,
46
+ event: ENABLE_SMS_EVENT,
47
+ ttl: 5.minutes,
48
+ invalid_redirect: RailsBase.url_routes.user_settings_path,
49
+ redirect: RailsBase.url_routes.user_settings_path,
50
+ flash_notice: ""
51
+ }
52
+
53
+ new(**params)
54
+ end
55
+
56
+ def self.sms_disable(user:)
57
+ params = {
58
+ user: user,
59
+ event: DISABLE_SMS_EVENT,
60
+ ttl: 5.minutes,
61
+ invalid_redirect: RailsBase.url_routes.user_settings_path,
62
+ redirect: RailsBase.url_routes.user_settings_path,
63
+ flash_notice: "SMS option for MFA is disabled"
64
+ }
65
+
66
+ new(**params)
67
+ end
68
+
69
+ def self.forgot_password(user:, data:)
70
+ params = {
71
+ user: user,
72
+ event: FORGOT_PASSWORD,
73
+ ttl: 2.minutes,
74
+ invalid_redirect: RailsBase.url_routes.unauthenticated_root_path,
75
+ redirect: RailsBase.url_routes.reset_password_input_path(data:),
76
+ flash_notice: "MFA success. You may now reset your forgotten password",
77
+ access_count_max: 1,
78
+ }
79
+
80
+ new(**params)
81
+ end
82
+
83
+ def initialize(event:, flash_notice:, redirect:, only_mfa: nil, phone_number: nil, ttl: nil, death_time: nil, user_id: nil, user: nil, invalid_redirect: nil, sign_in_user: false, access_count: 0, access_count_max: nil, satiated: false, set_satiated_on_success: true)
84
+ @death_time = begin
85
+ raw = (death_time || ttl&.from_now)
86
+ Time.zone.parse(raw.to_s) rescue nil
87
+ end
88
+
89
+ @access_count = access_count
90
+ @access_count_max = access_count_max
91
+ @event = event
92
+ @flash_notice = flash_notice
93
+ @invalid_redirect = invalid_redirect || RailsBase.url_routes.authenticated_root_path
94
+ @only_mfa = only_mfa
95
+ @phone_number = phone_number
96
+ @redirect = redirect
97
+ @satiated = satiated
98
+ @set_satiated_on_success = set_satiated_on_success
99
+ @sign_in_user = sign_in_user
100
+ @user_id = user_id || user.id rescue nil
101
+
102
+ validate_data!
103
+ end
104
+
105
+ def to_hash
106
+ {
107
+ access_count:,
108
+ access_count_max:,
109
+ death_time:,
110
+ event:,
111
+ flash_notice:,
112
+ invalid_redirect:,
113
+ only_mfa:,
114
+ phone_number:,
115
+ redirect:,
116
+ satiated:,
117
+ set_satiated_on_success:,
118
+ sign_in_user:,
119
+ user_id:,
120
+ }
121
+ end
122
+
123
+ def access_count
124
+ @access_count
125
+ end
126
+
127
+ def satiated!
128
+ @satiated = true
129
+ end
130
+
131
+ def satiated?
132
+ @satiated
133
+ end
134
+
135
+ def increase_access_count!
136
+ @access_count += 1
137
+ end
138
+
139
+ def valid?
140
+ valid_by_death_time? && valid_by_access_count?
141
+ end
142
+
143
+ def valid_by_death_time?
144
+ death_time >= Time.now
145
+ end
146
+
147
+ def valid_by_access_count?
148
+ return true if @access_count_max.nil?
149
+
150
+ @access_count_max
151
+ end
152
+
153
+ def invalid_reasons
154
+ arr = []
155
+ arr << "Max Access count reached" unless valid_by_death_time?
156
+ arr << "#{event} has expired" unless valid_by_access_count?
157
+
158
+ arr
159
+ end
160
+
161
+ private
162
+
163
+ def validate_data!
164
+ raise_event!(value: @event, name: :event, klass: [String, Symbol])
165
+ raise_event!(value: @death_time, name: :death_time, klass: [ActiveSupport::TimeWithZone])
166
+ raise_event!(value: @redirect, name: :redirect, klass: [String])
167
+ raise_event!(value: @user_id, name: :user, klass: [Integer])
168
+ raise_event!(value: @flash_notice, name: :flash_notice, klass: [String])
169
+ raise_event!(value: @access_count, name: :access_count, klass: [Integer])
170
+ raise_event!(value: @access_count_max, name: :access_count_max, klass: [Integer, NilClass])
171
+ end
172
+
173
+ def raise_event!(value:, name:, klass:, &blk)
174
+ boolean = klass.include?(value.class)
175
+ raise_message = nil
176
+ if boolean && block_given?
177
+ raise_message = yield(value)
178
+ end
179
+ boolean = false if raise_message
180
+ return if boolean
181
+
182
+ message = raise_message || "@#{name}=#{value}. Value is expected to be in #{klass}"
183
+ raise InvalidParameter, message
184
+ end
185
+ end
186
+ end
@@ -1,7 +1,7 @@
1
1
  module RailsBase
2
- MAJOR = '0'
3
- MINOR = '75'
4
- PATCH = '6'
2
+ MAJOR = "0"
3
+ MINOR = "80"
4
+ PATCH = "0"
5
5
  VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
6
6
 
7
7
  def self.print_version
data/lib/rails_base.rb CHANGED
@@ -15,6 +15,7 @@ require 'switch_user'
15
15
 
16
16
  require 'rails_base/admin/action_cache'
17
17
  require 'rails_base/config'
18
+ require 'rails_base/mfa_event'
18
19
 
19
20
  module RailsBase
20
21
 
data/lib/twilio_helper.rb CHANGED
@@ -2,9 +2,9 @@ require 'twilio-ruby'
2
2
 
3
3
  class TwilioHelper
4
4
  class << self
5
- TWILIO_ACCOUNT_SID = RailsBase.config.mfa.twilio_sid
6
- TWILIO_AUTH_TOKEN = RailsBase.config.mfa.twilio_auth_token
7
- TWILIO_FROM_NUMBER = RailsBase.config.mfa.twilio_from_number
5
+ TWILIO_ACCOUNT_SID = RailsBase.config.twilio.twilio_sid
6
+ TWILIO_AUTH_TOKEN = RailsBase.config.twilio.twilio_auth_token
7
+ TWILIO_FROM_NUMBER = RailsBase.config.twilio.twilio_from_number
8
8
 
9
9
  def send_sms(message:, to:)
10
10
  Rails.logger.info "Sending Twilio message:[#{message}] to [#{to}]"