mirror of https://github.com/docusealco/docuseal
parent
c5d71505ef
commit
ced88476cc
@ -0,0 +1,52 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class TimestampServerController < ApplicationController
|
||||||
|
before_action :build_encrypted_config
|
||||||
|
authorize_resource :encrypted_config
|
||||||
|
|
||||||
|
def create
|
||||||
|
return head :not_found if Docuseal.multitenant?
|
||||||
|
|
||||||
|
test_timeserver_url(@encrypted_config.value) if @encrypted_config.value.present?
|
||||||
|
|
||||||
|
if @encrypted_config.value.present? ? @encrypted_config.save : @encrypted_config.delete
|
||||||
|
redirect_back fallback_location: settings_notifications_path, notice: 'Changes have been saved'
|
||||||
|
else
|
||||||
|
redirect_back fallback_location: settings_notifications_path, alert: 'Unable to save'
|
||||||
|
end
|
||||||
|
rescue HexaPDF::Error, SocketError, Submissions::TimestampHandler::TimestampError
|
||||||
|
redirect_back fallback_location: settings_notifications_path, alert: 'Invalid Timeserver'
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def test_timeserver_url(url)
|
||||||
|
pdf = HexaPDF::Document.new
|
||||||
|
pdf.pages.add
|
||||||
|
|
||||||
|
pkcs = Accounts.load_signing_pkcs(current_account)
|
||||||
|
|
||||||
|
pdf.sign(StringIO.new,
|
||||||
|
reason: 'Test',
|
||||||
|
certificate: pkcs.certificate,
|
||||||
|
key: pkcs.key,
|
||||||
|
certificate_chain: pkcs.ca_certs || [],
|
||||||
|
timestamp_handler: Submissions::TimestampHandler.new(tsa_url: url))
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_encrypted_config
|
||||||
|
@encrypted_config
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_encrypted_config
|
||||||
|
@encrypted_config =
|
||||||
|
EncryptedConfig.find_or_initialize_by(account: current_account,
|
||||||
|
key: EncryptedConfig::TIMESTAMP_SERVER_URL_KEY)
|
||||||
|
|
||||||
|
@encrypted_config.assign_attributes(encrypted_config_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def encrypted_config_params
|
||||||
|
params.require(:encrypted_config).permit(:value)
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Submissions
|
||||||
|
class TimestampHandler
|
||||||
|
HASH_ALGORITHM = 'SHA512'
|
||||||
|
|
||||||
|
TimestampError = Class.new(StandardError)
|
||||||
|
|
||||||
|
attr_reader :tsa_url
|
||||||
|
|
||||||
|
def initialize(tsa_url:)
|
||||||
|
@tsa_url = tsa_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def finalize_objects(_signature_field, signature)
|
||||||
|
signature.document.version = '2.0'
|
||||||
|
|
||||||
|
signature[:Type] = :DocTimeStamp
|
||||||
|
signature[:Filter] = :'Adobe.PPKLite'
|
||||||
|
signature[:SubFilter] = :'ETSI.RFC3161'
|
||||||
|
end
|
||||||
|
|
||||||
|
def sign(io, byte_range)
|
||||||
|
digest = OpenSSL::Digest.new(HASH_ALGORITHM)
|
||||||
|
|
||||||
|
io.pos = byte_range[0]
|
||||||
|
digest << io.read(byte_range[1])
|
||||||
|
io.pos = byte_range[2]
|
||||||
|
digest << io.read(byte_range[3])
|
||||||
|
|
||||||
|
uri = Addressable::URI.parse(tsa_url)
|
||||||
|
|
||||||
|
conn = Faraday.new(uri.origin) do |c|
|
||||||
|
c.basic_auth(uri.user, uri.password) if uri.password.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
response = conn.post(uri.path, build_payload(digest.digest),
|
||||||
|
'content-type' => 'application/timestamp-query')
|
||||||
|
|
||||||
|
raise TimestampError if response.status != 200 || response.body.blank?
|
||||||
|
|
||||||
|
OpenSSL::Timestamp::Response.new(response.body).token.to_der
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_payload(digest)
|
||||||
|
req = OpenSSL::Timestamp::Request.new
|
||||||
|
req.algorithm = HASH_ALGORITHM
|
||||||
|
req.message_imprint = digest
|
||||||
|
|
||||||
|
req.to_der
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in new issue