mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 02:28:18 +00:00
Minor fix on keys
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
|||||||
validateAndMapAltNameType,
|
validateAndMapAltNameType,
|
||||||
validateCaDateField
|
validateCaDateField
|
||||||
} from "@app/services/certificate-authority/certificate-authority-validators";
|
} from "@app/services/certificate-authority/certificate-authority-validators";
|
||||||
|
import { mapEnumsForValidation } from "@app/services/certificate-common/certificate-utils";
|
||||||
import { validateTemplateRegexField } from "@app/services/certificate-template/certificate-template-validators";
|
import { validateTemplateRegexField } from "@app/services/certificate-template/certificate-template-validators";
|
||||||
|
|
||||||
export const registerCertificatesRouter = async (server: FastifyZodProvider) => {
|
export const registerCertificatesRouter = async (server: FastifyZodProvider) => {
|
||||||
@@ -49,13 +50,7 @@ export const registerCertificatesRouter = async (server: FastifyZodProvider) =>
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const data = await server.services.certificateV3.issueCertificateFromProfile({
|
const rawCertificateRequest = {
|
||||||
actor: req.permission.type,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
profileId: req.body.profileId,
|
|
||||||
certificateRequest: {
|
|
||||||
commonName: req.body.commonName,
|
commonName: req.body.commonName,
|
||||||
keyUsages: req.body.keyUsages,
|
keyUsages: req.body.keyUsages,
|
||||||
extendedKeyUsages: req.body.extendedKeyUsages,
|
extendedKeyUsages: req.body.extendedKeyUsages,
|
||||||
@@ -86,7 +81,17 @@ export const registerCertificatesRouter = async (server: FastifyZodProvider) =>
|
|||||||
notAfter: req.body.notAfter ? new Date(req.body.notAfter) : undefined,
|
notAfter: req.body.notAfter ? new Date(req.body.notAfter) : undefined,
|
||||||
signatureAlgorithm: req.body.signatureAlgorithm,
|
signatureAlgorithm: req.body.signatureAlgorithm,
|
||||||
keyAlgorithm: req.body.keyAlgorithm
|
keyAlgorithm: req.body.keyAlgorithm
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const mappedCertificateRequest = mapEnumsForValidation(rawCertificateRequest);
|
||||||
|
|
||||||
|
const data = await server.services.certificateV3.issueCertificateFromProfile({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
profileId: req.body.profileId,
|
||||||
|
certificateRequest: mappedCertificateRequest
|
||||||
});
|
});
|
||||||
|
|
||||||
const profile = await server.services.certificateProfile.getProfileById({
|
const profile = await server.services.certificateProfile.getProfileById({
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
interface CertificateRequestInput {
|
interface CertificateRequestInput {
|
||||||
keyUsages?: string[];
|
keyUsages?: string[];
|
||||||
extendedKeyUsages?: string[];
|
extendedKeyUsages?: string[];
|
||||||
[key: string]: unknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mapEnumsForValidation = <T extends CertificateRequestInput>(request: T): T => {
|
export const mapEnumsForValidation = <T extends CertificateRequestInput>(request: T): T => {
|
||||||
@@ -12,7 +11,7 @@ export const mapEnumsForValidation = <T extends CertificateRequestInput>(request
|
|||||||
dataEncipherment: "data_encipherment",
|
dataEncipherment: "data_encipherment",
|
||||||
keyAgreement: "key_agreement",
|
keyAgreement: "key_agreement",
|
||||||
keyCertSign: "key_cert_sign",
|
keyCertSign: "key_cert_sign",
|
||||||
crlSign: "crl_sign",
|
cRLSign: "crl_sign",
|
||||||
encipherOnly: "encipher_only",
|
encipherOnly: "encipher_only",
|
||||||
decipherOnly: "decipher_only"
|
decipherOnly: "decipher_only"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -608,6 +608,31 @@ describe("CertificateTemplateV2Service", () => {
|
|||||||
expect(result.isValid).toBe(true);
|
expect(result.isValid).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should handle camelCase key usage mapping correctly", async () => {
|
||||||
|
const templateWithOptionalUsages = {
|
||||||
|
...sampleTemplate,
|
||||||
|
keyUsages: {
|
||||||
|
requiredUsages: { all: ["digital_signature", "non_repudiation", "key_agreement"] },
|
||||||
|
optionalUsages: { all: ["crl_sign", "decipher_only"] }
|
||||||
|
},
|
||||||
|
extendedKeyUsages: {
|
||||||
|
requiredUsages: { all: ["client_auth", "code_signing"] },
|
||||||
|
optionalUsages: { all: ["server_auth", "ocsp_signing"] }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mockCertificateTemplateV2DAL.findById.mockResolvedValue(templateWithOptionalUsages);
|
||||||
|
|
||||||
|
const requestWithCamelCaseUsages = {
|
||||||
|
...validRequest,
|
||||||
|
keyUsages: ["digital_signature", "non_repudiation", "key_agreement", "crl_sign", "decipher_only"],
|
||||||
|
extendedKeyUsages: ["client_auth", "code_signing"]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await service.validateCertificateRequest("template-123", requestWithCamelCaseUsages);
|
||||||
|
expect(result.isValid).toBe(true);
|
||||||
|
expect(result.errors).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
it("should validate wildcard patterns in optional attributes", async () => {
|
it("should validate wildcard patterns in optional attributes", async () => {
|
||||||
const wildcardTemplate = {
|
const wildcardTemplate = {
|
||||||
...sampleTemplate,
|
...sampleTemplate,
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ export const certificateTemplateV2ServiceFactory = ({
|
|||||||
request: TCertificateRequest
|
request: TCertificateRequest
|
||||||
): TTemplateValidationResult => {
|
): TTemplateValidationResult => {
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
|
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
|
|
||||||
template.attributes?.forEach((attrPolicy) => {
|
template.attributes?.forEach((attrPolicy) => {
|
||||||
|
|||||||
@@ -141,6 +141,86 @@ describe("CertificateV3Service", () => {
|
|||||||
expect(result).toHaveProperty("certificateId", "cert-123");
|
expect(result).toHaveProperty("certificateId", "cert-123");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should correctly map camelCase key usages to snake_case before validation", async () => {
|
||||||
|
const profileId = "profile-123";
|
||||||
|
const mockProfile = {
|
||||||
|
id: profileId,
|
||||||
|
projectId: "project-123",
|
||||||
|
enrollmentType: EnrollmentType.API,
|
||||||
|
caId: "ca-123",
|
||||||
|
certificateTemplateId: "template-123"
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockCA = {
|
||||||
|
id: "ca-123",
|
||||||
|
externalCa: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockTemplate = {
|
||||||
|
id: "template-123",
|
||||||
|
signatureAlgorithm: { defaultAlgorithm: "RSA-SHA256" },
|
||||||
|
keyAlgorithm: { defaultKeyType: "RSA_2048" },
|
||||||
|
attributes: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockCertificateResult = {
|
||||||
|
certificate: Buffer.from("cert"),
|
||||||
|
certificateChain: Buffer.from("chain"),
|
||||||
|
privateKey: Buffer.from("key"),
|
||||||
|
serialNumber: "123456"
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockCertRecord = {
|
||||||
|
id: "cert-123",
|
||||||
|
serialNumber: "123456"
|
||||||
|
};
|
||||||
|
|
||||||
|
const camelCaseRequest = {
|
||||||
|
commonName: "test.example.com",
|
||||||
|
keyUsages: [
|
||||||
|
CertKeyUsage.DIGITAL_SIGNATURE,
|
||||||
|
CertKeyUsage.NON_REPUDIATION,
|
||||||
|
CertKeyUsage.KEY_AGREEMENT,
|
||||||
|
CertKeyUsage.CRL_SIGN,
|
||||||
|
CertKeyUsage.DECIPHER_ONLY
|
||||||
|
],
|
||||||
|
extendedKeyUsages: [
|
||||||
|
CertExtendedKeyUsage.CLIENT_AUTH,
|
||||||
|
CertExtendedKeyUsage.CODE_SIGNING,
|
||||||
|
CertExtendedKeyUsage.OCSP_SIGNING,
|
||||||
|
CertExtendedKeyUsage.SERVER_AUTH
|
||||||
|
],
|
||||||
|
validity: { ttl: "10d" }
|
||||||
|
};
|
||||||
|
|
||||||
|
mockCertificateProfileDAL.findByIdWithConfigs.mockResolvedValue(mockProfile);
|
||||||
|
mockCertificateTemplateV2Service.validateCertificateRequest.mockResolvedValue({
|
||||||
|
isValid: true,
|
||||||
|
errors: [],
|
||||||
|
warnings: []
|
||||||
|
});
|
||||||
|
mockCertificateAuthorityDAL.findByIdWithAssociatedCa.mockResolvedValue(mockCA);
|
||||||
|
mockCertificateTemplateV2Service.getTemplateV2ById.mockResolvedValue(mockTemplate);
|
||||||
|
mockInternalCaService.issueCertFromCa.mockResolvedValue(mockCertificateResult);
|
||||||
|
mockCertificateDAL.findOne.mockResolvedValue(mockCertRecord);
|
||||||
|
mockCertificateDAL.updateById.mockResolvedValue({});
|
||||||
|
|
||||||
|
await service.issueCertificateFromProfile({
|
||||||
|
profileId,
|
||||||
|
certificateRequest: camelCaseRequest,
|
||||||
|
...mockActor
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify that the template validation service was called with mapped snake_case values
|
||||||
|
expect(mockCertificateTemplateV2Service.validateCertificateRequest).toHaveBeenCalledWith(
|
||||||
|
"template-123",
|
||||||
|
expect.objectContaining({
|
||||||
|
keyUsages: ["digital_signature", "non_repudiation", "key_agreement", "crl_sign", "decipher_only"],
|
||||||
|
extendedKeyUsages: ["client_auth", "code_signing", "ocsp_signing", "server_auth"]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("should throw ForbiddenRequestError when profile is not configured for API enrollment", async () => {
|
it("should throw ForbiddenRequestError when profile is not configured for API enrollment", async () => {
|
||||||
const profileId = "profile-123";
|
const profileId = "profile-123";
|
||||||
const mockProfile = {
|
const mockProfile = {
|
||||||
|
|||||||
Reference in New Issue
Block a user