mirror of
https://github.com/documenso/documenso.git
synced 2026-06-22 04:12:06 +10:00
d5ce222482
Adds Cloud Signature Consortium (CSC) integration for AES/QES signing against a configured TSP. v1 ships as instance-wide configuration via environment variables, with per-envelope signature level selection, license gating, and an OAuth-driven signing flow (capture + FIFO signers, SAD session, blocking/in-progress recipient pages). Includes signature level compatibility checks (role, signing order, dictate next signer), envelope mutability assertions, Prisma migration for signature level and CSC tables, and docs for the new signing certificate options.
88 lines
3.8 KiB
TypeScript
88 lines
3.8 KiB
TypeScript
import { CSC_INSTANCE_SIGNATURE_LEVEL, IS_INSTANCE_CSC_MODE } from '../../constants/app';
|
|
import { AppError, AppErrorCode } from '../../errors/app-error';
|
|
import { SignatureLevel, type TSignatureLevel } from '../../types/signature-level';
|
|
|
|
type ResolveSignatureLevelOptions = {
|
|
/**
|
|
* The signature level the caller wants the envelope created at. Optional;
|
|
* when omitted the resolver returns the instance-mode default (`SES` for
|
|
* non-CSC instances, `AES` for CSC instances).
|
|
*/
|
|
requested?: TSignatureLevel;
|
|
|
|
/**
|
|
* When `true`, a conflict between `requested` and the current instance mode
|
|
* throws `CSC_INSTANCE_MODE_MISMATCH` rather than being silently coerced.
|
|
* When `false` (default), the resolver coerces incompatible inputs to the
|
|
* instance default without throwing.
|
|
*
|
|
* Omitting `requested` is accepted in both modes — the resolver returns the
|
|
* instance default rather than throwing.
|
|
*
|
|
* Use `strict: true` at call sites that take the level from external input
|
|
* (e.g. a public API) where silent coercion would mask caller mistakes.
|
|
*/
|
|
strict?: boolean;
|
|
};
|
|
|
|
/**
|
|
* Resolve the signature level for a new envelope.
|
|
*
|
|
* Server-only. Reads the `NEXT_PRIVATE_SIGNING_TRANSPORT` env var via
|
|
* {@link IS_INSTANCE_CSC_MODE} so call sites do not have to thread the
|
|
* instance mode through their own arguments. On CSC instances the coerced
|
|
* default also reads {@link CSC_INSTANCE_SIGNATURE_LEVEL} so operators can
|
|
* pick `AES` (default) or `QES` per their TSP capability.
|
|
*
|
|
* Source of truth for the `Envelope.signatureLevel` write at create-time. The
|
|
* column has no DB default by design — every caller flows through here so the
|
|
* instance-mode contract is enforced consistently.
|
|
*
|
|
* Coerce mode (default, `strict: false`):
|
|
*
|
|
* | Instance | requested | Result |
|
|
* |----------|----------------|-------------------------------------|
|
|
* | non-CSC | omitted | `SES` |
|
|
* | non-CSC | `SES` | `SES` |
|
|
* | non-CSC | `AES` / `QES` | `SES` (coerced) |
|
|
* | CSC | omitted | `CSC_INSTANCE_SIGNATURE_LEVEL()` |
|
|
* | CSC | `SES` | `CSC_INSTANCE_SIGNATURE_LEVEL()` |
|
|
* | CSC | `AES` / `QES` | passes through |
|
|
*
|
|
* Strict mode (`strict: true`): same instance defaults for the omitted case,
|
|
* but any conflict between `requested` and the instance mode throws
|
|
* `CSC_INSTANCE_MODE_MISMATCH` instead of silently coercing.
|
|
*
|
|
* Note: on CSC instances an explicit `AES`/`QES` request always passes
|
|
* through, even when it disagrees with `CSC_INSTANCE_SIGNATURE_LEVEL`. The
|
|
* env var sets the *default* legal tier; it doesn't restrict what callers
|
|
* can ask for. Cert-capability checks live at the TSP boundary.
|
|
*/
|
|
export const resolveSignatureLevel = ({
|
|
requested,
|
|
strict = false,
|
|
}: ResolveSignatureLevelOptions = {}): TSignatureLevel => {
|
|
const isCscInstance = IS_INSTANCE_CSC_MODE();
|
|
const instanceDefault = isCscInstance ? CSC_INSTANCE_SIGNATURE_LEVEL() : SignatureLevel.SES;
|
|
|
|
if (requested === undefined) {
|
|
return instanceDefault;
|
|
}
|
|
|
|
const isCompatible = isCscInstance ? requested !== SignatureLevel.SES : requested === SignatureLevel.SES;
|
|
|
|
if (isCompatible) {
|
|
return requested;
|
|
}
|
|
|
|
if (strict) {
|
|
throw new AppError(AppErrorCode.CSC_INSTANCE_MODE_MISMATCH, {
|
|
message: isCscInstance
|
|
? `signatureLevel '${requested}' is not supported on a CSC-mode instance — every recipient must sign through the configured Trust Service Provider.`
|
|
: `signatureLevel '${requested}' is not supported on a non-CSC instance — only 'SES' is permitted unless the CSC signing transport is configured.`,
|
|
});
|
|
}
|
|
|
|
return instanceDefault;
|
|
};
|