From 2b67fe760845577e7419c6b12f98a800f97a047a Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Sat, 8 Nov 2025 03:04:00 +0800 Subject: [PATCH 1/6] misc: endpoint for fetching latest active bundle of profile --- backend/src/server/routes/index.ts | 120 +++++++++--------- .../routes/v1/certificate-profiles-router.ts | 47 ++++++- .../certificate-profile-dal.ts | 19 +++ .../certificate-profile-service.ts | 118 ++++++++++++++++- 4 files changed, 242 insertions(+), 62 deletions(-) diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 961c0a940..ea4f9ceb5 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -17,30 +17,30 @@ import { accessApprovalRequestDALFactory } from "@app/ee/services/access-approva import { accessApprovalRequestReviewerDALFactory } from "@app/ee/services/access-approval-request/access-approval-request-reviewer-dal"; import { accessApprovalRequestServiceFactory } from "@app/ee/services/access-approval-request/access-approval-request-service"; import { assumePrivilegeServiceFactory } from "@app/ee/services/assume-privilege/assume-privilege-service"; -import { auditLogStreamDALFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-dal"; -import { auditLogStreamServiceFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-service"; import { auditLogDALFactory } from "@app/ee/services/audit-log/audit-log-dal"; import { auditLogQueueServiceFactory } from "@app/ee/services/audit-log/audit-log-queue"; import { auditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service"; +import { auditLogStreamDALFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-dal"; +import { auditLogStreamServiceFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-service"; import { certificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { certificateAuthorityCrlServiceFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-service"; import { certificateEstServiceFactory } from "@app/ee/services/certificate-est/certificate-est-service"; -import { dynamicSecretLeaseDALFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-dal"; -import { dynamicSecretLeaseQueueServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue"; -import { dynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service"; import { dynamicSecretDALFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-dal"; import { dynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service"; import { buildDynamicSecretProviders } from "@app/ee/services/dynamic-secret/providers"; +import { dynamicSecretLeaseDALFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-dal"; +import { dynamicSecretLeaseQueueServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue"; +import { dynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service"; import { eventBusFactory } from "@app/ee/services/event/event-bus-service"; import { sseServiceFactory } from "@app/ee/services/event/event-sse-service"; import { externalKmsDALFactory } from "@app/ee/services/external-kms/external-kms-dal"; import { externalKmsServiceFactory } from "@app/ee/services/external-kms/external-kms-service"; -import { gatewayV2DalFactory } from "@app/ee/services/gateway-v2/gateway-v2-dal"; -import { gatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service"; -import { orgGatewayConfigV2DalFactory } from "@app/ee/services/gateway-v2/org-gateway-config-v2-dal"; import { gatewayDALFactory } from "@app/ee/services/gateway/gateway-dal"; import { gatewayServiceFactory } from "@app/ee/services/gateway/gateway-service"; import { orgGatewayConfigDALFactory } from "@app/ee/services/gateway/org-gateway-config-dal"; +import { gatewayV2DalFactory } from "@app/ee/services/gateway-v2/gateway-v2-dal"; +import { gatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service"; +import { orgGatewayConfigV2DalFactory } from "@app/ee/services/gateway-v2/org-gateway-config-v2-dal"; import { githubOrgSyncDALFactory } from "@app/ee/services/github-org-sync/github-org-sync-dal"; import { githubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/github-org-sync-service"; import { groupDALFactory } from "@app/ee/services/group/group-dal"; @@ -74,9 +74,12 @@ import { pamSessionServiceFactory } from "@app/ee/services/pam-session/pam-sessi import { permissionDALFactory } from "@app/ee/services/permission/permission-dal"; import { permissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { pitServiceFactory } from "@app/ee/services/pit/pit-service"; +import { pkiAcmeAccountDALFactory } from "@app/ee/services/pki-acme/pki-acme-account-dal"; import { pkiAcmeAuthDALFactory } from "@app/ee/services/pki-acme/pki-acme-auth-dal"; +import { pkiAcmeChallengeDALFactory } from "@app/ee/services/pki-acme/pki-acme-challenge-dal"; import { pkiAcmeChallengeServiceFactory } from "@app/ee/services/pki-acme/pki-acme-challenge-service"; import { pkiAcmeOrderAuthDALFactory } from "@app/ee/services/pki-acme/pki-acme-order-auth-dal"; +import { pkiAcmeOrderDALFactory } from "@app/ee/services/pki-acme/pki-acme-order-dal"; import { pkiAcmeServiceFactory } from "@app/ee/services/pki-acme/pki-acme-service"; import { projectTemplateDALFactory } from "@app/ee/services/project-template/project-template-dal"; import { projectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service"; @@ -102,39 +105,39 @@ import { secretApprovalRequestReviewerDALFactory } from "@app/ee/services/secret import { secretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal"; import { secretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service"; import { secretReplicationServiceFactory } from "@app/ee/services/secret-replication/secret-replication-service"; -import { secretRotationV2DALFactory } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-dal"; -import { secretRotationV2QueueServiceFactory } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-queue"; -import { secretRotationV2ServiceFactory } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-service"; import { secretRotationDALFactory } from "@app/ee/services/secret-rotation/secret-rotation-dal"; import { secretRotationQueueFactory } from "@app/ee/services/secret-rotation/secret-rotation-queue"; import { secretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service"; -import { secretScanningV2DALFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-dal"; -import { secretScanningV2QueueServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-queue"; -import { secretScanningV2ServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-service"; +import { secretRotationV2DALFactory } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-dal"; +import { secretRotationV2QueueServiceFactory } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-queue"; +import { secretRotationV2ServiceFactory } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-service"; import { gitAppDALFactory } from "@app/ee/services/secret-scanning/git-app-dal"; import { gitAppInstallSessionDALFactory } from "@app/ee/services/secret-scanning/git-app-install-session-dal"; import { secretScanningDALFactory } from "@app/ee/services/secret-scanning/secret-scanning-dal"; import { secretScanningQueueFactory } from "@app/ee/services/secret-scanning/secret-scanning-queue"; import { secretScanningServiceFactory } from "@app/ee/services/secret-scanning/secret-scanning-service"; +import { secretScanningV2DALFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-dal"; +import { secretScanningV2QueueServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-queue"; +import { secretScanningV2ServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-service"; import { secretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { snapshotDALFactory } from "@app/ee/services/secret-snapshot/snapshot-dal"; import { snapshotFolderDALFactory } from "@app/ee/services/secret-snapshot/snapshot-folder-dal"; import { snapshotSecretDALFactory } from "@app/ee/services/secret-snapshot/snapshot-secret-dal"; import { snapshotSecretV2DALFactory } from "@app/ee/services/secret-snapshot/snapshot-secret-v2-dal"; -import { sshCertificateTemplateDALFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-dal"; -import { sshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service"; +import { sshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal"; +import { sshCertificateAuthoritySecretDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-secret-dal"; +import { sshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service"; import { sshCertificateBodyDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-body-dal"; import { sshCertificateDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-dal"; -import { sshHostGroupDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-dal"; -import { sshHostGroupMembershipDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-membership-dal"; -import { sshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service"; +import { sshCertificateTemplateDALFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-dal"; +import { sshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service"; import { sshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal"; import { sshHostLoginUserMappingDALFactory } from "@app/ee/services/ssh-host/ssh-host-login-user-mapping-dal"; import { sshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-service"; import { sshHostLoginUserDALFactory } from "@app/ee/services/ssh-host/ssh-login-user-dal"; -import { sshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal"; -import { sshCertificateAuthoritySecretDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-secret-dal"; -import { sshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service"; +import { sshHostGroupDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-dal"; +import { sshHostGroupMembershipDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-membership-dal"; +import { sshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service"; import { subOrgServiceFactory } from "@app/ee/services/sub-org/sub-org-service"; import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal"; import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service"; @@ -154,12 +157,16 @@ import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal"; import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service"; import { appConnectionDALFactory } from "@app/services/app-connection/app-connection-dal"; import { appConnectionServiceFactory } from "@app/services/app-connection/app-connection-service"; -import { tokenDALFactory } from "@app/services/auth-token/auth-token-dal"; -import { tokenServiceFactory } from "@app/services/auth-token/auth-token-service"; import { authDALFactory } from "@app/services/auth/auth-dal"; import { authLoginServiceFactory } from "@app/services/auth/auth-login-service"; import { authPaswordServiceFactory } from "@app/services/auth/auth-password-service"; import { authSignupServiceFactory } from "@app/services/auth/auth-signup-service"; +import { tokenDALFactory } from "@app/services/auth-token/auth-token-dal"; +import { tokenServiceFactory } from "@app/services/auth-token/auth-token-service"; +import { certificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal"; +import { certificateDALFactory } from "@app/services/certificate/certificate-dal"; +import { certificateSecretDALFactory } from "@app/services/certificate/certificate-secret-dal"; +import { certificateServiceFactory } from "@app/services/certificate/certificate-service"; import { certificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal"; import { certificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal"; import { certificateAuthorityQueueFactory } from "@app/services/certificate-authority/certificate-authority-queue"; @@ -172,18 +179,14 @@ import { internalCertificateAuthorityServiceFactory } from "@app/services/certif import { certificateEstV3ServiceFactory } from "@app/services/certificate-est-v3/certificate-est-v3-service"; import { certificateProfileDALFactory } from "@app/services/certificate-profile/certificate-profile-dal"; import { certificateProfileServiceFactory } from "@app/services/certificate-profile/certificate-profile-service"; -import { certificateTemplateV2DALFactory } from "@app/services/certificate-template-v2/certificate-template-v2-dal"; -import { certificateTemplateV2ServiceFactory } from "@app/services/certificate-template-v2/certificate-template-v2-service"; import { certificateSyncDALFactory } from "@app/services/certificate-sync/certificate-sync-dal"; import { certificateTemplateDALFactory } from "@app/services/certificate-template/certificate-template-dal"; import { certificateTemplateEstConfigDALFactory } from "@app/services/certificate-template/certificate-template-est-config-dal"; import { certificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service"; +import { certificateTemplateV2DALFactory } from "@app/services/certificate-template-v2/certificate-template-v2-dal"; +import { certificateTemplateV2ServiceFactory } from "@app/services/certificate-template-v2/certificate-template-v2-service"; import { certificateV3QueueServiceFactory } from "@app/services/certificate-v3/certificate-v3-queue"; import { certificateV3ServiceFactory } from "@app/services/certificate-v3/certificate-v3-service"; -import { certificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal"; -import { certificateDALFactory } from "@app/services/certificate/certificate-dal"; -import { certificateSecretDALFactory } from "@app/services/certificate/certificate-secret-dal"; -import { certificateServiceFactory } from "@app/services/certificate/certificate-service"; import { cmekServiceFactory } from "@app/services/cmek/cmek-service"; import { convertorServiceFactory } from "@app/services/convertor/convertor-service"; import { acmeEnrollmentConfigDALFactory } from "@app/services/enrollment-config/acme-enrollment-config-dal"; @@ -194,17 +197,21 @@ import { externalGroupOrgRoleMappingServiceFactory } from "@app/services/externa import { externalMigrationQueueFactory } from "@app/services/external-migration/external-migration-queue"; import { externalMigrationServiceFactory } from "@app/services/external-migration/external-migration-service"; import { vaultExternalMigrationConfigDALFactory } from "@app/services/external-migration/vault-external-migration-config-dal"; -import { folderCheckpointResourcesDALFactory } from "@app/services/folder-checkpoint-resources/folder-checkpoint-resources-dal"; import { folderCheckpointDALFactory } from "@app/services/folder-checkpoint/folder-checkpoint-dal"; -import { folderCommitChangesDALFactory } from "@app/services/folder-commit-changes/folder-commit-changes-dal"; +import { folderCheckpointResourcesDALFactory } from "@app/services/folder-checkpoint-resources/folder-checkpoint-resources-dal"; import { folderCommitDALFactory } from "@app/services/folder-commit/folder-commit-dal"; import { folderCommitQueueServiceFactory } from "@app/services/folder-commit/folder-commit-queue"; import { folderCommitServiceFactory } from "@app/services/folder-commit/folder-commit-service"; -import { folderTreeCheckpointResourcesDALFactory } from "@app/services/folder-tree-checkpoint-resources/folder-tree-checkpoint-resources-dal"; +import { folderCommitChangesDALFactory } from "@app/services/folder-commit-changes/folder-commit-changes-dal"; import { folderTreeCheckpointDALFactory } from "@app/services/folder-tree-checkpoint/folder-tree-checkpoint-dal"; +import { folderTreeCheckpointResourcesDALFactory } from "@app/services/folder-tree-checkpoint-resources/folder-tree-checkpoint-resources-dal"; import { groupProjectDALFactory } from "@app/services/group-project/group-project-dal"; import { groupProjectServiceFactory } from "@app/services/group-project/group-project-service"; import { healthAlertServiceFactory } from "@app/services/health-alert/health-alert-queue"; +import { identityDALFactory } from "@app/services/identity/identity-dal"; +import { identityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal"; +import { identityOrgDALFactory } from "@app/services/identity/identity-org-dal"; +import { identityServiceFactory } from "@app/services/identity/identity-service"; import { identityAccessTokenDALFactory } from "@app/services/identity-access-token/identity-access-token-dal"; import { identityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service"; import { identityAliCloudAuthDALFactory } from "@app/services/identity-alicloud-auth/identity-alicloud-auth-dal"; @@ -234,27 +241,23 @@ import { identityTokenAuthServiceFactory } from "@app/services/identity-token-au import { identityUaClientSecretDALFactory } from "@app/services/identity-ua/identity-ua-client-secret-dal"; import { identityUaDALFactory } from "@app/services/identity-ua/identity-ua-dal"; import { identityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service"; -import { identityDALFactory } from "@app/services/identity/identity-dal"; -import { identityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal"; -import { identityOrgDALFactory } from "@app/services/identity/identity-org-dal"; -import { identityServiceFactory } from "@app/services/identity/identity-service"; -import { integrationAuthDALFactory } from "@app/services/integration-auth/integration-auth-dal"; -import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service"; import { integrationDALFactory } from "@app/services/integration/integration-dal"; import { integrationServiceFactory } from "@app/services/integration/integration-service"; +import { integrationAuthDALFactory } from "@app/services/integration-auth/integration-auth-dal"; +import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service"; import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal"; import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal"; import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { kmsServiceFactory } from "@app/services/kms/kms-service"; import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types"; +import { membershipDALFactory } from "@app/services/membership/membership-dal"; +import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal"; import { membershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal"; import { membershipGroupServiceFactory } from "@app/services/membership-group/membership-group-service"; import { membershipIdentityDALFactory } from "@app/services/membership-identity/membership-identity-dal"; import { membershipIdentityServiceFactory } from "@app/services/membership-identity/membership-identity-service"; import { membershipUserDALFactory } from "@app/services/membership-user/membership-user-dal"; import { membershipUserServiceFactory } from "@app/services/membership-user/membership-user-service"; -import { membershipDALFactory } from "@app/services/membership/membership-dal"; -import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal"; import { microsoftTeamsIntegrationDALFactory } from "@app/services/microsoft-teams/microsoft-teams-integration-dal"; import { microsoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service"; import { projectMicrosoftTeamsConfigDALFactory } from "@app/services/microsoft-teams/project-microsoft-teams-config-dal"; @@ -263,11 +266,11 @@ import { notificationServiceFactory } from "@app/services/notification/notificat import { userNotificationDALFactory } from "@app/services/notification/user-notification-dal"; import { offlineUsageReportDALFactory } from "@app/services/offline-usage-report/offline-usage-report-dal"; import { offlineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service"; -import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service"; -import { orgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal"; import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal"; import { orgDALFactory } from "@app/services/org/org-dal"; import { orgServiceFactory } from "@app/services/org/org-service"; +import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service"; +import { orgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal"; import { pamAccountRotationServiceFactory } from "@app/services/pam-account-rotation/pam-account-rotation-queue"; import { dailyExpiringPkiItemAlertQueueServiceFactory } from "@app/services/pki-alert/expiring-pki-item-alert-queue"; import { pkiAlertDALFactory } from "@app/services/pki-alert/pki-alert-dal"; @@ -284,6 +287,10 @@ import { pkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue"; import { pkiSyncServiceFactory } from "@app/services/pki-sync/pki-sync-service"; import { pkiTemplatesDALFactory } from "@app/services/pki-templates/pki-templates-dal"; import { pkiTemplatesServiceFactory } from "@app/services/pki-templates/pki-templates-service"; +import { projectDALFactory } from "@app/services/project/project-dal"; +import { projectQueueFactory } from "@app/services/project/project-queue"; +import { projectServiceFactory } from "@app/services/project/project-service"; +import { projectSshConfigDALFactory } from "@app/services/project/project-ssh-config-dal"; import { projectBotDALFactory } from "@app/services/project-bot/project-bot-dal"; import { projectBotServiceFactory } from "@app/services/project-bot/project-bot-service"; import { projectEnvDALFactory } from "@app/services/project-env/project-env-dal"; @@ -292,18 +299,19 @@ import { projectKeyDALFactory } from "@app/services/project-key/project-key-dal" import { projectKeyServiceFactory } from "@app/services/project-key/project-key-service"; import { projectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { projectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service"; -import { projectDALFactory } from "@app/services/project/project-dal"; -import { projectQueueFactory } from "@app/services/project/project-queue"; -import { projectServiceFactory } from "@app/services/project/project-service"; -import { projectSshConfigDALFactory } from "@app/services/project/project-ssh-config-dal"; -import { reminderRecipientDALFactory } from "@app/services/reminder-recipients/reminder-recipient-dal"; import { reminderDALFactory } from "@app/services/reminder/reminder-dal"; import { dailyReminderQueueServiceFactory } from "@app/services/reminder/reminder-queue"; import { reminderServiceFactory } from "@app/services/reminder/reminder-service"; +import { reminderRecipientDALFactory } from "@app/services/reminder-recipients/reminder-recipient-dal"; import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue"; import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal"; import { roleDALFactory } from "@app/services/role/role-dal"; import { roleServiceFactory } from "@app/services/role/role-service"; +import { secretDALFactory } from "@app/services/secret/secret-dal"; +import { secretQueueFactory } from "@app/services/secret/secret-queue"; +import { secretServiceFactory } from "@app/services/secret/secret-service"; +import { secretVersionDALFactory } from "@app/services/secret/secret-version-dal"; +import { secretVersionTagDALFactory } from "@app/services/secret/secret-version-tag-dal"; import { secretBlindIndexDALFactory } from "@app/services/secret-blind-index/secret-blind-index-dal"; import { secretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service"; import { secretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; @@ -323,11 +331,6 @@ import { secretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret- import { secretV2BridgeServiceFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-service"; import { secretVersionV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-version-dal"; import { secretVersionV2TagBridgeDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal"; -import { secretDALFactory } from "@app/services/secret/secret-dal"; -import { secretQueueFactory } from "@app/services/secret/secret-queue"; -import { secretServiceFactory } from "@app/services/secret/secret-service"; -import { secretVersionDALFactory } from "@app/services/secret/secret-version-dal"; -import { secretVersionTagDALFactory } from "@app/services/secret/secret-version-tag-dal"; import { serviceTokenDALFactory } from "@app/services/service-token/service-token-dal"; import { serviceTokenServiceFactory } from "@app/services/service-token/service-token-service"; import { projectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal"; @@ -343,18 +346,15 @@ import { telemetryServiceFactory } from "@app/services/telemetry/telemetry-servi import { totpConfigDALFactory } from "@app/services/totp/totp-config-dal"; import { totpServiceFactory } from "@app/services/totp/totp-service"; import { upgradePathServiceFactory } from "@app/services/upgrade-path/upgrade-path-service"; -import { userAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; -import { userEngagementServiceFactory } from "@app/services/user-engagement/user-engagement-service"; import { userDALFactory } from "@app/services/user/user-dal"; import { userServiceFactory } from "@app/services/user/user-service"; +import { userAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; +import { userEngagementServiceFactory } from "@app/services/user-engagement/user-engagement-service"; import { webhookDALFactory } from "@app/services/webhook/webhook-dal"; import { webhookServiceFactory } from "@app/services/webhook/webhook-service"; import { workflowIntegrationDALFactory } from "@app/services/workflow-integration/workflow-integration-dal"; import { workflowIntegrationServiceFactory } from "@app/services/workflow-integration/workflow-integration-service"; -import { pkiAcmeAccountDALFactory } from "@app/ee/services/pki-acme/pki-acme-account-dal"; -import { pkiAcmeChallengeDALFactory } from "@app/ee/services/pki-acme/pki-acme-challenge-dal"; -import { pkiAcmeOrderDALFactory } from "@app/ee/services/pki-acme/pki-acme-order-dal"; import { injectAuditLogInfo } from "../plugins/audit-log"; import { injectAssumePrivilege } from "../plugins/auth/inject-assume-privilege"; import { injectIdentity } from "../plugins/auth/inject-identity"; @@ -1170,6 +1170,10 @@ export const registerRoutes = async ( apiEnrollmentConfigDAL, estEnrollmentConfigDAL, acmeEnrollmentConfigDAL, + certificateBodyDAL, + certificateSecretDAL, + certificateAuthorityDAL, + certificateAuthorityCertDAL, permissionService, kmsService, projectDAL diff --git a/backend/src/server/routes/v1/certificate-profiles-router.ts b/backend/src/server/routes/v1/certificate-profiles-router.ts index 7d4e04773..ecda1186a 100644 --- a/backend/src/server/routes/v1/certificate-profiles-router.ts +++ b/backend/src/server/routes/v1/certificate-profiles-router.ts @@ -7,8 +7,8 @@ import { ApiDocsTags } from "@app/lib/api-docs"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; -import { EnrollmentType } from "@app/services/certificate-profile/certificate-profile-types"; import { CertStatus } from "@app/services/certificate/certificate-types"; +import { EnrollmentType } from "@app/services/certificate-profile/certificate-profile-types"; export const registerCertificateProfilesRouter = async (server: FastifyZodProvider) => { server.route({ @@ -498,6 +498,51 @@ export const registerCertificateProfilesRouter = async (server: FastifyZodProvid } }); + server.route({ + method: "GET", + url: "/:id/certificates/latest-active-bundle", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.PkiCertificateProfiles], + description: "Get latest active certificate bundle for a profile", + params: z.object({ + id: z.string().uuid() + }), + response: { + 200: z.object({ + certificate: z.string().nullable(), + certificateChain: z.string().nullable(), + privateKey: z.string().nullable(), + serialNumber: z.string().nullable() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const bundle = await server.services.certificateProfile.getLatestActiveCertificateBundle({ + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId, + profileId: req.params.id + }); + + if (!bundle) { + return { + certificate: null, + certificateChain: null, + privateKey: null, + serialNumber: null + }; + } + + return bundle; + } + }); + server.route({ method: "GET", url: "/:id/acme/eab-secret/reveal", diff --git a/backend/src/services/certificate-profile/certificate-profile-dal.ts b/backend/src/services/certificate-profile/certificate-profile-dal.ts index 5296cb172..6415145ce 100644 --- a/backend/src/services/certificate-profile/certificate-profile-dal.ts +++ b/backend/src/services/certificate-profile/certificate-profile-dal.ts @@ -462,6 +462,24 @@ export const certificateProfileDALFactory = (db: TDbClient) => { } }; + const getLatestActiveCertificateForProfile = async (profileId: string, tx?: Knex) => { + try { + const now = new Date(); + + const certificate = await (tx || db)(TableName.Certificate) + .where("profileId", profileId) + .where("status", "active") + .where("notAfter", ">", now) + .whereNull("revokedAt") + .orderBy("createdAt", "desc") + .first(); + + return certificate; + } catch (error) { + throw new DatabaseError({ error, name: "Get latest active certificate by profile" }); + } + }; + const isProfileInUse = async (profileId: string, tx?: Knex) => { try { const doc = await (tx || db)(TableName.Certificate).where("profileId", profileId).count("*").first(); @@ -485,6 +503,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => { countByProjectId, findByNameAndProjectId, getCertificatesByProfile, + getLatestActiveCertificateForProfile, isProfileInUse }; }; diff --git a/backend/src/services/certificate-profile/certificate-profile-service.ts b/backend/src/services/certificate-profile/certificate-profile-service.ts index 8adbf2c45..73daae8b3 100644 --- a/backend/src/services/certificate-profile/certificate-profile-service.ts +++ b/backend/src/services/certificate-profile/certificate-profile-service.ts @@ -4,17 +4,24 @@ import * as x509 from "@peculiar/x509"; import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { + ProjectPermissionCertificateActions, ProjectPermissionCertificateProfileActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { buildUrl } from "@app/ee/services/pki-acme/pki-acme-fns"; import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate"; import { getConfig } from "@app/lib/config/env"; import { crypto } from "@app/lib/crypto/cryptography"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { ActorAuthMethod, ActorType } from "../auth/auth-type"; +import { TCertificateBodyDALFactory } from "../certificate/certificate-body-dal"; +import { getCertificateCredentials, isCertChainValid } from "../certificate/certificate-fns"; +import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal"; +import { TCertificateAuthorityCertDALFactory } from "../certificate-authority/certificate-authority-cert-dal"; +import { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-dal"; import { TCertificateTemplateV2DALFactory } from "../certificate-template-v2/certificate-template-v2-dal"; -import { isCertChainValid } from "../certificate/certificate-fns"; +import { TAcmeEnrollmentConfigDALFactory } from "../enrollment-config/acme-enrollment-config-dal"; import { TApiEnrollmentConfigDALFactory } from "../enrollment-config/api-enrollment-config-dal"; import { TAcmeConfigData, TApiConfigData, TEstConfigData } from "../enrollment-config/enrollment-config-types"; import { TEstEnrollmentConfigDALFactory } from "../enrollment-config/est-enrollment-config-dal"; @@ -30,8 +37,6 @@ import { TCertificateProfileUpdate, TCertificateProfileWithConfigs } from "./certificate-profile-types"; -import { TAcmeEnrollmentConfigDALFactory } from "../enrollment-config/acme-enrollment-config-dal"; -import { buildUrl } from "@app/ee/services/pki-acme/pki-acme-fns"; const generateAndEncryptAcmeEabSecret = async ( projectId: string, @@ -142,6 +147,10 @@ type TCertificateProfileServiceFactoryDep = { apiEnrollmentConfigDAL: TApiEnrollmentConfigDALFactory; estEnrollmentConfigDAL: TEstEnrollmentConfigDALFactory; acmeEnrollmentConfigDAL: TAcmeEnrollmentConfigDALFactory; + certificateBodyDAL: Pick; + certificateSecretDAL: Pick; + certificateAuthorityDAL: Pick; + certificateAuthorityCertDAL: Pick; permissionService: Pick; kmsService: Pick; projectDAL: Pick; @@ -162,6 +171,8 @@ export const certificateProfileServiceFactory = ({ apiEnrollmentConfigDAL, estEnrollmentConfigDAL, acmeEnrollmentConfigDAL, + certificateBodyDAL, + certificateSecretDAL, permissionService, kmsService, projectDAL @@ -729,6 +740,106 @@ export const certificateProfileServiceFactory = ({ return certificates; }; + const getLatestActiveCertificateBundle = async ({ + actor, + actorId, + actorAuthMethod, + actorOrgId, + profileId + }: { + actor: ActorType; + actorId: string; + actorAuthMethod: ActorAuthMethod; + actorOrgId: string; + profileId: string; + }) => { + const profile = await certificateProfileDAL.findById(profileId); + if (!profile) { + throw new NotFoundError({ message: "Certificate profile not found" }); + } + + const { permission } = await permissionService.getProjectPermission({ + actor, + actorId, + projectId: profile.projectId, + actorAuthMethod, + actorOrgId, + actionProjectType: ActionProjectType.CertificateManager + }); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateProfileActions.Read, + ProjectPermissionSub.CertificateProfiles + ); + + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateActions.Read, + ProjectPermissionSub.Certificates + ); + + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateActions.ReadPrivateKey, + ProjectPermissionSub.Certificates + ); + + const cert = await certificateProfileDAL.getLatestActiveCertificateForProfile(profileId); + + if (!cert) { + return null; + } + + const certBody = await certificateBodyDAL.findOne({ certId: cert.id }); + + const certificateManagerKeyId = await getProjectKmsCertificateKeyId({ + projectId: cert.projectId, + projectDAL, + kmsService + }); + + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: certificateManagerKeyId + }); + const decryptedCert = await kmsDecryptor({ + cipherTextBlob: certBody.encryptedCertificate + }); + + // @ts-expect-error - Buffer type is compatible at runtime despite TypeScript error + const certObj = new x509.X509Certificate(decryptedCert); + const certificate = certObj.toString("pem"); + + const decryptedCertChain = await kmsDecryptor({ + cipherTextBlob: certBody.encryptedCertificateChain! + }); + + const certificateChain = decryptedCertChain.toString(); + + let privateKey = null; + try { + const { certPrivateKey } = await getCertificateCredentials({ + certId: cert.id, + projectId: cert.projectId, + certificateSecretDAL, + projectDAL, + kmsService + }); + privateKey = certPrivateKey; + } catch (error) { + // Private key might not exist for ACME certificates or other external workflows + // where the key is generated client-side + if (error instanceof NotFoundError) { + privateKey = null; + } else { + throw error; + } + } + + return { + certificate, + certificateChain, + privateKey, + serialNumber: cert.serialNumber + }; + }; + const getEstConfigurationByProfile = async ( params: | { @@ -854,6 +965,7 @@ export const certificateProfileServiceFactory = ({ listProfiles, deleteProfile, getProfileCertificates, + getLatestActiveCertificateBundle, getEstConfigurationByProfile, revealAcmeEabSecret }; From 7d65ca7a9c7241c46cfc25d418bc5c45d8cd5f31 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Sat, 8 Nov 2025 03:26:33 +0800 Subject: [PATCH 2/6] misc: fixed tests --- .../certificate-profile-service.test.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/backend/src/services/certificate-profile/certificate-profile-service.test.ts b/backend/src/services/certificate-profile/certificate-profile-service.test.ts index ffca3c1cb..9d9ab5947 100644 --- a/backend/src/services/certificate-profile/certificate-profile-service.test.ts +++ b/backend/src/services/certificate-profile/certificate-profile-service.test.ts @@ -9,6 +9,10 @@ import type { TPermissionServiceFactory } from "@app/ee/services/permission/perm import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { ActorType, AuthMethod } from "../auth/auth-type"; +import type { TCertificateBodyDALFactory } from "../certificate/certificate-body-dal"; +import type { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal"; +import type { TCertificateAuthorityCertDALFactory } from "../certificate-authority/certificate-authority-cert-dal"; +import type { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-dal"; import type { TCertificateTemplateV2DALFactory } from "../certificate-template-v2/certificate-template-v2-dal"; import { TAcmeEnrollmentConfigDALFactory } from "../enrollment-config/acme-enrollment-config-dal"; import type { TApiEnrollmentConfigDALFactory } from "../enrollment-config/api-enrollment-config-dal"; @@ -178,6 +182,54 @@ describe("CertificateProfileService", () => { transaction: vi.fn() } as unknown as Pick; + const mockCertificateBodyDAL = { + create: vi.fn(), + findById: vi.fn(), + updateById: vi.fn(), + deleteById: vi.fn(), + transaction: vi.fn(), + find: vi.fn(), + findOne: vi.fn(), + update: vi.fn(), + delete: vi.fn() + } as unknown as TCertificateBodyDALFactory; + + const mockCertificateSecretDAL = { + create: vi.fn(), + findById: vi.fn(), + updateById: vi.fn(), + deleteById: vi.fn(), + transaction: vi.fn(), + find: vi.fn(), + findOne: vi.fn(), + update: vi.fn(), + delete: vi.fn() + } as unknown as TCertificateSecretDALFactory; + + const mockCertificateAuthorityDAL = { + create: vi.fn(), + findById: vi.fn(), + updateById: vi.fn(), + deleteById: vi.fn(), + transaction: vi.fn(), + find: vi.fn(), + findOne: vi.fn(), + update: vi.fn(), + delete: vi.fn() + } as unknown as TCertificateAuthorityDALFactory; + + const mockCertificateAuthorityCertDAL = { + create: vi.fn(), + findById: vi.fn(), + updateById: vi.fn(), + deleteById: vi.fn(), + transaction: vi.fn(), + find: vi.fn(), + findOne: vi.fn(), + update: vi.fn(), + delete: vi.fn() + } as unknown as TCertificateAuthorityCertDALFactory; + beforeEach(() => { vi.spyOn(ForbiddenError, "from").mockReturnValue({ throwUnlessCan: vi.fn() @@ -195,6 +247,10 @@ describe("CertificateProfileService", () => { apiEnrollmentConfigDAL: mockApiEnrollmentConfigDAL, estEnrollmentConfigDAL: mockEstEnrollmentConfigDAL, acmeEnrollmentConfigDAL: mockAcmeEnrollmentConfigDAL, + certificateBodyDAL: mockCertificateBodyDAL, + certificateSecretDAL: mockCertificateSecretDAL, + certificateAuthorityDAL: mockCertificateAuthorityDAL, + certificateAuthorityCertDAL: mockCertificateAuthorityCertDAL, permissionService: mockPermissionService, kmsService: mockKmsService, projectDAL: mockProjectDAL From efc1f2682e3e6a48c968b0f674123eeb069fb541 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Sat, 8 Nov 2025 03:47:50 +0800 Subject: [PATCH 3/6] mics: removed unused eslint directive --- .../services/certificate-profile/certificate-profile-service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/services/certificate-profile/certificate-profile-service.ts b/backend/src/services/certificate-profile/certificate-profile-service.ts index 73daae8b3..d5e34eec1 100644 --- a/backend/src/services/certificate-profile/certificate-profile-service.ts +++ b/backend/src/services/certificate-profile/certificate-profile-service.ts @@ -802,7 +802,6 @@ export const certificateProfileServiceFactory = ({ cipherTextBlob: certBody.encryptedCertificate }); - // @ts-expect-error - Buffer type is compatible at runtime despite TypeScript error const certObj = new x509.X509Certificate(decryptedCert); const certificate = certObj.toString("pem"); From eb2553270bd21741ccd7697b2abf7e3e35e75d40 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Mon, 10 Nov 2025 21:23:02 +0800 Subject: [PATCH 4/6] misc: added audit logs --- .../ee/services/audit-log/audit-log-types.ts | 13 ++++++++++ .../routes/v1/certificate-profiles-router.ts | 26 ++++++++++++++++--- .../certificate-profile-service.ts | 3 ++- .../src/hooks/api/auditLogs/constants.tsx | 13 +++++++++- frontend/src/hooks/api/auditLogs/enums.tsx | 12 ++++++++- 5 files changed, 61 insertions(+), 6 deletions(-) diff --git a/backend/src/ee/services/audit-log/audit-log-types.ts b/backend/src/ee/services/audit-log/audit-log-types.ts index 0b45544e3..bcc2a0770 100644 --- a/backend/src/ee/services/audit-log/audit-log-types.ts +++ b/backend/src/ee/services/audit-log/audit-log-types.ts @@ -371,6 +371,7 @@ export enum EventType { SIGN_CERTIFICATE_FROM_PROFILE = "sign-certificate-from-profile", ORDER_CERTIFICATE_FROM_PROFILE = "order-certificate-from-profile", RENEW_CERTIFICATE = "renew-certificate", + GET_CERTIFICATE_PROFILE_LATEST_ACTIVE_BUNDLE = "get-certificate-profile-latest-active-bundle", UPDATE_CERTIFICATE_RENEWAL_CONFIG = "update-certificate-renewal-config", DISABLE_CERTIFICATE_RENEWAL_CONFIG = "disable-certificate-renewal-config", ATTEMPT_CREATE_SLACK_INTEGRATION = "attempt-create-slack-integration", @@ -2752,6 +2753,17 @@ interface OrderCertificateFromProfile { }; } +interface GetCertificateProfileLatestActiveBundle { + type: EventType.GET_CERTIFICATE_PROFILE_LATEST_ACTIVE_BUNDLE; + metadata: { + certificateProfileId: string; + certificateId: string; + commonName: string; + profileName: string; + serialNumber: string; + }; +} + interface RenewCertificate { type: EventType.RENEW_CERTIFICATE; metadata: { @@ -4282,6 +4294,7 @@ export type Event = | DeleteCertificateProfile | GetCertificateProfile | ListCertificateProfiles + | GetCertificateProfileLatestActiveBundle | IssueCertificateFromProfile | SignCertificateFromProfile | OrderCertificateFromProfile diff --git a/backend/src/server/routes/v1/certificate-profiles-router.ts b/backend/src/server/routes/v1/certificate-profiles-router.ts index ecda1186a..5792c5e83 100644 --- a/backend/src/server/routes/v1/certificate-profiles-router.ts +++ b/backend/src/server/routes/v1/certificate-profiles-router.ts @@ -522,7 +522,7 @@ export const registerCertificateProfilesRouter = async (server: FastifyZodProvid }, onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { - const bundle = await server.services.certificateProfile.getLatestActiveCertificateBundle({ + const response = await server.services.certificateProfile.getLatestActiveCertificateBundle({ actor: req.permission.type, actorId: req.permission.id, actorAuthMethod: req.permission.authMethod, @@ -530,7 +530,7 @@ export const registerCertificateProfilesRouter = async (server: FastifyZodProvid profileId: req.params.id }); - if (!bundle) { + if (!response) { return { certificate: null, certificateChain: null, @@ -539,7 +539,27 @@ export const registerCertificateProfilesRouter = async (server: FastifyZodProvid }; } - return bundle; + await server.services.auditLog.createAuditLog({ + ...req.auditLogInfo, + projectId: response.certObj.projectId, + event: { + type: EventType.GET_CERTIFICATE_PROFILE_LATEST_ACTIVE_BUNDLE, + metadata: { + certificateProfileId: response.profile.id, + certificateId: response.certObj.id, + commonName: response.certObj.commonName, + profileName: response.profile.slug, + serialNumber: response.certObj.serialNumber + } + } + }); + + return { + certificate: response.certificate, + certificateChain: response.certificateChain, + privateKey: response.privateKey, + serialNumber: response.certObj.serialNumber + }; } }); diff --git a/backend/src/services/certificate-profile/certificate-profile-service.ts b/backend/src/services/certificate-profile/certificate-profile-service.ts index d5e34eec1..66a23a0e7 100644 --- a/backend/src/services/certificate-profile/certificate-profile-service.ts +++ b/backend/src/services/certificate-profile/certificate-profile-service.ts @@ -835,7 +835,8 @@ export const certificateProfileServiceFactory = ({ certificate, certificateChain, privateKey, - serialNumber: cert.serialNumber + profile, + certObj: cert }; }; diff --git a/frontend/src/hooks/api/auditLogs/constants.tsx b/frontend/src/hooks/api/auditLogs/constants.tsx index 33bc2107d..b465474e8 100644 --- a/frontend/src/hooks/api/auditLogs/constants.tsx +++ b/frontend/src/hooks/api/auditLogs/constants.tsx @@ -268,7 +268,18 @@ export const eventToNameMap: { [K in EventType]: string } = { [EventType.PAM_RESOURCE_GET]: "PAM Resource Get", [EventType.PAM_RESOURCE_CREATE]: "PAM Resource Create", [EventType.PAM_RESOURCE_UPDATE]: "PAM Resource Update", - [EventType.PAM_RESOURCE_DELETE]: "PAM Resource Delete" + [EventType.PAM_RESOURCE_DELETE]: "PAM Resource Delete", + + [EventType.CREATE_CERTIFICATE_PROFILE]: "Create Certificate Profile", + [EventType.UPDATE_CERTIFICATE_PROFILE]: "Update Certificate Profile", + [EventType.DELETE_CERTIFICATE_PROFILE]: "Delete Certificate Profile", + [EventType.GET_CERTIFICATE_PROFILE]: "Get Certificate Profile", + [EventType.LIST_CERTIFICATE_PROFILES]: "List Certificate Profiles", + [EventType.ISSUE_CERTIFICATE_FROM_PROFILE]: "Issue Certificate From Profile", + [EventType.SIGN_CERTIFICATE_FROM_PROFILE]: "Sign Certificate From Profile", + [EventType.ORDER_CERTIFICATE_FROM_PROFILE]: "Order Certificate From Profile", + [EventType.GET_CERTIFICATE_PROFILE_LATEST_ACTIVE_BUNDLE]: + "Get Certificate Profile Latest Active Bundle" }; export const userAgentTypeToNameMap: { [K in UserAgentType]: string } = { diff --git a/frontend/src/hooks/api/auditLogs/enums.tsx b/frontend/src/hooks/api/auditLogs/enums.tsx index ea684a4bb..995d22624 100644 --- a/frontend/src/hooks/api/auditLogs/enums.tsx +++ b/frontend/src/hooks/api/auditLogs/enums.tsx @@ -260,5 +260,15 @@ export enum EventType { PAM_RESOURCE_GET = "pam-resource-get", PAM_RESOURCE_CREATE = "pam-resource-create", PAM_RESOURCE_UPDATE = "pam-resource-update", - PAM_RESOURCE_DELETE = "pam-resource-delete" + PAM_RESOURCE_DELETE = "pam-resource-delete", + + CREATE_CERTIFICATE_PROFILE = "create-certificate-profile", + UPDATE_CERTIFICATE_PROFILE = "update-certificate-profile", + DELETE_CERTIFICATE_PROFILE = "delete-certificate-profile", + GET_CERTIFICATE_PROFILE = "get-certificate-profile", + LIST_CERTIFICATE_PROFILES = "list-certificate-profiles", + ISSUE_CERTIFICATE_FROM_PROFILE = "issue-certificate-from-profile", + SIGN_CERTIFICATE_FROM_PROFILE = "sign-certificate-from-profile", + ORDER_CERTIFICATE_FROM_PROFILE = "order-certificate-from-profile", + GET_CERTIFICATE_PROFILE_LATEST_ACTIVE_BUNDLE = "get-certificate-profile-latest-active-bundle" } From a336cfa0c50669f6fe9d2a0be6c5b7c15875598b Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Mon, 10 Nov 2025 21:44:25 +0800 Subject: [PATCH 5/6] misc: updated certificates v3 endpoint to be under /pki --- backend/src/server/routes/v3/index.ts | 2 +- backend/src/services/certificate-v3/certificate-v3-service.ts | 2 +- .../endpoints/certificate-authorities/issue-cert.mdx | 4 ---- .../endpoints/certificate-authorities/sign-cert.mdx | 4 ---- .../endpoints/certificates/issue-certificate.mdx | 2 +- docs/api-reference/endpoints/certificates/renew.mdx | 2 +- .../api-reference/endpoints/certificates/sign-certificate.mdx | 2 +- docs/api-reference/endpoints/certificates/update-config.mdx | 2 +- docs/docs.json | 2 -- docs/documentation/platform/pki/enrollment-methods/api.mdx | 4 ++-- frontend/src/hooks/api/ca/mutations.tsx | 4 ++-- frontend/src/hooks/api/certificates/mutations.tsx | 4 ++-- 12 files changed, 12 insertions(+), 22 deletions(-) delete mode 100644 docs/api-reference/endpoints/certificate-authorities/issue-cert.mdx delete mode 100644 docs/api-reference/endpoints/certificate-authorities/sign-cert.mdx diff --git a/backend/src/server/routes/v3/index.ts b/backend/src/server/routes/v3/index.ts index 47c3c2cb8..4ee4566c1 100644 --- a/backend/src/server/routes/v3/index.ts +++ b/backend/src/server/routes/v3/index.ts @@ -11,5 +11,5 @@ export const registerV3Routes = async (server: FastifyZodProvider) => { await server.register(registerUserRouter, { prefix: "/users" }); await server.register(registerDeprecatedSecretRouter, { prefix: "/secrets" }); await server.register(registerExternalMigrationRouter, { prefix: "/external-migration" }); - await server.register(registerCertificatesRouter, { prefix: "/certificates" }); + await server.register(registerCertificatesRouter, { prefix: "/pki/certificates" }); }; diff --git a/backend/src/services/certificate-v3/certificate-v3-service.ts b/backend/src/services/certificate-v3/certificate-v3-service.ts index 7f4c6c637..6e505f338 100644 --- a/backend/src/services/certificate-v3/certificate-v3-service.ts +++ b/backend/src/services/certificate-v3/certificate-v3-service.ts @@ -675,7 +675,7 @@ export const certificateV3ServiceFactory = ({ status: CertificateOrderStatus.VALID })), authorizations: [], - finalize: `/api/v3/certificates/orders/${orderId}/completed`, + finalize: `/api/v3/pki/certificates/orders/${orderId}/completed`, certificate: certificateResult.certificate, projectId: certificateResult.projectId, profileName: certificateResult.profileName diff --git a/docs/api-reference/endpoints/certificate-authorities/issue-cert.mdx b/docs/api-reference/endpoints/certificate-authorities/issue-cert.mdx deleted file mode 100644 index 045cada58..000000000 --- a/docs/api-reference/endpoints/certificate-authorities/issue-cert.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Issue certificate" -openapi: "POST /api/v1/pki/ca/{caId}/issue-certificate" ---- diff --git a/docs/api-reference/endpoints/certificate-authorities/sign-cert.mdx b/docs/api-reference/endpoints/certificate-authorities/sign-cert.mdx deleted file mode 100644 index 95c8d8c65..000000000 --- a/docs/api-reference/endpoints/certificate-authorities/sign-cert.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Sign certificate" -openapi: "POST /api/v1/pki/ca/{caId}/sign-certificate" ---- diff --git a/docs/api-reference/endpoints/certificates/issue-certificate.mdx b/docs/api-reference/endpoints/certificates/issue-certificate.mdx index 90a79a4af..13a464b67 100644 --- a/docs/api-reference/endpoints/certificates/issue-certificate.mdx +++ b/docs/api-reference/endpoints/certificates/issue-certificate.mdx @@ -1,4 +1,4 @@ --- title: "Issue Certificate" -openapi: "POST /api/v1/pki/certificates/issue-certificate" +openapi: "POST /api/v3/pki/certificates/issue-certificate" --- diff --git a/docs/api-reference/endpoints/certificates/renew.mdx b/docs/api-reference/endpoints/certificates/renew.mdx index 3c8e03498..b44424369 100644 --- a/docs/api-reference/endpoints/certificates/renew.mdx +++ b/docs/api-reference/endpoints/certificates/renew.mdx @@ -1,4 +1,4 @@ --- title: "Renew Certificate" -openapi: "POST /api/v3/certificates/{certificateId}/renew" +openapi: "POST /api/v3/pki/certificates/{certificateId}/renew" --- \ No newline at end of file diff --git a/docs/api-reference/endpoints/certificates/sign-certificate.mdx b/docs/api-reference/endpoints/certificates/sign-certificate.mdx index 3132d5846..7291025fc 100644 --- a/docs/api-reference/endpoints/certificates/sign-certificate.mdx +++ b/docs/api-reference/endpoints/certificates/sign-certificate.mdx @@ -1,4 +1,4 @@ --- title: "Sign Certificate" -openapi: "POST /api/v1/pki/certificates/sign-certificate" +openapi: "POST /api/v3/pki/certificates/sign-certificate" --- diff --git a/docs/api-reference/endpoints/certificates/update-config.mdx b/docs/api-reference/endpoints/certificates/update-config.mdx index 1d92a0407..70520bf68 100644 --- a/docs/api-reference/endpoints/certificates/update-config.mdx +++ b/docs/api-reference/endpoints/certificates/update-config.mdx @@ -1,4 +1,4 @@ --- title: "Update Certificate Config" -openapi: "PATCH /api/v3/certificates/{certificateId}/config" +openapi: "PATCH /api/v3/pki/certificates/{certificateId}/config" --- \ No newline at end of file diff --git a/docs/docs.json b/docs/docs.json index dc96c9976..dd74fb670 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -2585,8 +2585,6 @@ "api-reference/endpoints/certificate-authorities/cert", "api-reference/endpoints/certificate-authorities/sign-intermediate", "api-reference/endpoints/certificate-authorities/import-cert", - "api-reference/endpoints/certificate-authorities/issue-cert", - "api-reference/endpoints/certificate-authorities/sign-cert", "api-reference/endpoints/certificate-authorities/crl" ] }, diff --git a/docs/documentation/platform/pki/enrollment-methods/api.mdx b/docs/documentation/platform/pki/enrollment-methods/api.mdx index cd657d7d4..dac7b6386 100644 --- a/docs/documentation/platform/pki/enrollment-methods/api.mdx +++ b/docs/documentation/platform/pki/enrollment-methods/api.mdx @@ -105,7 +105,7 @@ Here, select the certificate profile from step 1 that will be used to issue the ### Sample request ```bash Request - curl --location --request POST 'https://app.infisical.com/api/v3/certificates/issue-certificate' \ + curl --location --request POST 'https://app.infisical.com/api/v3/pki/certificates/issue-certificate' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data-raw '{ @@ -151,7 +151,7 @@ Here, select the certificate profile from step 1 that will be used to issue the ### Sample request ```bash Request - curl --location --request POST 'https://app.infisical.com/api/v3/certificates/sign-certificate' \ + curl --location --request POST 'https://app.infisical.com/api/v3/pki/certificates/sign-certificate' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data-raw '{ diff --git a/frontend/src/hooks/api/ca/mutations.tsx b/frontend/src/hooks/api/ca/mutations.tsx index 49a53dce7..e19069984 100644 --- a/frontend/src/hooks/api/ca/mutations.tsx +++ b/frontend/src/hooks/api/ca/mutations.tsx @@ -157,7 +157,7 @@ export const useCreateCertificateV3 = (options?: { projectId?: string }) => { return useMutation({ mutationFn: async (body) => { const { data } = await apiRequest.post( - "/api/v3/certificates/issue-certificate", + "/api/v3/pki/certificates/issue-certificate", body ); return data; @@ -185,7 +185,7 @@ export const useOrderCertificateWithProfile = () => { return useMutation({ mutationFn: async (body) => { const { data } = await apiRequest.post( - "/api/v3/certificates/order-certificate", + "/api/v3/pki/certificates/order-certificate", body ); return data; diff --git a/frontend/src/hooks/api/certificates/mutations.tsx b/frontend/src/hooks/api/certificates/mutations.tsx index eed1d9e5f..2ee470551 100644 --- a/frontend/src/hooks/api/certificates/mutations.tsx +++ b/frontend/src/hooks/api/certificates/mutations.tsx @@ -86,7 +86,7 @@ export const useRenewCertificate = () => { return useMutation({ mutationFn: async ({ certificateId }) => { const { data } = await apiRequest.post( - `/api/v3/certificates/${certificateId}/renew`, + `/api/v3/pki/certificates/${certificateId}/renew`, {} ); return data; @@ -119,7 +119,7 @@ export const useUpdateRenewalConfig = () => { >({ mutationFn: async ({ certificateId, renewBeforeDays, enableAutoRenewal }) => { const { data } = await apiRequest.patch<{ message: string; renewBeforeDays?: number }>( - `/api/v3/certificates/${certificateId}/config`, + `/api/v3/pki/certificates/${certificateId}/config`, { renewBeforeDays, enableAutoRenewal } ); return data; From 6a7a413fd322d5ee07c9f1e41f22d98d576a2d2c Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Mon, 10 Nov 2025 22:02:14 +0800 Subject: [PATCH 6/6] misc: added get latest active cert bundle to docs --- .../certificate-profiles/get-latest-active-bundle.mdx | 4 ++++ docs/docs.json | 3 ++- .../platform/pki/certificates/certificates.mdx | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 docs/api-reference/endpoints/certificate-profiles/get-latest-active-bundle.mdx diff --git a/docs/api-reference/endpoints/certificate-profiles/get-latest-active-bundle.mdx b/docs/api-reference/endpoints/certificate-profiles/get-latest-active-bundle.mdx new file mode 100644 index 000000000..aa033418d --- /dev/null +++ b/docs/api-reference/endpoints/certificate-profiles/get-latest-active-bundle.mdx @@ -0,0 +1,4 @@ +--- +title: "Get Latest Active Certificate Bundle" +openapi: "GET /api/v1/pki/certificate-profiles/{id}/certificates/latest-active-bundle" +--- \ No newline at end of file diff --git a/docs/docs.json b/docs/docs.json index dd74fb670..8cce3197d 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -2644,7 +2644,8 @@ "api-reference/endpoints/certificate-profiles/get-by-id", "api-reference/endpoints/certificate-profiles/get-by-slug", "api-reference/endpoints/certificate-profiles/delete", - "api-reference/endpoints/certificate-profiles/list-certificates" + "api-reference/endpoints/certificate-profiles/list-certificates", + "api-reference/endpoints/certificate-profiles/get-latest-active-bundle" ] }, { diff --git a/docs/documentation/platform/pki/certificates/certificates.mdx b/docs/documentation/platform/pki/certificates/certificates.mdx index bc3698e6c..b30a53091 100644 --- a/docs/documentation/platform/pki/certificates/certificates.mdx +++ b/docs/documentation/platform/pki/certificates/certificates.mdx @@ -48,6 +48,12 @@ The resulting renewed certificate is stored in the platform and made available t Note that server-driven certificate renewal is only available for certificates issued via the [API enrollment method](/documentation/platform/pki/enrollment-methods/api) where key pairs are generated server-side. A certificate can be considered for auto-renewal at time of issuance if the **Enable Auto-Renewal By Default** option is selected on its [certificate profile](/documentation/platform/pki/certificates/profiles) or after issuance by toggling this option manually. + + For server-driven certificate renewal workflows, you can programmatically fetch the latest active certificate bundle for a certificate profile using the [Get Latest Active Certificate Bundle](/api-reference/endpoints/certificate-profiles/get-latest-active-bundle) API endpoint. + + This ensures you always retrieve the most current valid certificate, including any that have been automatically renewed, making it particularly useful for deployment pipelines and automation workflows where you don't want to track individual serial numbers. + + The following examples demonstrate different approaches to certificate renewal: - Using the ACME enrollment method, you may connect an ACME client like [certbot](https://certbot.eff.org/) to fetch back and renew certificates for Apache, Nginx, or other server. The ACME client will pursue a client-driven approach and submit certificate requests upon certificate expiration for you, saving renewed certificates back to the server's configuration.