feat: add PDF encryption feature (#1217)

* Adding PDF encryption option

* LibreOffice
* QPDF
* PDFtk
* pdfcpu

* Update pkg/modules/pdfengines/pdfengines.go

Co-authored-by: Julien Neuhart <neuhart.julien@gmail.com>

* npx prettier

* go fmt

* PR comments

* Update test/integration/scenario/scenario.go

Co-authored-by: Julien Neuhart <neuhart.julien@gmail.com>

* renamed ProtectWithPassword to encrypt

* more clean up

* Use the same input path as requested

* This commit completes the encryption implementation

* Clean up. Added webhook, disable route and download from tests

---------

Co-authored-by: Julien Neuhart <neuhart.julien@gmail.com>
This commit is contained in:
Stevenson Michel
2025-05-27 08:49:24 -04:00
committed by Julien Neuhart
parent 638c4e6b23
commit 99307befdd
23 changed files with 1168 additions and 6 deletions

View File

@@ -254,6 +254,29 @@ func WriteMetadataStub(ctx *api.Context, engine gotenberg.PdfEngine, metadata ma
return nil
}
// EncryptPdfStub adds password protection to PDF files.
// FormDataPdfEncrypt extracts encryption parameters from form data.
func FormDataPdfEncrypt(form *api.FormData) (userPassword, ownerPassword string) {
form.String("userPassword", &userPassword, "")
form.String("ownerPassword", &ownerPassword, "")
return userPassword, ownerPassword
}
func EncryptPdfStub(ctx *api.Context, engine gotenberg.PdfEngine, userPassword, ownerPassword string, inputPaths []string) error {
if userPassword == "" {
return nil
}
for _, inputPath := range inputPaths {
err := engine.Encrypt(ctx, ctx.Log(), inputPath, userPassword, ownerPassword)
if err != nil {
return fmt.Errorf("encrypt PDF '%s': %w", inputPath, err)
}
}
return nil
}
// mergeRoute returns an [api.Route] which can merge PDFs.
func mergeRoute(engine gotenberg.PdfEngine) api.Route {
return api.Route{
@@ -266,6 +289,7 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
form := ctx.FormData()
pdfFormats := FormDataPdfFormats(form)
metadata := FormDataPdfMetadata(form, false)
userPassword, ownerPassword := FormDataPdfEncrypt(form)
var inputPaths []string
var flatten bool
@@ -300,6 +324,11 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
}
}
err = EncryptPdfStub(ctx, engine, userPassword, ownerPassword, outputPaths)
if err != nil {
return fmt.Errorf("encrypt PDFs: %w", err)
}
err = ctx.AddOutputPaths(outputPaths...)
if err != nil {
return fmt.Errorf("add output paths: %w", err)
@@ -323,6 +352,7 @@ func splitRoute(engine gotenberg.PdfEngine) api.Route {
mode := FormDataPdfSplitMode(form, true)
pdfFormats := FormDataPdfFormats(form)
metadata := FormDataPdfMetadata(form, false)
userPassword, ownerPassword := FormDataPdfEncrypt(form)
var inputPaths []string
var flatten bool
@@ -356,6 +386,11 @@ func splitRoute(engine gotenberg.PdfEngine) api.Route {
}
}
err = EncryptPdfStub(ctx, engine, userPassword, ownerPassword, convertOutputPaths)
if err != nil {
return fmt.Errorf("encrypt PDFs: %w", err)
}
zeroValuedSplitMode := gotenberg.SplitMode{}
zeroValuedPdfFormats := gotenberg.PdfFormats{}
if mode != zeroValuedSplitMode && pdfFormats != zeroValuedPdfFormats {
@@ -424,6 +459,7 @@ func convertRoute(engine gotenberg.PdfEngine) api.Route {
form := ctx.FormData()
pdfFormats := FormDataPdfFormats(form)
userPassword, ownerPassword := FormDataPdfEncrypt(form)
var inputPaths []string
err := form.
@@ -456,11 +492,15 @@ func convertRoute(engine gotenberg.PdfEngine) api.Route {
if err != nil {
return fmt.Errorf("rename output path: %w", err)
}
outputPaths[i] = inputPath
}
}
err = EncryptPdfStub(ctx, engine, userPassword, ownerPassword, outputPaths)
if err != nil {
return fmt.Errorf("encrypt PDFs: %w", err)
}
err = ctx.AddOutputPaths(outputPaths...)
if err != nil {
return fmt.Errorf("add output paths: %w", err)
@@ -548,3 +588,41 @@ func writeMetadataRoute(engine gotenberg.PdfEngine) api.Route {
},
}
}
// encryptRoute returns an [api.Route] which can add password protection to PDFs.
func encryptRoute(engine gotenberg.PdfEngine) api.Route {
return api.Route{
Method: http.MethodPost,
Path: "/forms/pdfengines/encrypt",
IsMultipart: true,
Handler: func(c echo.Context) error {
ctx := c.Get("context").(*api.Context)
form := ctx.FormData()
var inputPaths []string
var userPassword string
var ownerPassword string
err := form.
MandatoryPaths([]string{".pdf"}, &inputPaths).
MandatoryString("userPassword", &userPassword).
String("ownerPassword", &ownerPassword, "").
Validate()
if err != nil {
return fmt.Errorf("validate form data: %w", err)
}
err = EncryptPdfStub(ctx, engine, userPassword, ownerPassword, inputPaths)
if err != nil {
return fmt.Errorf("encrypt PDFs: %w", err)
}
err = ctx.AddOutputPaths(inputPaths...)
if err != nil {
return fmt.Errorf("add output paths: %w", err)
}
return nil
},
}
}