Public/Protect-VellumPdfDocument.ps1
|
function Protect-VellumPdfDocument { <# .SYNOPSIS Applies password protection and usage permissions to a VellumPdf document. .DESCRIPTION Wraps Document.Encrypt(PdfEncryptionSettings). Encryption is staged in memory and takes effect when the document is written by Save-VellumPdfDocument. The same Document instance is returned so the call can be chained in a pipeline. At least one of -UserPassword or -OwnerPassword must be supplied. Supplying both is the most common configuration: the user password opens the document for reading and the owner password unlocks all operations regardless of the -Permission set. PDF/A CONSTRAINT: PDF/A (ISO 19005) explicitly forbids encryption. If the document's Conformance is anything other than None (e.g. PdfA2b, PdfA2u, PdfA2a) this cmdlet throws a clear terminating error before calling Encrypt(). The VellumPdf library also enforces this constraint at Save() time, so the fail-fast check here gives an earlier, more actionable message. SIGNING CONSTRAINT: encryption and digital signatures cannot be combined (the library rejects the pair at save time). This cmdlet throws if a signature has been staged with Set-VellumPdfSignature, and vice versa. PASSWORDS: Both password parameters accept [securestring] to keep credentials out of command history and verbose output. Use Read-Host -AsSecureString for interactive entry, or ConvertTo-SecureString for scripts. .PARAMETER Document The VellumPdf document to protect. Accepts pipeline input. .PARAMETER UserPassword The password required to open the document. Plain-text intermediates are never stored in variables or written to output streams. .PARAMETER OwnerPassword The password that grants unrestricted access, overriding -Permission restrictions. Recommended when using -Permission to limit operations. .PARAMETER Permission One or more permission flags to allow. Valid values: None - no permissions granted beyond opening Print - low-resolution printing Modify - modify document content Copy - copy or extract text and graphics Annotate - add or modify annotations and fill forms FillForms - fill in existing form fields Extract - extract text and graphics (accessibility) Assemble - insert, rotate, or delete pages and bookmarks PrintHighRes - high-resolution (faithful) printing All - all of the above (default) Multiple values are combined as flags. Example: -Permission Print,Copy Defaults to All. Note that an owner password with the default All and no user password leaves the document fully open (the cmdlet warns in that case). PDF permission flags are advisory: a reader may ignore them, and anyone with the owner password can remove them. .PARAMETER EncryptMetadata When specified, document metadata (XMP) is also encrypted. When omitted the library default applies (metadata is encrypted by default). .EXAMPLE $pw = Read-Host -Prompt 'Password' -AsSecureString New-VellumPdfDocument | Add-VellumPdfParagraph -Text 'Confidential.' | Protect-VellumPdfDocument -UserPassword $pw | Save-VellumPdfDocument -Path ./protected.pdf .EXAMPLE $userPw = ConvertTo-SecureString 'userpass' -AsPlainText -Force $ownerPw = ConvertTo-SecureString 'ownerpass' -AsPlainText -Force New-VellumPdfDocument | Add-VellumPdfParagraph -Text 'Restricted copy.' | Protect-VellumPdfDocument -UserPassword $userPw -OwnerPassword $ownerPw ` -Permission Print,Copy | Save-VellumPdfDocument -Path ./restricted.pdf .EXAMPLE # Owner-only (no user password needed to open; permissions still enforced) $ownerPw = ConvertTo-SecureString 's3cret' -AsPlainText -Force New-VellumPdfDocument | Add-VellumPdfParagraph -Text 'Body.' | Protect-VellumPdfDocument -OwnerPassword $ownerPw -Permission Print | Save-VellumPdfDocument -Path ./owner-only.pdf .OUTPUTS VellumPdf.Layout.Document (the same instance, for chaining) #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Mutates an in-memory document object only; no external/system state change.')] [CmdletBinding()] [OutputType([VellumPdf.Layout.Document])] param( [Parameter(Mandatory, ValueFromPipeline)] [VellumPdf.Layout.Document]$Document, [securestring]$UserPassword, [securestring]$OwnerPassword, [ValidateSet('None', 'Print', 'Modify', 'Copy', 'Annotate', 'FillForms', 'Extract', 'Assemble', 'PrintHighRes', 'All')] [string[]]$Permission = @('All'), [switch]$EncryptMetadata ) process { Assert-VellumPdfDocumentOpen -Document $Document -CommandName 'Protect-VellumPdfDocument' # Applying encryption twice leaves an orphaned /Encrypt object in the # output (the second settings win, the first linger unreferenced). if ($Document.PSObject.Properties['PSVellumProtected']) { throw ('Protect-VellumPdfDocument: this document is already protected. ' + 'Encryption can only be applied once per document.') } # The library rejects Encrypt() + Sign() at save time; fail fast here # with the same constraint so the error points at the right cmdlet. if ($Document.PSObject.Properties['PSVellumSignature']) { throw ('Protect-VellumPdfDocument: encryption and digital signatures cannot be combined. ' + 'Remove the Set-VellumPdfSignature call to encrypt this document.') } # Require at least one password. if (-not $PSBoundParameters.ContainsKey('UserPassword') -and -not $PSBoundParameters.ContainsKey('OwnerPassword')) { throw 'Protect-VellumPdfDocument: at least one of -UserPassword or -OwnerPassword must be supplied.' } # Owner-password-only protection with the default (All) permission set # opens the document with no password and grants every permission - i.e. # nothing is actually restricted. That is rarely the intent of "protect # with an owner password", so warn the caller to narrow -Permission. if ($PSBoundParameters.ContainsKey('OwnerPassword') -and -not $PSBoundParameters.ContainsKey('UserPassword') -and -not $PSBoundParameters.ContainsKey('Permission')) { Write-Warning ('Protect-VellumPdfDocument: an owner password with no user password and the ' + 'default -Permission All opens the document without a password and grants every ' + 'permission, so nothing is restricted. Pass -Permission to limit operations. PDF ' + 'permission flags are advisory - a reader may ignore them, and the owner password removes them.') } # Fail fast: PDF/A forbids encryption (ISO 19005-2 section 6.3.1). if ($Document.Conformance -ne [VellumPdf.Document.PdfConformance]::None) { throw ("Protect-VellumPdfDocument: PDF/A conformance ($($Document.Conformance)) does not allow " + 'encryption. Remove -Conformance or use a non-conformant document.') } # Build combined permissions flags. $flags = [VellumPdf.Encryption.PdfPermissions]::None foreach ($p in $Permission) { $flags = $flags -bor [VellumPdf.Encryption.PdfPermissions]$p } $settings = [VellumPdf.Encryption.PdfEncryptionSettings]::new() $settings.Permissions = $flags # Convert SecureString passwords to plain text only at the point of assignment. if ($PSBoundParameters.ContainsKey('UserPassword')) { $settings.UserPassword = [System.Net.NetworkCredential]::new('', $UserPassword).Password } if ($PSBoundParameters.ContainsKey('OwnerPassword')) { $settings.OwnerPassword = [System.Net.NetworkCredential]::new('', $OwnerPassword).Password } if ($PSBoundParameters.ContainsKey('EncryptMetadata')) { $settings.EncryptMetadata = $EncryptMetadata.IsPresent } [void]$Document.Encrypt($settings) $Document.PSObject.Properties.Add([psnoteproperty]::new('PSVellumProtected', $true)) $Document } } |