feat: add ID verification requirement for signers (B7) (#20)

* feat: add ID verification requirement for signers (B7)

* fix: add missing message and dryRun props to FileDropzone in ID verification gate

* fix: guard Complete button and submitStep() against ID verification bypass

---------

Co-authored-by: Bob Develop <developbob50@gmail.com>
pull/639/head
devin-ai-integration[bot] 1 week ago committed by GitHub
parent 76b7f27fcf
commit 4f21ce22d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -27,8 +27,9 @@ class AccountConfigsController < ApplicationController
AccountConfig::DOCUMENT_FILENAME_FORMAT_KEY, AccountConfig::DOCUMENT_FILENAME_FORMAT_KEY,
AccountConfig::ENABLE_MCP_KEY, AccountConfig::ENABLE_MCP_KEY,
AccountConfig::IP_ALLOWLIST_KEY, AccountConfig::IP_ALLOWLIST_KEY,
AccountConfig::AUTO_ARCHIVE_DAYS_KEY AccountConfig::AUTO_ARCHIVE_DAYS_KEY,
AccountConfig::REQUIRE_CONSENT_KEY AccountConfig::REQUIRE_CONSENT_KEY,
AccountConfig::REQUIRE_ID_VERIFICATION_KEY
].freeze ].freeze
InvalidKey = Class.new(StandardError) InvalidKey = Class.new(StandardError)

@ -0,0 +1,16 @@
# frozen_string_literal: true
module Api
class SubmitterIdDocumentsController < ApiBaseController
skip_before_action :authenticate_user!
skip_authorization_check
def create
@submitter = Submitter.find_by!(slug: params[:submitter_slug])
SubmissionEvents.create_with_tracking_data(@submitter, 'id_document_uploaded', request)
render json: {}
end
end
end

