mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 11:29:21 +00:00
logging, finalizing some functions, and other tweaks
This commit is contained in:
@@ -209,6 +209,7 @@ export enum EventType {
|
|||||||
IMPORT_CA_CERT = "import-certificate-authority-cert",
|
IMPORT_CA_CERT = "import-certificate-authority-cert",
|
||||||
GET_CA_CRLS = "get-certificate-authority-crls",
|
GET_CA_CRLS = "get-certificate-authority-crls",
|
||||||
ISSUE_CERT = "issue-cert",
|
ISSUE_CERT = "issue-cert",
|
||||||
|
IMPORT_CERT = "import-cert",
|
||||||
SIGN_CERT = "sign-cert",
|
SIGN_CERT = "sign-cert",
|
||||||
GET_CA_CERTIFICATE_TEMPLATES = "get-ca-certificate-templates",
|
GET_CA_CERTIFICATE_TEMPLATES = "get-ca-certificate-templates",
|
||||||
GET_CERT = "get-cert",
|
GET_CERT = "get-cert",
|
||||||
@@ -1666,6 +1667,15 @@ interface IssueCert {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ImportCert {
|
||||||
|
type: EventType.IMPORT_CERT;
|
||||||
|
metadata: {
|
||||||
|
certId: string;
|
||||||
|
cn: string;
|
||||||
|
serialNumber: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface SignCert {
|
interface SignCert {
|
||||||
type: EventType.SIGN_CERT;
|
type: EventType.SIGN_CERT;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -2685,6 +2695,7 @@ export type Event =
|
|||||||
| ImportCaCert
|
| ImportCaCert
|
||||||
| GetCaCrls
|
| GetCaCrls
|
||||||
| IssueCert
|
| IssueCert
|
||||||
|
| ImportCert
|
||||||
| SignCert
|
| SignCert
|
||||||
| GetCaCertificateTemplates
|
| GetCaCertificateTemplates
|
||||||
| GetCert
|
| GetCert
|
||||||
|
|||||||
@@ -805,7 +805,9 @@ export const registerRoutes = async (
|
|||||||
certificateAuthoritySecretDAL,
|
certificateAuthoritySecretDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
permissionService
|
permissionService,
|
||||||
|
pkiCollectionDAL,
|
||||||
|
pkiCollectionItemDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const certificateAuthorityQueue = certificateAuthorityQueueFactory({
|
const certificateAuthorityQueue = certificateAuthorityQueueFactory({
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
|
|||||||
projectSlug: z.string().trim().min(1).describe(CERTIFICATES.IMPORT.projectSlug),
|
projectSlug: z.string().trim().min(1).describe(CERTIFICATES.IMPORT.projectSlug),
|
||||||
|
|
||||||
certificatePem: z.string().trim().min(1).describe(CERTIFICATES.IMPORT.certificatePem),
|
certificatePem: z.string().trim().min(1).describe(CERTIFICATES.IMPORT.certificatePem),
|
||||||
privateKeyPem: z.string().trim().optional().describe(CERTIFICATES.IMPORT.privateKeyPem),
|
privateKeyPem: z.string().trim().describe(CERTIFICATES.IMPORT.privateKeyPem),
|
||||||
chainPem: z.string().trim().optional().describe(CERTIFICATES.IMPORT.chainPem),
|
chainPem: z.string().trim().describe(CERTIFICATES.IMPORT.chainPem),
|
||||||
|
|
||||||
friendlyName: z.string().trim().optional().describe(CERTIFICATES.IMPORT.friendlyName),
|
friendlyName: z.string().trim().optional().describe(CERTIFICATES.IMPORT.friendlyName),
|
||||||
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATES.IMPORT.pkiCollectionId)
|
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATES.IMPORT.pkiCollectionId)
|
||||||
@@ -201,14 +201,15 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
|
|||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
certificate: z.string().trim().describe(CERTIFICATES.IMPORT.certificate),
|
certificate: z.string().trim().describe(CERTIFICATES.IMPORT.certificate),
|
||||||
certificateChain: z.string().trim().optional().describe(CERTIFICATES.IMPORT.certificateChain),
|
certificateChain: z.string().trim().describe(CERTIFICATES.IMPORT.certificateChain),
|
||||||
privateKey: z.string().trim().optional().describe(CERTIFICATES.IMPORT.privateKey),
|
privateKey: z.string().trim().describe(CERTIFICATES.IMPORT.privateKey),
|
||||||
serialNumber: z.string().trim().describe(CERTIFICATES.IMPORT.serialNumber)
|
serialNumber: z.string().trim().describe(CERTIFICATES.IMPORT.serialNumber)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { certificate, certificateChain, privateKey, serialNumber } = await server.services.certificate.importCert({
|
const { certificate, certificateChain, privateKey, serialNumber, cert } =
|
||||||
|
await server.services.certificate.importCert({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
@@ -216,30 +217,18 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
|
|||||||
...req.body
|
...req.body
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(andrey): Add logs
|
await server.services.auditLog.createAuditLog({
|
||||||
// await server.services.auditLog.createAuditLog({
|
...req.auditLogInfo,
|
||||||
// ...req.auditLogInfo,
|
projectId: cert.projectId,
|
||||||
// projectId: ca.projectId,
|
event: {
|
||||||
// event: {
|
type: EventType.IMPORT_CERT,
|
||||||
// type: EventType.ISSUE_CERT,
|
metadata: {
|
||||||
// metadata: {
|
certId: cert.id,
|
||||||
// caId: ca.id,
|
cn: cert.commonName,
|
||||||
// dn: ca.dn,
|
serialNumber
|
||||||
// serialNumber
|
}
|
||||||
// }
|
}
|
||||||
// }
|
});
|
||||||
// });
|
|
||||||
|
|
||||||
// await server.services.telemetry.sendPostHogEvents({
|
|
||||||
// event: PostHogEventTypes.IssueCert,
|
|
||||||
// distinctId: getTelemetryDistinctId(req),
|
|
||||||
// properties: {
|
|
||||||
// caId: req.body.caId,
|
|
||||||
// certificateTemplateId: req.body.certificateTemplateId,
|
|
||||||
// commonName: req.body.commonName,
|
|
||||||
// ...req.auditLogInfo
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
certificate,
|
certificate,
|
||||||
|
|||||||
@@ -1389,7 +1389,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
notAfter: notAfterDate,
|
notAfter: notAfterDate,
|
||||||
keyUsages: selectedKeyUsages,
|
keyUsages: selectedKeyUsages,
|
||||||
extendedKeyUsages: selectedExtendedKeyUsages,
|
extendedKeyUsages: selectedExtendedKeyUsages,
|
||||||
projectId: ca.projectId
|
projectId: (ca as TCertificateAuthorities).projectId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -1782,7 +1782,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
notAfter: notAfterDate,
|
notAfter: notAfterDate,
|
||||||
keyUsages: selectedKeyUsages,
|
keyUsages: selectedKeyUsages,
|
||||||
extendedKeyUsages: selectedExtendedKeyUsages,
|
extendedKeyUsages: selectedExtendedKeyUsages,
|
||||||
projectId: ca.projectId
|
projectId: (ca as TCertificateAuthorities).projectId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -46,3 +46,6 @@ export const constructPemChainFromCerts = (certificates: x509.X509Certificate[])
|
|||||||
.map((cert) => cert.toString("pem"))
|
.map((cert) => cert.toString("pem"))
|
||||||
.join("\n")
|
.join("\n")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
|
export const splitPemChain = (pemText: string) =>
|
||||||
|
pemText.match(/-----BEGIN CERTIFICATE-----[^-]+-----END CERTIFICATE-----/g) || [];
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
|
|||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|
||||||
import { getCaCertChain, rebuildCaCrl } from "../certificate-authority/certificate-authority-fns";
|
import { getCaCertChain, rebuildCaCrl } from "../certificate-authority/certificate-authority-fns";
|
||||||
import { revocationReasonToCrlCode } from "./certificate-fns";
|
import { revocationReasonToCrlCode, splitPemChain } from "./certificate-fns";
|
||||||
import {
|
import {
|
||||||
|
CertExtendedKeyUsage,
|
||||||
|
CertExtendedKeyUsageOIDToName,
|
||||||
|
CertKeyUsage,
|
||||||
CertStatus,
|
CertStatus,
|
||||||
TDeleteCertDTO,
|
TDeleteCertDTO,
|
||||||
TGetCertBodyDTO,
|
TGetCertBodyDTO,
|
||||||
@@ -273,18 +276,27 @@ export const certificateServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the certificate
|
// Parse the certificate
|
||||||
const certObj = new x509.X509Certificate(certificatePem);
|
const leafCert = new x509.X509Certificate(certificatePem);
|
||||||
|
|
||||||
// Verify the certificate chain
|
// Verify the certificate chain
|
||||||
if (chainPem) {
|
const chainCerts = splitPemChain(chainPem).map((pem) => new x509.X509Certificate(pem));
|
||||||
const chainCert = new x509.X509Certificate(chainPem);
|
if (chainCerts.length === 0) {
|
||||||
if (!(await certObj.verify({ publicKey: chainCert.publicKey }))) {
|
throw new BadRequestError({
|
||||||
throw new BadRequestError({ message: "Certificate chain verification failed" });
|
message: "Certificate chain must contain at least one issuer certificate"
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If private key provided, verify it matches the certificate
|
const chainValidationPromises = chainCerts.map((issuerCert) =>
|
||||||
if (privateKeyPem) {
|
leafCert.verify({ publicKey: issuerCert.publicKey }).catch(() => false)
|
||||||
|
);
|
||||||
|
|
||||||
|
const results = await Promise.all(chainValidationPromises);
|
||||||
|
|
||||||
|
if (!results.some((result) => result === true)) {
|
||||||
|
throw new BadRequestError({ message: "Certificate chain verification failed" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify private key matches the certificate
|
||||||
try {
|
try {
|
||||||
const message = Buffer.from("certificate-verification-test");
|
const message = Buffer.from("certificate-verification-test");
|
||||||
|
|
||||||
@@ -300,19 +312,18 @@ export const certificateServiceFactory = ({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new BadRequestError({ message: "Invalid private key format" });
|
throw new BadRequestError({ message: "Invalid private key format" });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Get certificate attributes
|
// Get certificate attributes
|
||||||
const commonName = Array.from(certObj.subjectName.getField("CN")?.values() || [])[0] || "";
|
const commonName = Array.from(leafCert.subjectName.getField("CN")?.values() || [])[0] || "";
|
||||||
|
|
||||||
let altNames: undefined | string;
|
let altNames: undefined | string;
|
||||||
const sanExtension = certObj.extensions.find((ext) => ext.type === "2.5.29.17");
|
const sanExtension = leafCert.extensions.find((ext) => ext.type === "2.5.29.17");
|
||||||
if (sanExtension) {
|
if (sanExtension) {
|
||||||
const sanNames = new x509.GeneralNames(sanExtension.value);
|
const sanNames = new x509.GeneralNames(sanExtension.value);
|
||||||
altNames = sanNames.items.map((name) => name.value).join(", ");
|
altNames = sanNames.items.map((name) => name.value).join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { serialNumber, notBefore, notAfter } = certObj;
|
const { serialNumber, notBefore, notAfter } = leafCert;
|
||||||
|
|
||||||
// Encrypt certificate for storage
|
// Encrypt certificate for storage
|
||||||
const certificateManagerKeyId = await getProjectKmsCertificateKeyId({
|
const certificateManagerKeyId = await getProjectKmsCertificateKeyId({
|
||||||
@@ -328,8 +339,27 @@ export const certificateServiceFactory = ({
|
|||||||
plainText: Buffer.from(certificatePem)
|
plainText: Buffer.from(certificatePem)
|
||||||
});
|
});
|
||||||
|
|
||||||
await certificateDAL.transaction(async (tx) => {
|
// Extract Key Usage
|
||||||
const cert = await certificateDAL.create(
|
const keyUsagesExt = leafCert.getExtension("2.5.29.15") as x509.KeyUsagesExtension;
|
||||||
|
|
||||||
|
let keyUsages: CertKeyUsage[] = [];
|
||||||
|
if (keyUsagesExt) {
|
||||||
|
keyUsages = Object.values(CertKeyUsage).filter(
|
||||||
|
// eslint-disable-next-line no-bitwise
|
||||||
|
(keyUsage) => (x509.KeyUsageFlags[keyUsage] & keyUsagesExt.usages) !== 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract Extended Key Usage
|
||||||
|
const extKeyUsageExt = leafCert.getExtension("2.5.29.37") as x509.ExtendedKeyUsageExtension;
|
||||||
|
let extendedKeyUsages: CertExtendedKeyUsage[] = [];
|
||||||
|
if (extKeyUsageExt) {
|
||||||
|
extendedKeyUsages = extKeyUsageExt.usages.map((ekuOid) => CertExtendedKeyUsageOIDToName[ekuOid as string]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cert = await certificateDAL.transaction(async (tx) => {
|
||||||
|
try {
|
||||||
|
const txCert = await certificateDAL.create(
|
||||||
{
|
{
|
||||||
status: CertStatus.ACTIVE,
|
status: CertStatus.ACTIVE,
|
||||||
friendlyName: friendlyName || commonName,
|
friendlyName: friendlyName || commonName,
|
||||||
@@ -338,17 +368,16 @@ export const certificateServiceFactory = ({
|
|||||||
serialNumber,
|
serialNumber,
|
||||||
notBefore,
|
notBefore,
|
||||||
notAfter,
|
notAfter,
|
||||||
projectId
|
projectId,
|
||||||
// TODO(andrey): Add keyUsages and extendedKeyUsages
|
keyUsages,
|
||||||
// keyUsages,
|
extendedKeyUsages
|
||||||
// extendedKeyUsages
|
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
await certificateBodyDAL.create(
|
await certificateBodyDAL.create(
|
||||||
{
|
{
|
||||||
certId: cert.id,
|
certId: txCert.id,
|
||||||
encryptedCertificate
|
encryptedCertificate
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
@@ -358,20 +387,35 @@ export const certificateServiceFactory = ({
|
|||||||
await pkiCollectionItemDAL.create(
|
await pkiCollectionItemDAL.create(
|
||||||
{
|
{
|
||||||
pkiCollectionId: collectionId,
|
pkiCollectionId: collectionId,
|
||||||
certId: cert.id
|
certId: txCert.id
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cert;
|
return txCert;
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (
|
||||||
|
typeof error === "object" &&
|
||||||
|
error !== null &&
|
||||||
|
"error" in error &&
|
||||||
|
error.error &&
|
||||||
|
typeof error.error === "object" &&
|
||||||
|
"code" in error.error &&
|
||||||
|
error.error.code === "23505"
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({ message: "Certificate serial already exists in your project" });
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
certificate: certificatePem,
|
certificate: certificatePem,
|
||||||
certificateChain: chainPem,
|
certificateChain: chainPem,
|
||||||
privateKey: privateKeyPem,
|
privateKey: privateKeyPem,
|
||||||
serialNumber
|
serialNumber,
|
||||||
|
cert
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,6 @@ export type TImportCertDTO = {
|
|||||||
pkiCollectionId?: string;
|
pkiCollectionId?: string;
|
||||||
|
|
||||||
certificatePem: string;
|
certificatePem: string;
|
||||||
privateKeyPem?: string;
|
privateKeyPem: string;
|
||||||
chainPem?: string;
|
chainPem: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|||||||
@@ -269,14 +269,8 @@ export const pkiCollectionServiceFactory = ({
|
|||||||
});
|
});
|
||||||
if (isCertAdded) throw new BadRequestError({ message: "Certificate already part of the PKI collection" });
|
if (isCertAdded) throw new BadRequestError({ message: "Certificate already part of the PKI collection" });
|
||||||
|
|
||||||
// validate that there exists a certificate in same project as PKI collection
|
|
||||||
const cas = await certificateAuthorityDAL.find({ projectId: pkiCollection.projectId });
|
|
||||||
|
|
||||||
// TODO: consider making this more efficient
|
|
||||||
const [certificate] = await certificateDAL.find({
|
const [certificate] = await certificateDAL.find({
|
||||||
$in: {
|
projectId: pkiCollection.projectId,
|
||||||
caId: cas.map((ca) => ca.id)
|
|
||||||
},
|
|
||||||
id: itemId
|
id: itemId
|
||||||
});
|
});
|
||||||
if (!certificate) throw new NotFoundError({ message: `Certificate with ID '${itemId}' not found` });
|
if (!certificate) throw new NotFoundError({ message: `Certificate with ID '${itemId}' not found` });
|
||||||
|
|||||||
@@ -929,13 +929,9 @@ export const projectServiceFactory = ({
|
|||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
||||||
|
|
||||||
const cas = await certificateAuthorityDAL.find({ projectId });
|
|
||||||
|
|
||||||
const certificates = await certificateDAL.find(
|
const certificates = await certificateDAL.find(
|
||||||
{
|
{
|
||||||
$in: {
|
projectId,
|
||||||
caId: cas.map((ca) => ca.id)
|
|
||||||
},
|
|
||||||
...(friendlyName && { friendlyName }),
|
...(friendlyName && { friendlyName }),
|
||||||
...(commonName && { commonName })
|
...(commonName && { commonName })
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ProjectType, SecretsV2Schema, SecretType, TableName, TSecretsV2, TSecre
|
|||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { generateCacheKeyFromData } from "@app/lib/crypto/cache";
|
import { generateCacheKeyFromData } from "@app/lib/crypto/cache";
|
||||||
|
import { applyJitter } from "@app/lib/dates";
|
||||||
import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
|
||||||
import {
|
import {
|
||||||
buildFindFilter,
|
buildFindFilter,
|
||||||
@@ -22,7 +23,6 @@ import type {
|
|||||||
TFindSecretsByFolderIdsFilter,
|
TFindSecretsByFolderIdsFilter,
|
||||||
TGetSecretsDTO
|
TGetSecretsDTO
|
||||||
} from "@app/services/secret-v2-bridge/secret-v2-bridge-types";
|
} from "@app/services/secret-v2-bridge/secret-v2-bridge-types";
|
||||||
import { applyJitter } from "@app/lib/dates";
|
|
||||||
|
|
||||||
export const SecretServiceCacheKeys = {
|
export const SecretServiceCacheKeys = {
|
||||||
get productKey() {
|
get productKey() {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
|||||||
[EventType.IMPORT_CA_CERT]: "Import CA certificate",
|
[EventType.IMPORT_CA_CERT]: "Import CA certificate",
|
||||||
[EventType.GET_CA_CRL]: "Get CA CRL",
|
[EventType.GET_CA_CRL]: "Get CA CRL",
|
||||||
[EventType.ISSUE_CERT]: "Issue certificate",
|
[EventType.ISSUE_CERT]: "Issue certificate",
|
||||||
|
[EventType.IMPORT_CERT]: "Import certificate",
|
||||||
[EventType.GET_CERT]: "Get certificate",
|
[EventType.GET_CERT]: "Get certificate",
|
||||||
[EventType.DELETE_CERT]: "Delete certificate",
|
[EventType.DELETE_CERT]: "Delete certificate",
|
||||||
[EventType.REVOKE_CERT]: "Revoke certificate",
|
[EventType.REVOKE_CERT]: "Revoke certificate",
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export enum EventType {
|
|||||||
IMPORT_CA_CERT = "import-certificate-authority-cert",
|
IMPORT_CA_CERT = "import-certificate-authority-cert",
|
||||||
GET_CA_CRL = "get-certificate-authority-crl",
|
GET_CA_CRL = "get-certificate-authority-crl",
|
||||||
ISSUE_CERT = "issue-cert",
|
ISSUE_CERT = "issue-cert",
|
||||||
|
IMPORT_CERT = "import-cert",
|
||||||
GET_CERT = "get-cert",
|
GET_CERT = "get-cert",
|
||||||
DELETE_CERT = "delete-cert",
|
DELETE_CERT = "delete-cert",
|
||||||
REVOKE_CERT = "revoke-cert",
|
REVOKE_CERT = "revoke-cert",
|
||||||
|
|||||||
@@ -582,6 +582,14 @@ interface IssueCert {
|
|||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
interface ImportCert {
|
||||||
|
type: EventType.IMPORT_CERT;
|
||||||
|
metadata: {
|
||||||
|
certId: string;
|
||||||
|
cn: string;
|
||||||
|
serialNumber: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface GetCert {
|
interface GetCert {
|
||||||
type: EventType.GET_CERT;
|
type: EventType.GET_CERT;
|
||||||
@@ -874,6 +882,7 @@ export type Event =
|
|||||||
| ImportCaCert
|
| ImportCaCert
|
||||||
| GetCaCrl
|
| GetCaCrl
|
||||||
| IssueCert
|
| IssueCert
|
||||||
|
| ImportCert
|
||||||
| GetCert
|
| GetCert
|
||||||
| DeleteCert
|
| DeleteCert
|
||||||
| RevokeCert
|
| RevokeCert
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { useDeleteCert, useRevokeCert, useImportCertificate } from "./mutations";
|
export { useDeleteCert, useImportCertificate, useRevokeCert } from "./mutations";
|
||||||
export { useGetCert, useGetCertBody } from "./queries";
|
export { useGetCert, useGetCertBody } from "./queries";
|
||||||
|
|||||||
@@ -30,17 +30,15 @@ export type TImportCertificateDTO = {
|
|||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
|
|
||||||
certificatePem: string;
|
certificatePem: string;
|
||||||
privateKeyPem?: string;
|
privateKeyPem: string;
|
||||||
chainPem?: string;
|
chainPem: string;
|
||||||
|
|
||||||
pkiCollectionId?: string;
|
pkiCollectionId?: string;
|
||||||
friendlyName?: string;
|
friendlyName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(andrey): Change this
|
|
||||||
export type TImportCertificateResponse = {
|
export type TImportCertificateResponse = {
|
||||||
certificate: string;
|
certificate: string;
|
||||||
issuingCertificate: string;
|
|
||||||
certificateChain: string;
|
certificateChain: string;
|
||||||
privateKey: string;
|
privateKey: string;
|
||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import {
|
|||||||
TBreadcrumbFormat
|
TBreadcrumbFormat
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
useProjectPermission,
|
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
|
useProjectPermission,
|
||||||
useSubscription,
|
useSubscription,
|
||||||
useWorkspace
|
useWorkspace
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
|
|||||||
+29
-52
@@ -15,43 +15,18 @@ import {
|
|||||||
TextArea
|
TextArea
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import { useImportCertificate, useGetCert, useListWorkspacePkiCollections } from "@app/hooks/api";
|
import { useGetCert, useImportCertificate, useListWorkspacePkiCollections } from "@app/hooks/api";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { CertificateContent } from "./CertificateContent";
|
import { CertificateContent } from "./CertificateContent";
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
certificatePem: z.string().trim().min(1, "Certificate PEM is required"),
|
certificatePem: z.string().trim().min(1, "Certificate PEM is required"),
|
||||||
privateKeyPem: z.string().trim().optional(),
|
privateKeyPem: z.string().trim(),
|
||||||
chainPem: z.string().trim().optional(),
|
chainPem: z.string().trim(),
|
||||||
|
|
||||||
friendlyName: z.string(),
|
friendlyName: z.string(),
|
||||||
collectionId: z.string().optional()
|
collectionId: z.string().optional()
|
||||||
|
|
||||||
// Can be added as override fields in the future | Also edit /frontend/src/hooks/api/ca/types.ts
|
|
||||||
// commonName: z.string().trim().min(1),
|
|
||||||
// altNames: z.string(),
|
|
||||||
|
|
||||||
// Can be added as override fields in the future | Also edit /frontend/src/hooks/api/ca/types.ts
|
|
||||||
// keyUsages: z.object({
|
|
||||||
// [CertKeyUsage.DIGITAL_SIGNATURE]: z.boolean().optional(),
|
|
||||||
// [CertKeyUsage.KEY_ENCIPHERMENT]: z.boolean().optional(),
|
|
||||||
// [CertKeyUsage.NON_REPUDIATION]: z.boolean().optional(),
|
|
||||||
// [CertKeyUsage.DATA_ENCIPHERMENT]: z.boolean().optional(),
|
|
||||||
// [CertKeyUsage.KEY_AGREEMENT]: z.boolean().optional(),
|
|
||||||
// [CertKeyUsage.KEY_CERT_SIGN]: z.boolean().optional(),
|
|
||||||
// [CertKeyUsage.CRL_SIGN]: z.boolean().optional(),
|
|
||||||
// [CertKeyUsage.ENCIPHER_ONLY]: z.boolean().optional(),
|
|
||||||
// [CertKeyUsage.DECIPHER_ONLY]: z.boolean().optional()
|
|
||||||
// }),
|
|
||||||
// extendedKeyUsages: z.object({
|
|
||||||
// [CertExtendedKeyUsage.CLIENT_AUTH]: z.boolean().optional(),
|
|
||||||
// [CertExtendedKeyUsage.CODE_SIGNING]: z.boolean().optional(),
|
|
||||||
// [CertExtendedKeyUsage.EMAIL_PROTECTION]: z.boolean().optional(),
|
|
||||||
// [CertExtendedKeyUsage.OCSP_SIGNING]: z.boolean().optional(),
|
|
||||||
// [CertExtendedKeyUsage.SERVER_AUTH]: z.boolean().optional(),
|
|
||||||
// [CertExtendedKeyUsage.TIMESTAMPING]: z.boolean().optional()
|
|
||||||
// })
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FormData = z.infer<typeof schema>;
|
export type FormData = z.infer<typeof schema>;
|
||||||
@@ -195,33 +170,14 @@ export const CertificateImportModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
name="certificatePem"
|
name="certificatePem"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Certificate PEM"
|
label="Leaf Certificate PEM"
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
isRequired
|
isRequired
|
||||||
errorText={error?.message}
|
|
||||||
>
|
>
|
||||||
<TextArea
|
<TextArea
|
||||||
{...field}
|
{...field}
|
||||||
placeholder="TODO(andrey): Pem placeholder"
|
placeholder={"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"}
|
||||||
isDisabled={Boolean(cert)}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="chainPem"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Certificate Chain PEM"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
isOptional
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
{...field}
|
|
||||||
placeholder="TODO(andrey): Pem placeholder"
|
|
||||||
isDisabled={Boolean(cert)}
|
isDisabled={Boolean(cert)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -235,12 +191,33 @@ export const CertificateImportModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
<FormControl
|
<FormControl
|
||||||
label="Private Key PEM"
|
label="Private Key PEM"
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
isOptional
|
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
|
isRequired
|
||||||
>
|
>
|
||||||
<TextArea
|
<TextArea
|
||||||
{...field}
|
{...field}
|
||||||
placeholder="TODO(andrey): Pem placeholder"
|
placeholder={
|
||||||
|
"-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----"
|
||||||
|
}
|
||||||
|
isDisabled={Boolean(cert)}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="chainPem"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Certificate Chain PEM"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
isRequired
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
{...field}
|
||||||
|
placeholder={"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"}
|
||||||
isDisabled={Boolean(cert)}
|
isDisabled={Boolean(cert)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import { useDeleteCert } from "@app/hooks/api";
|
|||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { CertificateCertModal } from "./CertificateCertModal";
|
import { CertificateCertModal } from "./CertificateCertModal";
|
||||||
|
import { CertificateImportModal } from "./CertificateImportModal";
|
||||||
import { CertificateModal } from "./CertificateModal";
|
import { CertificateModal } from "./CertificateModal";
|
||||||
import { CertificateRevocationModal } from "./CertificateRevocationModal";
|
import { CertificateRevocationModal } from "./CertificateRevocationModal";
|
||||||
import { CertificatesTable } from "./CertificatesTable";
|
import { CertificatesTable } from "./CertificatesTable";
|
||||||
import { CertificateImportModal } from "./CertificateImportModal";
|
|
||||||
|
|
||||||
export const CertificatesSection = () => {
|
export const CertificatesSection = () => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|||||||
Reference in New Issue
Block a user