add hmac webhook secret

This commit is contained in:
Pete Matsyburka
2026-05-06 11:45:35 +03:00
parent 1304849b55
commit 45ae954c0c
13 changed files with 205 additions and 22 deletions
@@ -0,0 +1,7 @@
# frozen_string_literal: true
class WebhookHmacController < ApplicationController
load_and_authorize_resource :webhook_url, parent: false
def show; end
end
+15 -9
View File
@@ -4,14 +4,15 @@
#
# Table name: webhook_urls
#
# id :bigint not null, primary key
# events :text not null
# secret :text not null
# sha1 :string not null
# url :text not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint not null
# id :bigint not null, primary key
# events :text not null
# hmac_secret :text not null
# secret :text not null
# sha1 :string not null
# url :text not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint not null
#
# Indexes
#
@@ -47,10 +48,15 @@ class WebhookUrl < ApplicationRecord
serialize :secret, coder: JSON
before_validation :set_sha1
before_validation :set_hmac_secret
encrypts :url, :secret
encrypts :url, :secret, :hmac_secret
def set_sha1
self.sha1 = Digest::SHA1.hexdigest(url)
end
def set_hmac_secret
self.hmac_secret ||= WebhookUrls::Signatures.generate_secret
end
end
+20
View File
@@ -0,0 +1,20 @@
<%= render 'shared/turbo_modal', title: t('webhook_security') do %>
<div class="text-center mb-4">
<div class="inline-flex justify-center">
<%= link_to t('secret'), webhook_secret_path(@webhook_url), class: 'block bg-base-200 md:min-w-[112px] text-sm font-semibold whitespace-nowrap py-1.5 px-4 rounded-l-3xl', data: { turbo_frame: 'modal' } %>
<%= link_to t('hmac'), webhook_hmac_path(@webhook_url), class: 'block bg-base-300 md:min-w-[112px] text-sm font-semibold whitespace-nowrap py-1.5 px-4 rounded-r-3xl', data: { turbo_frame: 'modal' } %>
</div>
</div>
<div class="form-control">
<label class="label" for="hmac_secret"><%= t('hmac_signing_secret') %></label>
<% token = @webhook_url.hmac_secret %>
<% obscured = "#{token[0, 10]}#{'*' * [token.length - 10, 0].max}" %>
<div class="flex gap-2">
<masked-input class="block w-full" data-token="<%= token %>">
<input id="hmac_secret" type="text" value="<%= obscured %>" class="base-input font-mono w-full" autocomplete="off" readonly>
</masked-input>
<%= render 'shared/clipboard_copy', icon: 'copy', text: token, class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
</div>
<p class="text-sm mt-2 opacity-70"><%= t('hmac_signature_header_hint_html', header: '<code>X-Docuseal-Signature</code>'.html_safe) %></p>
</div>
<% end %>
+7 -1
View File
@@ -1,4 +1,10 @@
<%= render 'shared/turbo_modal', title: t('webhook_secret') do %>
<%= render 'shared/turbo_modal', title: t('webhook_security') do %>
<div class="text-center mb-4">
<div class="inline-flex justify-center">
<%= link_to t('secret'), webhook_secret_path(@webhook_url), class: 'block bg-base-300 md:min-w-[112px] text-sm font-semibold whitespace-nowrap py-1.5 px-4 rounded-l-3xl', data: { turbo_frame: 'modal' } %>
<%= link_to t('hmac'), webhook_hmac_path(@webhook_url), class: 'block bg-base-200 md:min-w-[112px] text-sm font-semibold whitespace-nowrap py-1.5 px-4 rounded-r-3xl', data: { turbo_frame: 'modal' } %>
</div>
</div>
<%= form_for @webhook_url, url: webhook_secret_path, method: :patch, html: { class: 'space-y-4' }, data: { turbo_frame: :_top } do |f| %>
<div class="space-y-2">
<%= f.fields_for :secret, Struct.new(:key, :value).new(*@webhook_url.secret.to_a.first) do |ff| %>
+1 -1
View File
@@ -23,7 +23,7 @@
<div class="flex items-center space-x-2 md:absolute md:right-0">
<%= link_to webhook_secret_path(@webhook_url), class: 'btn btn-outline btn-sm bg-white', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('lock', class: 'w-4 h-4') %>
<span><%= @webhook_url.secret.present? ? t('edit_secret') : t('add_secret') %></span>
<span><%= t('security') %></span>
<% end %>
<div class="tooltip tooltip-left md:tooltip-top" data-tip="<%= t('delete_webhook') %>">
<%= button_to settings_webhook_path(@webhook_url), class: 'btn btn-warning btn-sm', method: :delete, data: { turbo_confirm: t('are_you_sure_') } do %>