mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 07:26:45 +00:00
misc: finalize KMIP management
This commit is contained in:
Vendored
+1
-1
@@ -227,7 +227,7 @@ declare module "fastify" {
|
|||||||
// everywhere else access using service layer
|
// everywhere else access using service layer
|
||||||
store: {
|
store: {
|
||||||
user: Pick<TUserDALFactory, "findById">;
|
user: Pick<TUserDALFactory, "findById">;
|
||||||
kmipClient: Pick<TKmipClientDALFactory, "findOne">;
|
kmipClient: Pick<TKmipClientDALFactory, "findByProjectAndClientId">;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import crypto from "crypto";
|
|
||||||
import jwt, { JwtPayload } from "jsonwebtoken";
|
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
import { KmsKeysSchema } from "@app/db/schemas";
|
import { KmsKeysSchema } from "@app/db/schemas";
|
||||||
@@ -7,21 +5,17 @@ import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
|||||||
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
|
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
|
||||||
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerKmipOperationRouter = async (server: FastifyZodProvider) => {
|
export const registerKmipOperationRouter = async (server: FastifyZodProvider) => {
|
||||||
server.decorateRequest("kmipUser", null);
|
server.decorateRequest("kmipUser", null);
|
||||||
|
|
||||||
server.addHook("preHandler", async (req) => {
|
server.addHook("onRequest", async (req) => {
|
||||||
const token = req.headers["x-kmip-jwt"] as string;
|
const clientId = req.headers["x-kmip-client-id"] as string;
|
||||||
const serverCertSerialNumber = req.headers["x-server-certificate-serial-number"] as string;
|
const projectId = req.headers["x-kmip-project-id"] as string;
|
||||||
|
const clientCertSerialNumber = req.headers["x-kmip-client-certificate-serial-number"] as string;
|
||||||
if (!jwt) {
|
const serverCertSerialNumber = req.headers["x-kmip-server-certificate-serial-number"] as string;
|
||||||
throw new ForbiddenRequestError({
|
|
||||||
message: "Missing KMIP JWT"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!serverCertSerialNumber) {
|
if (!serverCertSerialNumber) {
|
||||||
throw new ForbiddenRequestError({
|
throw new ForbiddenRequestError({
|
||||||
@@ -29,25 +23,28 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverCert = await server.services.kmip.getServerCertificateBySerialNumber(serverCertSerialNumber);
|
if (!clientCertSerialNumber) {
|
||||||
|
throw new ForbiddenRequestError({
|
||||||
|
message: "Missing client certificate serial number from request"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clientId) {
|
||||||
|
throw new ForbiddenRequestError({
|
||||||
|
message: "Missing client ID from request"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!projectId) {
|
||||||
|
throw new ForbiddenRequestError({
|
||||||
|
message: "Missing project ID from request"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: assert that server certificate used is not revoked
|
// TODO: assert that server certificate used is not revoked
|
||||||
// TODO: assert that client certificate used is not revoked
|
// TODO: assert that client certificate used is not revoked
|
||||||
|
|
||||||
const publicKey = crypto.createPublicKey({
|
const kmipClient = await server.store.kmipClient.findByProjectAndClientId(projectId, clientId);
|
||||||
key: serverCert.publicKey,
|
|
||||||
format: "pem",
|
|
||||||
type: [CertKeyAlgorithm.ECDSA_P256, CertKeyAlgorithm.ECDSA_P384].includes(serverCert.keyAlgorithm)
|
|
||||||
? "spki"
|
|
||||||
: "pkcs1"
|
|
||||||
});
|
|
||||||
|
|
||||||
const decodedToken = jwt.verify(token, publicKey) as JwtPayload & { projectId: string; clientId: string };
|
|
||||||
|
|
||||||
const kmipClient = await server.store.kmipClient.findOne({
|
|
||||||
id: decodedToken.clientId,
|
|
||||||
projectId: decodedToken.projectId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!kmipClient) {
|
if (!kmipClient) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
@@ -55,9 +52,15 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kmipClient.orgId !== req.permission.orgId) {
|
||||||
|
throw new ForbiddenRequestError({
|
||||||
|
message: "Client specified in the request does not belong in the organization"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
req.kmipUser = {
|
req.kmipUser = {
|
||||||
projectId: decodedToken.projectId,
|
projectId,
|
||||||
clientId: decodedToken.clientId,
|
clientId,
|
||||||
name: kmipClient.name
|
name: kmipClient.name
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -77,9 +80,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
200: KmsKeysSchema
|
200: KmsKeysSchema
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const object = await server.services.kmipOperation.create({
|
const object = await server.services.kmipOperation.create({
|
||||||
...req.kmipUser,
|
...req.kmipUser,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
algorithm: req.body.algorithm
|
algorithm: req.body.algorithm
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -124,9 +132,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const object = await server.services.kmipOperation.get({
|
const object = await server.services.kmipOperation.get({
|
||||||
...req.kmipUser,
|
...req.kmipUser,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
id: req.body.id
|
id: req.body.id
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,9 +185,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const object = await server.services.kmipOperation.getAttributes({
|
const object = await server.services.kmipOperation.getAttributes({
|
||||||
...req.kmipUser,
|
...req.kmipUser,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
id: req.body.id
|
id: req.body.id
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -216,9 +234,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const object = await server.services.kmipOperation.deleteOp({
|
const object = await server.services.kmipOperation.deleteOp({
|
||||||
...req.kmipUser,
|
...req.kmipUser,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
id: req.body.id
|
id: req.body.id
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -261,9 +284,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const object = await server.services.kmipOperation.activate({
|
const object = await server.services.kmipOperation.activate({
|
||||||
...req.kmipUser,
|
...req.kmipUser,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
id: req.body.id
|
id: req.body.id
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -306,9 +334,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const object = await server.services.kmipOperation.revoke({
|
const object = await server.services.kmipOperation.revoke({
|
||||||
...req.kmipUser,
|
...req.kmipUser,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
id: req.body.id
|
id: req.body.id
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -356,9 +389,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const objects = await server.services.kmipOperation.locate({
|
const objects = await server.services.kmipOperation.locate({
|
||||||
...req.kmipUser
|
...req.kmipUser,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
@@ -403,10 +441,15 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const object = await server.services.kmipOperation.register({
|
const object = await server.services.kmipOperation.register({
|
||||||
...req.kmipUser,
|
...req.kmipUser,
|
||||||
...req.body
|
...req.body,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
|
|||||||
@@ -306,13 +306,26 @@ export const registerKmipRouter = 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) => {
|
||||||
return server.services.kmip.setupOrgKmip({
|
const chains = await server.services.kmip.setupOrgKmip({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.body
|
...req.body
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.SETUP_KMIP,
|
||||||
|
metadata: {
|
||||||
|
keyAlgorithm: req.body.caKeyAlgorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return chains;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -332,46 +345,76 @@ export const registerKmipRouter = 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) => {
|
||||||
return server.services.kmip.getOrgKmip({
|
const kmip = await server.services.kmip.getOrgKmip({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_KMIP,
|
||||||
|
metadata: {
|
||||||
|
id: kmip.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return kmip;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/server-certificates",
|
url: "/server-registration",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
commonName: z.string().trim().min(1),
|
hostnamesOrIps: validateAltNamesField,
|
||||||
altNames: validateAltNamesField,
|
commonName: z.string().trim().min(1).optional(),
|
||||||
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
|
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm).optional().default(CertKeyAlgorithm.RSA_2048),
|
||||||
ttl: z.string().refine((val) => ms(val) > 0, "TTL must be a positive number")
|
ttl: z.string().refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
serialNumber: z.string(),
|
clientCertificateChain: z.string(),
|
||||||
certificateChain: z.string(),
|
certificateChain: z.string(),
|
||||||
certificate: z.string(),
|
certificate: z.string(),
|
||||||
privateKey: z.string()
|
privateKey: z.string()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
return server.services.kmip.generateOrgKmipServerCertificate({
|
const configs = await server.services.kmip.registerServer({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.body
|
...req.body
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.REGISTER_KMIP_SERVER,
|
||||||
|
metadata: {
|
||||||
|
serverCertificateSerialNumber: configs.serverCertificateSerialNumber,
|
||||||
|
hostnamesOrIps: req.body.hostnamesOrIps,
|
||||||
|
commonName: req.body.commonName ?? "kmip-server",
|
||||||
|
keyAlgorithm: req.body.keyAlgorithm,
|
||||||
|
ttl: req.body.ttl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return configs;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -267,6 +267,11 @@ export enum EventType {
|
|||||||
GET_KMIP_CLIENT = "get-kmip-client",
|
GET_KMIP_CLIENT = "get-kmip-client",
|
||||||
GET_KMIP_CLIENTS = "get-kmip-clients",
|
GET_KMIP_CLIENTS = "get-kmip-clients",
|
||||||
CREATE_KMIP_CLIENT_CERTIFICATE = "create-kmip-client-certificate",
|
CREATE_KMIP_CLIENT_CERTIFICATE = "create-kmip-client-certificate",
|
||||||
|
|
||||||
|
SETUP_KMIP = "setup-kmip",
|
||||||
|
GET_KMIP = "get-kmip",
|
||||||
|
REGISTER_KMIP_SERVER = "register-kmip-server",
|
||||||
|
|
||||||
KMIP_OPERATION_CREATE = "kmip-operation-create",
|
KMIP_OPERATION_CREATE = "kmip-operation-create",
|
||||||
KMIP_OPERATION_GET = "kmip-operation-get",
|
KMIP_OPERATION_GET = "kmip-operation-get",
|
||||||
KMIP_OPERATION_DELETE = "kmip-operation-delete",
|
KMIP_OPERATION_DELETE = "kmip-operation-delete",
|
||||||
@@ -2207,6 +2212,31 @@ interface KmipOperationRegisterEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SetupKmipEvent {
|
||||||
|
type: EventType.SETUP_KMIP;
|
||||||
|
metadata: {
|
||||||
|
keyAlgorithm: CertKeyAlgorithm;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetKmipEvent {
|
||||||
|
type: EventType.GET_KMIP;
|
||||||
|
metadata: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegisterKmipServerEvent {
|
||||||
|
type: EventType.REGISTER_KMIP_SERVER;
|
||||||
|
metadata: {
|
||||||
|
serverCertificateSerialNumber: string;
|
||||||
|
hostnamesOrIps: string;
|
||||||
|
commonName: string;
|
||||||
|
keyAlgorithm: CertKeyAlgorithm;
|
||||||
|
ttl: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type Event =
|
export type Event =
|
||||||
| GetSecretsEvent
|
| GetSecretsEvent
|
||||||
| GetSecretEvent
|
| GetSecretEvent
|
||||||
@@ -2404,6 +2434,9 @@ export type Event =
|
|||||||
| GetKmipClientEvent
|
| GetKmipClientEvent
|
||||||
| GetKmipClientsEvent
|
| GetKmipClientsEvent
|
||||||
| CreateKmipClientCertificateEvent
|
| CreateKmipClientCertificateEvent
|
||||||
|
| SetupKmipEvent
|
||||||
|
| GetKmipEvent
|
||||||
|
| RegisterKmipServerEvent
|
||||||
| KmipOperationGetEvent
|
| KmipOperationGetEvent
|
||||||
| KmipOperationDeleteEvent
|
| KmipOperationDeleteEvent
|
||||||
| KmipOperationCreateEvent
|
| KmipOperationCreateEvent
|
||||||
|
|||||||
@@ -13,6 +13,26 @@ export type TKmipClientDALFactory = ReturnType<typeof kmipClientDALFactory>;
|
|||||||
export const kmipClientDALFactory = (db: TDbClient) => {
|
export const kmipClientDALFactory = (db: TDbClient) => {
|
||||||
const kmipClientOrm = ormify(db, TableName.KmipClient);
|
const kmipClientOrm = ormify(db, TableName.KmipClient);
|
||||||
|
|
||||||
|
const findByProjectAndClientId = async (projectId: string, clientId: string) => {
|
||||||
|
try {
|
||||||
|
const client = await db
|
||||||
|
.replicaNode()(TableName.KmipClient)
|
||||||
|
.join(TableName.Project, `${TableName.Project}.id`, `${TableName.KmipClient}.projectId`)
|
||||||
|
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Project}.orgId`)
|
||||||
|
.where({
|
||||||
|
[`${TableName.KmipClient}.projectId` as "projectId"]: projectId,
|
||||||
|
[`${TableName.KmipClient}.id` as "id"]: clientId
|
||||||
|
})
|
||||||
|
.select(selectAllTableCols(TableName.KmipClient))
|
||||||
|
.select(db.ref("id").withSchema(TableName.Organization).as("orgId"))
|
||||||
|
.first();
|
||||||
|
|
||||||
|
return client;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "Find by project and client ID" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const findByProjectId = async (
|
const findByProjectId = async (
|
||||||
{
|
{
|
||||||
projectId,
|
projectId,
|
||||||
@@ -60,6 +80,7 @@ export const kmipClientDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...kmipClientOrm,
|
...kmipClientOrm,
|
||||||
findByProjectId
|
findByProjectId,
|
||||||
|
findByProjectAndClientId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { ProjectType } from "@app/db/schemas";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
|
import { OrgPermissionKmipActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { TKmipClientDALFactory } from "./kmip-client-dal";
|
import { TKmipClientDALFactory } from "./kmip-client-dal";
|
||||||
import { KmipPermission } from "./kmip-enum";
|
import { KmipPermission } from "./kmip-enum";
|
||||||
import {
|
import {
|
||||||
@@ -21,6 +24,7 @@ type TKmipOperationServiceFactoryDep = {
|
|||||||
kmsDAL: TKmsKeyDALFactory;
|
kmsDAL: TKmsKeyDALFactory;
|
||||||
kmipClientDAL: TKmipClientDALFactory;
|
kmipClientDAL: TKmipClientDALFactory;
|
||||||
projectDAL: Pick<TProjectDALFactory, "getProjectFromSplitId" | "findById">;
|
projectDAL: Pick<TProjectDALFactory, "getProjectFromSplitId" | "findById">;
|
||||||
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TKmipOperationServiceFactory = ReturnType<typeof kmipOperationServiceFactory>;
|
export type TKmipOperationServiceFactory = ReturnType<typeof kmipOperationServiceFactory>;
|
||||||
@@ -29,16 +33,28 @@ export const kmipOperationServiceFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
kmsDAL,
|
kmsDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmipClientDAL
|
kmipClientDAL,
|
||||||
|
permissionService
|
||||||
}: TKmipOperationServiceFactoryDep) => {
|
}: TKmipOperationServiceFactoryDep) => {
|
||||||
const create = async ({ projectId: preSplitProjectId, clientId, algorithm }: TKmipCreateDTO) => {
|
const create = async ({
|
||||||
let projectId = preSplitProjectId;
|
projectId,
|
||||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
clientId,
|
||||||
if (cmekProjectFromSplit) {
|
algorithm,
|
||||||
projectId = cmekProjectFromSplit.id;
|
actor,
|
||||||
}
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
}: TKmipCreateDTO) => {
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
const project = await projectDAL.findById(projectId);
|
|
||||||
const kmipClient = await kmipClientDAL.findOne({
|
const kmipClient = await kmipClientDAL.findOne({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
projectId
|
projectId
|
||||||
@@ -52,7 +68,7 @@ export const kmipOperationServiceFactory = ({
|
|||||||
|
|
||||||
const kmsKey = await kmsService.generateKmsKey({
|
const kmsKey = await kmsService.generateKmsKey({
|
||||||
encryptionAlgorithm: algorithm,
|
encryptionAlgorithm: algorithm,
|
||||||
orgId: project.orgId,
|
orgId: actorOrgId,
|
||||||
projectId,
|
projectId,
|
||||||
isReserved: false
|
isReserved: false
|
||||||
});
|
});
|
||||||
@@ -60,13 +76,16 @@ export const kmipOperationServiceFactory = ({
|
|||||||
return kmsKey;
|
return kmsKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteOp = async ({ projectId: preSplitProjectId, id, clientId }: TKmipDeleteDTO) => {
|
const deleteOp = async ({ projectId, id, clientId, actor, actorId, actorOrgId, actorAuthMethod }: TKmipDeleteDTO) => {
|
||||||
let projectId = preSplitProjectId;
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
if (cmekProjectFromSplit) {
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
projectId = cmekProjectFromSplit.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const kmipClient = await kmipClientDAL.findOne({
|
const kmipClient = await kmipClientDAL.findOne({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
@@ -104,13 +123,16 @@ export const kmipOperationServiceFactory = ({
|
|||||||
return kms;
|
return kms;
|
||||||
};
|
};
|
||||||
|
|
||||||
const get = async ({ projectId: preSplitProjectId, id, clientId }: TKmipGetDTO) => {
|
const get = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipGetDTO) => {
|
||||||
let projectId = preSplitProjectId;
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
if (cmekProjectFromSplit) {
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
projectId = cmekProjectFromSplit.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const kmipClient = await kmipClientDAL.findOne({
|
const kmipClient = await kmipClientDAL.findOne({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
@@ -158,13 +180,16 @@ export const kmipOperationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const activate = async ({ projectId: preSplitProjectId, id, clientId }: TKmipGetDTO) => {
|
const activate = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipGetDTO) => {
|
||||||
let projectId = preSplitProjectId;
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
if (cmekProjectFromSplit) {
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
projectId = cmekProjectFromSplit.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const kmipClient = await kmipClientDAL.findOne({
|
const kmipClient = await kmipClientDAL.findOne({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
@@ -192,13 +217,16 @@ export const kmipOperationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const revoke = async ({ projectId: preSplitProjectId, id, clientId }: TKmipRevokeDTO) => {
|
const revoke = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipRevokeDTO) => {
|
||||||
let projectId = preSplitProjectId;
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
if (cmekProjectFromSplit) {
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
projectId = cmekProjectFromSplit.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const kmipClient = await kmipClientDAL.findOne({
|
const kmipClient = await kmipClientDAL.findOne({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
@@ -242,13 +270,24 @@ export const kmipOperationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAttributes = async ({ projectId: preSplitProjectId, id, clientId }: TKmipGetAttributesDTO) => {
|
const getAttributes = async ({
|
||||||
let projectId = preSplitProjectId;
|
projectId,
|
||||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
id,
|
||||||
|
clientId,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
}: TKmipGetAttributesDTO) => {
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
if (cmekProjectFromSplit) {
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
projectId = cmekProjectFromSplit.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const kmipClient = await kmipClientDAL.findOne({
|
const kmipClient = await kmipClientDAL.findOne({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
@@ -291,13 +330,16 @@ export const kmipOperationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const locate = async ({ projectId: preSplitProjectId, clientId }: TKmipLocateDTO) => {
|
const locate = async ({ projectId, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipLocateDTO) => {
|
||||||
let projectId = preSplitProjectId;
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
if (cmekProjectFromSplit) {
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
projectId = cmekProjectFromSplit.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const kmipClient = await kmipClientDAL.findOne({
|
const kmipClient = await kmipClientDAL.findOne({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
@@ -315,13 +357,26 @@ export const kmipOperationServiceFactory = ({
|
|||||||
return keys;
|
return keys;
|
||||||
};
|
};
|
||||||
|
|
||||||
const register = async ({ projectId: preSplitProjectId, clientId, key, algorithm, name }: TKmipRegisterDTO) => {
|
const register = async ({
|
||||||
let projectId = preSplitProjectId;
|
projectId,
|
||||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
clientId,
|
||||||
|
key,
|
||||||
|
algorithm,
|
||||||
|
name,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
}: TKmipRegisterDTO) => {
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
if (cmekProjectFromSplit) {
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
projectId = cmekProjectFromSplit.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const kmipClient = await kmipClientDAL.findOne({
|
const kmipClient = await kmipClientDAL.findOne({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { hostnameRegex } from "@app/services/certificate-authority/certificate-a
|
|||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionKmipActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionKmipActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { ProjectPermissionKmipActions, ProjectPermissionSub } from "../permission/project-permission";
|
import { ProjectPermissionKmipActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
@@ -30,6 +31,7 @@ import {
|
|||||||
TGetKmipClientDTO,
|
TGetKmipClientDTO,
|
||||||
TGetOrgKmipDTO,
|
TGetOrgKmipDTO,
|
||||||
TListKmipClientsByProjectIdDTO,
|
TListKmipClientsByProjectIdDTO,
|
||||||
|
TRegisterServerDTO,
|
||||||
TSetupOrgKmipDTO,
|
TSetupOrgKmipDTO,
|
||||||
TUpdateKmipClientDTO
|
TUpdateKmipClientDTO
|
||||||
} from "./kmip-types";
|
} from "./kmip-types";
|
||||||
@@ -41,6 +43,7 @@ type TKmipServiceFactoryDep = {
|
|||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
kmipOrgConfigDAL: TKmipOrgConfigDALFactory;
|
kmipOrgConfigDAL: TKmipOrgConfigDALFactory;
|
||||||
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TKmipServiceFactory = ReturnType<typeof kmipServiceFactory>;
|
export type TKmipServiceFactory = ReturnType<typeof kmipServiceFactory>;
|
||||||
@@ -51,7 +54,8 @@ export const kmipServiceFactory = ({
|
|||||||
kmipClientCertificateDAL,
|
kmipClientCertificateDAL,
|
||||||
kmipOrgConfigDAL,
|
kmipOrgConfigDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
kmipOrgServerCertificateDAL
|
kmipOrgServerCertificateDAL,
|
||||||
|
licenseService
|
||||||
}: TKmipServiceFactoryDep) => {
|
}: TKmipServiceFactoryDep) => {
|
||||||
const createKmipClient = async ({
|
const createKmipClient = async ({
|
||||||
actor,
|
actor,
|
||||||
@@ -77,6 +81,12 @@ export const kmipServiceFactory = ({
|
|||||||
ProjectPermissionSub.Kmip
|
ProjectPermissionSub.Kmip
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
|
if (!plan.kmip)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to create KMIP client. Upgrade your plan to enterprise."
|
||||||
|
});
|
||||||
|
|
||||||
const kmipClient = await kmipClientDAL.create({
|
const kmipClient = await kmipClientDAL.create({
|
||||||
projectId,
|
projectId,
|
||||||
name,
|
name,
|
||||||
@@ -105,6 +115,12 @@ export const kmipServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
|
if (!plan.kmip)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to update KMIP client. Upgrade your plan to enterprise."
|
||||||
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -151,6 +167,12 @@ export const kmipServiceFactory = ({
|
|||||||
ProjectPermissionSub.Kmip
|
ProjectPermissionSub.Kmip
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
|
if (!plan.kmip)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to delete KMIP client. Upgrade your plan to enterprise."
|
||||||
|
});
|
||||||
|
|
||||||
const deletedKmipClient = await kmipClientDAL.deleteById(id);
|
const deletedKmipClient = await kmipClientDAL.deleteById(id);
|
||||||
|
|
||||||
return deletedKmipClient;
|
return deletedKmipClient;
|
||||||
@@ -218,6 +240,12 @@ export const kmipServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
|
if (!plan.kmip)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to create KMIP client. Upgrade your plan to enterprise."
|
||||||
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -340,6 +368,31 @@ export const kmipServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getServerCertificateBySerialNumber = async (orgId: string, serialNumber: string) => {
|
||||||
|
const serverCert = await kmipOrgServerCertificateDAL.findOne({
|
||||||
|
serialNumber,
|
||||||
|
orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!serverCert) {
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: "Server certificate not found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.Organization,
|
||||||
|
orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
const parsedCertificate = new x509.X509Certificate(decryptor({ cipherTextBlob: serverCert.encryptedCertificate }));
|
||||||
|
|
||||||
|
return {
|
||||||
|
publicKey: parsedCertificate.publicKey.toString("pem"),
|
||||||
|
keyAlgorithm: serverCert.keyAlgorithm as CertKeyAlgorithm
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const setupOrgKmip = async ({ caKeyAlgorithm, actorOrgId, actor, actorId, actorAuthMethod }: TSetupOrgKmipDTO) => {
|
const setupOrgKmip = async ({ caKeyAlgorithm, actorOrgId, actor, actorId, actorAuthMethod }: TSetupOrgKmipDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
@@ -360,6 +413,12 @@ export const kmipServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
|
if (!plan.kmip)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to setup KMIP. Upgrade your plan to enterprise."
|
||||||
|
});
|
||||||
|
|
||||||
const alg = keyAlgorithmToAlgCfg(caKeyAlgorithm);
|
const alg = keyAlgorithmToAlgCfg(caKeyAlgorithm);
|
||||||
|
|
||||||
// generate root CA
|
// generate root CA
|
||||||
@@ -499,7 +558,9 @@ export const kmipServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOrgKmip = async ({ actorOrgId }: TGetOrgKmipDTO) => {
|
const getOrgKmip = async ({ actorOrgId, actor, actorId, actorAuthMethod }: TGetOrgKmipDTO) => {
|
||||||
|
await permissionService.getOrgPermission(actor, actorId, actorOrgId, actorAuthMethod, actorOrgId);
|
||||||
|
|
||||||
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
||||||
orgId: actorOrgId
|
orgId: actorOrgId
|
||||||
});
|
});
|
||||||
@@ -525,32 +586,21 @@ export const kmipServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
id: kmipConfig.id,
|
||||||
serverCertificateChain: `${serverIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim(),
|
serverCertificateChain: `${serverIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim(),
|
||||||
clientCertificateChain: `${clientIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim()
|
clientCertificateChain: `${clientIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateOrgKmipServerCertificate = async ({
|
const generateOrgKmipServerCertificate = async ({
|
||||||
actorOrgId,
|
orgId,
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod,
|
|
||||||
ttl,
|
ttl,
|
||||||
commonName,
|
commonName,
|
||||||
altNames,
|
altNames,
|
||||||
keyAlgorithm
|
keyAlgorithm
|
||||||
}: TGenerateOrgKmipServerCertificateDTO) => {
|
}: TGenerateOrgKmipServerCertificateDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Setup, OrgPermissionSubjects.Kmip);
|
|
||||||
|
|
||||||
const kmipOrgConfig = await kmipOrgConfigDAL.findOne({
|
const kmipOrgConfig = await kmipOrgConfigDAL.findOne({
|
||||||
orgId: actorOrgId
|
orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!kmipOrgConfig) {
|
if (!kmipOrgConfig) {
|
||||||
@@ -559,9 +609,15 @@ export const kmipServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(orgId);
|
||||||
|
if (!plan.kmip)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to generate KMIP server certificate. Upgrade your plan to enterprise."
|
||||||
|
});
|
||||||
|
|
||||||
const { decryptor, encryptor } = await kmsService.createCipherPairWithDataKey({
|
const { decryptor, encryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
type: KmsDataKey.Organization,
|
type: KmsDataKey.Organization,
|
||||||
orgId: actorOrgId
|
orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
const caCertObj = new x509.X509Certificate(
|
const caCertObj = new x509.X509Certificate(
|
||||||
@@ -668,7 +724,7 @@ export const kmipServiceFactory = ({
|
|||||||
const certificateChain = `${caCertObj.toString("pem")}\n${decryptedCaCertChain}`.trim();
|
const certificateChain = `${caCertObj.toString("pem")}\n${decryptedCaCertChain}`.trim();
|
||||||
|
|
||||||
await kmipOrgServerCertificateDAL.create({
|
await kmipOrgServerCertificateDAL.create({
|
||||||
orgId: actorOrgId,
|
orgId,
|
||||||
keyAlgorithm,
|
keyAlgorithm,
|
||||||
issuedAt: notBeforeDate,
|
issuedAt: notBeforeDate,
|
||||||
expiration: notAfterDate,
|
expiration: notAfterDate,
|
||||||
@@ -687,6 +743,66 @@ export const kmipServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const registerServer = async ({
|
||||||
|
actorOrgId,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
ttl,
|
||||||
|
commonName,
|
||||||
|
keyAlgorithm,
|
||||||
|
hostnamesOrIps
|
||||||
|
}: TRegisterServerDTO) => {
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
|
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!kmipConfig) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "KMIP has not been configured for the organization"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
|
if (!plan.kmip)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to register KMIP server. Upgrade your plan to enterprise."
|
||||||
|
});
|
||||||
|
|
||||||
|
const { privateKey, certificate, certificateChain, serialNumber } = await generateOrgKmipServerCertificate({
|
||||||
|
orgId: actorOrgId,
|
||||||
|
commonName: commonName ?? "kmip-server",
|
||||||
|
altNames: hostnamesOrIps,
|
||||||
|
keyAlgorithm: keyAlgorithm ?? (kmipConfig.caKeyAlgorithm as CertKeyAlgorithm),
|
||||||
|
ttl
|
||||||
|
});
|
||||||
|
|
||||||
|
const { clientCertificateChain } = await getOrgKmip({
|
||||||
|
actor,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorId,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
serverCertificateSerialNumber: serialNumber,
|
||||||
|
clientCertificateChain,
|
||||||
|
privateKey,
|
||||||
|
certificate,
|
||||||
|
certificateChain
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createKmipClient,
|
createKmipClient,
|
||||||
updateKmipClient,
|
updateKmipClient,
|
||||||
@@ -696,6 +812,8 @@ export const kmipServiceFactory = ({
|
|||||||
createKmipClientCertificate,
|
createKmipClientCertificate,
|
||||||
setupOrgKmip,
|
setupOrgKmip,
|
||||||
generateOrgKmipServerCertificate,
|
generateOrgKmipServerCertificate,
|
||||||
getOrgKmip
|
getOrgKmip,
|
||||||
|
getServerCertificateBySerialNumber,
|
||||||
|
registerServer
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export type TListKmipClientsByProjectIdDTO = {
|
|||||||
type KmipOperationBaseDTO = {
|
type KmipOperationBaseDTO = {
|
||||||
clientId: string;
|
clientId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
};
|
} & Omit<TOrgPermission, "orgId">;
|
||||||
|
|
||||||
export type TKmipCreateDTO = {
|
export type TKmipCreateDTO = {
|
||||||
algorithm: SymmetricEncryption;
|
algorithm: SymmetricEncryption;
|
||||||
@@ -91,4 +91,12 @@ export type TGenerateOrgKmipServerCertificateDTO = {
|
|||||||
altNames: string;
|
altNames: string;
|
||||||
keyAlgorithm: CertKeyAlgorithm;
|
keyAlgorithm: CertKeyAlgorithm;
|
||||||
ttl: string;
|
ttl: string;
|
||||||
|
orgId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TRegisterServerDTO = {
|
||||||
|
hostnamesOrIps: string;
|
||||||
|
commonName?: string;
|
||||||
|
keyAlgorithm?: CertKeyAlgorithm;
|
||||||
|
ttl: string;
|
||||||
} & Omit<TOrgPermission, "orgId">;
|
} & Omit<TOrgPermission, "orgId">;
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
},
|
},
|
||||||
pkiEst: false,
|
pkiEst: false,
|
||||||
enforceMfa: false,
|
enforceMfa: false,
|
||||||
projectTemplates: false
|
projectTemplates: false,
|
||||||
|
kmip: false
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export type TFeatureSet = {
|
|||||||
pkiEst: boolean;
|
pkiEst: boolean;
|
||||||
enforceMfa: boolean;
|
enforceMfa: boolean;
|
||||||
projectTemplates: false;
|
projectTemplates: false;
|
||||||
|
kmip: false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TOrgPlansTableDTO = {
|
export type TOrgPlansTableDTO = {
|
||||||
|
|||||||
@@ -268,6 +268,9 @@ const buildAdminPermission = () => {
|
|||||||
|
|
||||||
can(OrgPermissionKmipActions.Setup, OrgPermissionSubjects.Kmip);
|
can(OrgPermissionKmipActions.Setup, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
|
// the proxy assignment is temporary in order to prevent "more privilege" error during role assignment to MI
|
||||||
|
can(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1434,14 +1434,16 @@ export const registerRoutes = async (
|
|||||||
kmipClientCertificateDAL,
|
kmipClientCertificateDAL,
|
||||||
kmipOrgConfigDAL,
|
kmipOrgConfigDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
kmipOrgServerCertificateDAL
|
kmipOrgServerCertificateDAL,
|
||||||
|
licenseService
|
||||||
});
|
});
|
||||||
|
|
||||||
const kmipOperationService = kmipOperationServiceFactory({
|
const kmipOperationService = kmipOperationServiceFactory({
|
||||||
kmsService,
|
kmsService,
|
||||||
kmsDAL,
|
kmsDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmipClientDAL
|
kmipClientDAL,
|
||||||
|
permissionService
|
||||||
});
|
});
|
||||||
|
|
||||||
await superAdminService.initServerCfg();
|
await superAdminService.initServerCfg();
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
|||||||
[EventType.GET_KMIP_CLIENT]: "Get KMIP client",
|
[EventType.GET_KMIP_CLIENT]: "Get KMIP client",
|
||||||
[EventType.GET_KMIP_CLIENTS]: "Get KMIP clients",
|
[EventType.GET_KMIP_CLIENTS]: "Get KMIP clients",
|
||||||
[EventType.CREATE_KMIP_CLIENT_CERTIFICATE]: "Create KMIP client certificate",
|
[EventType.CREATE_KMIP_CLIENT_CERTIFICATE]: "Create KMIP client certificate",
|
||||||
|
[EventType.SETUP_KMIP]: "Setup KMIP configuration",
|
||||||
|
[EventType.GET_KMIP]: "Get KMIP configuration",
|
||||||
|
[EventType.REGISTER_KMIP_SERVER]: "Register KMIP server",
|
||||||
[EventType.KMIP_OPERATION_CREATE]: "KMIP operation create",
|
[EventType.KMIP_OPERATION_CREATE]: "KMIP operation create",
|
||||||
[EventType.KMIP_OPERATION_GET]: "KMIP operation get",
|
[EventType.KMIP_OPERATION_GET]: "KMIP operation get",
|
||||||
[EventType.KMIP_OPERATION_DELETE]: "KMIP operation delete",
|
[EventType.KMIP_OPERATION_DELETE]: "KMIP operation delete",
|
||||||
|
|||||||
@@ -137,6 +137,9 @@ export enum EventType {
|
|||||||
GET_KMIP_CLIENT = "get-kmip-client",
|
GET_KMIP_CLIENT = "get-kmip-client",
|
||||||
GET_KMIP_CLIENTS = "get-kmip-clients",
|
GET_KMIP_CLIENTS = "get-kmip-clients",
|
||||||
CREATE_KMIP_CLIENT_CERTIFICATE = "create-kmip-client-certificate",
|
CREATE_KMIP_CLIENT_CERTIFICATE = "create-kmip-client-certificate",
|
||||||
|
SETUP_KMIP = "setup-kmip",
|
||||||
|
GET_KMIP = "get-kmip",
|
||||||
|
REGISTER_KMIP_SERVER = "register-kmip-server",
|
||||||
KMIP_OPERATION_CREATE = "kmip-operation-create",
|
KMIP_OPERATION_CREATE = "kmip-operation-create",
|
||||||
KMIP_OPERATION_GET = "kmip-operation-get",
|
KMIP_OPERATION_GET = "kmip-operation-get",
|
||||||
KMIP_OPERATION_DELETE = "kmip-operation-delete",
|
KMIP_OPERATION_DELETE = "kmip-operation-delete",
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ import { apiRequest } from "@app/config/request";
|
|||||||
import { kmipKeys } from "./queries";
|
import { kmipKeys } from "./queries";
|
||||||
import {
|
import {
|
||||||
KmipClientCertificate,
|
KmipClientCertificate,
|
||||||
OrgKmipServerCert,
|
|
||||||
TCreateKmipClient,
|
TCreateKmipClient,
|
||||||
TDeleteKmipClient,
|
TDeleteKmipClient,
|
||||||
TGenerateKmipClientCertificate,
|
TGenerateKmipClientCertificate,
|
||||||
TGenerateOrgKmipServerCertDTO,
|
|
||||||
TSetupOrgKmipDTO,
|
TSetupOrgKmipDTO,
|
||||||
TUpdateKmipClient
|
TUpdateKmipClient
|
||||||
} from "./types";
|
} from "./types";
|
||||||
@@ -90,11 +88,3 @@ export const useSetupOrgKmip = (orgId: string) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGenerateOrgKmipServerCert = () => {
|
|
||||||
return useMutation({
|
|
||||||
mutationFn: async (payload: TGenerateOrgKmipServerCertDTO) => {
|
|
||||||
return apiRequest.post<OrgKmipServerCert>("/api/v1/kmip/server-certificates", payload);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -73,13 +73,6 @@ export type TSetupOrgKmipDTO = {
|
|||||||
caKeyAlgorithm: CertKeyAlgorithm;
|
caKeyAlgorithm: CertKeyAlgorithm;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGenerateOrgKmipServerCertDTO = {
|
|
||||||
commonName: string;
|
|
||||||
keyAlgorithm: CertKeyAlgorithm;
|
|
||||||
altNames: string;
|
|
||||||
ttl: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type OrgKmipServerCert = {
|
export type OrgKmipServerCert = {
|
||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
certificate: string;
|
certificate: string;
|
||||||
|
|||||||
@@ -46,4 +46,5 @@ export type SubscriptionPlan = {
|
|||||||
pkiEst: boolean;
|
pkiEst: boolean;
|
||||||
enforceMfa: boolean;
|
enforceMfa: boolean;
|
||||||
projectTemplates: boolean;
|
projectTemplates: boolean;
|
||||||
|
kmip: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ const KmipClientCertificateForm = ({
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center">
|
<div className="mt-8 flex items-center">
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -39,6 +40,7 @@ import {
|
|||||||
ProjectPermissionKmipActions,
|
ProjectPermissionKmipActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
useProjectPermission,
|
useProjectPermission,
|
||||||
|
useSubscription,
|
||||||
useWorkspace
|
useWorkspace
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
import { usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||||
@@ -81,6 +83,7 @@ export const KmipClientTable = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = useProjectPermission();
|
const { permission } = useProjectPermission();
|
||||||
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
const { kmipClients = [], totalCount = 0 } = data ?? {};
|
const { kmipClients = [], totalCount = 0 } = data ?? {};
|
||||||
useResetPageHelper({
|
useResetPageHelper({
|
||||||
@@ -93,7 +96,8 @@ export const KmipClientTable = () => {
|
|||||||
"upsertKmipClient",
|
"upsertKmipClient",
|
||||||
"deleteKmipClient",
|
"deleteKmipClient",
|
||||||
"generateKmipClientCert",
|
"generateKmipClientCert",
|
||||||
"displayKmipClientCert"
|
"displayKmipClientCert",
|
||||||
|
"upgradePlan"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const handleSort = () => {
|
const handleSort = () => {
|
||||||
@@ -152,7 +156,14 @@ export const KmipClientTable = () => {
|
|||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
onClick={() => handlePopUpOpen("upsertKmipClient", null)}
|
onClick={() => {
|
||||||
|
if (subscription && !subscription.kmip) {
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upsertKmipClient", null);
|
||||||
|
}}
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
>
|
>
|
||||||
Add KMIP Client
|
Add KMIP Client
|
||||||
@@ -202,7 +213,7 @@ export const KmipClientTable = () => {
|
|||||||
<Tr className="group h-10 hover:bg-mineshaft-700" key={`st-v3-${id}`}>
|
<Tr className="group h-10 hover:bg-mineshaft-700" key={`st-v3-${id}`}>
|
||||||
<Td>{name}</Td>
|
<Td>{name}</Td>
|
||||||
<Td className="max-w-80 break-all">{description}</Td>
|
<Td className="max-w-80 break-all">{description}</Td>
|
||||||
<Td className="max-w-40 break-all">{[permissions.join(", ")]}</Td>
|
<Td className="max-w-40">{[permissions.join(", ")]}</Td>
|
||||||
<Td className="flex justify-end">
|
<Td className="flex justify-end">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@@ -316,6 +327,11 @@ export const KmipClientTable = () => {
|
|||||||
onOpenChange={(isOpen) => handlePopUpToggle("displayKmipClientCert", isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle("displayKmipClientCert", isOpen)}
|
||||||
certificate={popUp.displayKmipClientCert.data}
|
certificate={popUp.displayKmipClientCert.data}
|
||||||
/>
|
/>
|
||||||
|
<UpgradePlanModal
|
||||||
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
|
text="KMIP requires an enterprise plan."
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
FormControl,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
|
||||||
Modal,
|
Modal,
|
||||||
ModalContent,
|
ModalContent,
|
||||||
Select,
|
Select,
|
||||||
@@ -18,18 +18,13 @@ import {
|
|||||||
TextArea,
|
TextArea,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useOrganization } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import { downloadTxtFile } from "@app/helpers/download";
|
import { downloadTxtFile } from "@app/helpers/download";
|
||||||
import { usePopUp, useTimedReset } from "@app/hooks";
|
import { usePopUp, useTimedReset } from "@app/hooks";
|
||||||
import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants";
|
import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants";
|
||||||
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
|
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
|
||||||
import {
|
import { useGetOrgKmipConfig, useSetupOrgKmip } from "@app/hooks/api/kmip";
|
||||||
useGenerateOrgKmipServerCert,
|
|
||||||
useGetOrgKmipConfig,
|
|
||||||
useSetupOrgKmip
|
|
||||||
} from "@app/hooks/api/kmip";
|
|
||||||
import { OrgKmipConfig } from "@app/hooks/api/kmip/types";
|
import { OrgKmipConfig } from "@app/hooks/api/kmip/types";
|
||||||
import { CertificateContent } from "@app/pages/cert-manager/CertificatesPage/components/CertificatesTab/components/CertificateContent";
|
|
||||||
|
|
||||||
const orgConfigFormSchema = z.object({
|
const orgConfigFormSchema = z.object({
|
||||||
caKeyAlgorithm: z.nativeEnum(CertKeyAlgorithm)
|
caKeyAlgorithm: z.nativeEnum(CertKeyAlgorithm)
|
||||||
@@ -45,8 +40,11 @@ const OrgConfigSection = ({
|
|||||||
isKmipConfigLoading: boolean;
|
isKmipConfigLoading: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
||||||
"configureKmip"
|
"configureKmip",
|
||||||
|
"upgradePlan"
|
||||||
] as const);
|
] as const);
|
||||||
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
control,
|
control,
|
||||||
@@ -181,6 +179,11 @@ const OrgConfigSection = ({
|
|||||||
<Button
|
<Button
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (subscription && !subscription.kmip) {
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("configureKmip");
|
handlePopUpOpen("configureKmip");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -244,178 +247,15 @@ const OrgConfigSection = ({
|
|||||||
</form>
|
</form>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<UpgradePlanModal
|
||||||
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
|
text="KMIP requires an enterprise plan."
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const orgServerCertFormSchema = z.object({
|
|
||||||
commonName: z.string(),
|
|
||||||
altNames: z.string(),
|
|
||||||
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
|
|
||||||
ttl: z.string()
|
|
||||||
});
|
|
||||||
|
|
||||||
type TOrgServerCertForm = z.infer<typeof orgServerCertFormSchema>;
|
|
||||||
|
|
||||||
export const KmipServerConfigSection = () => {
|
|
||||||
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
|
||||||
"configureKmipServerCert",
|
|
||||||
"showCertificate"
|
|
||||||
] as const);
|
|
||||||
|
|
||||||
const certificateData = popUp.showCertificate?.data as {
|
|
||||||
serialNumber: string;
|
|
||||||
certificate: string;
|
|
||||||
certificateChain: string;
|
|
||||||
privateKey: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
|
||||||
handleSubmit,
|
|
||||||
control,
|
|
||||||
formState: { isSubmitting }
|
|
||||||
} = useForm<TOrgServerCertForm>({
|
|
||||||
resolver: zodResolver(orgServerCertFormSchema)
|
|
||||||
});
|
|
||||||
|
|
||||||
const { mutateAsync: generateKmipServerCert } = useGenerateOrgKmipServerCert();
|
|
||||||
|
|
||||||
const onFormSubmit = async (formData: TOrgServerCertForm) => {
|
|
||||||
const { data: certificate } = await generateKmipServerCert(formData);
|
|
||||||
handlePopUpOpen("showCertificate", certificate);
|
|
||||||
|
|
||||||
createNotification({
|
|
||||||
type: "success",
|
|
||||||
text: "Successfully created KMIP server certificate"
|
|
||||||
});
|
|
||||||
|
|
||||||
handlePopUpClose("configureKmipServerCert");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mt-8 flex flex-col justify-start">
|
|
||||||
<div className="text-lg">KMIP Server Certificate</div>
|
|
||||||
<div className="mt-2 max-w-lg text-sm text-mineshaft-400">
|
|
||||||
These certificates should be used to configure TLS for the KMIP servers.
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
className="mt-2 w-fit"
|
|
||||||
onClick={() => {
|
|
||||||
handlePopUpOpen("configureKmipServerCert");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Generate KMIP server certificate
|
|
||||||
</Button>
|
|
||||||
<Modal
|
|
||||||
isOpen={popUp.configureKmipServerCert.isOpen}
|
|
||||||
onOpenChange={(state) => handlePopUpToggle("configureKmipServerCert", state)}
|
|
||||||
>
|
|
||||||
<ModalContent title="Configure KMIP for the organization">
|
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="commonName"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Common Name (CN)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
isRequired
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="service.acme.com" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="altNames"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Alternative Names (SANs)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="app1.acme.com, app2.acme.com, ..." />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="ttl"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="TTL"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
isRequired
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2 days, 1d, 2h, 1y, ..." />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="keyAlgorithm"
|
|
||||||
defaultValue={CertKeyAlgorithm.RSA_2048}
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Key Algorithm"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
helperText="This defines the key algorithm to use for signing the server certificate."
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
defaultValue={field.value}
|
|
||||||
{...field}
|
|
||||||
onValueChange={(e) => onChange(e)}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{certKeyAlgorithms.map(({ label, value }) => (
|
|
||||||
<SelectItem value={String(value || "")} key={label}>
|
|
||||||
{label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<div className="mt-6 flex w-full gap-4">
|
|
||||||
<Button
|
|
||||||
className=""
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
Continue
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
className=""
|
|
||||||
size="sm"
|
|
||||||
variant="outline_bg"
|
|
||||||
type="button"
|
|
||||||
onClick={() => handlePopUpClose("configureKmipServerCert")}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
<Modal
|
|
||||||
isOpen={popUp.showCertificate.isOpen}
|
|
||||||
onOpenChange={(state) => handlePopUpToggle("showCertificate", state)}
|
|
||||||
>
|
|
||||||
<ModalContent title="Configure KMIP for the organization">
|
|
||||||
<CertificateContent {...certificateData} />
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const KmipTab = () => {
|
export const KmipTab = () => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const { data: kmipConfig, isPending } = useGetOrgKmipConfig(currentOrg.id);
|
const { data: kmipConfig, isPending } = useGetOrgKmipConfig(currentOrg.id);
|
||||||
@@ -423,7 +263,6 @@ export const KmipTab = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<OrgConfigSection kmipConfig={kmipConfig} isKmipConfigLoading={isPending} />
|
<OrgConfigSection kmipConfig={kmipConfig} isKmipConfigLoading={isPending} />
|
||||||
{kmipConfig && <KmipServerConfigSection />}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user