diff --git a/Gemfile b/Gemfile index 61eae029..d4983509 100644 --- a/Gemfile +++ b/Gemfile @@ -26,6 +26,7 @@ gem 'pagy' gem 'pdf-reader' gem 'pg', require: false gem 'premailer-rails' +gem 'pretender' gem 'puma' gem 'rack' gem 'rails' diff --git a/Gemfile.lock b/Gemfile.lock index 66882014..19cb2e0a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -354,6 +354,8 @@ GEM actionmailer (>= 3) net-smtp premailer (~> 1.7, >= 1.7.9) + pretender (0.5.0) + actionpack (>= 6.1) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) @@ -596,6 +598,7 @@ DEPENDENCIES pdf-reader pg premailer-rails + pretender pry-rails puma rack diff --git a/app/controllers/api/api_base_controller.rb b/app/controllers/api/api_base_controller.rb index 6232e3dc..60963b8d 100644 --- a/app/controllers/api/api_base_controller.rb +++ b/app/controllers/api/api_base_controller.rb @@ -8,6 +8,8 @@ module Api DEFAULT_LIMIT = 10 MAX_LIMIT = 100 + impersonates :user, with: ->(uuid) { User.find_by(uuid:) } + wrap_parameters false before_action :authenticate_user! diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 30e0a488..d7aa782a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -16,6 +16,8 @@ class ApplicationController < ActionController::Base :current_account, :svg_icon + impersonates :user, with: ->(uuid) { User.find_by(uuid:) } + rescue_from Pagy::OverflowError do redirect_to request.path end @@ -24,7 +26,7 @@ class ApplicationController < ActionController::Base rescue_from CanCan::AccessDenied do |e| Rollbar.error(e) if defined?(Rollbar) - redirect_back(fallback_location: root_path, alert: e.message) + redirect_to root_path, alert: e.message end end @@ -32,6 +34,15 @@ class ApplicationController < ActionController::Base Docuseal.default_url_options end + def impersonate_user(user) + raise ArgumentError unless user + raise Pretender::Error unless true_user + + @impersonated_user = user + + request.session[:impersonated_user_id] = user.uuid + end + private def with_browser_locale(&) diff --git a/app/controllers/console_redirect_controller.rb b/app/controllers/console_redirect_controller.rb index 5c4b037d..f6042225 100644 --- a/app/controllers/console_redirect_controller.rb +++ b/app/controllers/console_redirect_controller.rb @@ -5,9 +5,9 @@ class ConsoleRedirectController < ApplicationController skip_authorization_check def index - return redirect_to(new_user_session_path({ redir: params[:redir] }.compact)) if current_user.blank? + return redirect_to(new_user_session_path({ redir: params[:redir] }.compact)) if true_user.blank? - auth = JsonWebToken.encode(uuid: current_user.uuid, + auth = JsonWebToken.encode(uuid: true_user.uuid, scope: :console, exp: 1.minute.from_now.to_i) diff --git a/app/controllers/testing_accounts_controller.rb b/app/controllers/testing_accounts_controller.rb new file mode 100644 index 00000000..1bce910e --- /dev/null +++ b/app/controllers/testing_accounts_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class TestingAccountsController < ApplicationController + skip_authorization_check only: :destroy + + def show + authorize!(:manage, current_account) + authorize!(:manage, current_user) + + impersonate_user(Accounts.find_or_create_testing_user(current_account)) + + redirect_back(fallback_location: root_path) + end + + def destroy + stop_impersonating_user + + redirect_back(fallback_location: root_path) + end +end diff --git a/app/controllers/testing_api_settings_controller.rb b/app/controllers/testing_api_settings_controller.rb new file mode 100644 index 00000000..5eade647 --- /dev/null +++ b/app/controllers/testing_api_settings_controller.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class TestingApiSettingsController < ApplicationController + def index + authorize!(:manage, current_user.access_token) + + @webhook_config = + current_account.encrypted_configs.find_or_initialize_by(key: EncryptedConfig::WEBHOOK_URL_KEY) + + authorize!(:manage, @webhook_config) + end +end diff --git a/app/models/account.rb b/app/models/account.rb index 8c2792e9..2689bd1c 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -22,12 +22,26 @@ class Account < ApplicationRecord class_name: 'TemplateFolder', dependent: :destroy, inverse_of: :account has_many :submissions, through: :templates has_many :submitters, through: :submissions + has_many :account_linked_accounts, dependent: :destroy + has_many :account_testing_accounts, -> { testing }, dependent: :destroy, + class_name: 'AccountLinkedAccount', + inverse_of: :account + has_one :linked_account_account, dependent: :destroy, + foreign_key: :linked_account_id, + class_name: 'AccountLinkedAccount', + inverse_of: :linked_account + has_many :linked_accounts, through: :account_linked_accounts + has_many :testing_accounts, through: :account_testing_accounts, source: :linked_account has_many :active_users, -> { active }, dependent: :destroy, inverse_of: :account, class_name: 'User' attribute :timezone, :string, default: 'UTC' attribute :locale, :string, default: 'en-US' + def testing? + linked_account_account&.testing? + end + def default_template_folder super || build_default_template_folder(name: TemplateFolder::DEFAULT_NAME, author_id: users.minimum(:id)).tap(&:save!) diff --git a/app/models/account_linked_account.rb b/app/models/account_linked_account.rb new file mode 100644 index 00000000..3d68aea7 --- /dev/null +++ b/app/models/account_linked_account.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: account_linked_accounts +# +# id :bigint not null, primary key +# account_type :text not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint not null +# linked_account_id :bigint not null +# +# Indexes +# +# idx_on_account_id_linked_account_id_48ab9f79d2 (account_id,linked_account_id) UNIQUE +# index_account_linked_accounts_on_account_id (account_id) +# index_account_linked_accounts_on_linked_account_id (linked_account_id) +# +# Foreign Keys +# +# fk_rails_... (account_id => accounts.id) +# fk_rails_... (linked_account_id => accounts.id) +# +class AccountLinkedAccount < ApplicationRecord + belongs_to :account + belongs_to :linked_account, class_name: 'Account' + + attribute :account_type, :string, default: 'testing' + + scope :testing, -> { where(account_type: :testing) } + + def testing? + account_type == 'testing' + end +end diff --git a/app/views/accounts/show.html.erb b/app/views/accounts/show.html.erb index 7e570f67..06c86853 100644 --- a/app/views/accounts/show.html.erb +++ b/app/views/accounts/show.html.erb @@ -22,7 +22,7 @@ <% end %> <% encrypted_config = @encrypted_config || EncryptedConfig.find_or_initialize_by(account: current_account, key: EncryptedConfig::APP_URL_KEY) %> - <% if !Docuseal.multitenant? && can?(:manage, encrypted_config) %> + <% if !Docuseal.multitenant? && can?(:manage, encrypted_config) && !current_account.testing? %> <%= f.fields_for encrypted_config do |ff| %>