diff --git a/app/controllers/submissions_download_controller.rb b/app/controllers/submissions_download_controller.rb index 4bcd3237..eb216bc5 100644 --- a/app/controllers/submissions_download_controller.rb +++ b/app/controllers/submissions_download_controller.rb @@ -27,20 +27,18 @@ class SubmissionsDownloadController < ApplicationController Submissions::EnsureResultGenerated.call(last_submitter) - if last_submitter.completed_at < TTL.ago && !signature_valid && !current_user_submitter?(last_submitter) - Rollbar.info("TTL: #{last_submitter.id}") if defined?(Rollbar) + if !signature_valid && !current_user_submitter?(last_submitter) + return head :not_found unless Submitters::AuthorizedForForm.call(@submitter, current_user, request) - return head :not_found + if last_submitter.completed_at < TTL.ago + Rollbar.info("TTL: #{last_submitter.id}") if defined?(Rollbar) + + return head :not_found + end end if params[:combined] == 'true' - url = build_combined_url(@submitter) - - if url - render json: [url] - else - head :not_found - end + respond_with_combined(last_submitter) else render json: build_urls(last_submitter) end @@ -48,8 +46,18 @@ class SubmissionsDownloadController < ApplicationController private + def respond_with_combined(submitter) + url = build_combined_url(submitter) + + if url + render json: [url] + else + head :not_found + end + end + def current_user_submitter?(submitter) - current_user && current_user.account.submitters.exists?(id: submitter.id) + current_user && current_ability.can?(:read, submitter) end def build_urls(submitter) diff --git a/app/controllers/submit_form_controller.rb b/app/controllers/submit_form_controller.rb index f723aa32..5a6a0ae5 100644 --- a/app/controllers/submit_form_controller.rb +++ b/app/controllers/submit_form_controller.rb @@ -9,15 +9,15 @@ class SubmitFormController < ApplicationController before_action :load_submitter, only: %i[show update completed] before_action :maybe_render_locked_page, only: :show - before_action :maybe_require_link_2fa, only: %i[show update] + before_action :maybe_require_link_2fa, only: %i[show] CONFIG_KEYS = [].freeze def show submission = @submitter.submission + return render :email_2fa unless Submitters::AuthorizedForForm.pass_email_2fa?(@submitter, request) return redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at? - return render :email_2fa if require_email_2fa?(@submitter) @form_configs = Submitters::FormConfigs.call(@submitter, CONFIG_KEYS) @@ -48,7 +48,7 @@ class SubmitFormController < ApplicationController end def update - if require_email_2fa?(@submitter) + unless Submitters::AuthorizedForForm.call(@submitter, current_user, request) return render json: { error: I18n.t('verification_required_refresh_the_page_and_pass_2fa') }, status: :unprocessable_content end @@ -84,7 +84,9 @@ class SubmitFormController < ApplicationController def completed raise ActionController::RoutingError, I18n.t('not_found') if @submitter.account.archived_at? - redirect_to submit_form_path(params[:submit_form_slug]) if require_email_2fa?(@submitter) + return if Submitters::AuthorizedForForm.call(@submitter, current_user, request) + + redirect_to submit_form_path(params[:submit_form_slug]) end def success; end @@ -92,10 +94,7 @@ class SubmitFormController < ApplicationController private def maybe_require_link_2fa - return if @submitter.submission.source != 'link' - return unless @submitter.submission.template&.preferences&.dig('shared_link_2fa') == true - return if cookies.encrypted[:email_2fa_slug] == @submitter.slug - return if @submitter.email == current_user&.email && current_user&.account_id == @submitter.account_id + return if Submitters::AuthorizedForForm.pass_link_2fa?(@submitter, current_user, request) redirect_to start_form_path(@submitter.submission.template.slug) end @@ -117,12 +116,4 @@ class SubmitFormController < ApplicationController ActiveStorage::Attachment.where(record: submission.submitters, name: :attachments) .preload(:blob).index_by(&:uuid) end - - def require_email_2fa?(submitter) - return false if submitter.submission.template&.preferences&.dig('require_email_2fa') != true && - submitter.preferences['require_email_2fa'] != true - return false if cookies.encrypted[:email_2fa_slug] == submitter.slug - - true - end end diff --git a/app/controllers/submit_form_decline_controller.rb b/app/controllers/submit_form_decline_controller.rb index 918903fe..a8f969c3 100644 --- a/app/controllers/submit_form_decline_controller.rb +++ b/app/controllers/submit_form_decline_controller.rb @@ -11,7 +11,9 @@ class SubmitFormDeclineController < ApplicationController submitter.completed_at? || submitter.submission.archived_at? || submitter.submission.expired? || - submitter.submission.template&.archived_at? + submitter.submission.template&.archived_at? || + !Submitters::AuthorizedForForm.call(submitter, current_user, + request) ApplicationRecord.transaction do submitter.update!(declined_at: Time.current) diff --git a/app/controllers/submit_form_download_controller.rb b/app/controllers/submit_form_download_controller.rb index d6e0b692..3ebdc5e2 100644 --- a/app/controllers/submit_form_download_controller.rb +++ b/app/controllers/submit_form_download_controller.rb @@ -17,7 +17,8 @@ class SubmitFormDownloadController < ApplicationController @submitter.submission.template&.archived_at? || AccountConfig.exists?(account_id: @submitter.account_id, key: AccountConfig::ALLOW_TO_PARTIAL_DOWNLOAD_KEY, - value: false) + value: false) || + !Submitters::AuthorizedForForm.call(@submitter, current_user, request) last_completed_submitter = @submitter.submission.submitters .where.not(id: @submitter.id) diff --git a/app/controllers/submit_form_draw_signature_controller.rb b/app/controllers/submit_form_draw_signature_controller.rb index 773eb9e7..5ba141c1 100644 --- a/app/controllers/submit_form_draw_signature_controller.rb +++ b/app/controllers/submit_form_draw_signature_controller.rb @@ -12,7 +12,8 @@ class SubmitFormDrawSignatureController < ApplicationController return redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at? - if @submitter.submission.template&.archived_at? || @submitter.submission.archived_at? + if @submitter.submission.template&.archived_at? || @submitter.submission.archived_at? || + !Submitters::AuthorizedForForm.call(@submitter, current_user, request) return redirect_to submit_form_path(@submitter.slug) end diff --git a/app/controllers/submit_form_invite_controller.rb b/app/controllers/submit_form_invite_controller.rb index ab1f26c3..bcc848ae 100644 --- a/app/controllers/submit_form_invite_controller.rb +++ b/app/controllers/submit_form_invite_controller.rb @@ -45,7 +45,8 @@ class SubmitFormInviteController < ApplicationController !submitter.completed_at? && !submitter.submission.archived_at? && !submitter.submission.expired? && - !submitter.submission.template&.archived_at? + !submitter.submission.template&.archived_at? && + Submitters::AuthorizedForForm.call(submitter, current_user, request) end def filter_invite_submitters(submitter, key = 'invite_by_uuid') diff --git a/app/controllers/submit_form_values_controller.rb b/app/controllers/submit_form_values_controller.rb index e1a6b9ab..affd37ba 100644 --- a/app/controllers/submit_form_values_controller.rb +++ b/app/controllers/submit_form_values_controller.rb @@ -7,10 +7,12 @@ class SubmitFormValuesController < ApplicationController def index submitter = Submitter.find_by!(slug: params[:submit_form_slug]) - return render json: {} if submitter.completed_at? || submitter.declined_at? - return render json: {} if submitter.submission.template&.archived_at? || + return render json: {} if submitter.completed_at? || + submitter.declined_at? || + submitter.submission.template&.archived_at? || submitter.submission.archived_at? || - submitter.submission.expired? + submitter.submission.expired? || + !Submitters::AuthorizedForForm.call(submitter, current_user, request) value = submitter.values[params['field_uuid']] attachment = submitter.attachments.where(created_at: params[:after]..).find_by(uuid: value) if value.present? diff --git a/app/controllers/templates_uploads_controller.rb b/app/controllers/templates_uploads_controller.rb index e8c00aea..ddee4e90 100644 --- a/app/controllers/templates_uploads_controller.rb +++ b/app/controllers/templates_uploads_controller.rb @@ -56,7 +56,7 @@ class TemplatesUploadsController < ApplicationController def create_file_params_from_url tempfile = Tempfile.new tempfile.binmode - tempfile.write(DownloadUtils.call(params[:url]).body) + tempfile.write(DownloadUtils.call(params[:url], validate: true).body) tempfile.rewind filename = URI.decode_www_form_component(params[:filename]) if params[:filename].present? diff --git a/app/javascript/submission_form/completed.vue b/app/javascript/submission_form/completed.vue index 708843bf..c87f9c4f 100644 --- a/app/javascript/submission_form/completed.vue +++ b/app/javascript/submission_form/completed.vue @@ -161,6 +161,11 @@ export default { required: false, default: false }, + fetchOptions: { + type: Object, + required: false, + default: () => ({}) + }, completedButton: { type: Object, required: false, @@ -214,7 +219,10 @@ export default { download () { this.isDownloading = true - fetch(this.baseUrl + `/submitters/${this.submitterSlug}/download`).then(async (response) => { + fetch(this.baseUrl + `/submitters/${this.submitterSlug}/download`, { + method: 'GET', + ...this.fetchOptions + }).then(async (response) => { if (response.ok) { const urls = await response.json() const isMobileSafariIos = 'ontouchstart' in window && navigator.maxTouchPoints > 0 && /AppleWebKit/i.test(navigator.userAgent) diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index e3a3fadd..e2bdcf42 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -530,6 +530,7 @@ v-else-if="isInvite" :submitters="inviteSubmitters" :optional-submitters="optionalInviteSubmitters" + :fetch-options="fetchOptions" :submitter-slug="submitterSlug" :authenticity-token="authenticityToken" :url="baseUrl + submitPath + '/invite'" @@ -543,6 +544,7 @@ :has-signature-fields="stepFields.some((fields) => fields.some((f) => ['signature', 'initials'].includes(f.type)))" :has-multiple-documents="hasMultipleDocuments" :completed-button="completedRedirectUrl ? {} : completedButton" + :fetch-options="fetchOptions" :completed-message="completedRedirectUrl ? {} : completedMessage" :with-send-copy-button="withSendCopyButton && !completedRedirectUrl" :with-download-button="withDownloadButton && !completedRedirectUrl && !dryRun" @@ -678,6 +680,11 @@ export default { required: false, default: () => [] }, + fetchOptions: { + type: Object, + required: false, + default: () => ({}) + }, optionalInviteSubmitters: { type: Array, required: false, @@ -1467,7 +1474,8 @@ export default { } else { return fetch(this.baseUrl + this.submitPath, { method: 'POST', - body: formData || new FormData(this.$refs.form) + body: formData || new FormData(this.$refs.form), + ...this.fetchOptions }).then((response) => { if (response.status === 200) { currentFieldUuids.forEach((fieldUuid) => { diff --git a/app/javascript/submission_form/invite_form.vue b/app/javascript/submission_form/invite_form.vue index 10c3927d..3189b0d8 100644 --- a/app/javascript/submission_form/invite_form.vue +++ b/app/javascript/submission_form/invite_form.vue @@ -78,6 +78,11 @@ export default { type: Array, required: true }, + fetchOptions: { + type: Object, + required: false, + default: () => ({}) + }, optionalSubmitters: { type: Array, required: false, @@ -108,7 +113,8 @@ export default { return fetch(this.url, { method: 'POST', - body: new FormData(this.$refs.form) + body: new FormData(this.$refs.form), + ...this.fetchOptions }).then((response) => { if (response.status === 200) { this.$emit('success') diff --git a/app/javascript/template_builder/page.vue b/app/javascript/template_builder/page.vue index 550658c4..8580198c 100644 --- a/app/javascript/template_builder/page.vue +++ b/app/javascript/template_builder/page.vue @@ -261,7 +261,7 @@ export default { }, computed: { isSelectMode () { - return this.isSelectModeRef.value && !this.drawFieldType && this.editable && !this.drawField + return this.isSelectModeRef.value && !this.drawFieldType && this.editable && !this.drawField && !this.drawCustomField }, pageSelectedAreas () { if (!this.selectedAreasRef.value) return [] diff --git a/app/jobs/send_test_webhook_request_job.rb b/app/jobs/send_test_webhook_request_job.rb index 158363ec..0116cdaf 100644 --- a/app/jobs/send_test_webhook_request_job.rb +++ b/app/jobs/send_test_webhook_request_job.rb @@ -26,7 +26,7 @@ class SendTestWebhookRequestJob Addressable::URI.parse(webhook_url.url).normalize end - raise HttpsError, 'Only HTTPS is allowed.' if uri.scheme != 'https' + raise HttpsError, 'Only HTTPS is allowed.' if uri.scheme != 'https' || [443, nil].exclude?(uri.port) raise LocalhostError, "Can't send to localhost." if uri.host.in?(SendWebhookRequest::LOCALHOSTS) end diff --git a/app/views/email_smtp_settings/index.html.erb b/app/views/email_smtp_settings/index.html.erb index 7e556a47..821d8335 100644 --- a/app/views/email_smtp_settings/index.html.erb +++ b/app/views/email_smtp_settings/index.html.erb @@ -22,7 +22,7 @@