@ -43,6 +43,7 @@ safeRegisterElement('submission-form', class extends HTMLElement {
withFieldLabels: this.dataset.withFieldLabels !== 'false', withFieldLabels: this.dataset.withFieldLabels !== 'false',
withDisclosure: this.dataset.withDisclosure === 'true', withDisclosure: this.dataset.withDisclosure === 'true',
requireConsent: this.dataset.requireConsent === 'true', requireConsent: this.dataset.requireConsent === 'true',
requireIdVerification: this.dataset.requireIdVerification === 'true',
reuseSignature: this.dataset.reuseSignature !== 'false', reuseSignature: this.dataset.reuseSignature !== 'false',
withTypedSignature: this.dataset.withTypedSignature !== 'false', withTypedSignature: this.dataset.withTypedSignature !== 'false',
authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content, authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content,

@ -99,7 +99,7 @@
type="submit" type="submit"
name="completed" name="completed"
value="true" value="true"
:disabled="isSubmittingComplete || (requireConsent && !consentAccepted)" :disabled="isSubmittingComplete || (requireConsent && !consentAccepted) || (requireIdVerification && !idDocumentUploaded)"
> >
<span class="flex items-center"> <span class="flex items-center">
<IconInnerShadowTop <IconInnerShadowTop
@ -206,7 +206,25 @@
type="hidden" type="hidden"
> >
<div class="md:mt-4"> <div class="md:mt-4">
<div v-if="['cells', 'text'].includes(currentField.type)"> <div
v-if="requireIdVerification && !idDocumentUploaded"
class="text-center"
>
<p class="text-xl sm:text-2xl font-semibold mb-2">
{{ t('id_verification_required') }}
</p>
<p class="text-sm text-base-content/70 mb-4">
{{ t('please_upload_id_document') }}
</p>
<FileDropzone
:message="t('please_upload_id_document')"
:submitter-slug="submitterSlug"
:dry-run="dryRun"
accept="image/*,.pdf"
@upload="onIdDocumentUpload"
/>
</div>
<div v-else-if="['cells', 'text'].includes(currentField.type)">
<TextStep <TextStep
:key="currentField.uuid" :key="currentField.uuid"
v-model="values[currentField.uuid]" v-model="values[currentField.uuid]"
@ -589,7 +607,7 @@
ref="submitButton" ref="submitButton"
type="submit" type="submit"
class="base-button w-full flex justify-center submit-form-button" class="base-button w-full flex justify-center submit-form-button"
:disabled="isButtonDisabled || (requireConsent && isLastStep && !consentAccepted)" :disabled="isButtonDisabled || (requireConsent && isLastStep && !consentAccepted) || (requireIdVerification && !idDocumentUploaded)"
> >
<span class="flex"> <span class="flex">
<IconInnerShadowTop <IconInnerShadowTop
@ -679,6 +697,7 @@ import ImageStep from './image_step'
import SignatureStep from './signature_step' import SignatureStep from './signature_step'
import InitialsStep from './initials_step' import InitialsStep from './initials_step'
import AttachmentStep from './attachment_step' import AttachmentStep from './attachment_step'
import FileDropzone from './dropzone'
import MultiSelectStep from './multi_select_step' import MultiSelectStep from './multi_select_step'
import PhoneStep from './phone_step' import PhoneStep from './phone_step'
import PaymentStep from './payment_step' import PaymentStep from './payment_step'
@ -739,6 +758,7 @@ export default {
AppearsOn, AppearsOn,
IconWritingSign, IconWritingSign,
AttachmentStep, AttachmentStep,
FileDropzone,
InitialsStep, InitialsStep,
VerificationStep, VerificationStep,
KbaStep, KbaStep,
@ -876,6 +896,11 @@ export default {
required: false, required: false,
default: false default: false
}, },
requireIdVerification: {
type: Boolean,
required: false,
default: false
},
reuseSignature: { reuseSignature: {
type: Boolean, type: Boolean,
required: false, required: false,
@ -1045,7 +1070,8 @@ export default {
recalculateButtonDisabledKey: '', recalculateButtonDisabledKey: '',
isAccessibilityMode: false, isAccessibilityMode: false,
consentAccepted: false, consentAccepted: false,
consentRecorded: false consentRecorded: false,
idDocumentUploaded: false
} }
}, },
computed: { computed: {
@ -1382,6 +1408,19 @@ export default {
t (key) { t (key) {
return this.i18n[key] || i18n[this.selectedLanguage]?.[key] || i18n.en[key] || key return this.i18n[key] || i18n[this.selectedLanguage]?.[key] || i18n.en[key] || key
}, },
onIdDocumentUpload (files) {
if (files?.length) {
this.idDocumentUploaded = true
if (!this.dryRun) {
fetch(this.baseUrl + '/api/submitter_id_documents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ submitter_slug: this.submitterSlug })
})
}
}
},
onConsentChange (event) { onConsentChange (event) {
this.consentAccepted = event.target.checked this.consentAccepted = event.target.checked
@ -1652,6 +1691,10 @@ export default {
return return
} }
if (this.requireIdVerification && !this.idDocumentUploaded) {
return
}
this.isSubmitting = true this.isSubmitting = true
if (forceComplete) { if (forceComplete) {

@ -106,7 +106,9 @@ const en = {
wait_countdown_seconds: 'Wait {countdown} seconds', wait_countdown_seconds: 'Wait {countdown} seconds',
enter_screen_reader_mode: 'Enter screen reader mode', enter_screen_reader_mode: 'Enter screen reader mode',
generated_by_docuseal: 'Generated by DocuSeal', generated_by_docuseal: 'Generated by DocuSeal',
i_consent_to_sign_this_document_electronically: 'I consent to sign this document electronically' i_consent_to_sign_this_document_electronically: 'I consent to sign this document electronically',
id_verification_required: 'ID Verification Required',
please_upload_id_document: 'Please upload a photo of your government-issued ID to verify your identity before signing.'
} }
const es = { const es = {
@ -217,7 +219,9 @@ const es = {
wait_countdown_seconds: 'Espera {countdown} segundos', wait_countdown_seconds: 'Espera {countdown} segundos',
enter_screen_reader_mode: 'Activar modo lector de pantalla', enter_screen_reader_mode: 'Activar modo lector de pantalla',
generated_by_docuseal: 'Generado por DocuSeal', generated_by_docuseal: 'Generado por DocuSeal',
i_consent_to_sign_this_document_electronically: 'Consiento en firmar este documento electrónicamente' i_consent_to_sign_this_document_electronically: 'Consiento en firmar este documento electrónicamente',
id_verification_required: 'Verificación de identidad requerida',
please_upload_id_document: 'Suba una foto de su documento de identidad oficial para verificar su identidad antes de firmar.'
} }
const it = { const it = {
@ -328,7 +332,9 @@ const it = {
wait_countdown_seconds: 'Attendi {countdown} secondi', wait_countdown_seconds: 'Attendi {countdown} secondi',
enter_screen_reader_mode: 'Attiva modalità lettore di schermo', enter_screen_reader_mode: 'Attiva modalità lettore di schermo',
generated_by_docuseal: 'Generato da DocuSeal', generated_by_docuseal: 'Generato da DocuSeal',
i_consent_to_sign_this_document_electronically: 'Acconsento a firmare questo documento elettronicamente' i_consent_to_sign_this_document_electronically: 'Acconsento a firmare questo documento elettronicamente',
id_verification_required: 'Verifica dell\'identità richiesta',
please_upload_id_document: 'Carica una foto del tuo documento d\'identità per verificare la tua identità prima di firmare.'
} }
const de = { const de = {
@ -439,7 +445,9 @@ const de = {
wait_countdown_seconds: 'Bitte {countdown} Sekunden warten', wait_countdown_seconds: 'Bitte {countdown} Sekunden warten',
enter_screen_reader_mode: 'Screenreader-Modus aktivieren', enter_screen_reader_mode: 'Screenreader-Modus aktivieren',
generated_by_docuseal: 'Erstellt von DocuSeal', generated_by_docuseal: 'Erstellt von DocuSeal',
i_consent_to_sign_this_document_electronically: 'Ich stimme zu, dieses Dokument elektronisch zu unterzeichnen' i_consent_to_sign_this_document_electronically: 'Ich stimme zu, dieses Dokument elektronisch zu unterzeichnen',
id_verification_required: 'Identitätsprüfung erforderlich',
please_upload_id_document: 'Bitte laden Sie ein Foto Ihres amtlichen Ausweises hoch, um Ihre Identität vor der Unterzeichnung zu bestätigen.'
} }
const fr = { const fr = {
@ -550,7 +558,9 @@ const fr = {
wait_countdown_seconds: 'Veuillez patienter {countdown} secondes', wait_countdown_seconds: 'Veuillez patienter {countdown} secondes',
enter_screen_reader_mode: 'Activer le mode lecteur d\'écran', enter_screen_reader_mode: 'Activer le mode lecteur d\'écran',
generated_by_docuseal: 'Généré par DocuSeal', generated_by_docuseal: 'Généré par DocuSeal',
i_consent_to_sign_this_document_electronically: 'Je consens à signer ce document électroniquement' i_consent_to_sign_this_document_electronically: 'Je consens à signer ce document électroniquement',
id_verification_required: 'Vérification d\'identité requise',
please_upload_id_document: 'Veuillez télécharger une photo de votre pièce d\'identité officielle pour vérifier votre identité avant de signer.'
} }
const pl = { const pl = {
@ -661,7 +671,9 @@ const pl = {
wait_countdown_seconds: 'Poczekaj {countdown} sekund', wait_countdown_seconds: 'Poczekaj {countdown} sekund',
enter_screen_reader_mode: 'Włącz tryb czytnika ekranu', enter_screen_reader_mode: 'Włącz tryb czytnika ekranu',
generated_by_docuseal: 'Wygenerowano przez DocuSeal', generated_by_docuseal: 'Wygenerowano przez DocuSeal',
i_consent_to_sign_this_document_electronically: 'Wyrażam zgodę na podpisanie tego dokumentu elektronicznie' i_consent_to_sign_this_document_electronically: 'Wyrażam zgodę na podpisanie tego dokumentu elektronicznie',
id_verification_required: 'Wymagana weryfikacja tożsamości',
please_upload_id_document: 'Prześlij zdjęcie dokumentu tożsamości, aby zweryfikować swoją tożsamość przed podpisaniem.'
} }
const uk = { const uk = {
@ -772,7 +784,9 @@ const uk = {
wait_countdown_seconds: 'Зачекайте {countdown} секунд', wait_countdown_seconds: 'Зачекайте {countdown} секунд',
enter_screen_reader_mode: 'Увімкнути режим читання з екрану', enter_screen_reader_mode: 'Увімкнути режим читання з екрану',
generated_by_docuseal: 'Створено за допомогою DocuSeal', generated_by_docuseal: 'Створено за допомогою DocuSeal',
i_consent_to_sign_this_document_electronically: 'Я даю згоду на електронний підпис цього документа' i_consent_to_sign_this_document_electronically: 'Я даю згоду на електронний підпис цього документа',
id_verification_required: 'Потрібна перевірка особи',
please_upload_id_document: 'Будь ласка, завантажте фото вашого посвідчення особи для підтвердження вашої особи перед підписанням.'
} }
const cs = { const cs = {
@ -883,7 +897,9 @@ const cs = {
wait_countdown_seconds: 'Počkejte {countdown} sekund', wait_countdown_seconds: 'Počkejte {countdown} sekund',
enter_screen_reader_mode: 'Zapnout režim čtečky obrazovky', enter_screen_reader_mode: 'Zapnout režim čtečky obrazovky',
generated_by_docuseal: 'Vygenerováno pomocí DocuSeal', generated_by_docuseal: 'Vygenerováno pomocí DocuSeal',
i_consent_to_sign_this_document_electronically: 'Souhlasím s elektronickým podpisem tohoto dokumentu' i_consent_to_sign_this_document_electronically: 'Souhlasím s elektronickým podpisem tohoto dokumentu',
id_verification_required: 'Vyžadováno ověření totožnosti',
please_upload_id_document: 'Nahrajte prosím fotografii vašeho úředního dokladu totožnosti pro ověření vaší identity před podpisem.'
} }
const pt = { const pt = {
@ -994,7 +1010,9 @@ const pt = {
wait_countdown_seconds: 'Aguarde {countdown} segundos', wait_countdown_seconds: 'Aguarde {countdown} segundos',
enter_screen_reader_mode: 'Ativar modo leitor de tela', enter_screen_reader_mode: 'Ativar modo leitor de tela',
generated_by_docuseal: 'Gerado pelo DocuSeal', generated_by_docuseal: 'Gerado pelo DocuSeal',
i_consent_to_sign_this_document_electronically: 'Consinto em assinar este documento eletronicamente' i_consent_to_sign_this_document_electronically: 'Consinto em assinar este documento eletronicamente',
id_verification_required: 'Verificação de identidade necessária',
please_upload_id_document: 'Carregue uma foto do seu documento de identidade oficial para verificar a sua identidade antes de assinar.'
} }
const he = { const he = {
@ -1105,7 +1123,9 @@ const he = {
wait_countdown_seconds: 'המתן {countdown} שניות', wait_countdown_seconds: 'המתן {countdown} שניות',
enter_screen_reader_mode: 'הפעל מצב קורא מסך', enter_screen_reader_mode: 'הפעל מצב קורא מסך',
generated_by_docuseal: 'נוצר על ידי DocuSeal', generated_by_docuseal: 'נוצר על ידי DocuSeal',
i_consent_to_sign_this_document_electronically: 'אני מסכים/ה לחתום על מסמך זה באופן אלקטרוני' i_consent_to_sign_this_document_electronically: 'אני מסכים/ה לחתום על מסמך זה באופן אלקטרוני',
id_verification_required: 'נדרש אימות זהות',
please_upload_id_document: 'אנא העלה תמונה של תעודת הזהות הרשמית שלך כדי לאמת את זהותך לפני החתימה.'
} }
const nl = { const nl = {
@ -1216,7 +1236,9 @@ const nl = {
wait_countdown_seconds: 'Wacht {countdown} seconden', wait_countdown_seconds: 'Wacht {countdown} seconden',
enter_screen_reader_mode: 'Schermlezer-modus inschakelen', enter_screen_reader_mode: 'Schermlezer-modus inschakelen',
generated_by_docuseal: 'Gegenereerd door DocuSeal', generated_by_docuseal: 'Gegenereerd door DocuSeal',
i_consent_to_sign_this_document_electronically: 'Ik geef toestemming om dit document elektronisch te ondertekenen' i_consent_to_sign_this_document_electronically: 'Ik geef toestemming om dit document elektronisch te ondertekenen',
id_verification_required: 'Identiteitsverificatie vereist',
please_upload_id_document: 'Upload een foto van uw officiële identiteitsbewijs om uw identiteit te verifiëren voordat u tekent.'
} }
const ar = { const ar = {
@ -1327,7 +1349,9 @@ const ar = {
wait_countdown_seconds: 'انتظر {countdown} ثانية', wait_countdown_seconds: 'انتظر {countdown} ثانية',
enter_screen_reader_mode: 'تفعيل وضع قارئ الشاشة', enter_screen_reader_mode: 'تفعيل وضع قارئ الشاشة',
generated_by_docuseal: 'تم إنشاؤه بواسطة DocuSeal', generated_by_docuseal: 'تم إنشاؤه بواسطة DocuSeal',
i_consent_to_sign_this_document_electronically: 'أوافق على توقيع هذا المستند إلكترونيًا' i_consent_to_sign_this_document_electronically: 'أوافق على توقيع هذا المستند إلكترونيًا',
id_verification_required: 'مطلوب التحقق من الهوية',
please_upload_id_document: 'يرجى تحميل صورة من وثيقة الهوية الرسمية للتحقق من هويتك قبل التوقيع.'
} }
const ko = { const ko = {
@ -1438,7 +1462,9 @@ const ko = {
wait_countdown_seconds: '{countdown}초 기다리세요', wait_countdown_seconds: '{countdown}초 기다리세요',
enter_screen_reader_mode: '스크린 리더 모드 활성화', enter_screen_reader_mode: '스크린 리더 모드 활성화',
generated_by_docuseal: 'DocuSeal로 생성됨', generated_by_docuseal: 'DocuSeal로 생성됨',
i_consent_to_sign_this_document_electronically: '본 문서에 전자적으로 서명하는 것에 동의합니다' i_consent_to_sign_this_document_electronically: '본 문서에 전자적으로 서명하는 것에 동의합니다',
id_verification_required: '신분증 확인 필요',
please_upload_id_document: '서명하기 전에 본인 확인을 위해 정부 발급 신분증 사진을 업로드해 주세요.'
} }
const ja = { const ja = {
@ -1549,7 +1575,9 @@ const ja = {
wait_countdown_seconds: '{countdown} 秒お待ちください', wait_countdown_seconds: '{countdown} 秒お待ちください',
enter_screen_reader_mode: 'スクリーンリーダーモードを有効にする', enter_screen_reader_mode: 'スクリーンリーダーモードを有効にする',
generated_by_docuseal: 'DocuSealで生成', generated_by_docuseal: 'DocuSealで生成',
i_consent_to_sign_this_document_electronically: 'この文書に電子的に署名することに同意します' i_consent_to_sign_this_document_electronically: 'この文書に電子的に署名することに同意します',
id_verification_required: '本人確認が必要です',
please_upload_id_document: '署名前に本人確認のため、公的身分証明書の写真をアップロードしてください。'
} }
const i18n = { en, es, it, de, fr, pl, uk, cs, pt, he, nl, ar, ko, ja } const i18n = { en, es, it, de, fr, pl, uk, cs, pt, he, nl, ar, ko, ja }

@ -68,6 +68,7 @@ class AccountConfig < ApplicationRecord
IP_ALLOWLIST_KEY = 'ip_allowlist' IP_ALLOWLIST_KEY = 'ip_allowlist'
AUTO_ARCHIVE_DAYS_KEY = 'auto_archive_days' AUTO_ARCHIVE_DAYS_KEY = 'auto_archive_days'
REQUIRE_CONSENT_KEY = 'require_consent' REQUIRE_CONSENT_KEY = 'require_consent'
REQUIRE_ID_VERIFICATION_KEY = 'require_id_verification'
BRAND_NAME_FONTS = [ BRAND_NAME_FONTS = [
'Inter', 'Inter',

@ -66,7 +66,8 @@ class SubmissionEvent < ApplicationRecord
decline_form: 'decline_form', decline_form: 'decline_form',
delegate_form: 'delegate_form', delegate_form: 'delegate_form',
api_complete_form: 'api_complete_form', api_complete_form: 'api_complete_form',
consent_given: 'consent_given' consent_given: 'consent_given',
id_document_uploaded: 'id_document_uploaded'
}, scope: false }, scope: false
private private

@ -114,6 +114,23 @@
</div> </div>
<% end %> <% end %>
<% end %> <% end %>
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::REQUIRE_ID_VERIFICATION_KEY) %>
<% if can?(:manage, account_config) %>
<%= form_for account_config, url: account_configs_path, method: :post do |f| %>
<%= f.hidden_field :key %>
<div class="flex items-center justify-between gap-4 py-2.5">
<div class="flex items-center space-x-1">
<span class="text-left"><%= t('require_id_verification_before_signing') %></span>
<span class="tooltip tooltip-top flex cursor-pointer" data-tip="<%= t('require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign') %>">
<%= svg_icon('info_circle', class: 'hidden md:inline-block w-4 h-4 shrink-0') %>
</span>
</div>
<submit-form data-on="change" class="flex">
<%= f.check_box :value, class: 'toggle', checked: account_config.value == true %>
</submit-form>
</div>
<% end %>
<% end %>
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::ALLOW_TYPED_SIGNATURE) %> <% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::ALLOW_TYPED_SIGNATURE) %>
<% if can?(:manage, account_config) %> <% if can?(:manage, account_config) %>
<%= form_for account_config, url: account_configs_path, method: :post do |f| %> <%= form_for account_config, url: account_configs_path, method: :post do |f| %>

@ -2,4 +2,4 @@
<% data_fields = Submissions.filtered_conditions_fields(submitter).to_json %> <% data_fields = Submissions.filtered_conditions_fields(submitter).to_json %>
<% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %> <% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
<% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %> <% optional_invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['optional_invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-field-labels="<%= configs[:with_field_labels] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template&.preferences&.dig('completed_redirect_url') %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template&.preferences&.dig('completed_message') || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-optional-invite-submitters="<%= optional_invite_submitters %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-signature-text="<%= params[:signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-require-consent="<%= configs[:require_consent] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form> <submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-field-labels="<%= configs[:with_field_labels] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template&.preferences&.dig('completed_redirect_url') %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template&.preferences&.dig('completed_message') || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-optional-invite-submitters="<%= optional_invite_submitters %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-signature-text="<%= params[:signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-require-consent="<%= configs[:require_consent] %>" data-require-id-verification="<%= configs[:require_id_verification] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form>

@ -217,6 +217,8 @@ en: &en
require_signing_reason: Require signing reason require_signing_reason: Require signing reason
require_consent_before_signing: Require consent before signing require_consent_before_signing: Require consent before signing
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Require signers to explicitly consent to signing the document electronically before they can submit require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Require signers to explicitly consent to signing the document electronically before they can submit
require_id_verification_before_signing: Require ID verification before signing
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Require signers to upload a government-issued ID document to verify their identity before they can sign
allow_typed_text_signatures: Allow typed text signatures allow_typed_text_signatures: Allow typed text signatures
allow_to_resubmit_completed_forms: Allow to resubmit completed forms allow_to_resubmit_completed_forms: Allow to resubmit completed forms
allow_to_decline_documents: Allow to decline documents allow_to_decline_documents: Allow to decline documents
@ -1288,6 +1290,8 @@ es: &es
require_signing_reason: Requerir motivo de firma require_signing_reason: Requerir motivo de firma
require_consent_before_signing: Requerir consentimiento antes de firmar require_consent_before_signing: Requerir consentimiento antes de firmar
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Requerir que los firmantes consientan explícitamente firmar el documento electrónicamente antes de enviar require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Requerir que los firmantes consientan explícitamente firmar el documento electrónicamente antes de enviar
require_id_verification_before_signing: Requerir verificación de identidad antes de firmar
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Requerir que los firmantes suban un documento de identidad oficial para verificar su identidad antes de firmar
allow_typed_text_signatures: Permitir firmas de texto mecanografiadas allow_typed_text_signatures: Permitir firmas de texto mecanografiadas
allow_to_resubmit_completed_forms: Permitir reenviar formularios completados allow_to_resubmit_completed_forms: Permitir reenviar formularios completados
allow_to_decline_documents: Permitir rechazar documentos allow_to_decline_documents: Permitir rechazar documentos
@ -2352,6 +2356,8 @@ it: &it
require_signing_reason: Richiedere il motivo della firma require_signing_reason: Richiedere il motivo della firma
require_consent_before_signing: Richiedere il consenso prima della firma require_consent_before_signing: Richiedere il consenso prima della firma
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Richiedere ai firmatari di acconsentire esplicitamente alla firma elettronica del documento prima dell'invio require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Richiedere ai firmatari di acconsentire esplicitamente alla firma elettronica del documento prima dell'invio
require_id_verification_before_signing: Richiedere la verifica dell'identità prima della firma
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Richiedere ai firmatari di caricare un documento d'identità ufficiale per verificare la propria identità prima di firmare
allow_typed_text_signatures: Permettere firme con testo digitato allow_typed_text_signatures: Permettere firme con testo digitato
allow_to_resubmit_completed_forms: Permettere di reinviare i moduli completati allow_to_resubmit_completed_forms: Permettere di reinviare i moduli completati
allow_to_decline_documents: Permettere di rifiutare i documenti allow_to_decline_documents: Permettere di rifiutare i documenti
@ -3420,6 +3426,8 @@ fr: &fr
require_signing_reason: Exiger un motif de signature require_signing_reason: Exiger un motif de signature
require_consent_before_signing: Exiger le consentement avant la signature require_consent_before_signing: Exiger le consentement avant la signature
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Exiger des signataires qu'ils consentent explicitement à signer le document électroniquement avant de soumettre require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Exiger des signataires qu'ils consentent explicitement à signer le document électroniquement avant de soumettre
require_id_verification_before_signing: Exiger la vérification d'identité avant la signature
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Exiger des signataires qu'ils téléchargent une pièce d'identité officielle pour vérifier leur identité avant de signer
allow_typed_text_signatures: Autoriser les signatures tapées allow_typed_text_signatures: Autoriser les signatures tapées
allow_to_resubmit_completed_forms: Autoriser la nouvelle soumission des formulaires complétés allow_to_resubmit_completed_forms: Autoriser la nouvelle soumission des formulaires complétés
allow_to_decline_documents: Autoriser le refus de documents allow_to_decline_documents: Autoriser le refus de documents
@ -4481,6 +4489,8 @@ pt: &pt
require_signing_reason: Requerer motivo para assinatura require_signing_reason: Requerer motivo para assinatura
require_consent_before_signing: Exigir consentimento antes de assinar require_consent_before_signing: Exigir consentimento antes de assinar
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Exigir que os signatários consintam explicitamente em assinar o documento eletronicamente antes de enviar require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Exigir que os signatários consintam explicitamente em assinar o documento eletronicamente antes de enviar
require_id_verification_before_signing: Exigir verificação de identidade antes de assinar
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Exigir que os signatários carreguem um documento de identidade oficial para verificar a sua identidade antes de assinar
allow_typed_text_signatures: Permitir assinaturas digitadas allow_typed_text_signatures: Permitir assinaturas digitadas
allow_to_resubmit_completed_forms: Permitir reenviar formulários concluídos allow_to_resubmit_completed_forms: Permitir reenviar formulários concluídos
allow_to_decline_documents: Permitir recusar documentos allow_to_decline_documents: Permitir recusar documentos
@ -5545,6 +5555,8 @@ de: &de
require_signing_reason: Grund für die Signatur erforderlich require_signing_reason: Grund für die Signatur erforderlich
require_consent_before_signing: Zustimmung vor dem Unterzeichnen erforderlich require_consent_before_signing: Zustimmung vor dem Unterzeichnen erforderlich
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Unterzeichner müssen vor dem Absenden ausdrücklich der elektronischen Unterzeichnung des Dokuments zustimmen require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Unterzeichner müssen vor dem Absenden ausdrücklich der elektronischen Unterzeichnung des Dokuments zustimmen
require_id_verification_before_signing: Identitätsprüfung vor der Unterzeichnung erforderlich
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Unterzeichner müssen einen amtlichen Ausweis hochladen, um ihre Identität vor der Unterzeichnung zu bestätigen
allow_typed_text_signatures: Getippte Unterschriften zulassen allow_typed_text_signatures: Getippte Unterschriften zulassen
allow_to_resubmit_completed_forms: Erneutes Einreichen abgeschlossener Formulare zulassen allow_to_resubmit_completed_forms: Erneutes Einreichen abgeschlossener Formulare zulassen
allow_to_decline_documents: Ablehnen von Dokumenten erlauben allow_to_decline_documents: Ablehnen von Dokumenten erlauben
@ -6530,6 +6542,8 @@ pl:
days: dni days: dni
require_consent_before_signing: Wymagaj zgody przed podpisaniem require_consent_before_signing: Wymagaj zgody przed podpisaniem
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Wymagaj od podpisujących wyraźnej zgody na elektroniczne podpisanie dokumentu przed wysłaniem require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Wymagaj od podpisujących wyraźnej zgody na elektroniczne podpisanie dokumentu przed wysłaniem
require_id_verification_before_signing: Wymagaj weryfikacji tożsamości przed podpisaniem
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Wymagaj od podpisujących przesłania dokumentu tożsamości w celu weryfikacji tożsamości przed podpisaniem
uk: uk:
require_phone_2fa_to_open: Вимагати двофакторну автентифікацію через телефон для відкриття require_phone_2fa_to_open: Вимагати двофакторну автентифікацію через телефон для відкриття
@ -6641,6 +6655,8 @@ uk:
days: днів days: днів
require_consent_before_signing: Вимагати згоду перед підписанням require_consent_before_signing: Вимагати згоду перед підписанням
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Вимагати від підписантів явної згоди на електронний підпис документа перед відправленням require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Вимагати від підписантів явної згоди на електронний підпис документа перед відправленням
require_id_verification_before_signing: Вимагати перевірку особи перед підписанням
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Вимагати від підписантів завантаження посвідчення особи для підтвердження їхньої особи перед підписанням
cs: cs:
require_phone_2fa_to_open: Vyžadovat otevření pomocí telefonního 2FA require_phone_2fa_to_open: Vyžadovat otevření pomocí telefonního 2FA
@ -6752,6 +6768,8 @@ cs:
days: dní days: dní
require_consent_before_signing: Vyžadovat souhlas před podpisem require_consent_before_signing: Vyžadovat souhlas před podpisem
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Vyžadovat od podepisujících výslovný souhlas s elektronickým podpisem dokumentu před odesláním require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Vyžadovat od podepisujících výslovný souhlas s elektronickým podpisem dokumentu před odesláním
require_id_verification_before_signing: Vyžadovat ověření totožnosti před podpisem
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Vyžadovat od podepisujících nahrání úředního dokladu totožnosti pro ověření identity před podpisem
he: he:
require_phone_2fa_to_open: דרוש אימות דו-שלבי באמצעות טלפון לפתיחה require_phone_2fa_to_open: דרוש אימות דו-שלבי באמצעות טלפון לפתיחה
@ -6863,6 +6881,8 @@ he:
days: ימים days: ימים
require_consent_before_signing: דרוש הסכמה לפני החתימה require_consent_before_signing: דרוש הסכמה לפני החתימה
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: דרוש מהחותמים להסכים במפורש לחתום על המסמך באופן אלקטרוני לפני השליחה require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: דרוש מהחותמים להסכים במפורש לחתום על המסמך באופן אלקטרוני לפני השליחה
require_id_verification_before_signing: דרוש אימות זהות לפני חתימה
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: דרוש מהחותמים להעלות תעודת זהות רשמית לאימות זהותם לפני החתימה
nl: &nl nl: &nl
knowledge_based_authentication: Kennisgebaseerde authenticatie knowledge_based_authentication: Kennisgebaseerde authenticatie
@ -7054,6 +7074,8 @@ nl: &nl
require_signing_reason: Reden voor ondertekening verplicht stellen require_signing_reason: Reden voor ondertekening verplicht stellen
require_consent_before_signing: Toestemming vereist voor ondertekening require_consent_before_signing: Toestemming vereist voor ondertekening
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Ondertekenaars moeten expliciet toestemming geven voor het elektronisch ondertekenen van het document voordat ze kunnen indienen require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: Ondertekenaars moeten expliciet toestemming geven voor het elektronisch ondertekenen van het document voordat ze kunnen indienen
require_id_verification_before_signing: Identiteitsverificatie vereist voor ondertekening
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: Ondertekenaars moeten een officieel identiteitsbewijs uploaden om hun identiteit te verifiëren voordat ze kunnen tekenen
allow_typed_text_signatures: Handtekeningen met getypte tekst toestaan allow_typed_text_signatures: Handtekeningen met getypte tekst toestaan
allow_to_resubmit_completed_forms: Opnieuw indienen van voltooide formulieren toestaan allow_to_resubmit_completed_forms: Opnieuw indienen van voltooide formulieren toestaan
allow_to_decline_documents: Weigeren van documenten toestaan allow_to_decline_documents: Weigeren van documenten toestaan
@ -8035,6 +8057,8 @@ ar:
days: أيام days: أيام
require_consent_before_signing: طلب الموافقة قبل التوقيع require_consent_before_signing: طلب الموافقة قبل التوقيع
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: يجب على الموقعين الموافقة صراحة على توقيع المستند إلكترونيًا قبل الإرسال require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: يجب على الموقعين الموافقة صراحة على توقيع المستند إلكترونيًا قبل الإرسال
require_id_verification_before_signing: مطلوب التحقق من الهوية قبل التوقيع
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: يجب على الموقعين تحميل وثيقة هوية رسمية للتحقق من هويتهم قبل التوقيع
ko: ko:
require_phone_2fa_to_open: 휴대폰 2FA를 열 때 요구함 require_phone_2fa_to_open: 휴대폰 2FA를 열 때 요구함
@ -8146,6 +8170,8 @@ ko:
days: days:
require_consent_before_signing: 서명 전 동의 필요 require_consent_before_signing: 서명 전 동의 필요
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: 서명자가 제출 전에 전자 서명에 명시적으로 동의해야 합니다 require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: 서명자가 제출 전에 전자 서명에 명시적으로 동의해야 합니다
require_id_verification_before_signing: 서명 전 신분증 확인 필요
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: 서명자가 서명 전에 본인 확인을 위해 정부 발급 신분증을 업로드해야 합니다
ja: ja:
require_phone_2fa_to_open: 電話による2段階認証が必要です require_phone_2fa_to_open: 電話による2段階認証が必要です
@ -8257,6 +8283,8 @@ ja:
days: days:
require_consent_before_signing: 署名前に同意が必要 require_consent_before_signing: 署名前に同意が必要
require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: 署名者は送信前に電子署名に明示的に同意する必要があります require_signers_to_explicitly_consent_to_signing_the_document_electronically_before_they_can_submit: 署名者は送信前に電子署名に明示的に同意する必要があります
require_id_verification_before_signing: 署名前に本人確認が必要
require_signers_to_upload_a_government_issued_id_document_to_verify_their_identity_before_they_can_sign: 署名者は署名前に本人確認のため公的身分証明書をアップロードする必要があります
en-US: en-US:
<<: *en <<: *en

@ -29,6 +29,7 @@ Rails.application.routes.draw do
resources :submitter_email_clicks, only: %i[create] resources :submitter_email_clicks, only: %i[create]
resources :submitter_form_views, only: %i[create] resources :submitter_form_views, only: %i[create]
resources :submitter_consents, only: %i[create] resources :submitter_consents, only: %i[create]
resources :submitter_id_documents, only: %i[create]
resources :submitters, only: %i[index show update] resources :submitters, only: %i[index show update]
resources :submissions, only: %i[index show create destroy] do resources :submissions, only: %i[index show create destroy] do
resources :documents, only: %i[index], controller: 'submission_documents' resources :documents, only: %i[index], controller: 'submission_documents'

@ -19,6 +19,7 @@ module Submitters
AccountConfig::WITH_TIMESTAMP_SECONDS_KEY, AccountConfig::WITH_TIMESTAMP_SECONDS_KEY,
AccountConfig::WITH_SIGNATURE_ID_REASON_KEY, AccountConfig::WITH_SIGNATURE_ID_REASON_KEY,
AccountConfig::REQUIRE_CONSENT_KEY, AccountConfig::REQUIRE_CONSENT_KEY,
AccountConfig::REQUIRE_ID_VERIFICATION_KEY,
*(Docuseal.multitenant? ? [] : [AccountConfig::POLICY_LINKS_KEY])].freeze *(Docuseal.multitenant? ? [] : [AccountConfig::POLICY_LINKS_KEY])].freeze
module_function module_function
@ -44,13 +45,14 @@ module Submitters
with_field_labels = find_safe_value(configs, AccountConfig::WITH_FIELD_LABELS_KEY) != false with_field_labels = find_safe_value(configs, AccountConfig::WITH_FIELD_LABELS_KEY) != false
policy_links = find_safe_value(configs, AccountConfig::POLICY_LINKS_KEY) policy_links = find_safe_value(configs, AccountConfig::POLICY_LINKS_KEY)
require_consent = find_safe_value(configs, AccountConfig::REQUIRE_CONSENT_KEY) == true require_consent = find_safe_value(configs, AccountConfig::REQUIRE_CONSENT_KEY) == true
require_id_verification = find_safe_value(configs, AccountConfig::REQUIRE_ID_VERIFICATION_KEY) == true
attrs = { completed_button:, with_typed_signature:, with_confetti:, attrs = { completed_button:, with_typed_signature:, with_confetti:,
reuse_signature:, with_decline:, with_delegate:, with_partial_download:, reuse_signature:, with_decline:, with_delegate:, with_partial_download:,
policy_links:, enforce_signing_order:, completed_message:, policy_links:, enforce_signing_order:, completed_message:,
require_signing_reason:, prefill_signature:, with_submitter_timezone:, require_signing_reason:, prefill_signature:, with_submitter_timezone:,
with_signature_id_reason:, with_signature_id:, with_field_labels:, with_timestamp_seconds:, with_signature_id_reason:, with_signature_id:, with_field_labels:, with_timestamp_seconds:,
require_consent: } require_consent:, require_id_verification: }
keys.each do |key| keys.each do |key|
attrs[key.to_sym] = configs.find { |e| e.key == key.to_s }&.value attrs[key.to_sym] = configs.find { |e| e.key == key.to_s }&.value

Loading…
Cancel
Save