This commit is contained in:
David Arranz 2026-01-30 14:12:05 +01:00
parent e7aea83bdb
commit 1a76b4aa22
4 changed files with 50 additions and 14 deletions

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "factuges-document-signing-service" name = "factuges-document-signing-service"
version = "0.1.1" version = "0.1.3"
description = "FastAPI service for signing PDF documents using external secret managers" description = "FastAPI service for signing PDF documents using external secret managers"
requires-python = ">=3.11" requires-python = ">=3.11"
@ -14,7 +14,7 @@ dependencies = [
"fastapi>=0.110", "fastapi>=0.110",
"uvicorn[standard]>=0.27", "uvicorn[standard]>=0.27",
"pyhanko>=0.25", "pyhanko>=0.32",
"cryptography>=42", "cryptography>=42",
# Infisical SDK for secret management # Infisical SDK for secret management

View File

@ -66,7 +66,7 @@ fi
# --- Detectar nombre y versión de la API --- # --- Detectar nombre y versión de la API ---
IMAGE_NAME=$(node -p "require('${PROJECT_DIR}/setup.cfg').name" 2>/dev/null || echo "factuges-document-signing-service") IMAGE_NAME=$(node -p "require('${PROJECT_DIR}/setup.cfg').name" 2>/dev/null || echo "factuges-document-signing-service")
IMAGE_VERSION=$(node -p "require('${PROJECT_DIR}/setup.cfg').version" 2>/dev/null || echo "0.1.2") IMAGE_VERSION=$(node -p "require('${PROJECT_DIR}/setup.cfg').version" 2>/dev/null || echo "0.1.3")
PORT="8000" # valor por defecto PORT="8000" # valor por defecto

View File

@ -1,7 +1,7 @@
[metadata] [metadata]
name = "factuges-document-signing-service" name = "factuges-document-signing-service"
version = "0.1.2" version = "0.1.3"
description = "FastAPI service for signing PDF documents using external secret managers" description = "FastAPI service for signing PDF documents using external secret managers"
author = Rodax Software author = Rodax Software
author_email = info@rodax-software.com author_email = info@rodax-software.com

View File

@ -1,11 +1,15 @@
import base64 import base64
from csv import writer
import io import io
from pydoc import doc from datetime import datetime, timezone
from pyhanko import stamp
from pyhanko.sign import fields, signers
from pyhanko.sign import signers from pyhanko.sign import signers
from pyhanko.pdf_utils import text
from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
from signing_service.domain.ports.pdf_signer import PDFSignerPort from signing_service.domain.ports.pdf_signer import PDFSignerPort
@ -19,6 +23,9 @@ class PyHankoPDFSigner(PDFSignerPort):
# 1⃣ Decodificar certificado # 1⃣ Decodificar certificado
pfx_bytes = base64.b64decode(certificate) pfx_bytes = base64.b64decode(certificate)
# 3⃣ Preparar PDF en modo incremental
writer = IncrementalPdfFileWriter(io.BytesIO(pdf_bytes))
# 2⃣ Cargar clave y certificado # 2⃣ Cargar clave y certificado
signer = signers.SimpleSigner.load_pkcs12_data( signer = signers.SimpleSigner.load_pkcs12_data(
pkcs12_bytes=pfx_bytes, pkcs12_bytes=pfx_bytes,
@ -26,16 +33,45 @@ class PyHankoPDFSigner(PDFSignerPort):
other_certs=[], other_certs=[],
) )
# 3⃣ Preparar PDF # 3⃣ Extract certificate info (from signer)
input_pdf = IncrementalPdfFileWriter(io.BytesIO(pdf_bytes)) cert = signer.signing_cert
subject = cert.subject.native.get('common_name', 'Desconocido')
# 5⃣ Define signature field position
fields.append_signature_field(
writer,
sig_field_spec=fields.SigFieldSpec(
sig_field_name="Signature1",
on_page=-1,
box=(380, 100, 560, 160), # x, y, x+width, y+height
)
)
# 4⃣ Signature metadata (panel de firmas)
meta = signers.PdfSignatureMetadata(
field_name="Signature1",
name=subject,
)
pdf_signer = signers.PdfSigner(
meta, signer=signer, stamp_style=stamp.TextStampStyle(
stamp_text='Firmado digitalmente por: %(signer)s\nFecha: %(ts)s',
text_box_style=text.TextBoxStyle(
border_width=0,
font_size=12
#font=opentype.GlyphAccumulatorFactory('path to font police.ttf'),
),
#background=images.PdfImage('path to img.jpg')
)
)
# 4⃣ Firmar # 4⃣ Firmar
signed_pdf: io.BytesIO = await signers.async_sign_pdf( signed_pdf: io.BytesIO = await pdf_signer.async_sign_pdf(
input_pdf, writer,
signers.PdfSignatureMetadata(
field_name="Signature1"
),
signer=signer,
) )
# 5⃣ Obtener PDF firmado # 5⃣ Obtener PDF firmado