diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 49314f32..162d553c 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -47,7 +47,8 @@ class AccountsController < ApplicationController authorize!(:manage, current_account) true_user.skip_reconfirmation! - true_user.update!(locked_at: Time.current, email: true_user.email.sub('@', '+removed@')) + true_user.update!(locked_at: Time.current, archived_at: Time.current, + email: true_user.email.sub('@', '+removed@')) true_user.account.update!(archived_at: Time.current) # rubocop:disable Layout/LineLength diff --git a/app/controllers/api/tools_controller.rb b/app/controllers/api/tools_controller.rb index 09ea9235..aa937c44 100644 --- a/app/controllers/api/tools_controller.rb +++ b/app/controllers/api/tools_controller.rb @@ -17,24 +17,23 @@ module Api def verify file = Base64.decode64(params[:file]) - pdf = HexaPDF::Document.new(io: StringIO.new(file)) trusted_certs = Accounts.load_trusted_certs(current_account) is_checksum_found = CompletedDocument.exists?(sha256: Base64.urlsafe_encode64(Digest::SHA256.digest(file))) render json: { checksum_status: is_checksum_found ? 'verified' : 'not_found', - signatures: pdf.signatures.map do |sig| + signatures: VerifyPdfSignature.call(StringIO.new(file), trusted_certs).map do |sig| { - verification_result: sig.verify(trusted_certs:).messages, - signer_name: sig.signer_name, - signing_reason: sig.signing_reason, + verification_result: sig.messages.map { |m| { type: m.status || :info, content: m.text } }, + signer_name: sig.common_name, + signing_reason: sig.reason, signing_time: sig.signing_time, - signature_type: sig.signature_type + signature_type: sig.type } end } - rescue HexaPDF::MalformedPDFError + rescue Pdfium::PdfiumError render json: { error: 'Malformed PDF' }, status: :unprocessable_content end end diff --git a/app/controllers/mfa_setup_controller.rb b/app/controllers/mfa_setup_controller.rb index 1860c1ed..702bd6e9 100644 --- a/app/controllers/mfa_setup_controller.rb +++ b/app/controllers/mfa_setup_controller.rb @@ -14,14 +14,14 @@ class MfaSetupController < ApplicationController def edit; end def create + RateLimit.call("mfa-setup-otp-#{current_user.id}", limit: 5, ttl: 5.minutes, enabled: true) + if current_user.validate_and_consume_otp!(params[:otp_attempt]) current_user.otp_required_for_login = true current_user.save! 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') @@ -31,13 +31,13 @@ class MfaSetupController < ApplicationController end def destroy + RateLimit.call("mfa-setup-otp-#{current_user.id}", limit: 5, ttl: 5.minutes, enabled: true) + if current_user.validate_and_consume_otp!(params[:otp_attempt]) current_user.update!(otp_required_for_login: false, otp_secret: nil) 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 diff --git a/app/controllers/submit_form_completed_download_controller.rb b/app/controllers/submit_form_completed_download_controller.rb index 17503778..7e244fe6 100644 --- a/app/controllers/submit_form_completed_download_controller.rb +++ b/app/controllers/submit_form_completed_download_controller.rb @@ -48,7 +48,7 @@ class SubmitFormCompletedDownloadController < ApplicationController private def submitter_slug - params[:submit_form_slug] || params[:submitter_slug] || params[:submitter_id] + params[:submit_form_slug] || params[:submitter_slug] end def respond_with_combined(submitter) diff --git a/app/controllers/submit_form_controller.rb b/app/controllers/submit_form_controller.rb index 43db9514..7cde8f69 100644 --- a/app/controllers/submit_form_controller.rb +++ b/app/controllers/submit_form_controller.rb @@ -107,6 +107,8 @@ class SubmitFormController < ApplicationController submitter_version = SubmitterVersion.find_by!(slug: params[:slug] || params[:submit_form_slug]) @submitter = submitter_version.submitter + + maybe_render_locked_page end private diff --git a/app/controllers/testing_accounts_controller.rb b/app/controllers/testing_accounts_controller.rb index 44274eef..a96f21e5 100644 --- a/app/controllers/testing_accounts_controller.rb +++ b/app/controllers/testing_accounts_controller.rb @@ -6,6 +6,7 @@ class TestingAccountsController < ApplicationController def create authorize!(:manage, current_account) authorize!(:manage, current_user) + authorize!(:manage, EncryptedConfig) impersonate_user(Accounts.find_or_create_testing_user(true_user.account)) diff --git a/app/javascript/submission_form/completed.vue b/app/javascript/submission_form/completed.vue index 6c07a09c..0cfe3733 100644 --- a/app/javascript/submission_form/completed.vue +++ b/app/javascript/submission_form/completed.vue @@ -230,7 +230,7 @@ export default { download () { this.isDownloading = true - fetch(this.baseUrl + `/submitters/${this.submitterSlug}/download`, { + fetch(this.baseUrl + `/s/${this.submitterSlug}/documents`, { method: 'GET', ...this.fetchOptions }).then(async (response) => { diff --git a/app/javascript/template_builder/import_list.vue b/app/javascript/template_builder/import_list.vue index d5f00711..94291e75 100644 --- a/app/javascript/template_builder/import_list.vue +++ b/app/javascript/template_builder/import_list.vue @@ -366,6 +366,9 @@ export default { } } }, + beforeUnmount () { + document.getElementById('list_form_buttons')?.classList?.add('hidden') + }, methods: { t (key) { return this.i18n[key] || key diff --git a/app/views/submissions_archived/index.html.erb b/app/views/submissions_archived/index.html.erb index a51b8099..a9ca204e 100644 --- a/app/views/submissions_archived/index.html.erb +++ b/app/views/submissions_archived/index.html.erb @@ -17,7 +17,7 @@
- <%= render 'submissions_filters/applied_filters', filter_params:, with_status: true %> + <%= render 'submissions_filters/applied_filters', filter_params:, with_status: true, with_default_status: true %> <%= render 'submissions_filters/filter_button', filter_params: %>
diff --git a/app/views/submissions_filters/_applied_filters.html.erb b/app/views/submissions_filters/_applied_filters.html.erb index 35323184..d3609361 100644 --- a/app/views/submissions_filters/_applied_filters.html.erb +++ b/app/views/submissions_filters/_applied_filters.html.erb @@ -3,15 +3,16 @@ <% ordered_filters = request.query_parameters.keys.filter_map { |key| filter_names.find { |name| key.start_with?(name) } }.uniq %> <% chip_order = ordered_filters.reverse.each_with_index.to_h { |name, index| [name, index + 1] } %> <% chip_order.default = ordered_filters.size + 1 %> -<% status_icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit', 'sent' => 'send', 'opened' => 'mail_opened' }[params[:status]] %> <% status_icons = { 'all' => 'list', 'pending' => 'clock', 'completed' => 'circle_check' } %> <% current_status = status_icons.key?(params[:status].to_s) ? params[:status].to_s : 'all' %> +<% status_icon = { 'declined' => 'x_circle', 'expired' => 'clock_cancel', 'partially_completed' => 'clock_edit', 'sent' => 'send', 'opened' => 'mail_opened' }[params[:status]] %> +<% default_status_icon = status_icons[params[:status]] if status_icon.blank? && local_assigns[:with_default_status] && current_status != 'all' %> <% with_status_button = local_assigns[:with_status] && status_icon.blank? %> -<% chips_html = capture do %> - <% if status_icon %> -
+<% status_chip_html = capture do %> + <% if status_icon || default_status_icon %> +
<%= link_to submissions_filter_path('status', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %> - <%= svg_icon(status_icon, class: 'w-5 h-5 shrink-0') %> + <%= svg_icon(status_icon || default_status_icon, class: 'w-5 h-5 shrink-0') %> <%= t(params[:status]) %> <% end %> <%= link_to url_for(params: request.query_parameters.except('status')), class: 'rounded-lg ml-1 shrink-0 hover:bg-base-content hover:text-white' do %> @@ -19,6 +20,8 @@ <% end %>
<% end %> +<% end %> +<% chips_html = capture do %> <% if params[:folder].present? %>
<%= link_to submissions_filter_path('folder', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 flex-1 min-w-0 pr-1' do %> @@ -78,7 +81,8 @@
<% end %> <% end %> -<% if with_status_button && chips_html.blank? %> +<% with_status_dropdown = with_status_button && chips_html.blank? %> +<% if with_status_dropdown %> <% end %> -<% if chips_html.present? %> - - <% if with_status_button %> +<% if chips_html.present? || status_chip_html.present? %> + + <% if with_status_button && !with_status_dropdown %> <%= svg_icon(status_icons[current_status], class: 'w-5 h-5 shrink-0') %> <%= t(current_status) %> <%= svg_icon('chevron_down', class: 'w-4 h-4 shrink-0') %> <% end %> + <%= status_chip_html %> <%= chips_html %> <% end %> diff --git a/app/views/templates/_title.html.erb b/app/views/templates/_title.html.erb index d9446ebd..02d6b3f4 100644 --- a/app/views/templates/_title.html.erb +++ b/app/views/templates/_title.html.erb @@ -64,8 +64,12 @@ <% end %>
- <% if !template.archived_at? && can?(:destroy, template) %> - <%= button_to button_title(title: t('archive'), disabled_with: t('archiving')[..-4], title_class: 'inline', icon: svg_icon('archive', class: 'w-6 h-6')), template_path(template), class: 'btn btn-outline btn-sm w-full', form_class: 'flex-1', method: :delete %> + <% if can?(:destroy, template) %> + <% if template.archived_at? %> + <%= button_to button_title(title: t('restore'), disabled_with: t('restoring')[..-4], icon: svg_icon('rotate', class: 'w-6 h-6')), template_restore_index_path(template), class: 'btn btn-outline btn-sm w-full', form_class: 'flex-1' %> + <% else %> + <%= button_to button_title(title: t('archive'), disabled_with: t('archiving')[..-4], title_class: 'inline', icon: svg_icon('archive', class: 'w-6 h-6')), template_path(template), class: 'btn btn-outline btn-sm w-full', form_class: 'flex-1', method: :delete %> + <% end %> <% end %> <% if can?(:create, current_account.templates.new(author: current_user)) %>
@@ -99,9 +103,6 @@ <% end %> <% end %> <% if template.archived_at? %> - <% if can?(:destroy, template) %> - <%= button_to button_title(title: t('restore'), disabled_with: t('restoring')[..-4], icon: svg_icon('rotate', class: 'w-6 h-6')), template_restore_index_path(template), class: 'btn btn-outline btn-sm w-full', form_class: 'flex-1' %> - <% end %>
<%= link_to template_preview_path(template), class: 'btn btn-outline btn-sm w-full' do %> diff --git a/app/views/templates_archived_submissions/index.html.erb b/app/views/templates_archived_submissions/index.html.erb index 4bbf24c9..407432c0 100644 --- a/app/views/templates_archived_submissions/index.html.erb +++ b/app/views/templates_archived_submissions/index.html.erb @@ -19,7 +19,7 @@
- <%= render 'submissions_filters/applied_filters', filter_params:, with_status: true %> + <%= render 'submissions_filters/applied_filters', filter_params:, with_status: true, with_default_status: true %>