demarches-normaliennes/app/models/bill_signature.rb

122 lines
2.9 KiB
Ruby
Raw Normal View History

2019-06-17 11:01:41 +02:00
class BillSignature < ApplicationRecord
has_many :dossier_operation_logs
has_one_attached :serialized
has_one_attached :signature
validate :check_bill_digest
validate :check_serialized_bill_contents
validate :check_signature_contents
def self.build_with_operations(operations, day)
bill = new(dossier_operation_logs: operations)
bill.serialize_operations(day)
bill
end
def serialize_operations(day)
2020-03-12 14:32:06 +01:00
serialized.attach(
2019-06-17 11:01:41 +02:00
io: StringIO.new(operations_bill_json),
filename: "demarches-simplifiees-operations-#{day.to_date.iso8601}.json",
content_type: 'application/json',
# we don't want to run virus scanner on this file
2019-06-17 11:01:41 +02:00
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
)
self.digest = operations_bill_digest
end
def operations_bill
dossier_operation_logs.map { |op| [op.id.to_s, op.digest] }.to_h
end
def operations_bill_json
operations_bill.to_json
end
def operations_bill_digest
Digest::SHA256.hexdigest(operations_bill_json)
end
def set_signature(signature, day)
2020-12-11 15:49:05 +01:00
self.signature.attach(
2019-06-17 11:01:41 +02:00
io: StringIO.new(signature),
filename: "demarches-simplifiees-signature-#{day.to_date.iso8601}.der",
content_type: 'application/x-x509-ca-cert',
# we don't want to run virus scanner on this file
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
2019-06-17 11:01:41 +02:00
)
end
# Validations
def check_bill_digest
2020-03-12 14:32:06 +01:00
if digest != operations_bill_digest
2019-06-17 11:01:41 +02:00
errors.add(:digest)
end
end
def check_serialized_bill_contents
2020-03-12 14:32:06 +01:00
if !serialized.attached?
2019-06-17 11:01:41 +02:00
errors.add(:serialized, :blank)
return
end
2020-03-12 14:32:06 +01:00
if JSON.parse(read_serialized) != operations_bill
2019-06-17 11:01:41 +02:00
errors.add(:serialized)
end
end
def check_signature_contents
2020-03-12 14:32:06 +01:00
if !signature.attached?
2019-06-17 11:01:41 +02:00
errors.add(:signature, :blank)
return
end
2020-03-12 14:32:06 +01:00
timestamp_signature_date = ASN1::Timestamp.signature_time(read_signature)
2019-06-17 11:01:41 +02:00
if timestamp_signature_date > Time.zone.now
errors.add(:signature, :invalid_date)
end
2020-03-12 14:32:06 +01:00
timestamp_signed_digest = ASN1::Timestamp.signed_digest(read_signature)
if timestamp_signed_digest != digest
2019-06-17 11:01:41 +02:00
errors.add(:signature)
end
end
2020-03-12 14:32:06 +01:00
def read_signature
2020-12-14 14:08:39 +01:00
read_attachment('signature')
2020-03-12 14:32:06 +01:00
end
def read_serialized
2020-12-14 14:08:39 +01:00
read_attachment('serialized')
end
private
def read_attachment(attachment)
if attachment_changes[attachment]
io = io_for_changes(attachment_changes[attachment])
if io.present?
io.rewind
result = io.read
io.rewind
result
end
2020-03-12 14:32:06 +01:00
elsif serialized.attached?
serialized.download
end
end
def io_for_changes(attachment_changes)
attachable = attachment_changes.attachable
case attachable
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
attachable.open
when Hash
attachable.fetch(:io)
end
end
2019-06-17 11:01:41 +02:00
end