mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Issue cert
This commit is contained in:
@@ -63,10 +63,12 @@ import {
|
||||
TRawJwsPayload,
|
||||
TRespondToAcmeChallengeResponse
|
||||
} from "./pki-acme-types";
|
||||
import { TCertificateV3ServiceFactory } from "@app/services/certificate-v3/certificate-v3-service";
|
||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
type TPkiAcmeServiceFactoryDep = {
|
||||
certificateProfileDAL: Pick<TCertificateProfileDALFactory, "findById">;
|
||||
internalCertificateAuthorityService: Pick<TInternalCertificateAuthorityServiceFactory, "signCertFromCa">;
|
||||
certificateProfileDAL: Pick<TCertificateProfileDALFactory, "findByIdWithOwnerOrgId">;
|
||||
certificateV3Service: Pick<TCertificateV3ServiceFactory, "signCertificateFromProfile">;
|
||||
acmeAccountDAL: Pick<
|
||||
TPkiAcmeAccountDALFactory,
|
||||
"findByProjectIdAndAccountId" | "findByProfileIdAndPublicKeyThumbprintAndAlg" | "create"
|
||||
@@ -86,7 +88,7 @@ type TPkiAcmeServiceFactoryDep = {
|
||||
|
||||
export const pkiAcmeServiceFactory = ({
|
||||
certificateProfileDAL,
|
||||
internalCertificateAuthorityService,
|
||||
certificateV3Service,
|
||||
acmeAccountDAL,
|
||||
acmeOrderDAL,
|
||||
acmeAuthDAL,
|
||||
@@ -507,7 +509,8 @@ export const pkiAcmeServiceFactory = ({
|
||||
if (order.status === AcmeOrderStatus.Ready) {
|
||||
order = await acmeOrderDAL.transaction(async (tx) => {
|
||||
const order = (await acmeOrderDAL.findByIdForFinalization(orderId, tx))!;
|
||||
const profile = (await certificateProfileDAL.findById(profileId, tx))!;
|
||||
// TODO: ideally, this should be doen with onRequest: verifyAuth([AuthMode.ACME_JWS_SIGNATURE]), instead
|
||||
const { ownerOrgId: actorOrgId } = (await certificateProfileDAL.findByIdWithOwnerOrgId(profileId, tx))!;
|
||||
if (order.status !== AcmeOrderStatus.Ready) {
|
||||
throw new AcmeOrderNotReadyError({ message: "ACME order is not ready" });
|
||||
}
|
||||
@@ -516,20 +519,30 @@ export const pkiAcmeServiceFactory = ({
|
||||
}
|
||||
const { csr } = payload;
|
||||
// TODO: validate the CSR and return badCSR error if it's invalid
|
||||
const { certificate, certificateChain } = await internalCertificateAuthorityService.signCertFromCa({
|
||||
isInternal: true,
|
||||
certificateTemplateId: profile.certificateTemplateId,
|
||||
// TODO: this should be the same transaction?
|
||||
const { certificate, certificateChain, certificateId } = await certificateV3Service.signCertificateFromProfile({
|
||||
actor: ActorType.ACME_ACCOUNT,
|
||||
actorId: accountId,
|
||||
actorAuthMethod: null,
|
||||
actorOrgId,
|
||||
profileId,
|
||||
csr,
|
||||
notBefore: order.notBefore?.toISOString(),
|
||||
notAfter: order.notAfter?.toISOString()
|
||||
notBefore: order.notBefore ? new Date(order.notBefore) : undefined,
|
||||
notAfter: order.notAfter ? new Date(order.notAfter) : undefined,
|
||||
validity: {
|
||||
// TODO: read config from the profile to get the expiration time instead
|
||||
ttl: (24 * 60 * 60 * 1000).toString()
|
||||
},
|
||||
enrollmentType: EnrollmentType.ACME
|
||||
});
|
||||
// TODO: associate the certificate with the order
|
||||
await acmeOrderDAL.updateById(
|
||||
orderId,
|
||||
{
|
||||
status: AcmeOrderStatus.Valid,
|
||||
csr,
|
||||
certificateChain,
|
||||
certificate: certificate.toString("pem")
|
||||
certificate
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
@@ -1175,20 +1175,6 @@ export const registerRoutes = async (
|
||||
projectDAL
|
||||
});
|
||||
|
||||
const acmeChallengeService = pkiAcmeChallengeServiceFactory({
|
||||
acmeAuthDAL,
|
||||
acmeChallengeDAL
|
||||
});
|
||||
const pkiAcmeService = pkiAcmeServiceFactory({
|
||||
certificateProfileDAL,
|
||||
acmeAccountDAL,
|
||||
acmeOrderDAL,
|
||||
acmeAuthDAL,
|
||||
acmeOrderAuthDAL,
|
||||
acmeChallengeDAL,
|
||||
acmeChallengeService
|
||||
});
|
||||
|
||||
const pkiAlertService = pkiAlertServiceFactory({
|
||||
pkiAlertDAL,
|
||||
pkiCollectionDAL,
|
||||
@@ -2209,6 +2195,20 @@ export const registerRoutes = async (
|
||||
estEnrollmentConfigDAL
|
||||
});
|
||||
|
||||
const acmeChallengeService = pkiAcmeChallengeServiceFactory({
|
||||
acmeChallengeDAL
|
||||
});
|
||||
const pkiAcmeService = pkiAcmeServiceFactory({
|
||||
certificateV3Service,
|
||||
certificateProfileDAL,
|
||||
acmeAccountDAL,
|
||||
acmeOrderDAL,
|
||||
acmeAuthDAL,
|
||||
acmeOrderAuthDAL,
|
||||
acmeChallengeDAL,
|
||||
acmeChallengeService
|
||||
});
|
||||
|
||||
const pkiSubscriberService = pkiSubscriberServiceFactory({
|
||||
pkiSubscriberDAL,
|
||||
certificateAuthorityDAL,
|
||||
|
||||
@@ -6,12 +6,6 @@ import { ms } from "@app/lib/ms";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import {
|
||||
ACMESANType,
|
||||
CertificateOrderStatus,
|
||||
CertKeyAlgorithm,
|
||||
CertSignatureAlgorithm
|
||||
} from "@app/services/certificate/certificate-types";
|
||||
import { validateCaDateField } from "@app/services/certificate-authority/certificate-authority-validators";
|
||||
import {
|
||||
CertExtendedKeyUsageType,
|
||||
@@ -20,7 +14,14 @@ import {
|
||||
} from "@app/services/certificate-common/certificate-constants";
|
||||
import { extractCertificateRequestFromCSR } from "@app/services/certificate-common/certificate-csr-utils";
|
||||
import { mapEnumsForValidation } from "@app/services/certificate-common/certificate-utils";
|
||||
import { EnrollmentType } from "@app/services/certificate-profile/certificate-profile-types";
|
||||
import { validateTemplateRegexField } from "@app/services/certificate-template/certificate-template-validators";
|
||||
import {
|
||||
ACMESANType,
|
||||
CertificateOrderStatus,
|
||||
CertKeyAlgorithm,
|
||||
CertSignatureAlgorithm
|
||||
} from "@app/services/certificate/certificate-types";
|
||||
|
||||
interface CertificateRequestForService {
|
||||
commonName?: string;
|
||||
@@ -204,7 +205,8 @@ export const registerCertificatesRouter = async (server: FastifyZodProvider) =>
|
||||
ttl: req.body.ttl
|
||||
},
|
||||
notBefore: req.body.notBefore ? new Date(req.body.notBefore) : undefined,
|
||||
notAfter: req.body.notAfter ? new Date(req.body.notAfter) : undefined
|
||||
notAfter: req.body.notAfter ? new Date(req.body.notAfter) : undefined,
|
||||
enrollmentType: EnrollmentType.API
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
||||
@@ -41,6 +41,7 @@ export enum ActorType { // would extend to AWS, Azure, ...
|
||||
IDENTITY = "identity",
|
||||
Machine = "machine",
|
||||
SCIM_CLIENT = "scimClient",
|
||||
ACME_ACCOUNT = "acmeAccount",
|
||||
UNKNOWN_USER = "unknownUser"
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,23 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findByIdWithOwnerOrgId = async (
|
||||
id: string,
|
||||
tx?: Knex
|
||||
): Promise<(TCertificateProfile & { ownerOrgId: string }) | undefined> => {
|
||||
try {
|
||||
const certificateProfile = (await (tx || db)(TableName.PkiCertificateProfile)
|
||||
.join(TableName.Project, `${TableName.PkiCertificateProfile}.projectId`, `${TableName.Project}.id`)
|
||||
.select(selectAllTableCols(TableName.PkiCertificateProfile))
|
||||
.select(db.ref("orgId").withSchema(TableName.Project).as("ownerOrgId"))
|
||||
.where({ id })
|
||||
.first()) as (TCertificateProfile & { ownerOrgId: string }) | undefined;
|
||||
return certificateProfile;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find certificate profile by id with owner org id" });
|
||||
}
|
||||
};
|
||||
|
||||
const findByIdWithConfigs = async (id: string, tx?: Knex): Promise<TCertificateProfileWithConfigs | undefined> => {
|
||||
try {
|
||||
const query = (tx || db)(TableName.PkiCertificateProfile)
|
||||
@@ -444,6 +461,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
|
||||
updateById,
|
||||
deleteById,
|
||||
findById,
|
||||
findByIdWithOwnerOrgId,
|
||||
findByIdWithConfigs,
|
||||
findBySlugAndProjectId,
|
||||
findByProjectId,
|
||||
|
||||
@@ -697,6 +697,7 @@ describe("CertificateV3Service", () => {
|
||||
profileId,
|
||||
csr: mockCSR,
|
||||
validity: mockValidity,
|
||||
enrollmentType: EnrollmentType.API,
|
||||
...mockActor
|
||||
});
|
||||
|
||||
@@ -731,6 +732,7 @@ describe("CertificateV3Service", () => {
|
||||
profileId,
|
||||
csr: mockCSR,
|
||||
validity: mockValidity,
|
||||
enrollmentType: EnrollmentType.API,
|
||||
...mockActor
|
||||
})
|
||||
).rejects.toThrow(ForbiddenRequestError);
|
||||
@@ -740,6 +742,7 @@ describe("CertificateV3Service", () => {
|
||||
profileId,
|
||||
csr: mockCSR,
|
||||
validity: mockValidity,
|
||||
enrollmentType: EnrollmentType.API,
|
||||
...mockActor
|
||||
})
|
||||
).rejects.toThrow("Profile is not configured for api enrollment");
|
||||
|
||||
@@ -107,6 +107,13 @@ const validateProfileAndPermissions = async (
|
||||
});
|
||||
}
|
||||
|
||||
// XXX: NOT SURE IF THIS IS SECURE TO BY PASS THE PERMISSION CHECK FOR ACME ACCOUNTS
|
||||
// may need to consider this carefully
|
||||
// TODO: check actor/profile ownership as well
|
||||
if (actor === ActorType.ACME_ACCOUNT && requiredEnrollmentType === EnrollmentType.ACME) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
@@ -484,7 +491,8 @@ export const certificateV3ServiceFactory = ({
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
enrollmentType
|
||||
}: TSignCertificateFromProfileDTO): Promise<Omit<TCertificateFromProfileResponse, "privateKey">> => {
|
||||
const profile = await validateProfileAndPermissions(
|
||||
profileId,
|
||||
@@ -494,7 +502,7 @@ export const certificateV3ServiceFactory = ({
|
||||
actorOrgId,
|
||||
certificateProfileDAL,
|
||||
permissionService,
|
||||
EnrollmentType.API
|
||||
enrollmentType
|
||||
);
|
||||
|
||||
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(profile.caId);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
|
||||
import { ACMESANType, CertificateOrderStatus } from "../certificate/certificate-types";
|
||||
import {
|
||||
CertExtendedKeyUsageType,
|
||||
CertKeyUsageType,
|
||||
CertSubjectAlternativeNameType
|
||||
} from "../certificate-common/certificate-constants";
|
||||
import { EnrollmentType } from "../certificate-profile/certificate-profile-types";
|
||||
import { ACMESANType, CertificateOrderStatus } from "../certificate/certificate-types";
|
||||
|
||||
export type TIssueCertificateFromProfileDTO = {
|
||||
profileId: string;
|
||||
@@ -35,6 +36,7 @@ export type TSignCertificateFromProfileDTO = {
|
||||
};
|
||||
notBefore?: Date;
|
||||
notAfter?: Date;
|
||||
enrollmentType: EnrollmentType;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TOrderCertificateFromProfileDTO = {
|
||||
|
||||
Reference in New Issue
Block a user