adjust for multitenant
This commit is contained in:
@@ -12,11 +12,9 @@ class EsignSettingsController < ApplicationController
|
||||
HexaPDF::Document.new(io: StringIO.new(blob.download))
|
||||
end
|
||||
|
||||
cert = EncryptedConfig.find_by(account: current_account, key: EncryptedConfig::ESIGN_CERTS_KEY).value
|
||||
certs = Accounts.load_signing_certs(current_account)
|
||||
|
||||
trusted_certs = [OpenSSL::X509::Certificate.new(cert['cert']),
|
||||
OpenSSL::X509::Certificate.new(cert['sub_ca']),
|
||||
OpenSSL::X509::Certificate.new(cert['root_ca'])]
|
||||
trusted_certs = [certs[:cert], certs[:sub_ca], certs[:root_ca]]
|
||||
|
||||
render turbo_stream: turbo_stream.replace('result', partial: 'result', locals: { pdfs:, blobs:, trusted_certs: })
|
||||
rescue HexaPDF::MalformedPDFError
|
||||
|
||||
@@ -22,7 +22,7 @@ class SetupController < ApplicationController
|
||||
if @user.save
|
||||
encrypted_configs = [
|
||||
{ key: EncryptedConfig::APP_URL_KEY, value: encrypted_config_params[:value] },
|
||||
{ key: EncryptedConfig::ESIGN_CERTS_KEY, value: GenerateCertificate.call }
|
||||
{ key: EncryptedConfig::ESIGN_CERTS_KEY, value: GenerateCertificate.call.transform_values(&:to_pem) }
|
||||
]
|
||||
@account.encrypted_configs.create!(encrypted_configs)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export default targetable(class extends HTMLElement {
|
||||
const link = document.createElement('a')
|
||||
|
||||
link.href = blobUrl
|
||||
link.setAttribute('download', decodeURI(resp.headers.get('content-disposition').split('"')[1]))
|
||||
link.setAttribute('download', decodeURI(url.split('/').pop()))
|
||||
|
||||
link.click()
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ window.customElements.define('submission-form', class extends HTMLElement {
|
||||
submitterSlug: this.dataset.submitterSlug,
|
||||
submitterUuid: this.dataset.submitterUuid,
|
||||
authenticityToken: this.dataset.authenticityToken,
|
||||
canSendEmail: this.dataset.canSendEmail === 'true',
|
||||
values: reactive(JSON.parse(this.dataset.values)),
|
||||
attachments: reactive(JSON.parse(this.dataset.attachments)),
|
||||
fields: JSON.parse(this.dataset.fields)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
</p>
|
||||
<div class="space-y-3 mt-5">
|
||||
<button
|
||||
v-if="canSendEmail"
|
||||
class="white-button flex items-center space-x-1 w-full"
|
||||
:disabled="isSendingCopy"
|
||||
@click.prevent="sendCopyToEmail"
|
||||
@@ -58,6 +59,11 @@ export default {
|
||||
submitterSlug: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
canSendEmail: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
@@ -98,7 +104,7 @@ export default {
|
||||
const link = document.createElement('a')
|
||||
|
||||
link.href = blobUrl
|
||||
link.setAttribute('download', decodeURI(resp.headers.get('content-disposition').split('"')[1]))
|
||||
link.setAttribute('download', decodeURI(url.split('/').pop()))
|
||||
|
||||
link.click()
|
||||
|
||||
|
||||
@@ -249,6 +249,7 @@
|
||||
</form>
|
||||
<FormCompleted
|
||||
v-else
|
||||
:can-send-email="canSendEmail"
|
||||
:submitter-slug="submitterSlug"
|
||||
/>
|
||||
<div class="flex justify-center">
|
||||
@@ -291,6 +292,11 @@ export default {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
canSendEmail: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
submitterUuid: {
|
||||
type: String,
|
||||
required: true
|
||||
|
||||
@@ -74,8 +74,10 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
this.$refs.canvas.width = this.$refs.canvas.parentNode.clientWidth
|
||||
this.$refs.canvas.height = this.$refs.canvas.parentNode.clientWidth / 3
|
||||
this.$nextTick(() => {
|
||||
this.$refs.canvas.width = this.$refs.canvas.parentNode.clientWidth
|
||||
this.$refs.canvas.height = this.$refs.canvas.parentNode.clientWidth / 3
|
||||
})
|
||||
|
||||
import('@rails/activestorage')
|
||||
const { default: SignaturePad } = await import('signature_pad')
|
||||
|
||||
@@ -21,11 +21,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= f.fields_for @encrypted_config || EncryptedConfig.find_or_initialize_by(account: current_account, key: EncryptedConfig::APP_URL_KEY) do |ff| %>
|
||||
<div class="form-control">
|
||||
<%= ff.label :value, 'App URL', class: 'label' %>
|
||||
<%= ff.text_field :value, autocomplete: 'off', class: 'base-input' %>
|
||||
</div>
|
||||
<% unless Docuseal.multitenant? %>
|
||||
<%= f.fields_for @encrypted_config || EncryptedConfig.find_or_initialize_by(account: current_account, key: EncryptedConfig::APP_URL_KEY) do |ff| %>
|
||||
<div class="form-control">
|
||||
<%= ff.label :value, 'App URL', class: 'label' %>
|
||||
<%= ff.text_field :value, autocomplete: 'off', class: 'base-input' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="form-control pt-2">
|
||||
<%= f.button button_title(title: 'Update', disabled_with: 'Updating'), class: 'base-button' %>
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
</div>
|
||||
<% end %>
|
||||
<%= f.fields_for resource.account do |ff| %>
|
||||
<set-timezone data-input-id="_account_timezone"></set-timezone>
|
||||
<%= ff.hidden_field :timezone %>
|
||||
<div class="form-control">
|
||||
<%= ff.label :name, 'Company name', class: 'label' %>
|
||||
<%= ff.text_field :name, required: true, class: 'base-input' %>
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
<li>
|
||||
<%= link_to 'Account', settings_account_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to 'Email', settings_email_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to 'Storage', settings_storage_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% unless Docuseal.multitenant? %>
|
||||
<li>
|
||||
<%= link_to 'Email', settings_email_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to 'Storage', settings_storage_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<li>
|
||||
<%= link_to 'Signature', settings_esign_index_path, class: 'text-base hover:bg-base-300' %>
|
||||
</li>
|
||||
|
||||
@@ -29,9 +29,11 @@
|
||||
<div class="text-center text-2xl font-semibold">
|
||||
Form has been submitter already
|
||||
</div>
|
||||
<div>
|
||||
<%= button_to button_title(title: 'Send copy to Email', disabled_with: 'Sending', icon: svg_icon('mail_forward', class: 'w-6 h-6')), send_submission_email_index_path, params: { submitter_slug: @submitter.slug }, form: { onsubmit: 'event.submitter.disabled = true' }, class: 'base-button w-full' %>
|
||||
</div>
|
||||
<% if Accounts.can_send_emails?(@submitter.submission.template.account) %>
|
||||
<div>
|
||||
<%= button_to button_title(title: 'Send copy to Email', disabled_with: 'Sending', icon: svg_icon('mail_forward', class: 'w-6 h-6')), send_submission_email_index_path, params: { submitter_slug: @submitter.slug }, form: { onsubmit: 'event.submitter.disabled = true' }, class: 'base-button w-full' %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<%= render 'shared/attribution' %>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</dynamic-list>
|
||||
<% end %>
|
||||
<div class="form-control">
|
||||
<% is_smtp_configured = Docuseal.multitenant? || current_account.encrypted_configs.exists?(key: EncryptedConfig::EMAIL_SMTP_KEY) %>
|
||||
<% is_smtp_configured = Accounts.can_send_emails?(current_account) %>
|
||||
<%= f.label :send_email, class: 'flex items-center cursor-pointer' do %>
|
||||
<%= f.check_box :send_email, class: 'base-checkbox', disabled: !is_smtp_configured, onchange: "message_field.classList.toggle('hidden', !event.currentTarget.checked)" %>
|
||||
<span class="label">Send Email</span>
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<div class="flex items-center space-x-1 mt-1">
|
||||
<%= svg_icon('writing', class: 'w-5 h-5') %>
|
||||
<span>
|
||||
<%= submitter&.completed_at? ? l(submitter.completed_at, format: :long) : 'Not completed yet' %>
|
||||
<%= submitter&.completed_at? ? l(submitter.completed_at.in_time_zone(current_account.timezone), format: :long, locale: current_account.locale) : 'Not completed yet' %>
|
||||
</span>
|
||||
</div>
|
||||
<% if submitter && !submitter.completed_at? %>
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<%= button_to button_title(title: 'Send copy to Email', disabled_with: 'Sending', icon: svg_icon('mail_forward', class: 'w-6 h-6')), send_submission_email_index_path, params: { submitter_slug: @submitter.slug }, form: { onsubmit: 'event.submitter.disabled = true' }, class: 'base-button w-full' %>
|
||||
<div class="divider">OR</div>
|
||||
<% if Accounts.can_send_emails?(@submitter.submission.template.account) %>
|
||||
<%= button_to button_title(title: 'Send copy to Email', disabled_with: 'Sending', icon: svg_icon('mail_forward', class: 'w-6 h-6')), send_submission_email_index_path, params: { submitter_slug: @submitter.slug }, form: { onsubmit: 'event.submitter.disabled = true' }, class: 'base-button w-full' %>
|
||||
<div class="divider">OR</div>
|
||||
<% end %>
|
||||
<download-button data-src="<%= submitter_download_index_path(@submitter.slug) %>" class="base-button w-full">
|
||||
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
|
||||
<%= svg_icon('download', class: 'w-6 h-6') %>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<div class="mx-auto" style="max-width: 1000px">
|
||||
<div class="relative md:mx-32">
|
||||
<div class="shadow-md bg-base-100 absolute bottom-0 md:bottom-4 w-full border border-base-200 border p-4 rounded">
|
||||
<submission-form data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-attachments="<%= attachments_index.values.select { |e| e.record_id == @submitter.id }.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submitter.submission.template.fields.select { |f| f['submitter_uuid'] == @submitter.uuid }.to_json %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
|
||||
<submission-form data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-can-send-email="<%= Accounts.can_send_emails?(OpenStruct.new(id: @submitter.submission.template.account_id)) %>" data-attachments="<%= attachments_index.values.select { |e| e.record_id == @submitter.id }.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submitter.submission.template.fields.select { |f| f['submitter_uuid'] == @submitter.uuid }.to_json %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ Rails.application.configure do
|
||||
end
|
||||
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options).
|
||||
config.active_storage.service = :disk
|
||||
config.active_storage.service = :local
|
||||
|
||||
# Don't care if the mailer can't send.
|
||||
config.action_mailer.raise_delivery_errors = true
|
||||
|
||||
@@ -38,7 +38,16 @@ Rails.application.configure do
|
||||
# config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
|
||||
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options).
|
||||
config.active_storage.service = :local
|
||||
config.active_storage.service =
|
||||
if ENV['S3_ATTACHMENTS_BUCKET'].present?
|
||||
:aws_s3
|
||||
elsif ENV['GCS_BUCKET'].present?
|
||||
:google
|
||||
elsif ENV['AZURE_CONTAINER'].present?
|
||||
:azure
|
||||
else
|
||||
:local
|
||||
end
|
||||
|
||||
# Mount Action Cable outside main process or domain.
|
||||
# config.action_cable.mount_path = nil
|
||||
@@ -46,7 +55,7 @@ Rails.application.configure do
|
||||
# config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
|
||||
|
||||
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
|
||||
# config.force_ssl = true
|
||||
config.force_ssl = ENV['FORCE_SSL'] == 'true'
|
||||
|
||||
# Include generic and useful information about system operation, but avoid logging too much
|
||||
# information to avoid inadvertent exposure of personally identifiable information (PII).
|
||||
@@ -61,7 +70,20 @@ Rails.application.configure do
|
||||
|
||||
# Ignore bad email addresses and do not raise email delivery errors.
|
||||
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
||||
# config.action_mailer.raise_delivery_errors = false
|
||||
config.action_mailer.raise_delivery_errors = false
|
||||
|
||||
if ENV['SMTP_USERNAME']
|
||||
config.action_mailer.delivery_method = :smtp
|
||||
config.action_mailer.smtp_settings = {
|
||||
address: ENV.fetch('SMTP_ADDRESS', nil),
|
||||
port: ENV.fetch('SMTP_PORT', 587),
|
||||
domain: ENV.fetch('SMTP_DOMAIN', nil),
|
||||
user_name: ENV.fetch('SMTP_USERNAME', nil),
|
||||
password: ENV.fetch('SMTP_PASSWORD', nil),
|
||||
authentication: ENV.fetch('SMTP_AUTHENTICATION', 'plain'),
|
||||
enable_starttls_auto: ENV['SMTP_ENABLE_STARTTLS_AUTO'] == 'true'
|
||||
}
|
||||
end
|
||||
|
||||
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
|
||||
# the I18n.default_locale when a translation cannot be found).
|
||||
|
||||
+5
-3
@@ -8,7 +8,7 @@ Rails.application.routes.draw do
|
||||
devise_for :users, path: '/', only: %i[sessions passwords]
|
||||
|
||||
devise_scope :user do
|
||||
if User.devise_modules.include?(:registerable)
|
||||
if Docuseal.multitenant?
|
||||
resource :registration, only: %i[create], path: 'sign_up' do
|
||||
get '' => :new, as: :new
|
||||
end
|
||||
@@ -52,8 +52,10 @@ Rails.application.routes.draw do
|
||||
end
|
||||
|
||||
scope '/settings', as: :settings do
|
||||
resources :storage, only: %i[index create], controller: 'storage_settings'
|
||||
resources :email, only: %i[index create], controller: 'email_settings'
|
||||
unless Docuseal.multitenant?
|
||||
resources :storage, only: %i[index create], controller: 'storage_settings'
|
||||
resources :email, only: %i[index create], controller: 'email_settings'
|
||||
end
|
||||
resources :esign, only: %i[index create], controller: 'esign_settings'
|
||||
resources :users, only: %i[index]
|
||||
resource :account, only: %i[show update]
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
module Accounts
|
||||
module_function
|
||||
|
||||
def load_signing_certs(account)
|
||||
certs =
|
||||
if Docuseal.multitenant?
|
||||
Docuseal::CERTS
|
||||
else
|
||||
EncryptedConfig.find_by(account:, key: EncryptedConfig::ESIGN_CERTS_KEY).value
|
||||
end
|
||||
|
||||
{
|
||||
cert: OpenSSL::X509::Certificate.new(certs['cert']),
|
||||
key: OpenSSL::PKey::RSA.new(certs['key']),
|
||||
sub_ca: OpenSSL::X509::Certificate.new(certs['sub_ca']),
|
||||
sub_key: OpenSSL::PKey::RSA.new(certs['sub_key']),
|
||||
root_ca: OpenSSL::X509::Certificate.new(certs['root_ca']),
|
||||
root_key: OpenSSL::PKey::RSA.new(certs['root_key'])
|
||||
}
|
||||
end
|
||||
|
||||
def can_send_emails?(account)
|
||||
return true if Docuseal.multitenant?
|
||||
|
||||
EncryptedConfig.exists?(account_id: account.id, key: EncryptedConfig::EMAIL_SMTP_KEY)
|
||||
end
|
||||
|
||||
def normalize_timezone(timezone)
|
||||
tzinfo = TZInfo::Timezone.get(ActiveSupport::TimeZone::MAPPING[timezone] || timezone)
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ module ActionMailerConfigsInterceptor
|
||||
module_function
|
||||
|
||||
def delivering_email(message)
|
||||
return message unless Rails.env.production?
|
||||
if Rails.env.production? && Rails.application.config.action_mailer.delivery_method
|
||||
message.from = ENV.fetch('SMTP_FROM')
|
||||
|
||||
return message
|
||||
end
|
||||
|
||||
email_configs = EncryptedConfig.find_by(key: EncryptedConfig::EMAIL_SMTP_KEY)
|
||||
|
||||
|
||||
+10
-1
@@ -5,13 +5,22 @@ module Docuseal
|
||||
PRODUCT_NAME = 'DocuSeal'
|
||||
DEFAULT_APP_URL = 'http://localhost:3000'
|
||||
|
||||
CERTS = JSON.parse(ENV.fetch('CERTS', '{}'))
|
||||
|
||||
DEFAULT_URL_OPTIONS = {
|
||||
host: ENV.fetch('HOST', 'localhost'),
|
||||
protocol: ENV['FORCE_SSL'] == 'true' ? 'https' : 'http'
|
||||
}.freeze
|
||||
|
||||
module_function
|
||||
|
||||
def multitenant?
|
||||
ENV['MULTITENANT'] == true
|
||||
ENV['MULTITENANT'] == 'true'
|
||||
end
|
||||
|
||||
def default_url_options
|
||||
return DEFAULT_URL_OPTIONS if multitenant?
|
||||
|
||||
@default_url_options ||= begin
|
||||
value = EncryptedConfig.find_by(key: EncryptedConfig::APP_URL_KEY)&.value
|
||||
value ||= DEFAULT_APP_URL
|
||||
|
||||
@@ -13,12 +13,12 @@ module GenerateCertificate
|
||||
cert, key = generate_certificate(name, sub_cert, sub_key)
|
||||
|
||||
{
|
||||
cert: cert.to_pem,
|
||||
key: key.to_pem,
|
||||
root_ca: root_cert.to_pem,
|
||||
root_key: root_key.to_pem,
|
||||
sub_ca: sub_cert.to_pem,
|
||||
sub_key: sub_key.to_pem
|
||||
cert:,
|
||||
key:,
|
||||
root_ca: root_cert,
|
||||
root_key:,
|
||||
sub_ca: sub_cert,
|
||||
sub_key:
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
module LoadActiveStorageConfigs
|
||||
STORAGE_YML_PATH = Rails.root.join('config/storage.yml')
|
||||
IS_ENV_CONFIGURED =
|
||||
ENV['S3_ATTACHMENTS_BUCKET'].present? || ENV['GCS_BUCKET'].present? || ENV['AZURE_CONTAINER'].present?
|
||||
|
||||
module_function
|
||||
|
||||
@@ -14,6 +16,9 @@ module LoadActiveStorageConfigs
|
||||
end
|
||||
|
||||
def reload
|
||||
return if Docuseal.multitenant?
|
||||
return if IS_ENV_CONFIGURED
|
||||
|
||||
encrypted_config = EncryptedConfig.find_by(key: EncryptedConfig::FILES_STORAGE_KEY)
|
||||
|
||||
return unless encrypted_config
|
||||
|
||||
@@ -23,8 +23,7 @@ module Submissions
|
||||
|
||||
template = submitter.submission.template
|
||||
|
||||
cert = submitter.submission.template.account.encrypted_configs
|
||||
.find_by(key: EncryptedConfig::ESIGN_CERTS_KEY).value
|
||||
certs = Accounts.load_signing_certs(submitter.submission.template.account)
|
||||
|
||||
pdfs_index = build_pdfs_index(submitter)
|
||||
|
||||
@@ -158,7 +157,7 @@ module Submissions
|
||||
template.schema.map do |item|
|
||||
pdf = pdfs_index[item['attachment_uuid']]
|
||||
|
||||
attachment = save_signed_pdf(pdf:, submitter:, cert:, uuid: item['attachment_uuid'], name: item['name'])
|
||||
attachment = save_signed_pdf(pdf:, submitter:, certs:, uuid: item['attachment_uuid'], name: item['name'])
|
||||
|
||||
image_pdfs << pdf if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
|
||||
|
||||
@@ -176,7 +175,7 @@ module Submissions
|
||||
save_signed_pdf(
|
||||
pdf: images_pdf,
|
||||
submitter:,
|
||||
cert:,
|
||||
certs:,
|
||||
uuid: images_pdf_uuid(original_documents.select(&:image?)),
|
||||
name: template.name
|
||||
)
|
||||
@@ -185,16 +184,15 @@ module Submissions
|
||||
end
|
||||
# rubocop:enable Metrics
|
||||
|
||||
def save_signed_pdf(pdf:, submitter:, cert:, uuid:, name:)
|
||||
def save_signed_pdf(pdf:, submitter:, certs:, uuid:, name:)
|
||||
io = StringIO.new
|
||||
|
||||
pdf.trailer.info[:Creator] = INFO_CREATOR
|
||||
|
||||
pdf.sign(io, reason: format(SIGN_REASON, email: submitter.email),
|
||||
certificate: OpenSSL::X509::Certificate.new(cert['cert']),
|
||||
key: OpenSSL::PKey::RSA.new(cert['key']),
|
||||
certificate_chain: [OpenSSL::X509::Certificate.new(cert['sub_ca']),
|
||||
OpenSSL::X509::Certificate.new(cert['root_ca'])])
|
||||
certificate: certs[:cert],
|
||||
key: certs[:key],
|
||||
certificate_chain: [certs[:sub_ca], certs[:root_ca]])
|
||||
|
||||
ActiveStorage::Attachment.create!(
|
||||
uuid:,
|
||||
|
||||
Reference in New Issue
Block a user