From 2c6ddd7f9093677e90860f092d654ff0879a7503 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Mon, 27 Jul 2026 15:48:04 +0300 Subject: [PATCH 01/42] rate limit remove 2fa --- app/controllers/mfa_setup_controller.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/controllers/mfa_setup_controller.rb b/app/controllers/mfa_setup_controller.rb index 2ea90412..1860c1ed 100644 --- a/app/controllers/mfa_setup_controller.rb +++ b/app/controllers/mfa_setup_controller.rb @@ -20,6 +20,8 @@ class MfaSetupController < ApplicationController redirect_to settings_profile_index_path, notice: I18n.t('2fa_has_been_configured') else + RateLimit.call("mfa-setup-otp-#{current_user.id}", limit: 5, ttl: 5.minutes, enabled: true) + @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Docuseal.product_name) @error_message = I18n.t('code_is_invalid') @@ -34,6 +36,8 @@ class MfaSetupController < ApplicationController redirect_to settings_profile_index_path, notice: I18n.t('2fa_has_been_removed') else + RateLimit.call("mfa-setup-otp-#{current_user.id}", limit: 5, ttl: 5.minutes, enabled: true) + @error_message = I18n.t('code_is_invalid') render turbo_stream: turbo_stream.replace(:modal, template: 'mfa_setup/edit'), status: :unprocessable_content From 972f38242e824d483684e1a72ce5f8fbdd83872b Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Wed, 29 Jul 2026 21:53:03 +0300 Subject: [PATCH 02/42] update rails --- Gemfile.lock | 143 ++++++++++++++++++++++++++------------------------- 1 file changed, 72 insertions(+), 71 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 816c8acd..15b1bbc8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,31 +1,31 @@ GEM remote: https://rubygems.org/ specs: - action_text-trix (2.1.18) + action_text-trix (2.1.19) railties - actioncable (8.1.3) - actionpack (= 8.1.3) - activesupport (= 8.1.3) + actioncable (8.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.1.3) - actionpack (= 8.1.3) - activejob (= 8.1.3) - activerecord (= 8.1.3) - activestorage (= 8.1.3) - activesupport (= 8.1.3) + actionmailbox (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) mail (>= 2.8.0) - actionmailer (8.1.3) - actionpack (= 8.1.3) - actionview (= 8.1.3) - activejob (= 8.1.3) - activesupport (= 8.1.3) + actionmailer (8.1.3.1) + actionpack (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activesupport (= 8.1.3.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.1.3) - actionview (= 8.1.3) - activesupport (= 8.1.3) + actionpack (8.1.3.1) + actionview (= 8.1.3.1) + activesupport (= 8.1.3.1) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -33,36 +33,36 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.1.3) + actiontext (8.1.3.1) action_text-trix (~> 2.1.15) - actionpack (= 8.1.3) - activerecord (= 8.1.3) - activestorage (= 8.1.3) - activesupport (= 8.1.3) + actionpack (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.1.3) - activesupport (= 8.1.3) + actionview (8.1.3.1) + activesupport (= 8.1.3.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.1.3) - activesupport (= 8.1.3) + activejob (8.1.3.1) + activesupport (= 8.1.3.1) globalid (>= 0.3.6) - activemodel (8.1.3) - activesupport (= 8.1.3) - activerecord (8.1.3) - activemodel (= 8.1.3) - activesupport (= 8.1.3) + activemodel (8.1.3.1) + activesupport (= 8.1.3.1) + activerecord (8.1.3.1) + activemodel (= 8.1.3.1) + activesupport (= 8.1.3.1) timeout (>= 0.4.0) - activestorage (8.1.3) - actionpack (= 8.1.3) - activejob (= 8.1.3) - activerecord (= 8.1.3) - activesupport (= 8.1.3) + activestorage (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activesupport (= 8.1.3.1) marcel (~> 1.0) - activesupport (8.1.3) + activesupport (8.1.3.1) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) @@ -144,7 +144,7 @@ GEM cldr-plurals-runtime-rb (1.1.0) cmdparse (3.0.7) coderay (1.1.3) - concurrent-ruby (1.3.7) + concurrent-ruby (1.3.8) connection_pool (3.0.2) crack (1.0.1) bigdecimal @@ -179,7 +179,7 @@ GEM dotenv (3.2.0) drb (2.2.3) email_typo (0.2.3) - erb (6.0.4) + erb (6.0.6) erb_lint (0.9.0) activesupport better_html (>= 2.0.1) @@ -217,7 +217,7 @@ GEM foreman (0.90.0) thor (~> 1.4) geom2d (0.4.1) - globalid (1.3.0) + globalid (1.4.0) activesupport (>= 6.1) google-apis-core (1.0.2) addressable (~> 2.8, >= 2.8.7) @@ -262,7 +262,7 @@ GEM geom2d (~> 0.4, >= 0.4.1) openssl (>= 2.2.1) strscan (>= 3.1.2) - i18n (1.14.8) + i18n (1.15.2) concurrent-ruby (~> 1.0) io-console (0.8.2) irb (1.18.0) @@ -271,7 +271,7 @@ GEM rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.19.9) + json (2.21.1) jwt (3.2.0) base64 language_server-protocol (3.17.0.5) @@ -296,13 +296,13 @@ GEM loofah (2.25.2) crass (~> 1.0.2) nokogiri (>= 1.12.0) - mail (2.9.0) + mail (2.9.1) logger mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.1.0) + marcel (1.2.1) matrix (0.4.3) method_source (1.1.0) mini_mime (1.1.5) @@ -313,7 +313,7 @@ GEM multi_json (1.19.1) net-http (0.9.1) uri (>= 0.11.1) - net-imap (0.6.4.1) + net-imap (0.6.6) date net-protocol net-pop (0.1.2) @@ -357,7 +357,7 @@ GEM pg (1.6.3-arm64-darwin) pg (1.6.3-x86_64-linux) pg (1.6.3-x86_64-linux-musl) - pp (0.6.3) + pp (0.6.4) prettyprint pretender (1.0.0) actionpack (>= 7.2) @@ -369,9 +369,6 @@ GEM reline (>= 0.6.0) pry-rails (0.3.11) pry (>= 0.13.0) - psych (5.3.1) - date - stringio public_suffix (7.0.5) puma (8.0.2) nio4r (~> 2.0) @@ -386,20 +383,20 @@ GEM rack (>= 1.3) rackup (2.3.1) rack (>= 3) - rails (8.1.3) - actioncable (= 8.1.3) - actionmailbox (= 8.1.3) - actionmailer (= 8.1.3) - actionpack (= 8.1.3) - actiontext (= 8.1.3) - actionview (= 8.1.3) - activejob (= 8.1.3) - activemodel (= 8.1.3) - activerecord (= 8.1.3) - activestorage (= 8.1.3) - activesupport (= 8.1.3) + rails (8.1.3.1) + actioncable (= 8.1.3.1) + actionmailbox (= 8.1.3.1) + actionmailer (= 8.1.3.1) + actionpack (= 8.1.3.1) + actiontext (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activemodel (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) bundler (>= 1.15.0) - railties (= 8.1.3) + railties (= 8.1.3.1) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest @@ -410,9 +407,9 @@ GEM rails-i18n (8.1.0) i18n (>= 0.7, < 2) railties (>= 8.0.0, < 9) - railties (8.1.3) - actionpack (= 8.1.3) - activesupport (= 8.1.3) + railties (8.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -421,9 +418,14 @@ GEM zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.4.2) - rdoc (7.2.0) + rbs (4.1.0) + logger + prism (>= 1.6.0) + tsort + rdoc (8.0.0) erb - psych (>= 4.0.0) + prism (>= 1.6.0) + rbs (>= 4.0.0) tsort redis-client (0.29.0) connection_pool @@ -530,7 +532,6 @@ GEM sqlite3 (2.9.2-arm64-darwin) sqlite3 (2.9.2-x86_64-linux-gnu) sqlite3 (2.9.2-x86_64-linux-musl) - stringio (3.2.0) strip_attributes (2.0.1) activemodel (>= 3.0, < 9.0) strscan (3.1.8) @@ -570,14 +571,14 @@ GEM crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) webrick (1.9.2) - websocket-driver (0.8.0) + websocket-driver (0.8.2) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) yaml (0.4.0) - zeitwerk (2.7.5) + zeitwerk (2.8.2) PLATFORMS aarch64-linux From b8c29324b8af3df96881a1937b3a299a3a64b707 Mon Sep 17 00:00:00 2001 From: Alex Turchyn Date: Tue, 28 Jul 2026 19:27:25 +0300 Subject: [PATCH 03/42] paypal configs Co-authored-by: Pete Matsyburka --- app/controllers/account_configs_controller.rb | 6 +- .../templates_form_preview_controller.rb | 2 +- app/javascript/application.js | 5 +- app/javascript/form.js | 1 + app/javascript/submission_form/form.vue | 6 + app/javascript/submission_form/i18n.js | 14 ++ .../submission_form/payment_step.vue | 66 +++++++-- app/javascript/template_builder/builder.vue | 26 +++- .../template_builder/field_context_menu.vue | 13 +- .../template_builder/payment_settings.vue | 140 +++++++++++------- config/locales/i18n.yml | 127 ++++++++++++++++ 11 files changed, 334 insertions(+), 72 deletions(-) diff --git a/app/controllers/account_configs_controller.rb b/app/controllers/account_configs_controller.rb index 079866d7..23d82d94 100644 --- a/app/controllers/account_configs_controller.rb +++ b/app/controllers/account_configs_controller.rb @@ -37,7 +37,7 @@ class AccountConfigsController < ApplicationController end def destroy - raise InvalidKey unless allowed_keys.include?(@account_config.key) + raise InvalidKey unless allowed_destroy_keys.include?(@account_config.key) @account_config.destroy! @@ -50,6 +50,10 @@ class AccountConfigsController < ApplicationController ALLOWED_KEYS end + def allowed_destroy_keys + ALLOWED_KEYS + end + def load_account_config raise InvalidKey unless allowed_keys.include?(account_config_params[:key]) diff --git a/app/controllers/templates_form_preview_controller.rb b/app/controllers/templates_form_preview_controller.rb index 5c4f23c6..f5c1d7a6 100644 --- a/app/controllers/templates_form_preview_controller.rb +++ b/app/controllers/templates_form_preview_controller.rb @@ -18,6 +18,6 @@ class TemplatesFormPreviewController < ApplicationController @attachments_index = ActiveStorage::Attachment.where(record: @submitter.submission.submitters, name: :attachments) .preload(:blob).index_by(&:uuid) - @form_configs = Submitters::FormConfigs.call(@submitter) + @form_configs = Submitters::FormConfigs.call(@submitter, SubmitFormController::CONFIG_KEYS) end end diff --git a/app/javascript/application.js b/app/javascript/application.js index 458a7a4e..d834182a 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -181,7 +181,10 @@ safeRegisterElement('template-builder', class extends HTMLElement { authenticityToken: document.querySelector('meta[name="csrf-token"]')?.content, withCustomFields: true, withPayment: this.dataset.withPayment === 'true', - isPaymentConnected: this.dataset.isPaymentConnected === 'true', + isStripeConnected: this.dataset.isPaymentConnected === 'true' || this.dataset.isStripeConnected === 'true', + withStripe: this.dataset.withStripe !== 'false', + withPaypal: this.dataset.withPaypal === 'true', + isPaypalConnected: this.dataset.isPaypalConnected === 'true', withFormula: this.dataset.withFormula === 'true', withSendButton: this.dataset.withSendButton !== 'false', withSignYourselfButton: this.dataset.withSignYourselfButton !== 'false', diff --git a/app/javascript/form.js b/app/javascript/form.js index 75158250..ab9535ca 100644 --- a/app/javascript/form.js +++ b/app/javascript/form.js @@ -35,6 +35,7 @@ safeRegisterElement('submission-form', class extends HTMLElement { scrollPadding: this.dataset.scrollPadding || '-80px', signatureText: this.dataset.signatureText, language: this.dataset.language, + paymentProvider: this.dataset.paymentProvider, dryRun: this.dataset.dryRun === 'true', expand: ['true', 'false'].includes(this.dataset.expand) ? this.dataset.expand === 'true' : null, withSignatureId: this.dataset.withSignatureId === 'true', diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index e61feae5..882d7d08 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -560,6 +560,7 @@ :fields="formulaFields" :values="values" :readonly-values="readonlyFieldValues" + :provider="paymentProvider" @attached="attachments.push($event)" @focus="scrollIntoField(currentField)" @submit="!isSubmitting && submitStep()" @@ -1000,6 +1001,11 @@ export default { required: false, default: '' }, + paymentProvider: { + type: String, + required: false, + default: '' + }, values: { type: Object, required: false, diff --git a/app/javascript/submission_form/i18n.js b/app/javascript/submission_form/i18n.js index 71f73125..1da0bd06 100644 --- a/app/javascript/submission_form/i18n.js +++ b/app/javascript/submission_form/i18n.js @@ -107,6 +107,7 @@ const en = { email_has_been_sent: 'Email has been sent', processing: 'Processing', pay_with_stripe: 'Pay with Stripe', + pay_with_paypal: 'Pay with PayPal', reupload: 'Reupload', upload: 'Upload', files: 'Files', @@ -225,6 +226,7 @@ const es = { email_has_been_sent: 'El correo electrónico ha sido enviado', processing: 'Procesando', pay_with_stripe: 'Pagar con Stripe', + pay_with_paypal: 'Pagar con PayPal', reupload: 'Volver a subir', upload: 'Subir', files: 'Archivos', @@ -343,6 +345,7 @@ const it = { email_has_been_sent: "L'email è stata inviata", processing: 'Elaborazione', pay_with_stripe: 'Paga con Stripe', + pay_with_paypal: 'Paga con PayPal', reupload: 'Ricarica', upload: 'Carica', files: 'File', @@ -461,6 +464,7 @@ const de = { email_has_been_sent: 'E-Mail wurde gesendet', processing: 'Verarbeitung', pay_with_stripe: 'Mit Stripe bezahlen', + pay_with_paypal: 'Mit PayPal bezahlen', reupload: 'Erneut hochladen', upload: 'Hochladen', files: 'Dateien', @@ -579,6 +583,7 @@ const fr = { email_has_been_sent: "L'e-mail a été envoyé", processing: 'Traitement en cours', pay_with_stripe: 'Payer avec Stripe', + pay_with_paypal: 'Payer avec PayPal', reupload: 'Retéléverser', upload: 'Téléverser', files: 'Fichiers', @@ -697,6 +702,7 @@ const pl = { email_has_been_sent: 'E-mail został wysłany', processing: 'Przetwarzanie', pay_with_stripe: 'Zapłać za pomocą Stripe', + pay_with_paypal: 'Zapłać za pomocą PayPal', reupload: 'Ponowne przesłanie', upload: 'Przesyłanie', files: 'Pliki', @@ -815,6 +821,7 @@ const uk = { email_has_been_sent: 'Електронний лист був відправлений', processing: 'Обробка', pay_with_stripe: 'Сплатити за допомогою Stripe', + pay_with_paypal: 'Сплатити за допомогою PayPal', reupload: 'Перезавантажити', upload: 'Завантажити', files: 'Файли', @@ -933,6 +940,7 @@ const cs = { email_has_been_sent: 'E-mail byl odeslán', processing: 'Zpracování', pay_with_stripe: 'Zaplatit přes Stripe', + pay_with_paypal: 'Zaplatit přes PayPal', reupload: 'Znovu nahrát', upload: 'Nahrát', files: 'Soubory', @@ -1051,6 +1059,7 @@ const pt = { email_has_been_sent: 'Email enviado', processing: 'Processamento', pay_with_stripe: 'Pagar com Stripe', + pay_with_paypal: 'Pagar com PayPal', reupload: 'Recarregar', upload: 'Carregar', files: 'Arquivos', @@ -1169,6 +1178,7 @@ const he = { email_has_been_sent: 'האימייל נשלח', processing: 'מעבד', pay_with_stripe: 'שלם עם סטרייפ', + pay_with_paypal: 'שלם עם פייפאל', reupload: 'העלה שוב', upload: 'העלאה', files: 'קבצים', @@ -1287,6 +1297,7 @@ const nl = { email_has_been_sent: 'E-mail is verzonden', processing: 'Verwerken', pay_with_stripe: 'Betalen met Stripe', + pay_with_paypal: 'Betalen met PayPal', reupload: 'Opnieuw uploaden', upload: 'Uploaden', files: 'Bestanden', @@ -1405,6 +1416,7 @@ const ar = { email_has_been_sent: 'تم إرسال البريد الإلكتروني', processing: 'جارٍ المعالجة', pay_with_stripe: 'الدفع بواسطة Stripe', + pay_with_paypal: 'الدفع بواسطة PayPal', reupload: 'إعادة التحميل', upload: 'تحميل', files: 'الملفات', @@ -1523,6 +1535,7 @@ const ko = { email_has_been_sent: '이메일이 전송되었습니다', processing: '처리 중', pay_with_stripe: '스트라이프로 결제', + pay_with_paypal: 'PayPal로 결제', reupload: '다시 업로드', upload: '업로드', files: '파일', @@ -1641,6 +1654,7 @@ const ja = { email_has_been_sent: 'メールが送信されました', processing: '処理中', pay_with_stripe: 'Stripeで支払う', + pay_with_paypal: 'PayPalで支払う', reupload: '再アップロード', upload: 'アップロード', files: 'ファイル', diff --git a/app/javascript/submission_form/payment_step.vue b/app/javascript/submission_form/payment_step.vue index e1afbf40..f1ebabcc 100644 --- a/app/javascript/submission_form/payment_step.vue +++ b/app/javascript/submission_form/payment_step.vue @@ -1,6 +1,6 @@