rails_base 0.61.0 → 0.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/rails_base/admin_controller.rb +2 -2
- data/app/controllers/rails_base/errors_controller.rb +1 -1
- data/app/controllers/rails_base/mfa_auth_controller.rb +1 -1
- data/app/controllers/rails_base/secondary_authentication_controller.rb +1 -1
- data/app/controllers/rails_base/switch_user_controller.rb +1 -0
- data/app/controllers/rails_base/user_settings_controller.rb +1 -1
- data/app/controllers/rails_base_application_controller.rb +157 -0
- data/app/helpers/rails_base/user_settings_helper.rb +1 -1
- data/app/models/admin_action.rb +2 -1
- data/app/models/rails_base/application_record.rb +1 -1
- data/app/services/rails_base/admin_risky_mfa_send.rb +1 -1
- data/app/services/rails_base/authentication/mfa_set_encrypt_token.rb +1 -1
- data/app/services/rails_base/authentication/send_login_mfa_to_user.rb +1 -1
- data/app/services/rails_base/authentication/send_verification_email.rb +1 -1
- data/app/services/rails_base/authentication/single_sign_on_create.rb +1 -1
- data/app/services/rails_base/authentication/single_sign_on_send.rb +1 -1
- data/app/services/rails_base/authentication/single_sign_on_verify.rb +1 -1
- data/app/services/rails_base/encryption.rb +2 -2
- data/app/views/layouts/rails_base/application.html.erb +4 -8
- data/app/views/rails_base/shared/_appearance_mode_selector.html.erb +4 -4
- data/db/seeds.rb +7 -4
- data/lib/rails_base/admin/action_helper.rb +7 -4
- data/lib/rails_base/configuration/active_job.rb +1 -0
- data/lib/rails_base/configuration/admin.rb +4 -3
- data/lib/rails_base/configuration/app.rb +10 -5
- data/lib/rails_base/engine.rb +17 -6
- data/lib/rails_base/switch_user_helper.rb +36 -0
- data/lib/rails_base/version.rb +1 -1
- data/lib/rails_base.rb +7 -1
- metadata +9 -9
- data/app/controllers/rails_base/application_controller.rb +0 -153
- data/config/initializers/switch_user_helper.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b65060d8d2fc37e4df2c2cb83deda894be73dc4504570573580f2cf3b1c02919
|
4
|
+
data.tar.gz: 2bb0dc7ab74728e1cde281efa69362f21234dadaf4cd5edec1bec8f3c9a2bba0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4b519cd757c9513509e3141bdb429bb1be7eba89c91a67f7d7c23973d4d1869870651fd7d3aae95fea5ac625bc883c58ef630744687d1bad942b3eac4a39780
|
7
|
+
data.tar.gz: ba271fdbbab552800f0aa31052a1bd9e69342a365bbebfa7ec02bbbcc09d45b07f6c65055b2d321dccdca2ee21f115b92e8c91746c15463bdc07ba5c8c1d9fa8
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module RailsBase
|
2
|
-
class AdminController <
|
2
|
+
class AdminController < RailsBaseApplicationController
|
3
3
|
before_action :authenticate_user!, except: [:sso_retrieve]
|
4
4
|
before_action :admin_user?, only: [:index, :config, :sso_send]
|
5
5
|
before_action :validate_token!, only: [:update_email, :update_phone]
|
6
|
-
skip_before_action :admin_reset_impersonation_session
|
6
|
+
skip_before_action :admin_reset_impersonation_session!, raise: false
|
7
7
|
|
8
8
|
include AdminHelper
|
9
9
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module RailsBase
|
2
|
-
class SecondaryAuthenticationController <
|
2
|
+
class SecondaryAuthenticationController < RailsBaseApplicationController
|
3
3
|
before_action :authenticate_user!, only: [:remove_phone_mfa, :confirm_phone_registration]
|
4
4
|
|
5
5
|
before_action :validate_token!, only: [:resend_email, :wait, :confirm_phone_registration]
|
@@ -0,0 +1,157 @@
|
|
1
|
+
class RailsBaseApplicationController < ActionController::Base
|
2
|
+
layout 'rails_base/application'
|
3
|
+
|
4
|
+
before_action :configure_permitted_parameters, if: :devise_controller?
|
5
|
+
before_action :set_time_zone
|
6
|
+
before_action :is_timeout_error?
|
7
|
+
before_action :admin_reset_impersonation_session!
|
8
|
+
before_action :footer_mode_case
|
9
|
+
|
10
|
+
before_action :populate_admin_actions, if: -> { RailsBase.config.admin.enable_actions? }
|
11
|
+
after_action :capture_admin_action
|
12
|
+
|
13
|
+
include RailsBase::ApplicationHelper
|
14
|
+
helper_method :mfa_fallback?, :is_safari?, :is_mobile?
|
15
|
+
|
16
|
+
include RailsBase::AppearanceHelper
|
17
|
+
helper_method :appearance_text_class, :footer_mode_case, :appearance_mode_drop_down, :appearance_text_class
|
18
|
+
|
19
|
+
include RailsBase::CaptureReferenceHelper
|
20
|
+
|
21
|
+
def set_time_zone
|
22
|
+
return unless RailsBase.config.user.tz_user_defined?
|
23
|
+
return if current_user.nil?
|
24
|
+
|
25
|
+
# esape this since this is not signed
|
26
|
+
offset = cookies[TIMEZONE_OFFSET_COOKIE].to_i
|
27
|
+
|
28
|
+
cookie_tz = ActiveSupport::TimeZone[((offset * -1) / 60.0)]
|
29
|
+
|
30
|
+
if session_tz = session[TIMEZONE_SESSION_NAME]
|
31
|
+
# if session exists
|
32
|
+
if cookie_tz && session_tz != cookie_tz.name
|
33
|
+
# if cookie exists and cookie_tz does not match, update db and session
|
34
|
+
current_user.update_tz(tz_name: cookie_tz.name)
|
35
|
+
session[TIMEZONE_SESSION_NAME] = cookie_tz.name
|
36
|
+
end
|
37
|
+
else
|
38
|
+
# if session timezone does not exist, attempt to push to DB and set to session
|
39
|
+
current_user.update_tz(tz_name: cookie_tz.name)
|
40
|
+
session[TIMEZONE_SESSION_NAME] = cookie_tz.name
|
41
|
+
end
|
42
|
+
Thread.current[TIMEZONE_THREAD_NAME] = session[TIMEZONE_SESSION_NAME]
|
43
|
+
end
|
44
|
+
|
45
|
+
def is_timeout_error?
|
46
|
+
return if current_user || !params.keys.include?('timeout')
|
47
|
+
|
48
|
+
flash[:notice] = nil
|
49
|
+
flash[:alert] = 'Your session expired. Please sign in again to continue.'
|
50
|
+
end
|
51
|
+
|
52
|
+
def admin_impersonation_session?
|
53
|
+
return false if current_user.nil?
|
54
|
+
return false unless encrypted_val = session[RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON].presence
|
55
|
+
|
56
|
+
token = admin_get_token(encrypted_val: encrypted_val)
|
57
|
+
if token.failure?
|
58
|
+
logger.warn "Failed to parse encrypted token. Either expired or was not present"
|
59
|
+
flash[:alert] = 'Failed to retrieve Session token. Retry action'
|
60
|
+
redirect_to RailsBase.url_routes.admin_base_path
|
61
|
+
return false
|
62
|
+
else
|
63
|
+
logger.info "Found original_admin_user_id"
|
64
|
+
@original_admin_user_id = token.user_id
|
65
|
+
end
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def admin_reset_impersonation_session!
|
70
|
+
return unless admin_impersonation_session?
|
71
|
+
|
72
|
+
# at this point we know there is an impersonation
|
73
|
+
admin_user = User.find @original_admin_user_id
|
74
|
+
admin_set_token_on_session(admin_user: admin_user, other_user: current_user)
|
75
|
+
end
|
76
|
+
|
77
|
+
def admin_user?
|
78
|
+
return if RailsBase.config.admin.view_admin_page?(current_user)
|
79
|
+
|
80
|
+
session.clear
|
81
|
+
sign_out(current_user)
|
82
|
+
|
83
|
+
flash[:alert] = 'Unauthorized action. You have been signed out'
|
84
|
+
redirect_to RailsBase.url_routes.unauthenticated_root_path
|
85
|
+
end
|
86
|
+
|
87
|
+
def populate_admin_actions
|
88
|
+
return if session[RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON].present?
|
89
|
+
return if current_user.nil?
|
90
|
+
return unless request.fullpath == RailsBase.url_routes.authenticated_root_path
|
91
|
+
|
92
|
+
@__admin_actions_array = AdminAction.get_cache_items(user: current_user, alltime: true)
|
93
|
+
end
|
94
|
+
|
95
|
+
def capture_admin_action
|
96
|
+
# ToDo: Turn this into a service
|
97
|
+
# ToDo: All admin actions come there here: Allow this to be confirugable on or off
|
98
|
+
_controller = ActiveSupport::Inflector.camelize("#{params[:controller]}_controller")
|
99
|
+
admin_user =
|
100
|
+
if _controller == RailsBase::AdminController.to_s
|
101
|
+
current_user
|
102
|
+
else
|
103
|
+
@original_admin_user_id ? User.find(@original_admin_user_id) : nil
|
104
|
+
end
|
105
|
+
|
106
|
+
# Means we are not in the admin controller or we are not impersonating
|
107
|
+
return if admin_user.nil? || @_admin_action_struct == false
|
108
|
+
|
109
|
+
# Admin action for all routes
|
110
|
+
(RailsBase::Admin::ActionHelper.actions.dig(RailsBase::Admin::ActionHelper::ACTIONS_KEY) || []).each do |helper|
|
111
|
+
Rails.logger.warn("Admin Action for every action")
|
112
|
+
helper.call(req: request, params: params, admin_user: admin_user, user: current_user, struct: @_admin_action_struct)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Admin action for all controller routes
|
116
|
+
object = RailsBase::Admin::ActionHelper.actions.dig(_controller, RailsBase::Admin::ActionHelper::CONTROLLER_ACTIONS_KEY) || []
|
117
|
+
object.each do |helper|
|
118
|
+
Rails.logger.warn("Admin Action for #{_controller}")
|
119
|
+
helper.call(req: request, params: params, admin_user: admin_user, user: current_user, struct: @_admin_action_struct)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Admin action for all controller action specific routes
|
123
|
+
(RailsBase::Admin::ActionHelper.actions.dig(_controller, params[:action].to_s) || []).each do |helper|
|
124
|
+
Rails.logger.warn("Admin Action for #{_controller}##{params[:action]}")
|
125
|
+
helper.call(req: request, params: params, admin_user: admin_user, user: current_user, struct: @_admin_action_struct)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
protected
|
130
|
+
|
131
|
+
def admin_get_token(encrypted_val:)
|
132
|
+
params = {
|
133
|
+
mfa_randomized_token: encrypted_val,
|
134
|
+
purpose: RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON,
|
135
|
+
}
|
136
|
+
RailsBase::Authentication::SessionTokenVerifier.call(params)
|
137
|
+
end
|
138
|
+
|
139
|
+
def admin_set_token_on_session(admin_user:, other_user:)
|
140
|
+
if admin_user.id != other_user.id #dont do this if you are yourself
|
141
|
+
logger.warn { "Admin user [#{admin_user.id}] is impersonating user #{other_user.id}" }
|
142
|
+
params = {
|
143
|
+
user: admin_user,
|
144
|
+
purpose: RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON,
|
145
|
+
expires_at: RailsBase::Authentication::Constants::ADMIN_MAX_IDLE_TIME.from_now
|
146
|
+
}
|
147
|
+
encrpytion = RailsBase::Authentication::MfaSetEncryptToken.call(params)
|
148
|
+
session[RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON] = encrpytion.encrypted_val
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def configure_permitted_parameters
|
153
|
+
added_attrs = [:phone_number, :email, :password, :password_confirmation, :remember_me]
|
154
|
+
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
|
155
|
+
devise_parameter_sanitizer.permit :account_update, keys: added_attrs
|
156
|
+
end
|
157
|
+
end
|
data/app/models/admin_action.rb
CHANGED
@@ -20,6 +20,7 @@ class AdminAction < RailsBase::ApplicationRecord
|
|
20
20
|
|
21
21
|
class << self
|
22
22
|
include ActionView::Helpers::DateHelper
|
23
|
+
|
23
24
|
def action(admin_user:, action:, user: nil, original_attribute: nil, new_attribute: nil, long_action: nil)
|
24
25
|
params = { admin_user_id: admin_user.id, action: action }
|
25
26
|
params[:user_id] = user.id if user
|
@@ -27,7 +28,7 @@ class AdminAction < RailsBase::ApplicationRecord
|
|
27
28
|
params[:change_to] = new_attribute.to_s unless new_attribute.nil?
|
28
29
|
params[:long_action] = long_action unless long_action.nil?
|
29
30
|
begin
|
30
|
-
instance = AdminAction.create!(params)
|
31
|
+
instance = AdminAction.create!(**params)
|
31
32
|
ship_to_cache!(instance: instance, user: user, created_at: Time.zone.now) if user
|
32
33
|
instance
|
33
34
|
rescue StandardError => e
|
@@ -8,7 +8,7 @@ module RailsBase
|
|
8
8
|
|
9
9
|
# This is actually pretty cool. If you set the thread corectly, you can
|
10
10
|
define_method("#{column.name}") do
|
11
|
-
thread_tz = Thread.current[
|
11
|
+
thread_tz = Thread.current[RailsBaseApplicationController::TIMEZONE_THREAD_NAME]
|
12
12
|
return super() if thread_tz.nil?
|
13
13
|
time = self[column.name].in_time_zone(thread_tz) rescue self[column.name]
|
14
14
|
|
@@ -32,7 +32,7 @@ module RailsBase::Authentication
|
|
32
32
|
token_type: token_type,
|
33
33
|
url_redirect: url_redirect
|
34
34
|
}
|
35
|
-
datum = SingleSignOnCreate.call(params)
|
35
|
+
datum = SingleSignOnCreate.call(**params)
|
36
36
|
context.fail!(message: 'Failed to create SSO token. Try again') if datum.failure?
|
37
37
|
|
38
38
|
url = sso_url(data: datum.data.data)
|
@@ -26,7 +26,7 @@ class RailsBase::Encryption
|
|
26
26
|
raise "expires_at && expires_in are both nil" if expires_in.nil? && expires_at.nil?
|
27
27
|
|
28
28
|
log(level: :info, msg: "Encoding [#{value}] with params #{params}")
|
29
|
-
token = verifier.generate(value, params)
|
29
|
+
token = verifier.generate(value, **params)
|
30
30
|
token = CGI.escape(token) if url_safe
|
31
31
|
token
|
32
32
|
end
|
@@ -40,7 +40,7 @@ class RailsBase::Encryption
|
|
40
40
|
# TODO: matt-taylor
|
41
41
|
# Check if the message is valid and untampered with
|
42
42
|
# https://api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html#method-i-valid_message-3F
|
43
|
-
decoded = verifier.verified(value, params)
|
43
|
+
decoded = verifier.verified(value, **params)
|
44
44
|
if decoded.nil?
|
45
45
|
log(level: :warn, msg: "Failed to decode value: value: #{value}, purpose: #{purpose}")
|
46
46
|
end
|
@@ -50,20 +50,16 @@
|
|
50
50
|
}
|
51
51
|
}
|
52
52
|
|
53
|
-
<%
|
53
|
+
<% RailsBaseApplicationController::VIEWPORT_SIZES.each do |name, max_width| %>
|
54
54
|
function viewport_at_least_<%= name %>(){
|
55
55
|
return getViewportWidth() >= <%= max_width || 99_999 %>
|
56
56
|
|
57
57
|
}
|
58
|
-
<% if name == RailsBase::ApplicationController::VIEWPORT_MOBILE_MAX %>
|
59
|
-
<% puts "Rendering #{name} for viewport_probable_mobile" %>
|
60
|
-
|
61
|
-
<% end %>
|
62
58
|
<% end %>
|
63
59
|
|
64
60
|
<%
|
65
|
-
k =
|
66
|
-
size =
|
61
|
+
k = RailsBaseApplicationController::VIEWPORT_MOBILE_MAX
|
62
|
+
size = RailsBaseApplicationController::VIEWPORT_SIZES[k] || 99_999
|
67
63
|
%>
|
68
64
|
function viewport_probable_mobile(){
|
69
65
|
return getViewportWidth() <= <%= size %>
|
@@ -212,7 +208,7 @@
|
|
212
208
|
$('.tableFixHead').stickyTableHeaders();
|
213
209
|
|
214
210
|
// Attempt to set timezone on every request
|
215
|
-
set_cookie('<%=
|
211
|
+
set_cookie('<%= RailsBaseApplicationController::TIMEZONE_OFFSET_COOKIE %>', new Date().getTimezoneOffset())
|
216
212
|
});
|
217
213
|
<% end %>
|
218
214
|
<% if Rails.env == 'production' %>
|
@@ -53,8 +53,8 @@
|
|
53
53
|
<% light = RailsBase::Configuration::Appearance::LIGHT_MODE %>
|
54
54
|
var type_mapping = JSON.parse('<%= raw RailsBase::Configuration::Appearance::APPEARANCE_TYPES.to_json %>')
|
55
55
|
|
56
|
-
var cookie_name = `<%=
|
57
|
-
var cookie_actual_name = `<%=
|
56
|
+
var cookie_name = `<%= RailsBaseApplicationController::APPEARANCE_MODE_COOKIE %>`;
|
57
|
+
var cookie_actual_name = `<%= RailsBaseApplicationController::APPEARANCE_MODE_ACTUAL_COOKIE %>`;
|
58
58
|
var dark_mode_changes = [
|
59
59
|
{
|
60
60
|
'descriptor': 'thead',
|
@@ -151,11 +151,11 @@
|
|
151
151
|
})
|
152
152
|
// This function MUST be called outside of document ready
|
153
153
|
// to ensure dark mode does not activate after document is loaded
|
154
|
-
set_and_toggle_mode( `<%= cookies[
|
154
|
+
set_and_toggle_mode( `<%= cookies[RailsBaseApplicationController::APPEARANCE_MODE_COOKIE] || RailsBase.appearance.default_mode %>`)
|
155
155
|
$('#appearance_mode_selector').appendTo('body');
|
156
156
|
$(document).ready(function(){
|
157
157
|
// differentiation of load order means we need to call this twice
|
158
|
-
set_and_toggle_mode( `<%= cookies[
|
158
|
+
set_and_toggle_mode( `<%= cookies[RailsBaseApplicationController::APPEARANCE_MODE_COOKIE] || RailsBase.appearance.default_mode %>`)
|
159
159
|
$(document).ajaxComplete(function () {
|
160
160
|
set_and_toggle_mode()
|
161
161
|
});
|
data/db/seeds.rb
CHANGED
@@ -3,12 +3,14 @@ params = {
|
|
3
3
|
email: "mattius.taylor@gmail.com",
|
4
4
|
first_name: 'Some',
|
5
5
|
last_name: 'Guy',
|
6
|
-
phone_number: '
|
6
|
+
phone_number: ENV.fetch("PHONE_NUMBER", '6509410795'),
|
7
7
|
password: "password1",
|
8
|
-
password_confirmation: "password1"
|
8
|
+
password_confirmation: "password1",
|
9
|
+
email_validated: true,
|
9
10
|
}
|
10
11
|
|
11
|
-
User.create!(params)
|
12
|
+
user = User.create!(params)
|
13
|
+
user.admin_owner!
|
12
14
|
|
13
15
|
|
14
16
|
params = {
|
@@ -17,7 +19,8 @@ params = {
|
|
17
19
|
last_name: 'Guy2',
|
18
20
|
phone_number: '6508675309',
|
19
21
|
password: "password2",
|
20
|
-
password_confirmation: "password2"
|
22
|
+
password_confirmation: "password2",
|
23
|
+
email_validated: true,
|
21
24
|
}
|
22
25
|
|
23
26
|
User.create!(params)
|
@@ -1,8 +1,10 @@
|
|
1
|
+
require 'switch_user'
|
2
|
+
|
1
3
|
module RailsBase::Admin
|
2
4
|
class ActionHelper
|
3
5
|
ACTIONS_KEY = "___all_actions_#{(rand*10**10).to_i}___"
|
4
6
|
CONTROLLER_ACTIONS_KEY = "___all_controller_actions__#{(rand*10**10).to_i}___"
|
5
|
-
DEFAULT_ALLOWED_KLASSES = [ApplicationController,
|
7
|
+
DEFAULT_ALLOWED_KLASSES = [ApplicationController, RailsBaseApplicationController, ::SwitchUserController]
|
6
8
|
class << self
|
7
9
|
def allowed_inherited_klasses
|
8
10
|
DEFAULT_ALLOWED_KLASSES + (@allowed_klasses || [])
|
@@ -60,7 +62,7 @@ module RailsBase::Admin
|
|
60
62
|
class InvalidTitleError < StandardError; end;
|
61
63
|
|
62
64
|
attr_accessor :controller, :action, :proc, :title, :default
|
63
|
-
# controller is the controller class inherited by
|
65
|
+
# controller is the controller class inherited by RailsBaseApplicationController
|
64
66
|
# action is the method name on the controller
|
65
67
|
# title should be the AdminAction.action
|
66
68
|
# if proc available,
|
@@ -88,10 +90,11 @@ module RailsBase::Admin
|
|
88
90
|
action_params = proc.call(req, params, admin_user, user, title, struct)
|
89
91
|
return if action_params.nil?
|
90
92
|
|
91
|
-
AdminAction.action(action_params)
|
93
|
+
AdminAction.action(**action_params)
|
92
94
|
else
|
93
95
|
default_call(request: request, admin_user: admin_user, user: user, struct: struct)
|
94
96
|
end
|
97
|
+
|
95
98
|
rescue StandardError => e
|
96
99
|
Rails.logger.error(e.message)
|
97
100
|
Rails.logger.error(e.backtrace)
|
@@ -108,7 +111,7 @@ module RailsBase::Admin
|
|
108
111
|
change_from: struct&.original_attribute,
|
109
112
|
change_to: struct&.new_attribute,
|
110
113
|
}
|
111
|
-
AdminAction.action(action_params)
|
114
|
+
AdminAction.action(**action_params)
|
112
115
|
end
|
113
116
|
|
114
117
|
def valid_controller!
|
@@ -5,6 +5,7 @@ module RailsBase
|
|
5
5
|
if val.is_a?(Symbol)
|
6
6
|
begin
|
7
7
|
::ActiveJob::QueueAdapters.lookup(val)
|
8
|
+
RailsBase::ApplicationJob.queue_adapter = val.to_sym
|
8
9
|
Rails.configuration.active_job.queue_adapter = val.to_sym
|
9
10
|
rescue StandardError => e
|
10
11
|
raise ArgumentError, "config.app.active_job_adapter=#{val} is not a defined active job"
|
@@ -192,12 +192,12 @@ module RailsBase
|
|
192
192
|
},
|
193
193
|
admin_impersonate_redirect:{
|
194
194
|
type: :proc,
|
195
|
-
default: ->(
|
196
|
-
description: 'Redirection to impersonation -- Landing page when having an identity
|
195
|
+
default: ->(*) { RailsBase.url_routes.authenticated_root_path },
|
196
|
+
description: 'Redirection to impersonation -- Landing page when having an identity crisis',
|
197
197
|
},
|
198
198
|
admin_impersonate_return:{
|
199
199
|
type: :proc,
|
200
|
-
default: ->(
|
200
|
+
default: ->(*) { RailsBase.url_routes.admin_base_path },
|
201
201
|
description: 'Redirection from impersonation -- Page to return from when you have found yourself',
|
202
202
|
}
|
203
203
|
}
|
@@ -230,6 +230,7 @@ module RailsBase
|
|
230
230
|
# User.admin_owner
|
231
231
|
# User.admin_owners
|
232
232
|
# This is 100% dependent upon keeping `admin_types` in order of precedence
|
233
|
+
require RailsBase::Engine.root.join('app','models', 'user.rb')
|
233
234
|
admin_types.each do |type|
|
234
235
|
::User._def_admin_convenience_method!(admin_method: type)
|
235
236
|
end
|
@@ -4,6 +4,11 @@ module RailsBase
|
|
4
4
|
module Configuration
|
5
5
|
class App < Base
|
6
6
|
DEFAULT_VALUES = {
|
7
|
+
app_name: {
|
8
|
+
type: :string_proc,
|
9
|
+
default: ->(*) { RailsBase.default_app_name },
|
10
|
+
description: 'Name of the application when authenticated user is present. Name in the tab of the browser. Allows for dynamic tab names'
|
11
|
+
},
|
7
12
|
base_url: {
|
8
13
|
type: :string,
|
9
14
|
default: ENV.fetch('BASE_URL', 'http://localhost'),
|
@@ -16,27 +21,27 @@ module RailsBase
|
|
16
21
|
},
|
17
22
|
web_name_logged_in: {
|
18
23
|
type: :string_proc,
|
19
|
-
default: ->(
|
24
|
+
default: ->(*) { RailsBase.config.app.app_name },
|
20
25
|
description: 'Name of the application when authenticated user is present. Name in the tab of the browser. Allows for dynamic tab names'
|
21
26
|
},
|
22
27
|
web_name_logged_out: {
|
23
28
|
type: :string_proc,
|
24
|
-
default: ->(*) { RailsBase.app_name },
|
29
|
+
default: ->(*) { RailsBase.config.app.app_name },
|
25
30
|
description: 'Name of the application when no authenticated user. Name in the tab of the browser. Allows for dynamic tab names'
|
26
31
|
},
|
27
32
|
web_title_logged_in: {
|
28
33
|
type: :string_proc,
|
29
|
-
default: ->(user) { RailsBase.app_name },
|
34
|
+
default: ->(user) { RailsBase.config.app.app_name },
|
30
35
|
description: 'Title in nav for the web when logged in. String or proc accepted. When proc, current user will be passed in.'
|
31
36
|
},
|
32
37
|
web_title_logged_out: {
|
33
38
|
type: :string_proc,
|
34
|
-
default: ->(*) { RailsBase.app_name },
|
39
|
+
default: ->(*) { RailsBase.config.app.app_name },
|
35
40
|
description: 'Title in nav for the web when logged in. String or proc accepted. When proc, current user will be passed in.'
|
36
41
|
},
|
37
42
|
communication_name: {
|
38
43
|
type: :string_proc,
|
39
|
-
default: ->(*) { RailsBase.app_name },
|
44
|
+
default: ->(*) { RailsBase.config.app.app_name },
|
40
45
|
description: 'Name used when communicating with users.'
|
41
46
|
},
|
42
47
|
favicon_path: {
|
data/lib/rails_base/engine.rb
CHANGED
@@ -1,25 +1,29 @@
|
|
1
1
|
module RailsBase
|
2
2
|
class Engine < ::Rails::Engine
|
3
3
|
isolate_namespace RailsBase
|
4
|
+
|
5
|
+
# config.autoload_paths << File.expand_path("app", __dir__)
|
6
|
+
|
4
7
|
ActiveSupport::Reloader.to_prepare do
|
5
8
|
if RailsBase.___execute_initializer___?
|
6
9
|
RailsBase.config.admin.convenience_methods
|
7
10
|
|
11
|
+
Dir[RailsBase::Engine.root.join('app','models','**', '*.rb')].each {|f| require f }
|
8
12
|
RailsBase::ApplicationRecord.descendants.each do |model|
|
9
13
|
model._magically_defined_time_objects
|
10
14
|
end
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
14
|
-
initializer '
|
18
|
+
initializer 'rails_base.config.intantiate' do |_app|
|
15
19
|
RailsBase.config if RailsBase.___execute_initializer___?
|
16
20
|
end
|
17
21
|
|
18
|
-
initializer '
|
22
|
+
initializer 'rails_base.config.remove_write_acess', after: 'after_initialize' do |app|
|
19
23
|
RailsBase::Configuration::Base._unset_allow_write! if RailsBase.___execute_initializer___?
|
20
24
|
end
|
21
25
|
|
22
|
-
initializer '
|
26
|
+
initializer 'rails_base.magic_convenience_methods.model', after: 'active_record.initialize_database' do |app|
|
23
27
|
if RailsBase.___execute_initializer___?
|
24
28
|
# need to eager load Models
|
25
29
|
Rails.application.eager_load!
|
@@ -28,18 +32,17 @@ module RailsBase
|
|
28
32
|
ActiveRecord::Base.retrieve_connection
|
29
33
|
|
30
34
|
#explicitly load engine routes
|
31
|
-
Dir.entries(RailsBase::Engine.root.join('app','models')).select{|s| s.ends_with?('.rb')}.each {|f| require f}
|
32
35
|
RailsBase::ApplicationRecord.descendants.each do |model|
|
33
36
|
model._magically_defined_time_objects
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
38
|
-
initializer '
|
41
|
+
initializer 'rails_base.switch_user.remove_routes', after: 'add_routing_paths' do |app|
|
39
42
|
app.routes_reloader.paths.delete_if{ |path| path.include?('switch_user') }
|
40
43
|
end
|
41
44
|
|
42
|
-
initializer '
|
45
|
+
initializer 'rails_base.append_engine_migrations' do |app|
|
43
46
|
unless app.root.to_s.match root.to_s
|
44
47
|
config.paths["db/migrate"].expanded.each do |expanded_path|
|
45
48
|
app.config.paths["db/migrate"] << expanded_path
|
@@ -47,6 +50,14 @@ module RailsBase
|
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
53
|
+
initializer 'rails_base.switch_user.view' do
|
54
|
+
config.to_prepare do
|
55
|
+
ActiveSupport.on_load(:action_view) do
|
56
|
+
require RailsBase::Engine.root.join('lib', 'rails_base', 'switch_user_helper.rb')
|
50
57
|
|
58
|
+
include RailsBase::SwitchUserHelper
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
51
62
|
end
|
52
63
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'switch_user'
|
2
|
+
|
3
|
+
module RailsBase
|
4
|
+
module SwitchUserHelper
|
5
|
+
def switch_user_custom(options = {})
|
6
|
+
return unless available?
|
7
|
+
|
8
|
+
selected_user = nil
|
9
|
+
|
10
|
+
grouped_options_container =
|
11
|
+
{}.tap do |h|
|
12
|
+
SwitchUser.all_users.each do |record|
|
13
|
+
scope = record.is_a?(SwitchUser::GuestRecord) ? :Guest : record.scope.to_s.capitalize
|
14
|
+
h[scope] ||= []
|
15
|
+
h[scope] << [record.label, record.scope_id]
|
16
|
+
|
17
|
+
next unless selected_user.nil?
|
18
|
+
next if record.is_a?(SwitchUser::GuestRecord)
|
19
|
+
|
20
|
+
selected_user = record.scope_id if provider.current_user?(record.user, record.scope)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
option_tags = grouped_options_for_select(grouped_options_container.to_a, selected_user)
|
25
|
+
|
26
|
+
render partial: 'rails_base/switch_user/widget',
|
27
|
+
locals: { option_tags: option_tags, classes: options[:class], styles: options[:style] }
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def available?
|
33
|
+
SwitchUser.guard_class.new(controller, provider).view_available?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/rails_base/version.rb
CHANGED
data/lib/rails_base.rb
CHANGED
@@ -18,8 +18,10 @@ require 'rails_base/config'
|
|
18
18
|
|
19
19
|
module RailsBase
|
20
20
|
|
21
|
-
# Rails 6 does not play nice with this function -- Find a different work around
|
22
21
|
def self.___execute_initializer___?
|
22
|
+
# Fixes rails 6 changes to ARGV's -- dont reun initializers during rake tasks
|
23
|
+
return false if Rake.application.top_level_tasks.any? { |task| task.include?(":") } rescue nil
|
24
|
+
|
23
25
|
# Only execute when not doing DB actions
|
24
26
|
boolean = defined?(ARGV) ? true : false # for when no ARGVs are provided, we know its a railsc or rails s explicit
|
25
27
|
boolean = false if boolean && ARGV[0]&.include?('db') # when its the DB rake tasks
|
@@ -35,6 +37,10 @@ module RailsBase
|
|
35
37
|
end
|
36
38
|
|
37
39
|
def self.app_name
|
40
|
+
config.app.app_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.default_app_name
|
38
44
|
if ::Rails::VERSION::MAJOR >= 6
|
39
45
|
::Rails.application.class.module_parent_name
|
40
46
|
else
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_base
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.70.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Taylor
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: mysql2
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -420,7 +420,6 @@ files:
|
|
420
420
|
- app/assets/stylesheets/rails_base/secondary_authentication.scss
|
421
421
|
- app/assets/stylesheets/rails_base/user_settings.scss
|
422
422
|
- app/controllers/rails_base/admin_controller.rb
|
423
|
-
- app/controllers/rails_base/application_controller.rb
|
424
423
|
- app/controllers/rails_base/errors_controller.rb
|
425
424
|
- app/controllers/rails_base/mfa_auth_controller.rb
|
426
425
|
- app/controllers/rails_base/secondary_authentication_controller.rb
|
@@ -429,6 +428,7 @@ files:
|
|
429
428
|
- app/controllers/rails_base/users/passwords_controller.rb
|
430
429
|
- app/controllers/rails_base/users/registrations_controller.rb
|
431
430
|
- app/controllers/rails_base/users/sessions_controller.rb
|
431
|
+
- app/controllers/rails_base_application_controller.rb
|
432
432
|
- app/helpers/rails_base/admin_helper.rb
|
433
433
|
- app/helpers/rails_base/appearance_helper.rb
|
434
434
|
- app/helpers/rails_base/application_helper.rb
|
@@ -546,7 +546,6 @@ files:
|
|
546
546
|
- config/initializers/devise.rb
|
547
547
|
- config/initializers/encryption.rb
|
548
548
|
- config/initializers/switch_user.rb
|
549
|
-
- config/initializers/switch_user_helper.rb
|
550
549
|
- config/locales/devise.en.yml
|
551
550
|
- config/locales/en.yml
|
552
551
|
- config/routes.rb
|
@@ -594,6 +593,7 @@ files:
|
|
594
593
|
- lib/rails_base/configuration/templates.rb
|
595
594
|
- lib/rails_base/configuration/user.rb
|
596
595
|
- lib/rails_base/engine.rb
|
596
|
+
- lib/rails_base/switch_user_helper.rb
|
597
597
|
- lib/rails_base/version.rb
|
598
598
|
- lib/tasks/rails_base_tasks.rake
|
599
599
|
- lib/twilio_helper.rb
|
@@ -617,7 +617,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
617
617
|
- !ruby/object:Gem::Version
|
618
618
|
version: '0'
|
619
619
|
requirements: []
|
620
|
-
rubygems_version: 3.2.
|
620
|
+
rubygems_version: 3.2.32
|
621
621
|
signing_key:
|
622
622
|
specification_version: 4
|
623
623
|
summary: Rails engine that takes care of the stuff you dont want to!
|
@@ -1,153 +0,0 @@
|
|
1
|
-
module RailsBase
|
2
|
-
class ApplicationController < ActionController::Base
|
3
|
-
before_action :configure_permitted_parameters, if: :devise_controller?
|
4
|
-
before_action :set_time_zone
|
5
|
-
before_action :is_timeout_error?
|
6
|
-
before_action :admin_reset_impersonation_session!
|
7
|
-
before_action :footer_mode_case
|
8
|
-
|
9
|
-
before_action :populate_admin_actions, if: -> { RailsBase.config.admin.enable_actions? }
|
10
|
-
after_action :capture_admin_action
|
11
|
-
|
12
|
-
include ApplicationHelper
|
13
|
-
include AppearanceHelper
|
14
|
-
include CaptureReferenceHelper
|
15
|
-
|
16
|
-
def set_time_zone
|
17
|
-
return unless RailsBase.config.user.tz_user_defined?
|
18
|
-
return if current_user.nil?
|
19
|
-
|
20
|
-
# esape this since this is not signed
|
21
|
-
offset = cookies[TIMEZONE_OFFSET_COOKIE].to_i
|
22
|
-
|
23
|
-
cookie_tz = ActiveSupport::TimeZone[((offset * -1) / 60.0)]
|
24
|
-
|
25
|
-
if session_tz = session[TIMEZONE_SESSION_NAME]
|
26
|
-
# if session exists
|
27
|
-
if cookie_tz && session_tz != cookie_tz.name
|
28
|
-
# if cookie exists and cookie_tz does not match, update db and session
|
29
|
-
current_user.update_tz(tz_name: cookie_tz.name)
|
30
|
-
session[TIMEZONE_SESSION_NAME] = cookie_tz.name
|
31
|
-
end
|
32
|
-
else
|
33
|
-
# if session timezone does not exist, attempt to push to DB and set to session
|
34
|
-
current_user.update_tz(tz_name: cookie_tz.name)
|
35
|
-
session[TIMEZONE_SESSION_NAME] = cookie_tz.name
|
36
|
-
end
|
37
|
-
Thread.current[TIMEZONE_THREAD_NAME] = session[TIMEZONE_SESSION_NAME]
|
38
|
-
end
|
39
|
-
|
40
|
-
def is_timeout_error?
|
41
|
-
return if current_user || !params.keys.include?('timeout')
|
42
|
-
|
43
|
-
flash[:notice] = nil
|
44
|
-
flash[:alert] = 'Your session expired. Please sign in again to continue.'
|
45
|
-
end
|
46
|
-
|
47
|
-
def admin_impersonation_session?
|
48
|
-
return false if current_user.nil?
|
49
|
-
return false unless encrypted_val = session[RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON].presence
|
50
|
-
|
51
|
-
token = admin_get_token(encrypted_val: encrypted_val)
|
52
|
-
if token.failure?
|
53
|
-
logger.warn "Failed to parse encrypted token. Either expired or was not present"
|
54
|
-
flash[:alert] = 'Failed to retrieve Session token. Retry action'
|
55
|
-
redirect_to RailsBase.url_routes.admin_base_path
|
56
|
-
return false
|
57
|
-
else
|
58
|
-
logger.info "Found original_admin_user_id"
|
59
|
-
@original_admin_user_id = token.user_id
|
60
|
-
end
|
61
|
-
true
|
62
|
-
end
|
63
|
-
|
64
|
-
def admin_reset_impersonation_session!
|
65
|
-
return unless admin_impersonation_session?
|
66
|
-
|
67
|
-
# at this point we know there is an impersonation
|
68
|
-
admin_user = User.find @original_admin_user_id
|
69
|
-
admin_set_token_on_session(admin_user: admin_user, other_user: current_user)
|
70
|
-
end
|
71
|
-
|
72
|
-
def admin_user?
|
73
|
-
return if RailsBase.config.admin.view_admin_page?(current_user)
|
74
|
-
|
75
|
-
session.clear
|
76
|
-
sign_out(current_user)
|
77
|
-
|
78
|
-
flash[:alert] = 'Unauthorized action. You have been signed out'
|
79
|
-
redirect_to RailsBase.url_routes.unauthenticated_root_path
|
80
|
-
end
|
81
|
-
|
82
|
-
def populate_admin_actions
|
83
|
-
return if session[RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON].present?
|
84
|
-
return if current_user.nil?
|
85
|
-
return unless request.fullpath == RailsBase.url_routes.authenticated_root_path
|
86
|
-
|
87
|
-
@__admin_actions_array = AdminAction.get_cache_items(user: current_user, alltime: true)
|
88
|
-
end
|
89
|
-
|
90
|
-
def capture_admin_action
|
91
|
-
# ToDo: Turn this into a service
|
92
|
-
# ToDo: All admin actions come there here: Allow this to be confirugable on or off
|
93
|
-
_controller = ActiveSupport::Inflector.camelize("#{params[:controller]}_controller")
|
94
|
-
admin_user =
|
95
|
-
if _controller == RailsBase::AdminController.to_s
|
96
|
-
current_user
|
97
|
-
else
|
98
|
-
@original_admin_user_id ? User.find(@original_admin_user_id) : nil
|
99
|
-
end
|
100
|
-
|
101
|
-
# Means we are not in the admin controller or we are not impersonating
|
102
|
-
return if admin_user.nil? || @_admin_action_struct == false
|
103
|
-
|
104
|
-
# Admin action for all routes
|
105
|
-
(RailsBase::Admin::ActionHelper.actions.dig(RailsBase::Admin::ActionHelper::ACTIONS_KEY) || []).each do |helper|
|
106
|
-
Rails.logger.warn("Admin Action for every action")
|
107
|
-
helper.call(req: request, params: params, admin_user: admin_user, user: current_user, struct: @_admin_action_struct)
|
108
|
-
end
|
109
|
-
|
110
|
-
# Admin action for all controller routes
|
111
|
-
object = RailsBase::Admin::ActionHelper.actions.dig(_controller, RailsBase::Admin::ActionHelper::CONTROLLER_ACTIONS_KEY) || []
|
112
|
-
object.each do |helper|
|
113
|
-
Rails.logger.warn("Admin Action for #{_controller}")
|
114
|
-
helper.call(req: request, params: params, admin_user: admin_user, user: current_user, struct: @_admin_action_struct)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Admin action for all controller action specific routes
|
118
|
-
(RailsBase::Admin::ActionHelper.actions.dig(_controller, params[:action].to_s) || []).each do |helper|
|
119
|
-
Rails.logger.warn("Admin Action for #{_controller}##{params[:action]}")
|
120
|
-
helper.call(req: request, params: params, admin_user: admin_user, user: current_user, struct: @_admin_action_struct)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
protected
|
125
|
-
|
126
|
-
def admin_get_token(encrypted_val:)
|
127
|
-
params = {
|
128
|
-
mfa_randomized_token: encrypted_val,
|
129
|
-
purpose: RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON,
|
130
|
-
}
|
131
|
-
RailsBase::Authentication::SessionTokenVerifier.call(params)
|
132
|
-
end
|
133
|
-
|
134
|
-
def admin_set_token_on_session(admin_user:, other_user:)
|
135
|
-
if admin_user.id != other_user.id #dont do this if you are yourself
|
136
|
-
logger.warn { "Admin user [#{admin_user.id}] is impersonating user #{other_user.id}" }
|
137
|
-
params = {
|
138
|
-
user: admin_user,
|
139
|
-
purpose: RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON,
|
140
|
-
expires_at: RailsBase::Authentication::Constants::ADMIN_MAX_IDLE_TIME.from_now
|
141
|
-
}
|
142
|
-
encrpytion = RailsBase::Authentication::MfaSetEncryptToken.call(params)
|
143
|
-
session[RailsBase::Authentication::Constants::ADMIN_REMEMBER_REASON] = encrpytion.encrypted_val
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def configure_permitted_parameters
|
148
|
-
added_attrs = [:phone_number, :email, :password, :password_confirmation, :remember_me]
|
149
|
-
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
|
150
|
-
devise_parameter_sanitizer.permit :account_update, keys: added_attrs
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'switch_user'
|
2
|
-
require SwitchUser::Engine.root.join('app', 'helpers', 'switch_user_helper.rb')
|
3
|
-
|
4
|
-
module SwitchUserHelper
|
5
|
-
def switch_user_custom(options = {})
|
6
|
-
return unless available?
|
7
|
-
|
8
|
-
selected_user = nil
|
9
|
-
|
10
|
-
grouped_options_container =
|
11
|
-
{}.tap do |h|
|
12
|
-
SwitchUser.all_users.each do |record|
|
13
|
-
scope = record.is_a?(SwitchUser::GuestRecord) ? :Guest : record.scope.to_s.capitalize
|
14
|
-
h[scope] ||= []
|
15
|
-
h[scope] << [record.label, record.scope_id]
|
16
|
-
|
17
|
-
next unless selected_user.nil?
|
18
|
-
next if record.is_a?(SwitchUser::GuestRecord)
|
19
|
-
|
20
|
-
selected_user = record.scope_id if provider.current_user?(record.user, record.scope)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
option_tags = grouped_options_for_select(grouped_options_container.to_a, selected_user)
|
25
|
-
|
26
|
-
render partial: 'rails_base/switch_user/widget',
|
27
|
-
locals: { option_tags: option_tags, classes: options[:class], styles: options[:style] }
|
28
|
-
end
|
29
|
-
end
|