chore: merged main

This commit is contained in:
Catalin Pit
2026-06-17 12:04:51 +03:00
103 changed files with 6524 additions and 77 deletions
+28
View File
@@ -0,0 +1,28 @@
import { z } from 'zod';
/**
* One entry in a CSC sign-time session, pinning the bytes (`documentDataId`)
* whose digest (`hashB64`) was captured at prep time for a given envelope
* item. The `ordinal` is the position of this entry in the session items array
* — it lines up with the position-ordered `signatures/signHash` response per
* CSC v1.0.4.0 §11.9.
*/
export const ZCscSessionItemSchema = z.object({
envelopeItemId: z.string(),
documentDataId: z.string(),
hashB64: z.string(),
ordinal: z.number().int().nonnegative(),
});
/**
* Contract between CSC prep and sign on `CscSession.itemsJson`.
*
* Built at prep time alongside the captured signedAttrs digests. At sign time
* the mutation re-derives each item's digest against the pinned
* `documentDataId` bytes and dispatches one batched `signatures/signHash` per
* recipient.
*/
export const ZCscSessionItemsSchema = z.array(ZCscSessionItemSchema);
export type TCscSessionItem = z.infer<typeof ZCscSessionItemSchema>;
export type TCscSessionItems = z.infer<typeof ZCscSessionItemsSchema>;
+77
View File
@@ -54,6 +54,13 @@ export const ZDocumentAuditLogTypeSchema = z.enum([
'DOCUMENT_ACCESS_AUTH_2FA_REQUESTED', // When ACCESS AUTH 2FA is requested.
'DOCUMENT_ACCESS_AUTH_2FA_VALIDATED', // When ACCESS AUTH 2FA is successfully validated.
'DOCUMENT_ACCESS_AUTH_2FA_FAILED', // When ACCESS AUTH 2FA validation fails.
// CSC / TSP signing events.
'DOCUMENT_RECIPIENT_CSC_AUTHENTICATED', // Service-scope OAuth complete; CSC credential persisted.
'DOCUMENT_RECIPIENT_CSC_AUTHENTICATION_FAILED', // Service-scope OAuth completed but TSP returned a blocking error (empty credential list / invalid cert / refused algorithm).
'DOCUMENT_RECIPIENT_CSC_SIGN_REQUESTED', // Recipient clicked Sign; CSC session created with captured per-item hashes.
'DOCUMENT_RECIPIENT_CSC_AUTHORIZED', // Credential-scope OAuth complete; SAD attached to the CSC session.
'DOCUMENT_RECIPIENT_CSC_SIGNED', // TSP returned signatures and they were embedded into the recipient's PDF bytes.
]);
export const ZDocumentAuditLogEmailTypeSchema = z.enum([
@@ -722,6 +729,71 @@ export const ZDocumentAuditLogEventRecipientExpiredSchema = z.object({
}),
});
/**
* Event: Recipient completed CSC service-scope OAuth — credential discovered, certificate persisted.
*/
export const ZDocumentAuditLogEventDocumentRecipientCscAuthenticatedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_CSC_AUTHENTICATED),
data: ZBaseRecipientDataSchema.extend({
providerId: z.string(),
credentialId: z.string(),
signatureAlgorithm: z.string(),
digestAlgorithm: z.string(),
}),
});
/**
* Event: Recipient's CSC service-scope OAuth completed but the TSP returned a blocking error.
*/
export const ZDocumentAuditLogEventDocumentRecipientCscAuthenticationFailedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_CSC_AUTHENTICATION_FAILED),
data: ZBaseRecipientDataSchema.extend({
providerId: z.string(),
reason: z.string(),
}),
});
/**
* Event: Recipient initiated TSP signing — CSC session created with per-item hashes.
*/
export const ZDocumentAuditLogEventDocumentRecipientCscSignRequestedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_CSC_SIGN_REQUESTED),
data: ZBaseRecipientDataSchema.extend({
providerId: z.string(),
credentialId: z.string(),
sessionId: z.string(),
numSignatures: z.number(),
}),
});
/**
* Event: Recipient completed CSC credential-scope OAuth — SAD attached to session.
*/
export const ZDocumentAuditLogEventDocumentRecipientCscAuthorizedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_CSC_AUTHORIZED),
data: ZBaseRecipientDataSchema.extend({
providerId: z.string(),
credentialId: z.string(),
sessionId: z.string(),
sadExpiresAt: z.coerce.date(),
}),
});
/**
* Event: TSP returned signatures and they were embedded into the recipient's PDF bytes.
*/
export const ZDocumentAuditLogEventDocumentRecipientCscSignedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_CSC_SIGNED),
data: ZBaseRecipientDataSchema.extend({
providerId: z.string(),
credentialId: z.string(),
sessionId: z.string(),
numItemsSigned: z.number(),
signatureAlgorithm: z.string(),
digestAlgorithm: z.string(),
}),
});
export const ZDocumentAuditLogBaseSchema = z.object({
id: z.string(),
createdAt: z.date(),
@@ -770,6 +842,11 @@ export const ZDocumentAuditLogSchema = ZDocumentAuditLogBaseSchema.and(
ZDocumentAuditLogEventRecipientUpdatedSchema,
ZDocumentAuditLogEventRecipientRemovedSchema,
ZDocumentAuditLogEventRecipientExpiredSchema,
ZDocumentAuditLogEventDocumentRecipientCscAuthenticatedSchema,
ZDocumentAuditLogEventDocumentRecipientCscAuthenticationFailedSchema,
ZDocumentAuditLogEventDocumentRecipientCscSignRequestedSchema,
ZDocumentAuditLogEventDocumentRecipientCscAuthorizedSchema,
ZDocumentAuditLogEventDocumentRecipientCscSignedSchema,
]),
);
+9
View File
@@ -11,6 +11,8 @@ export const ZLicenseClaimSchema = z.object({
hipaa: z.boolean().optional(),
authenticationPortal: z.boolean().optional(),
billing: z.boolean().optional(),
instanceCscSigning: z.boolean().optional(),
cscQesSigning: z.boolean().optional(),
});
/**
@@ -43,6 +45,13 @@ export type TLicenseClaim = z.infer<typeof ZLicenseClaimSchema>;
export type TLicenseRequest = z.infer<typeof ZLicenseRequestSchema>;
export type TLicenseResponse = z.infer<typeof ZLicenseResponseSchema>;
/**
* String-literal union of every flag the licence server can grant. Adding a
* field to `ZLicenseClaimSchema` automatically extends this type so that
* `assertLicensedFor(flag)` and similar helpers stay in sync.
*/
export type LicenseFlag = keyof TLicenseClaim;
/**
* Schema for the cached license data stored in the file.
*/
+33
View File
@@ -0,0 +1,33 @@
import { z } from 'zod';
/**
* The cryptographic signature tier an envelope is signed at.
*
* - `SES` — Simple Electronic Signature; the default Documenso flow signed
* with the instance-held certificate.
* - `AES` — Advanced Electronic Signature; recipient-bound signing through a
* Cloud Signature Consortium (CSC) Trust Service Provider.
* - `QES` — Qualified Electronic Signature; the eIDAS-qualified variant of
* AES, also recipient-bound through a CSC TSP.
*
* Stored as free-form TEXT on `Envelope.signatureLevel` so the legal-tier
* taxonomy can expand without a DB enum migration. Validation lives here.
*/
export const ZSignatureLevelSchema = z.enum(['SES', 'AES', 'QES']);
export const SignatureLevel = ZSignatureLevelSchema.enum;
export type TSignatureLevel = z.infer<typeof ZSignatureLevelSchema>;
/**
* Whether an envelope's signature level routes through a Cloud Signature
* Consortium Trust Service Provider (`AES` or `QES`). The single branch point
* for TSP-vs-SES runtime divergence — seal handler, download endpoint,
* completion email, and send-time PDF prep all key off this.
*
* Accepts a raw `string` because the Prisma column is TEXT; unknown values
* are conservatively treated as non-TSP so a malformed row can't accidentally
* trigger TSP-only code paths.
*/
export const isTspEnvelope = (envelope: { signatureLevel: string }): boolean =>
envelope.signatureLevel === SignatureLevel.AES || envelope.signatureLevel === SignatureLevel.QES;
+7
View File
@@ -51,6 +51,8 @@ export const ZClaimFlagsSchema = z.object({
signingReminders: z.boolean().optional(),
cscQesSigning: z.boolean().optional(),
/**
* Controls whether an organisation is prevented from sending emails.
*
@@ -128,6 +130,11 @@ export const SUBSCRIPTION_CLAIM_FEATURE_FLAGS: Record<
key: 'signingReminders',
label: 'Signing reminders',
},
cscQesSigning: {
key: 'cscQesSigning',
label: 'QES signing',
isEnterprise: true,
},
disableEmails: {
key: 'disableEmails',
label: 'Disable emails',