Start work on PkiSubscriberDetailsByIDPage

This commit is contained in:
Tuan Dang
2025-05-06 15:46:54 -07:00
parent 0410c83cef
commit a32bb95703
14 changed files with 854 additions and 103 deletions

View File

@@ -20,6 +20,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
import { ActorType } from "@app/services/auth/auth-type";
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
import { CertExtendedKeyUsage, CertKeyUsage } from "@app/services/certificate/certificate-types";
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
@@ -31,7 +32,6 @@ import {
TUpdateSecretSyncDTO
} from "@app/services/secret-sync/secret-sync-types";
import { WorkflowIntegration } from "@app/services/workflow-integration/workflow-integration-types";
import { CertKeyUsage, CertExtendedKeyUsage } from "@app/services/certificate/certificate-types";
import { KmipPermission } from "../kmip/kmip-enum";
import { ApprovalStatus } from "../secret-approval-request/secret-approval-request-types";
@@ -242,6 +242,8 @@ export enum EventType {
UPDATE_PKI_SUBSCRIBER = "update-pki-subscriber",
DELETE_PKI_SUBSCRIBER = "delete-pki-subscriber",
GET_PKI_SUBSCRIBER = "get-pki-subscriber",
ISSUE_PKI_SUBSCRIBER_CERT = "issue-pki-subscriber-cert",
SIGN_PKI_SUBSCRIBER_CERT = "sign-pki-subscriber-cert",
CREATE_KMS = "create-kms",
UPDATE_KMS = "update-kms",
DELETE_KMS = "delete-kms",
@@ -1946,6 +1948,22 @@ interface GetPkiSubscriber {
};
}
interface IssuePkiSubscriberCert {
type: EventType.ISSUE_PKI_SUBSCRIBER_CERT;
metadata: {
subscriberId: string;
serialNumber: string;
};
}
interface SignPkiSubscriberCert {
type: EventType.SIGN_PKI_SUBSCRIBER_CERT;
metadata: {
subscriberId: string;
serialNumber: string;
};
}
interface CreateKmsEvent {
type: EventType.CREATE_KMS;
metadata: {
@@ -2908,6 +2926,8 @@ export type Event =
| UpdatePkiSubscriber
| DeletePkiSubscriber
| GetPkiSubscriber
| IssuePkiSubscriberCert
| SignPkiSubscriberCert
| CreateKmsEvent
| UpdateKmsEvent
| DeleteKmsEvent

View File

@@ -54,7 +54,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
projectTemplates: false,
kmip: false,
gateway: false,
sshHostGroups: false
sshHostGroups: true
});
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {

View File

@@ -179,13 +179,41 @@ export const sshHostGroupServiceFactory = ({
});
const updatedSshHostGroup = await sshHostGroupDAL.transaction(async (tx) => {
await sshHostGroupDAL.updateById(
sshHostGroupId,
{
name
},
tx
);
if (name) {
// (dangtony98): room to optimize check to ensure that
// the SSH host group name is unique across the whole org
const project = await projectDAL.findById(sshHostGroup.projectId, tx);
if (!project) throw new NotFoundError({ message: `Project with ID '${sshHostGroup.projectId}' not found` });
const projects = await projectDAL.find(
{
orgId: project.orgId
},
{ tx }
);
const existingSshHostGroup = await sshHostGroupDAL.find(
{
name,
$in: {
projectId: projects.map((p) => p.id)
}
},
{ tx }
);
if (existingSshHostGroup.length) {
throw new BadRequestError({
message: `SSH host group with name '${name}' already exists in the organization`
});
}
await sshHostGroupDAL.updateById(
sshHostGroupId,
{
name
},
tx
);
}
if (loginMappings) {
await sshHostLoginUserDAL.delete({ sshHostGroupId: sshHostGroup.id }, tx);
if (loginMappings.length) {

View File

@@ -954,6 +954,15 @@ export const registerRoutes = async (
const pkiSubscriberService = pkiSubscriberServiceFactory({
pkiSubscriberDAL,
certificateAuthorityDAL,
certificateAuthorityCertDAL,
certificateAuthoritySecretDAL,
certificateAuthorityCrlDAL,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
projectDAL,
kmsService,
permissionService
});

View File

@@ -5,11 +5,13 @@ import { ApiDocsTags, PKI_SUBSCRIBERS } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { CertExtendedKeyUsage, CertKeyUsage } from "@app/services/certificate/certificate-types";
import { validateAltNameField } from "@app/services/certificate-authority/certificate-authority-validators";
import { sanitizedPkiSubscriber } from "@app/services/pki-subscriber/pki-subscriber-schema";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) => {
server.route({
@@ -31,7 +33,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const subscriber = await server.services.pkiSubscriber.getPkiSubscriberById({
const subscriber = await server.services.pkiSubscriber.getSubscriberById({
subscriberId: req.params.subscriberId,
actor: req.permission.type,
actorId: req.permission.id,
@@ -103,7 +105,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const subscriber = await server.services.pkiSubscriber.createPkiSubscriber({
const subscriber = await server.services.pkiSubscriber.createSubscriber({
...req.body,
actor: req.permission.type,
actorId: req.permission.id,
@@ -185,7 +187,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
}
},
handler: async (req) => {
const subscriber = await server.services.pkiSubscriber.updatePkiSubscriber({
const subscriber = await server.services.pkiSubscriber.updateSubscriber({
subscriberId: req.params.subscriberId,
actor: req.permission.type,
actorId: req.permission.id,
@@ -235,7 +237,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const subscriber = await server.services.pkiSubscriber.deletePkiSubscriber({
const subscriber = await server.services.pkiSubscriber.deleteSubscriber({
subscriberId: req.params.subscriberId,
actor: req.permission.type,
actorId: req.permission.id,
@@ -264,7 +266,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiSubscribers],
@@ -283,37 +285,43 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
}
},
handler: async (req) => {
// TODO: reuse issueCertFromCa fn (or not since we are adding support for external CAs?)
// const { serialNumber, signedPublicKey, privateKey, publicKey, keyAlgorithm, host, principals } =
// await server.services.pkiSubscriber.issuePkiSubscriberCertificate({
// subscriberId: req.params.subscriberId,
// actor: req.permission.type,
// actorId: req.permission.id,
// actorAuthMethod: req.permission.authMethod,
// actorOrgId: req.permission.orgId
// });
const { certificate, certificateChain, issuingCaCertificate, privateKey, serialNumber, subscriber } =
await server.services.pkiSubscriber.issueSubscriberCert({
subscriberId: req.params.subscriberId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.ISSUE_SSH_HOST_USER_CERT,
type: EventType.ISSUE_PKI_SUBSCRIBER_CERT,
metadata: {
sshHostId: req.params.sshHostId,
hostname: host.hostname,
loginUser: req.body.loginUser,
principals,
ttl: host.userCertTtl
subscriberId: subscriber.id,
serialNumber
}
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.IssueCert,
distinctId: getTelemetryDistinctId(req),
properties: {
subscriberId: subscriber.id,
commonName: subscriber.commonName,
...req.auditLogInfo
}
});
return {
serialNumber,
signedKey: signedPublicKey,
certificate,
certificateChain,
issuingCaCertificate,
privateKey,
publicKey,
keyAlgorithm
serialNumber
};
}
});
@@ -324,7 +332,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.PkiSubscribers],
@@ -333,7 +341,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
subscriberId: z.string().describe(PKI_SUBSCRIBERS.ISSUE_CERT.subscriberId)
}),
body: z.object({
csr: z.string().trim().min(1).
csr: z.string().trim().min(1)
}),
response: {
200: z.object({
@@ -345,36 +353,43 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
}
},
handler: async (req) => {
// TODO: reuse issueCertFromCa fn (or not since we are adding support for external CAs?)
const { serialNumber, signedPublicKey, privateKey, publicKey, keyAlgorithm, host, principals } =
await server.services.pkiSubscriber.issuePkiSubscriberCertificate({
const { certificate, certificateChain, issuingCaCertificate, serialNumber, subscriber } =
await server.services.pkiSubscriber.signSubscriberCert({
subscriberId: req.params.subscriberId,
csr: req.body.csr,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
// await server.services.auditLog.createAuditLog({
// ...req.auditLogInfo,
// orgId: req.permission.orgId,
// event: {
// type: EventType.ISSUE_SSH_HOST_USER_CERT,
// metadata: {
// sshHostId: req.params.sshHostId,
// hostname: host.hostname,
// loginUser: req.body.loginUser,
// principals,
// ttl: host.userCertTtl
// }
// }
// });
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.SIGN_PKI_SUBSCRIBER_CERT,
metadata: {
subscriberId: subscriber.id,
serialNumber
}
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.SignCert,
distinctId: getTelemetryDistinctId(req),
properties: {
subscriberId: subscriber.id,
commonName: subscriber.commonName,
...req.auditLogInfo
}
});
return {
serialNumber,
signedKey: signedPublicKey,
publicKey,
keyAlgorithm
certificate: certificate.toString("pem"),
certificateChain,
issuingCaCertificate,
serialNumber
};
}
});

View File

@@ -1,5 +1,7 @@
/* eslint-disable no-bitwise */
import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import crypto, { KeyObject } from "crypto";
import { ActionProjectType } from "@app/db/schemas";
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
@@ -11,10 +13,26 @@ import {
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { ms } from "@app/lib/ms";
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
import { TCertificateSecretDALFactory } from "@app/services/certificate/certificate-secret-dal";
import {
CertExtendedKeyUsage,
CertExtendedKeyUsageOIDToName,
CertKeyAlgorithm,
CertKeyUsage,
CertStatus
} from "@app/services/certificate/certificate-types";
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
import { getCaCredentials, keyAlgorithmToAlgCfg } from "@app/services/certificate-authority/certificate-authority-fns";
import {
createSerialNumber,
getCaCertChain,
getCaCredentials,
keyAlgorithmToAlgCfg,
parseDistinguishedName
} from "@app/services/certificate-authority/certificate-authority-fns";
import { TCertificateAuthoritySecretDALFactory } from "@app/services/certificate-authority/certificate-authority-secret-dal";
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
@@ -31,32 +49,41 @@ import {
} from "./pki-subscriber-types";
type TPkiSubscriberServiceFactoryDep = {
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "create" | "findById" | "updateById" | "deleteById">;
pkiSubscriberDAL: Pick<
TPkiSubscriberDALFactory,
"create" | "findById" | "updateById" | "deleteById" | "transaction" | "find"
>;
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "findOne">;
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "decryptWithKmsKey">;
certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction">;
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction" | "findById" | "find">;
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "decryptWithKmsKey" | "encryptWithKmsKey">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
export type TPkiSubscriberServiceFactory = ReturnType<typeof pkiSubscriberServiceFactory>;
// TODO: bind subscribers to CA
export const pkiSubscriberServiceFactory = ({
pkiSubscriberDAL,
certificateAuthorityDAL,
certificateAuthorityCertDAL,
certificateAuthoritySecretDAL,
certificateAuthorityCrlDAL,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
projectDAL,
kmsService,
permissionService
}: TPkiSubscriberServiceFactoryDep) => {
const createPkiSubscriber = async ({
const createSubscriber = async ({
name,
commonName,
caId, // (dangtony98) consider by CA name instead (newly-introduced field)
caId,
ttl,
subjectAlternativeNames,
keyUsages,
@@ -82,21 +109,55 @@ export const pkiSubscriberServiceFactory = ({
ProjectPermissionSub.PkiSubscribers
);
const newSubscriber = await pkiSubscriberDAL.create({
caId,
projectId,
name,
commonName,
ttl,
subjectAlternativeNames,
keyUsages,
extendedKeyUsages
const newSubscriber = await pkiSubscriberDAL.transaction(async (tx) => {
// (dangtony98): room to optimize check to ensure that
// the PKI subscriber name is unique across the whole org
const project = await projectDAL.findById(projectId, tx);
if (!project) throw new NotFoundError({ message: `Project with ID '${projectId}' not found` });
const projects = await projectDAL.find(
{
orgId: project.orgId
},
{ tx }
);
const existingPkiSubscriber = await pkiSubscriberDAL.find(
{
name,
$in: {
projectId: projects.map((p) => p.id)
}
},
{ tx }
);
if (existingPkiSubscriber.length) {
throw new BadRequestError({
message: `PKI subscriber with name '${name}' already exists in the organization`
});
}
const subscriber = await pkiSubscriberDAL.create(
{
caId,
projectId,
name,
commonName,
ttl,
subjectAlternativeNames,
keyUsages,
extendedKeyUsages
},
tx
);
return subscriber;
});
return newSubscriber;
};
const getPkiSubscriberById = async ({
const getSubscriberById = async ({
subscriberId,
actorId,
actorAuthMethod,
@@ -124,7 +185,7 @@ export const pkiSubscriberServiceFactory = ({
return subscriber;
};
const updatePkiSubscriber = async ({
const updateSubscriber = async ({
subscriberId,
name,
commonName,
@@ -138,13 +199,13 @@ export const pkiSubscriberServiceFactory = ({
actor,
actorOrgId
}: TUpdatePkiSubscriberDTO) => {
const subscriber = await pkiSubscriberDAL.findById(subscriberId);
if (!subscriber) throw new NotFoundError({ message: `PKI subscriber with ID '${subscriberId}' not found` });
const foundSubscriber = await pkiSubscriberDAL.findById(subscriberId);
if (!foundSubscriber) throw new NotFoundError({ message: `PKI subscriber with ID '${subscriberId}' not found` });
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId: subscriber.projectId,
projectId: foundSubscriber.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
@@ -156,20 +217,57 @@ export const pkiSubscriberServiceFactory = ({
ProjectPermissionSub.PkiSubscribers
);
const updatedSubscriber = await pkiSubscriberDAL.updateById(subscriberId, {
caId,
name,
commonName,
ttl,
subjectAlternativeNames,
keyUsages,
extendedKeyUsages
const updatedSubscriber = await pkiSubscriberDAL.transaction(async (tx) => {
if (name) {
// (dangtony98): room to optimize check to ensure that
// the PKI subscriber name is unique across the whole org
const project = await projectDAL.findById(foundSubscriber.projectId, tx);
if (!project) throw new NotFoundError({ message: `Project with ID '${foundSubscriber.projectId}' not found` });
const projects = await projectDAL.find(
{
orgId: project.orgId
},
{ tx }
);
const existingPkiSubscriber = await pkiSubscriberDAL.find(
{
name,
$in: {
projectId: projects.map((p) => p.id)
}
},
{ tx }
);
if (existingPkiSubscriber.length) {
throw new BadRequestError({
message: `PKI subscriber with name '${name}' already exists in the organization`
});
}
}
const subscriber = await pkiSubscriberDAL.updateById(
subscriberId,
{
caId,
name,
commonName,
ttl,
subjectAlternativeNames,
keyUsages,
extendedKeyUsages
},
tx
);
return subscriber;
});
return updatedSubscriber;
};
const deletePkiSubscriber = async ({
const deleteSubscriber = async ({
subscriberId,
actorId,
actorAuthMethod,
@@ -199,7 +297,7 @@ export const pkiSubscriberServiceFactory = ({
return subscriber;
};
const issuePkiSubscriberCert = async ({
const issueSubscriberCert = async ({
subscriberId,
actorId,
actorAuthMethod,
@@ -300,15 +398,111 @@ export const pkiSubscriberServiceFactory = ({
}),
new x509.CertificatePolicyExtension(["2.5.29.32.0"]) // anyPolicy
];
const selectedKeyUsages = subscriber.keyUsages as CertKeyUsage[];
const keyUsagesBitValue = selectedKeyUsages.reduce((accum, keyUsage) => accum | x509.KeyUsageFlags[keyUsage], 0);
if (keyUsagesBitValue) {
extensions.push(new x509.KeyUsagesExtension(keyUsagesBitValue, true));
}
const selectedExtendedKeyUsages = subscriber.extendedKeyUsages as CertExtendedKeyUsage[];
const serialNumber = createSerialNumber();
const leafCert = await x509.X509CertificateGenerator.create({
serialNumber,
subject: csrObj.subject,
issuer: caCertObj.subject,
notBefore: notBeforeDate,
notAfter: notAfterDate,
signingKey: caPrivateKey,
publicKey: csrObj.publicKey,
signingAlgorithm: alg,
extensions
});
const skLeafObj = KeyObject.from(leafKeys.privateKey);
const skLeaf = skLeafObj.export({ format: "pem", type: "pkcs8" }) as string;
const kmsEncryptor = await kmsService.encryptWithKmsKey({
kmsId: certificateManagerKmsId
});
const { cipherTextBlob: encryptedCertificate } = await kmsEncryptor({
plainText: Buffer.from(new Uint8Array(leafCert.rawData))
});
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
plainText: Buffer.from(skLeaf)
});
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
caCertId: caCert.id,
certificateAuthorityDAL,
certificateAuthorityCertDAL,
projectDAL,
kmsService
});
const certificateChainPem = `${issuingCaCertificate}\n${caCertChain}`.trim();
const { cipherTextBlob: encryptedCertificateChain } = await kmsEncryptor({
plainText: Buffer.from(certificateChainPem)
});
await certificateDAL.transaction(async (tx) => {
const cert = await certificateDAL.create(
{
caId: ca.id,
caCertId: caCert.id,
status: CertStatus.ACTIVE,
friendlyName: subscriber.commonName,
commonName: subscriber.commonName,
altNames: subscriber.subjectAlternativeNames.join(","),
serialNumber,
notBefore: notBeforeDate,
notAfter: notAfterDate,
keyUsages: selectedKeyUsages,
extendedKeyUsages: selectedExtendedKeyUsages
},
tx
);
await certificateBodyDAL.create(
{
certId: cert.id,
encryptedCertificate,
encryptedCertificateChain
},
tx
);
await certificateSecretDAL.create(
{
certId: cert.id,
encryptedPrivateKey
},
tx
);
});
return {
certificate: leafCert.toString("pem"),
certificateChain: certificateChainPem,
issuingCaCertificate,
privateKey: skLeaf,
serialNumber,
ca,
subscriber
};
};
const signPkiSubscriberCert = async ({
const signSubscriberCert = async ({
subscriberId,
csr,
actorId,
actorAuthMethod,
actor,
actorOrgId
}: TSignPkiSubscriberCertDTO) => {
const appCfg = getConfig();
const subscriber = await pkiSubscriberDAL.findById(subscriberId);
if (!subscriber) throw new NotFoundError({ message: `PKI subscriber with ID '${subscriberId}' not found` });
const ca = await certificateAuthorityDAL.findById(subscriber.caId);
@@ -328,14 +522,237 @@ export const pkiSubscriberServiceFactory = ({
ProjectPermissionPkiSubscriberActions.SignCert,
ProjectPermissionSub.PkiSubscribers
);
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
if (ca.requireTemplateForIssuance) {
throw new BadRequestError({ message: "Certificate template is required for issuance" });
}
const caCert = await certificateAuthorityCertDAL.findById(ca.activeCaCertId);
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
projectId: ca.projectId,
projectDAL,
kmsService
});
const kmsDecryptor = await kmsService.decryptWithKmsKey({
kmsId: certificateManagerKmsId
});
const decryptedCaCert = await kmsDecryptor({
cipherTextBlob: caCert.encryptedCertificate
});
const caCertObj = new x509.X509Certificate(decryptedCaCert);
const notBeforeDate = new Date();
const notAfterDate = new Date(new Date().getTime() + ms(subscriber.ttl));
const caCertNotBeforeDate = new Date(caCertObj.notBefore);
const caCertNotAfterDate = new Date(caCertObj.notAfter);
// check not before constraint
if (notBeforeDate < caCertNotBeforeDate) {
throw new BadRequestError({ message: "notBefore date is before CA certificate's notBefore date" });
}
// check not after constraint
if (notAfterDate > caCertNotAfterDate) {
throw new BadRequestError({ message: "notAfter date is after CA certificate's notAfter date" });
}
const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
const csrObj = new x509.Pkcs10CertificateRequest(csr);
const dn = parseDistinguishedName(csrObj.subject);
const cn = dn.commonName;
if (cn !== subscriber.commonName) {
throw new BadRequestError({ message: "Common name (CN) in the CSR does not match the subscriber's common name" });
}
const { caPrivateKey, caSecret } = await getCaCredentials({
caId: ca.id,
certificateAuthorityDAL,
certificateAuthoritySecretDAL,
projectDAL,
kmsService
});
const caCrl = await certificateAuthorityCrlDAL.findOne({ caSecretId: caSecret.id });
const distributionPointUrl = `${appCfg.SITE_URL}/api/v1/pki/crl/${caCrl.id}/der`;
const caIssuerUrl = `${appCfg.SITE_URL}/api/v1/pki/ca/${ca.id}/certificates/${caCert.id}/der`;
const extensions: x509.Extension[] = [
new x509.BasicConstraintsExtension(false),
await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false),
await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey),
new x509.CRLDistributionPointsExtension([distributionPointUrl]),
new x509.AuthorityInfoAccessExtension({
caIssuers: new x509.GeneralName("url", caIssuerUrl)
}),
new x509.CertificatePolicyExtension(["2.5.29.32.0"]) // anyPolicy
];
// handle key usages
const csrKeyUsageExtension = csrObj.getExtension("2.5.29.15") as x509.KeyUsagesExtension;
let csrKeyUsages: CertKeyUsage[] = [];
if (csrKeyUsageExtension) {
csrKeyUsages = Object.values(CertKeyUsage).filter(
(keyUsage) => (x509.KeyUsageFlags[keyUsage] & csrKeyUsageExtension.usages) !== 0
);
}
const selectedKeyUsages = subscriber.keyUsages as CertKeyUsage[];
if (csrKeyUsages.some((keyUsage) => !selectedKeyUsages.includes(keyUsage))) {
throw new BadRequestError({
message: "Invalid key usage value based on subscriber's specified key usages"
});
}
const keyUsagesBitValue = selectedKeyUsages.reduce((accum, keyUsage) => accum | x509.KeyUsageFlags[keyUsage], 0);
if (keyUsagesBitValue) {
extensions.push(new x509.KeyUsagesExtension(keyUsagesBitValue, true));
}
// handle extended key usages
const csrExtendedKeyUsageExtension = csrObj.getExtension("2.5.29.37") as x509.ExtendedKeyUsageExtension;
let csrExtendedKeyUsages: CertExtendedKeyUsage[] = [];
if (csrExtendedKeyUsageExtension) {
csrExtendedKeyUsages = csrExtendedKeyUsageExtension.usages.map(
(ekuOid) => CertExtendedKeyUsageOIDToName[ekuOid as string]
);
}
const selectedExtendedKeyUsages = subscriber.extendedKeyUsages as CertExtendedKeyUsage[];
if (csrExtendedKeyUsages.some((eku) => !selectedExtendedKeyUsages.includes(eku))) {
throw new BadRequestError({
message: "Invalid extended key usage value based on subscriber's specified extended key usages"
});
}
if (selectedExtendedKeyUsages.length) {
extensions.push(
new x509.ExtendedKeyUsageExtension(
selectedExtendedKeyUsages.map((eku) => x509.ExtendedKeyUsage[eku]),
true
)
);
}
// attempt to read from CSR if altNames is not explicitly provided
let altNamesArray: {
type: "email" | "dns";
value: string;
}[] = [];
const sanExtension = csrObj.extensions.find((ext) => ext.type === "2.5.29.17");
if (sanExtension) {
const sanNames = new x509.GeneralNames(sanExtension.value);
altNamesArray = sanNames.items
.filter((value) => value.type === "email" || value.type === "dns")
.map((name) => ({
type: name.type as "email" | "dns",
value: name.value
}));
}
if (
altNamesArray
.map((altName) => altName.value)
.some((altName) => !subscriber.subjectAlternativeNames.includes(altName))
) {
throw new BadRequestError({
message: "Invalid subject alternative name based on subscriber's specified subject alternative names"
});
}
if (altNamesArray.length) {
const altNamesExtension = new x509.SubjectAlternativeNameExtension(altNamesArray, false);
extensions.push(altNamesExtension);
}
const serialNumber = createSerialNumber();
const leafCert = await x509.X509CertificateGenerator.create({
serialNumber,
subject: csrObj.subject,
issuer: caCertObj.subject,
notBefore: notBeforeDate,
notAfter: notAfterDate,
signingKey: caPrivateKey,
publicKey: csrObj.publicKey,
signingAlgorithm: alg,
extensions
});
const kmsEncryptor = await kmsService.encryptWithKmsKey({
kmsId: certificateManagerKmsId
});
const { cipherTextBlob: encryptedCertificate } = await kmsEncryptor({
plainText: Buffer.from(new Uint8Array(leafCert.rawData))
});
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
caCertId: ca.activeCaCertId,
certificateAuthorityDAL,
certificateAuthorityCertDAL,
projectDAL,
kmsService
});
const certificateChainPem = `${issuingCaCertificate}\n${caCertChain}`.trim();
const { cipherTextBlob: encryptedCertificateChain } = await kmsEncryptor({
plainText: Buffer.from(certificateChainPem)
});
await certificateDAL.transaction(async (tx) => {
const cert = await certificateDAL.create(
{
caId: ca.id,
caCertId: caCert.id,
status: CertStatus.ACTIVE,
friendlyName: subscriber.commonName,
commonName: subscriber.commonName,
altNames: subscriber.subjectAlternativeNames.join(","),
serialNumber,
notBefore: notBeforeDate,
notAfter: notAfterDate,
keyUsages: selectedKeyUsages,
extendedKeyUsages: selectedExtendedKeyUsages
},
tx
);
await certificateBodyDAL.create(
{
certId: cert.id,
encryptedCertificate,
encryptedCertificateChain
},
tx
);
return cert;
});
return {
certificate: leafCert,
certificateChain: `${issuingCaCertificate}\n${caCertChain}`.trim(),
issuingCaCertificate,
serialNumber,
ca,
commonName: subscriber.commonName,
subscriber
};
};
return {
createPkiSubscriber,
getPkiSubscriberById,
updatePkiSubscriber,
deletePkiSubscriber,
issuePkiSubscriberCert,
signPkiSubscriberCert
createSubscriber,
getSubscriberById,
updateSubscriber,
deleteSubscriber,
issueSubscriberCert,
signSubscriberCert
};
};

View File

@@ -37,4 +37,5 @@ export type TIssuePkiSubscriberCertDTO = {
export type TSignPkiSubscriberCertDTO = {
subscriberId: string;
csr: string;
} & Omit<TProjectPermission, "projectId">;

View File

@@ -188,6 +188,7 @@ export type TSignCertificateEvent = {
properties: {
caId?: string;
certificateTemplateId?: string;
subscriberId?: string;
commonName: string;
userAgent?: string;
};
@@ -198,6 +199,7 @@ export type TIssueCertificateEvent = {
properties: {
caId?: string;
certificateTemplateId?: string;
subscriberId?: string;
commonName: string;
userAgent?: string;
};

View File

@@ -298,6 +298,10 @@ export const ROUTE_PATHS = Object.freeze({
PkiCollectionDetailsByIDPage: setRoute(
"/cert-manager/$projectId/pki-collections/$collectionId",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/pki-collections/$collectionId"
),
PkiSubscriberDetailsByIDPage: setRoute(
"/cert-manager/$projectId/subscribers/$subscriberId",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId"
)
},
Ssh: {

View File

@@ -0,0 +1,179 @@
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "@tanstack/react-router";
import { twMerge } from "tailwind-merge";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import {
Button,
DeleteActionModal,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
PageHeader,
Tooltip
} from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import {
ProjectPermissionPkiSubscriberActions,
ProjectPermissionSub,
useWorkspace
} from "@app/context";
import { useDeletePkiSubscriber, useGetPkiSubscriberById } from "@app/hooks/api";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { usePopUp } from "@app/hooks/usePopUp";
import { PkiSubscriberModal } from "../PkiSubscribersPage/components/PkiSubscriberModal";
// import { PkiSubscriberCertificatesSection, PkiSubscriberDetailsSection } from "./components";
const Page = () => {
const { currentWorkspace } = useWorkspace();
const navigate = useNavigate();
const projectId = currentWorkspace?.id || "";
const subscriberId = useParams({
from: ROUTE_PATHS.CertManager.PkiSubscriberDetailsByIDPage.id,
select: (el) => el.subscriberId
});
const { data } = useGetPkiSubscriberById(subscriberId);
const { mutateAsync: deletePkiSubscriber } = useDeletePkiSubscriber();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"pkiSubscriber",
"deletePkiSubscriber"
] as const);
const onRemoveSubscriberSubmit = async (subscriberIdToDelete: string) => {
try {
if (!projectId) return;
await deletePkiSubscriber({ subscriberId: subscriberIdToDelete });
createNotification({
text: "Successfully deleted PKI subscriber",
type: "success"
});
handlePopUpClose("deletePkiSubscriber");
navigate({
to: `/${ProjectType.CertificateManager}/$projectId/subscribers` as const,
params: {
projectId
}
});
} catch (err) {
console.error(err);
createNotification({
text: "Failed to delete PKI subscriber",
type: "error"
});
}
};
return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
{data && (
<div className="mx-auto mb-6 w-full max-w-7xl">
<PageHeader title={data.name}>
<DropdownMenu>
<DropdownMenuTrigger asChild className="rounded-lg">
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
<Tooltip content="More options">
<Button variant="outline_bg">More</Button>
</Tooltip>
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="p-1">
<ProjectPermissionCan
I={ProjectPermissionPkiSubscriberActions.Delete}
a={ProjectPermissionSub.PkiSubscribers}
>
{(isAllowed) => (
<DropdownMenuItem
className={twMerge(
isAllowed
? "hover:!bg-red-500 hover:!text-white"
: "pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={() =>
handlePopUpOpen("deletePkiSubscriber", {
subscriberId: data.id,
name: data.name
})
}
disabled={!isAllowed}
>
Delete PKI Subscriber
</DropdownMenuItem>
)}
</ProjectPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
</PageHeader>
<div className="flex">
<div className="mr-4 w-96">
TODO: PkiSubscriberDetailsSection
{/* <PkiSubscriberDetailsSection
subscriberId={subscriberId}
handlePopUpOpen={handlePopUpOpen}
/> */}
</div>
<div className="w-full">
TODO: PkiSubscriberCertificatesSection
{/* <PkiSubscriberCertificatesSection subscriberId={subscriberId} /> */}
</div>
</div>
</div>
)}
<PkiSubscriberModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<DeleteActionModal
isOpen={popUp.deletePkiSubscriber.isOpen}
title={`Are you sure want to remove the PKI subscriber: ${
(popUp?.deletePkiSubscriber?.data as { name: string })?.name || ""
}?`}
onChange={(isOpen) => handlePopUpToggle("deletePkiSubscriber", isOpen)}
deleteKey="confirm"
onDeleteApproved={() =>
onRemoveSubscriberSubmit(
(popUp?.deletePkiSubscriber?.data as { subscriberId: string })?.subscriberId
)
}
/>
</div>
);
};
export const PkiSubscriberDetailsByIDPage = () => {
const { t } = useTranslation();
return (
<>
<Helmet>
<title>{t("common.head-title", { title: "PKI Subscriber" })}</title>
</Helmet>
{/* <ProjectPermissionCan
I={ProjectPermissionPkiSubscriberActions.Read}
a={ProjectPermissionSub.PkiSubscribers}
passThrough={false}
renderGuardBanner
>
<Page />
</ProjectPermissionCan> */}
TESTTT
</>
);
};
/**
* import { createFileRoute } from "@tanstack/react-router";
import { PkiSubscriberDetailsByIDPage } from "./PkiSubscriberDetailsByIDPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId"
)({
component: PkiSubscriberDetailsByIDPage
});
*/

View File

@@ -0,0 +1,16 @@
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId"
)({
component: RouteComponent
});
function RouteComponent() {
return (
<div>
Hello
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId"!
</div>
);
}

View File

@@ -1,5 +1,6 @@
import { faEllipsis, faPencil, faServer, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate } from "@tanstack/react-router";
import { twMerge } from "tailwind-merge";
import { ProjectPermissionCan } from "@app/components/permissions";
@@ -25,6 +26,7 @@ import {
useWorkspace
} from "@app/context";
import { useListWorkspacePkiSubscribers } from "@app/hooks/api";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
@@ -35,6 +37,7 @@ type Props = {
};
export const PkiSubscribersTable = ({ handlePopUpOpen }: Props) => {
const navigate = useNavigate();
const { currentWorkspace } = useWorkspace();
const { data, isPending } = useListWorkspacePkiSubscribers(currentWorkspace?.id || "");
return (
@@ -55,7 +58,20 @@ export const PkiSubscribersTable = ({ handlePopUpOpen }: Props) => {
data.length > 0 &&
data.map((subscriber) => {
return (
<Tr className="h-10" key={`pki-subscriber-${subscriber.id}`}>
<Tr
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
key={`pki-subscriber-${subscriber.id}`}
onClick={(e) => {
e.preventDefault();
navigate({
to: `/${ProjectType.CertificateManager}/$projectId/subscribers/$subscriberId` as const,
params: {
projectId: currentWorkspace.id,
subscriberId: subscriber.id
}
});
}}
>
<Td>{subscriber.name}</Td>
<Td>{subscriber.commonName}</Td>
<Td className="text-right align-middle">

View File

@@ -115,6 +115,7 @@ import { Route as secretManagerSecretDashboardPageRouteImport } from './pages/se
import { Route as secretManagerIntegrationsSelectIntegrationAuthPageRouteImport } from './pages/secret-manager/integrations/SelectIntegrationAuthPage/route'
import { Route as secretManagerIntegrationsDetailsByIDPageRouteImport } from './pages/secret-manager/IntegrationsDetailsByIDPage/route'
import { Route as organizationAppConnectionsOauthCallbackPageRouteImport } from './pages/organization/AppConnections/OauthCallbackPage/route'
import { Route as certManagerPkiSubscriberDetailsByIDPageRouteImport } from './pages/cert-manager/PkiSubscriberDetailsByIDPage/route'
import { Route as certManagerCertAuthDetailsByIDPageRouteImport } from './pages/cert-manager/CertAuthDetailsByIDPage/route'
import { Route as secretManagerIntegrationsListPageRouteImport } from './pages/secret-manager/IntegrationsListPage/route'
import { Route as secretManagerIntegrationsWindmillConfigurePageRouteImport } from './pages/secret-manager/integrations/WindmillConfigurePage/route'
@@ -1075,6 +1076,13 @@ const organizationAppConnectionsOauthCallbackPageRouteRoute =
AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRoute,
} as any)
const certManagerPkiSubscriberDetailsByIDPageRouteRoute =
certManagerPkiSubscriberDetailsByIDPageRouteImport.update({
id: '/$subscriberId',
path: '/$subscriberId',
getParentRoute: () => certManagerPkiSubscribersPageRouteRoute,
} as any)
const certManagerCertAuthDetailsByIDPageRouteRoute =
certManagerCertAuthDetailsByIDPageRouteImport.update({
id: '/ca/$caId',
@@ -2402,6 +2410,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof certManagerCertAuthDetailsByIDPageRouteImport
parentRoute: typeof certManagerLayoutImport
}
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId': {
id: '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId'
path: '/$subscriberId'
fullPath: '/cert-manager/$projectId/subscribers/$subscriberId'
preLoaderRoute: typeof certManagerPkiSubscriberDetailsByIDPageRouteImport
parentRoute: typeof certManagerPkiSubscribersPageRouteImport
}
'/_authenticate/_inject-org-details/_org-layout/organization/app-connections/$appConnection/oauth/callback': {
id: '/_authenticate/_inject-org-details/_org-layout/organization/app-connections/$appConnection/oauth/callback'
path: '/$appConnection/oauth/callback'
@@ -3231,12 +3246,27 @@ const AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteWithChildren =
AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren,
)
interface certManagerPkiSubscribersPageRouteRouteChildren {
certManagerPkiSubscriberDetailsByIDPageRouteRoute: typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute
}
const certManagerPkiSubscribersPageRouteRouteChildren: certManagerPkiSubscribersPageRouteRouteChildren =
{
certManagerPkiSubscriberDetailsByIDPageRouteRoute:
certManagerPkiSubscriberDetailsByIDPageRouteRoute,
}
const certManagerPkiSubscribersPageRouteRouteWithChildren =
certManagerPkiSubscribersPageRouteRoute._addFileChildren(
certManagerPkiSubscribersPageRouteRouteChildren,
)
interface certManagerLayoutRouteChildren {
certManagerAlertingPageRouteRoute: typeof certManagerAlertingPageRouteRoute
certManagerCertificateAuthoritiesPageRouteRoute: typeof certManagerCertificateAuthoritiesPageRouteRoute
certManagerCertificatesPageRouteRoute: typeof certManagerCertificatesPageRouteRoute
certManagerSettingsPageRouteRoute: typeof certManagerSettingsPageRouteRoute
certManagerPkiSubscribersPageRouteRoute: typeof certManagerPkiSubscribersPageRouteRoute
certManagerPkiSubscribersPageRouteRoute: typeof certManagerPkiSubscribersPageRouteRouteWithChildren
projectAccessControlPageRouteCertManagerRoute: typeof projectAccessControlPageRouteCertManagerRoute
certManagerCertAuthDetailsByIDPageRouteRoute: typeof certManagerCertAuthDetailsByIDPageRouteRoute
projectIdentityDetailsByIDPageRouteCertManagerRoute: typeof projectIdentityDetailsByIDPageRouteCertManagerRoute
@@ -3252,7 +3282,7 @@ const certManagerLayoutRouteChildren: certManagerLayoutRouteChildren = {
certManagerCertificatesPageRouteRoute: certManagerCertificatesPageRouteRoute,
certManagerSettingsPageRouteRoute: certManagerSettingsPageRouteRoute,
certManagerPkiSubscribersPageRouteRoute:
certManagerPkiSubscribersPageRouteRoute,
certManagerPkiSubscribersPageRouteRouteWithChildren,
projectAccessControlPageRouteCertManagerRoute:
projectAccessControlPageRouteCertManagerRoute,
certManagerCertAuthDetailsByIDPageRouteRoute:
@@ -3923,7 +3953,7 @@ export interface FileRoutesByFullPath {
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
'/cert-manager/$projectId/overview': typeof certManagerCertificatesPageRouteRoute
'/cert-manager/$projectId/settings': typeof certManagerSettingsPageRouteRoute
'/cert-manager/$projectId/subscribers': typeof certManagerPkiSubscribersPageRouteRoute
'/cert-manager/$projectId/subscribers': typeof certManagerPkiSubscribersPageRouteRouteWithChildren
'/kms/$projectId/kmip': typeof kmsKmipPageRouteRoute
'/kms/$projectId/overview': typeof kmsOverviewPageRouteRoute
'/kms/$projectId/settings': typeof kmsSettingsPageRouteRoute
@@ -3953,6 +3983,7 @@ export interface FileRoutesByFullPath {
'/ssh/$projectId/access-management': typeof projectAccessControlPageRouteSshRoute
'/secret-manager/$projectId/integrations/': typeof secretManagerIntegrationsListPageRouteRoute
'/cert-manager/$projectId/ca/$caId': typeof certManagerCertAuthDetailsByIDPageRouteRoute
'/cert-manager/$projectId/subscribers/$subscriberId': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute
'/organization/app-connections/$appConnection/oauth/callback': typeof organizationAppConnectionsOauthCallbackPageRouteRoute
'/secret-manager/$projectId/integrations/$integrationId': typeof secretManagerIntegrationsDetailsByIDPageRouteRoute
'/secret-manager/$projectId/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute
@@ -4103,7 +4134,7 @@ export interface FileRoutesByTo {
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
'/cert-manager/$projectId/overview': typeof certManagerCertificatesPageRouteRoute
'/cert-manager/$projectId/settings': typeof certManagerSettingsPageRouteRoute
'/cert-manager/$projectId/subscribers': typeof certManagerPkiSubscribersPageRouteRoute
'/cert-manager/$projectId/subscribers': typeof certManagerPkiSubscribersPageRouteRouteWithChildren
'/kms/$projectId/kmip': typeof kmsKmipPageRouteRoute
'/kms/$projectId/overview': typeof kmsOverviewPageRouteRoute
'/kms/$projectId/settings': typeof kmsSettingsPageRouteRoute
@@ -4132,6 +4163,7 @@ export interface FileRoutesByTo {
'/ssh/$projectId/access-management': typeof projectAccessControlPageRouteSshRoute
'/secret-manager/$projectId/integrations': typeof secretManagerIntegrationsListPageRouteRoute
'/cert-manager/$projectId/ca/$caId': typeof certManagerCertAuthDetailsByIDPageRouteRoute
'/cert-manager/$projectId/subscribers/$subscriberId': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute
'/organization/app-connections/$appConnection/oauth/callback': typeof organizationAppConnectionsOauthCallbackPageRouteRoute
'/secret-manager/$projectId/integrations/$integrationId': typeof secretManagerIntegrationsDetailsByIDPageRouteRoute
'/secret-manager/$projectId/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute
@@ -4300,7 +4332,7 @@ export interface FileRoutesById {
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/overview': typeof certManagerCertificatesPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/settings': typeof certManagerSettingsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers': typeof certManagerPkiSubscribersPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers': typeof certManagerPkiSubscribersPageRouteRouteWithChildren
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout/kmip': typeof kmsKmipPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout/overview': typeof kmsOverviewPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout/settings': typeof kmsSettingsPageRouteRoute
@@ -4330,6 +4362,7 @@ export interface FileRoutesById {
'/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/access-management': typeof projectAccessControlPageRouteSshRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/': typeof secretManagerIntegrationsListPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/ca/$caId': typeof certManagerCertAuthDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/app-connections/$appConnection/oauth/callback': typeof organizationAppConnectionsOauthCallbackPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/$integrationId': typeof secretManagerIntegrationsDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute
@@ -4520,6 +4553,7 @@ export interface FileRouteTypes {
| '/ssh/$projectId/access-management'
| '/secret-manager/$projectId/integrations/'
| '/cert-manager/$projectId/ca/$caId'
| '/cert-manager/$projectId/subscribers/$subscriberId'
| '/organization/app-connections/$appConnection/oauth/callback'
| '/secret-manager/$projectId/integrations/$integrationId'
| '/secret-manager/$projectId/integrations/select-integration-auth'
@@ -4698,6 +4732,7 @@ export interface FileRouteTypes {
| '/ssh/$projectId/access-management'
| '/secret-manager/$projectId/integrations'
| '/cert-manager/$projectId/ca/$caId'
| '/cert-manager/$projectId/subscribers/$subscriberId'
| '/organization/app-connections/$appConnection/oauth/callback'
| '/secret-manager/$projectId/integrations/$integrationId'
| '/secret-manager/$projectId/integrations/select-integration-auth'
@@ -4894,6 +4929,7 @@ export interface FileRouteTypes {
| '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/access-management'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/'
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/ca/$caId'
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId'
| '/_authenticate/_inject-org-details/_org-layout/organization/app-connections/$appConnection/oauth/callback'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/$integrationId'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/select-integration-auth'
@@ -5465,7 +5501,10 @@ export const routeTree = rootRoute
},
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers": {
"filePath": "cert-manager/PkiSubscribersPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout"
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout",
"children": [
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId"
]
},
"/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout/kmip": {
"filePath": "kms/KmipPage/route.tsx",
@@ -5663,6 +5702,10 @@ export const routeTree = rootRoute
"filePath": "cert-manager/CertAuthDetailsByIDPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout"
},
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers/$subscriberId": {
"filePath": "cert-manager/PkiSubscriberDetailsByIDPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/subscribers"
},
"/_authenticate/_inject-org-details/_org-layout/organization/app-connections/$appConnection/oauth/callback": {
"filePath": "organization/AppConnections/OauthCallbackPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization/app-connections"

View File

@@ -284,6 +284,7 @@ const secretManagerIntegrationsRedirect = route("/integrations", [
const certManagerRoutes = route("/cert-manager/$projectId", [
layout("cert-manager-layout", "cert-manager/layout.tsx", [
route("/subscribers/$subscriberId", "cert-manager/PkiSubscriberDetailsByIDPage/route.tsx"),
route("/subscribers", "cert-manager/PkiSubscribersPage/route.tsx"),
route("/overview", "cert-manager/CertificatesPage/route.tsx"),
route("/certificate-authorities", "cert-manager/CertificateAuthoritiesPage/route.tsx"),