mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 00:27:35 +00:00
misc: migrated KMIP PKI to be scoped at the org level
This commit is contained in:
Vendored
+14
-14
@@ -149,12 +149,12 @@ import {
|
|||||||
TKmipClients,
|
TKmipClients,
|
||||||
TKmipClientsInsert,
|
TKmipClientsInsert,
|
||||||
TKmipClientsUpdate,
|
TKmipClientsUpdate,
|
||||||
TKmipInstanceConfigs,
|
TKmipOrgConfigs,
|
||||||
TKmipInstanceConfigsInsert,
|
TKmipOrgConfigsInsert,
|
||||||
TKmipInstanceConfigsUpdate,
|
TKmipOrgConfigsUpdate,
|
||||||
TKmipInstanceServerCertificates,
|
TKmipOrgServerCertificates,
|
||||||
TKmipInstanceServerCertificatesInsert,
|
TKmipOrgServerCertificatesInsert,
|
||||||
TKmipInstanceServerCertificatesUpdate,
|
TKmipOrgServerCertificatesUpdate,
|
||||||
TKmsKeys,
|
TKmsKeys,
|
||||||
TKmsKeysInsert,
|
TKmsKeysInsert,
|
||||||
TKmsKeysUpdate,
|
TKmsKeysUpdate,
|
||||||
@@ -915,15 +915,15 @@ declare module "knex/types/tables" {
|
|||||||
>;
|
>;
|
||||||
[TableName.SecretSync]: KnexOriginal.CompositeTableType<TSecretSyncs, TSecretSyncsInsert, TSecretSyncsUpdate>;
|
[TableName.SecretSync]: KnexOriginal.CompositeTableType<TSecretSyncs, TSecretSyncsInsert, TSecretSyncsUpdate>;
|
||||||
[TableName.KmipClient]: KnexOriginal.CompositeTableType<TKmipClients, TKmipClientsInsert, TKmipClientsUpdate>;
|
[TableName.KmipClient]: KnexOriginal.CompositeTableType<TKmipClients, TKmipClientsInsert, TKmipClientsUpdate>;
|
||||||
[TableName.KmipInstanceConfig]: KnexOriginal.CompositeTableType<
|
[TableName.KmipOrgConfig]: KnexOriginal.CompositeTableType<
|
||||||
TKmipInstanceConfigs,
|
TKmipOrgConfigs,
|
||||||
TKmipInstanceConfigsInsert,
|
TKmipOrgConfigsInsert,
|
||||||
TKmipInstanceConfigsUpdate
|
TKmipOrgConfigsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.KmipInstanceServerCertificates]: KnexOriginal.CompositeTableType<
|
[TableName.KmipOrgServerCertificates]: KnexOriginal.CompositeTableType<
|
||||||
TKmipInstanceServerCertificates,
|
TKmipOrgServerCertificates,
|
||||||
TKmipInstanceServerCertificatesInsert,
|
TKmipOrgServerCertificatesInsert,
|
||||||
TKmipInstanceServerCertificatesUpdate
|
TKmipOrgServerCertificatesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.KmipClientCertificates]: KnexOriginal.CompositeTableType<
|
[TableName.KmipClientCertificates]: KnexOriginal.CompositeTableType<
|
||||||
TKmipClientCertificates,
|
TKmipClientCertificates,
|
||||||
|
|||||||
@@ -16,11 +16,15 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasKmipInstanceConfigTable = await knex.schema.hasTable(TableName.KmipInstanceConfig);
|
const hasKmipOrgPkiConfig = await knex.schema.hasTable(TableName.KmipOrgConfig);
|
||||||
if (!hasKmipInstanceConfigTable) {
|
if (!hasKmipOrgPkiConfig) {
|
||||||
await knex.schema.createTable(TableName.KmipInstanceConfig, (t) => {
|
await knex.schema.createTable(TableName.KmipOrgConfig, (t) => {
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
|
||||||
|
t.uuid("orgId").notNullable();
|
||||||
|
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
|
t.unique("orgId");
|
||||||
|
|
||||||
t.string("caKeyAlgorithm").notNullable();
|
t.string("caKeyAlgorithm").notNullable();
|
||||||
|
|
||||||
t.datetime("rootCaIssuedAt").notNullable();
|
t.datetime("rootCaIssuedAt").notNullable();
|
||||||
@@ -46,13 +50,15 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
t.timestamps(true, true, true);
|
t.timestamps(true, true, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.KmipInstanceConfig);
|
await createOnUpdateTrigger(knex, TableName.KmipOrgConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasKmipInstanceServerCertTable = await knex.schema.hasTable(TableName.KmipInstanceServerCertificates);
|
const hasKmipOrgServerCertTable = await knex.schema.hasTable(TableName.KmipOrgServerCertificates);
|
||||||
if (!hasKmipInstanceServerCertTable) {
|
if (!hasKmipOrgServerCertTable) {
|
||||||
await knex.schema.createTable(TableName.KmipInstanceServerCertificates, (t) => {
|
await knex.schema.createTable(TableName.KmipOrgServerCertificates, (t) => {
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("orgId").notNullable();
|
||||||
|
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
t.string("commonName").notNullable();
|
t.string("commonName").notNullable();
|
||||||
t.string("altNames").notNullable();
|
t.string("altNames").notNullable();
|
||||||
t.string("serialNumber").notNullable();
|
t.string("serialNumber").notNullable();
|
||||||
@@ -79,15 +85,15 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
export async function down(knex: Knex): Promise<void> {
|
||||||
const hasKmipInstanceConfigTable = await knex.schema.hasTable(TableName.KmipInstanceConfig);
|
const hasKmipOrgPkiConfig = await knex.schema.hasTable(TableName.KmipOrgConfig);
|
||||||
if (hasKmipInstanceConfigTable) {
|
if (hasKmipOrgPkiConfig) {
|
||||||
await knex.schema.dropTable(TableName.KmipInstanceConfig);
|
await knex.schema.dropTable(TableName.KmipOrgConfig);
|
||||||
await dropOnUpdateTrigger(knex, TableName.KmipInstanceConfig);
|
await dropOnUpdateTrigger(knex, TableName.KmipOrgConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasKmipInstanceServerCertTable = await knex.schema.hasTable(TableName.KmipInstanceServerCertificates);
|
const hasKmipOrgServerCertTable = await knex.schema.hasTable(TableName.KmipOrgServerCertificates);
|
||||||
if (hasKmipInstanceServerCertTable) {
|
if (hasKmipOrgServerCertTable) {
|
||||||
await knex.schema.dropTable(TableName.KmipInstanceServerCertificates);
|
await knex.schema.dropTable(TableName.KmipOrgServerCertificates);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasKmipClientCertTable = await knex.schema.hasTable(TableName.KmipClientCertificates);
|
const hasKmipClientCertTable = await knex.schema.hasTable(TableName.KmipClientCertificates);
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ export * from "./integrations";
|
|||||||
export * from "./internal-kms";
|
export * from "./internal-kms";
|
||||||
export * from "./kmip-client-certificates";
|
export * from "./kmip-client-certificates";
|
||||||
export * from "./kmip-clients";
|
export * from "./kmip-clients";
|
||||||
export * from "./kmip-instance-configs";
|
export * from "./kmip-org-configs";
|
||||||
export * from "./kmip-instance-server-certificates";
|
export * from "./kmip-org-server-certificates";
|
||||||
export * from "./kms-key-versions";
|
export * from "./kms-key-versions";
|
||||||
export * from "./kms-keys";
|
export * from "./kms-keys";
|
||||||
export * from "./kms-root-config";
|
export * from "./kms-root-config";
|
||||||
|
|||||||
+5
-4
@@ -9,8 +9,9 @@ import { zodBuffer } from "@app/lib/zod";
|
|||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
export const KmipInstanceConfigsSchema = z.object({
|
export const KmipOrgConfigsSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
|
orgId: z.string().uuid(),
|
||||||
caKeyAlgorithm: z.string(),
|
caKeyAlgorithm: z.string(),
|
||||||
rootCaIssuedAt: z.date(),
|
rootCaIssuedAt: z.date(),
|
||||||
rootCaExpiration: z.date(),
|
rootCaExpiration: z.date(),
|
||||||
@@ -33,6 +34,6 @@ export const KmipInstanceConfigsSchema = z.object({
|
|||||||
updatedAt: z.date()
|
updatedAt: z.date()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TKmipInstanceConfigs = z.infer<typeof KmipInstanceConfigsSchema>;
|
export type TKmipOrgConfigs = z.infer<typeof KmipOrgConfigsSchema>;
|
||||||
export type TKmipInstanceConfigsInsert = Omit<z.input<typeof KmipInstanceConfigsSchema>, TImmutableDBKeys>;
|
export type TKmipOrgConfigsInsert = Omit<z.input<typeof KmipOrgConfigsSchema>, TImmutableDBKeys>;
|
||||||
export type TKmipInstanceConfigsUpdate = Partial<Omit<z.input<typeof KmipInstanceConfigsSchema>, TImmutableDBKeys>>;
|
export type TKmipOrgConfigsUpdate = Partial<Omit<z.input<typeof KmipOrgConfigsSchema>, TImmutableDBKeys>>;
|
||||||
+6
-8
@@ -9,8 +9,9 @@ import { zodBuffer } from "@app/lib/zod";
|
|||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
export const KmipInstanceServerCertificatesSchema = z.object({
|
export const KmipOrgServerCertificatesSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
|
orgId: z.string().uuid(),
|
||||||
commonName: z.string(),
|
commonName: z.string(),
|
||||||
altNames: z.string(),
|
altNames: z.string(),
|
||||||
serialNumber: z.string(),
|
serialNumber: z.string(),
|
||||||
@@ -21,11 +22,8 @@ export const KmipInstanceServerCertificatesSchema = z.object({
|
|||||||
encryptedChain: zodBuffer
|
encryptedChain: zodBuffer
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TKmipInstanceServerCertificates = z.infer<typeof KmipInstanceServerCertificatesSchema>;
|
export type TKmipOrgServerCertificates = z.infer<typeof KmipOrgServerCertificatesSchema>;
|
||||||
export type TKmipInstanceServerCertificatesInsert = Omit<
|
export type TKmipOrgServerCertificatesInsert = Omit<z.input<typeof KmipOrgServerCertificatesSchema>, TImmutableDBKeys>;
|
||||||
z.input<typeof KmipInstanceServerCertificatesSchema>,
|
export type TKmipOrgServerCertificatesUpdate = Partial<
|
||||||
TImmutableDBKeys
|
Omit<z.input<typeof KmipOrgServerCertificatesSchema>, TImmutableDBKeys>
|
||||||
>;
|
|
||||||
export type TKmipInstanceServerCertificatesUpdate = Partial<
|
|
||||||
Omit<z.input<typeof KmipInstanceServerCertificatesSchema>, TImmutableDBKeys>
|
|
||||||
>;
|
>;
|
||||||
@@ -134,8 +134,8 @@ export enum TableName {
|
|||||||
AppConnection = "app_connections",
|
AppConnection = "app_connections",
|
||||||
SecretSync = "secret_syncs",
|
SecretSync = "secret_syncs",
|
||||||
KmipClient = "kmip_clients",
|
KmipClient = "kmip_clients",
|
||||||
KmipInstanceConfig = "kmip_instance_configs",
|
KmipOrgConfig = "kmip_org_configs",
|
||||||
KmipInstanceServerCertificates = "kmip_instance_server_certificates",
|
KmipOrgServerCertificates = "kmip_org_server_certificates",
|
||||||
KmipClientCertificates = "kmip_client_certificates"
|
KmipClientCertificates = "kmip_client_certificates"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
||||||
|
import { validateAltNamesField } from "@app/services/certificate-authority/certificate-authority-validators";
|
||||||
|
|
||||||
const KmipClientResponseSchema = KmipClientsSchema.pick({
|
const KmipClientResponseSchema = KmipClientsSchema.pick({
|
||||||
projectId: true,
|
projectId: true,
|
||||||
@@ -285,4 +286,92 @@ export const registerKmipRouter = async (server: FastifyZodProvider) => {
|
|||||||
return certificate;
|
return certificate;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
caKeyAlgorithm: z.nativeEnum(CertKeyAlgorithm)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
serverCertificateChain: z.string(),
|
||||||
|
clientCertificateChain: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
return server.services.kmip.setupOrgKmip({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
serverCertificateChain: z.string(),
|
||||||
|
clientCertificateChain: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
return server.services.kmip.getOrgKmip({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/server-certificates",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
commonName: z.string().trim().min(1),
|
||||||
|
altNames: validateAltNamesField,
|
||||||
|
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
|
||||||
|
ttl: z.string().refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
serialNumber: z.string(),
|
||||||
|
certificateChain: z.string(),
|
||||||
|
certificate: z.string(),
|
||||||
|
privateKey: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
return server.services.kmip.generateOrgKmipServerCertificate({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export const INSTANCE_KMIP_CONFIG_ID = "00000000-0000-0000-0000-000000000000";
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TKmipInstanceConfigDALFactory = ReturnType<typeof kmipInstanceConfigDALFactory>;
|
|
||||||
|
|
||||||
export const kmipInstanceConfigDALFactory = (db: TDbClient) => {
|
|
||||||
const kmipInstanceConfigOrm = ormify(db, TableName.KmipInstanceConfig);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...kmipInstanceConfigOrm
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TKmipInstanceServerCertificateDALFactory = ReturnType<typeof kmipInstanceServerCertificateDALFactory>;
|
|
||||||
|
|
||||||
export const kmipInstanceServerCertificateDALFactory = (db: TDbClient) => {
|
|
||||||
const kmipInstanceServerCertificateOrm = ormify(db, TableName.KmipInstanceServerCertificates);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...kmipInstanceServerCertificateOrm
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TKmipOrgConfigDALFactory = ReturnType<typeof kmipOrgConfigDALFactory>;
|
||||||
|
|
||||||
|
export const kmipOrgConfigDALFactory = (db: TDbClient) => {
|
||||||
|
const kmipOrgConfigOrm = ormify(db, TableName.KmipOrgConfig);
|
||||||
|
return {
|
||||||
|
...kmipOrgConfigOrm
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TKmipOrgServerCertificateDALFactory = ReturnType<typeof kmipOrgServerCertificateDALFactory>;
|
||||||
|
|
||||||
|
export const kmipOrgServerCertificateDALFactory = (db: TDbClient) => {
|
||||||
|
const kmipOrgServerCertificateOrm = ormify(db, TableName.KmipOrgServerCertificates);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...kmipOrgServerCertificateOrm
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -5,36 +5,42 @@ import ms from "ms";
|
|||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { isValidIp } from "@app/lib/ip";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import {
|
import {
|
||||||
createSerialNumber,
|
createSerialNumber,
|
||||||
keyAlgorithmToAlgCfg
|
keyAlgorithmToAlgCfg
|
||||||
} from "@app/services/certificate-authority/certificate-authority-fns";
|
} from "@app/services/certificate-authority/certificate-authority-fns";
|
||||||
|
import { hostnameRegex } from "@app/services/certificate-authority/certificate-authority-validators";
|
||||||
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 { 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";
|
||||||
import { TKmipClientCertificateDALFactory } from "./kmip-client-certificate-dal";
|
import { TKmipClientCertificateDALFactory } from "./kmip-client-certificate-dal";
|
||||||
import { TKmipClientDALFactory } from "./kmip-client-dal";
|
import { TKmipClientDALFactory } from "./kmip-client-dal";
|
||||||
import { INSTANCE_KMIP_CONFIG_ID } from "./kmip-constants";
|
import { TKmipOrgConfigDALFactory } from "./kmip-org-config-dal";
|
||||||
import { TKmipInstanceConfigDALFactory } from "./kmip-instance-config-dal";
|
import { TKmipOrgServerCertificateDALFactory } from "./kmip-org-server-certificate-dal";
|
||||||
import { TKmipInstanceServerCertificateDALFactory } from "./kmip-instance-server-certificate-dal";
|
|
||||||
import {
|
import {
|
||||||
TCreateKmipClientCertificateDTO,
|
TCreateKmipClientCertificateDTO,
|
||||||
TCreateKmipClientDTO,
|
TCreateKmipClientDTO,
|
||||||
TDeleteKmipClientDTO,
|
TDeleteKmipClientDTO,
|
||||||
|
TGenerateOrgKmipServerCertificateDTO,
|
||||||
TGetKmipClientDTO,
|
TGetKmipClientDTO,
|
||||||
|
TGetOrgKmipDTO,
|
||||||
TListKmipClientsByProjectIdDTO,
|
TListKmipClientsByProjectIdDTO,
|
||||||
|
TSetupOrgKmipDTO,
|
||||||
TUpdateKmipClientDTO
|
TUpdateKmipClientDTO
|
||||||
} from "./kmip-types";
|
} from "./kmip-types";
|
||||||
|
|
||||||
type TKmipServiceFactoryDep = {
|
type TKmipServiceFactoryDep = {
|
||||||
kmipClientDAL: TKmipClientDALFactory;
|
kmipClientDAL: TKmipClientDALFactory;
|
||||||
kmipClientCertificateDAL: TKmipClientCertificateDALFactory;
|
kmipClientCertificateDAL: TKmipClientCertificateDALFactory;
|
||||||
kmipInstanceServerCertificateDAL: TKmipInstanceServerCertificateDALFactory;
|
kmipOrgServerCertificateDAL: TKmipOrgServerCertificateDALFactory;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "decryptWithRootKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
kmipInstanceConfigDAL: TKmipInstanceConfigDALFactory;
|
kmipOrgConfigDAL: TKmipOrgConfigDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TKmipServiceFactory = ReturnType<typeof kmipServiceFactory>;
|
export type TKmipServiceFactory = ReturnType<typeof kmipServiceFactory>;
|
||||||
@@ -43,9 +49,9 @@ export const kmipServiceFactory = ({
|
|||||||
kmipClientDAL,
|
kmipClientDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
kmipClientCertificateDAL,
|
kmipClientCertificateDAL,
|
||||||
kmipInstanceConfigDAL,
|
kmipOrgConfigDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
kmipInstanceServerCertificateDAL
|
kmipOrgServerCertificateDAL
|
||||||
}: TKmipServiceFactoryDep) => {
|
}: TKmipServiceFactoryDep) => {
|
||||||
const createKmipClient = async ({
|
const createKmipClient = async ({
|
||||||
actor,
|
actor,
|
||||||
@@ -226,17 +232,23 @@ export const kmipServiceFactory = ({
|
|||||||
ProjectPermissionSub.Kmip
|
ProjectPermissionSub.Kmip
|
||||||
);
|
);
|
||||||
|
|
||||||
const kmipInstanceConfig = await kmipInstanceConfigDAL.findById(INSTANCE_KMIP_CONFIG_ID);
|
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
||||||
if (!kmipInstanceConfig) {
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!kmipConfig) {
|
||||||
throw new InternalServerError({
|
throw new InternalServerError({
|
||||||
message: "KMIP has not been configured for the instance."
|
message: "KMIP has not been configured for the organization"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const decryptWithRoot = kmsService.decryptWithRootKey();
|
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.Organization,
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
const caCertObj = new x509.X509Certificate(
|
const caCertObj = new x509.X509Certificate(
|
||||||
decryptWithRoot(kmipInstanceConfig.encryptedClientIntermediateCaCertificate)
|
decryptor({ cipherTextBlob: kmipConfig.encryptedClientIntermediateCaCertificate })
|
||||||
);
|
);
|
||||||
|
|
||||||
const notBeforeDate = new Date();
|
const notBeforeDate = new Date();
|
||||||
@@ -275,14 +287,14 @@ export const kmipServiceFactory = ({
|
|||||||
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.CLIENT_AUTH]], true)
|
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.CLIENT_AUTH]], true)
|
||||||
];
|
];
|
||||||
|
|
||||||
const caAlg = keyAlgorithmToAlgCfg(kmipInstanceConfig.caKeyAlgorithm as CertKeyAlgorithm);
|
const caAlg = keyAlgorithmToAlgCfg(kmipConfig.caKeyAlgorithm as CertKeyAlgorithm);
|
||||||
|
|
||||||
const decryptedCaCertChain = decryptWithRoot(kmipInstanceConfig.encryptedClientIntermediateCaChain).toString(
|
const decryptedCaCertChain = decryptor({ cipherTextBlob: kmipConfig.encryptedClientIntermediateCaChain }).toString(
|
||||||
"utf-8"
|
"utf-8"
|
||||||
);
|
);
|
||||||
|
|
||||||
const caSkObj = crypto.createPrivateKey({
|
const caSkObj = crypto.createPrivateKey({
|
||||||
key: decryptWithRoot(kmipInstanceConfig.encryptedClientIntermediateCaPrivateKey),
|
key: decryptor({ cipherTextBlob: kmipConfig.encryptedClientIntermediateCaPrivateKey }),
|
||||||
format: "der",
|
format: "der",
|
||||||
type: "pkcs8"
|
type: "pkcs8"
|
||||||
});
|
});
|
||||||
@@ -328,23 +340,350 @@ export const kmipServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getServerCertificateBySerialNumber = async (serialNumber: string) => {
|
const setupOrgKmip = async ({ caKeyAlgorithm, actorOrgId, actor, actorId, actorAuthMethod }: TSetupOrgKmipDTO) => {
|
||||||
const serverCert = await kmipInstanceServerCertificateDAL.findOne({
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
serialNumber
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Setup, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
|
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
||||||
|
orgId: actorOrgId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!serverCert) {
|
if (kmipConfig) {
|
||||||
throw new NotFoundError({
|
throw new BadRequestError({
|
||||||
message: "Server certificate not found"
|
message: "KMIP has already been configured for the organization"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const decryptWithRootKey = kmsService.decryptWithRootKey();
|
const alg = keyAlgorithmToAlgCfg(caKeyAlgorithm);
|
||||||
const parsedCertificate = new x509.X509Certificate(decryptWithRootKey(serverCert.encryptedCertificate));
|
|
||||||
|
// generate root CA
|
||||||
|
const rootCaSerialNumber = createSerialNumber();
|
||||||
|
const rootCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
||||||
|
const rootCaSkObj = KeyObject.from(rootCaKeys.privateKey);
|
||||||
|
const rootCaIssuedAt = new Date();
|
||||||
|
const rootCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 20));
|
||||||
|
|
||||||
|
const rootCaCert = await x509.X509CertificateGenerator.createSelfSigned({
|
||||||
|
name: `CN=KMIP Root CA,OU=${actorOrgId}`,
|
||||||
|
serialNumber: rootCaSerialNumber,
|
||||||
|
notBefore: rootCaIssuedAt,
|
||||||
|
notAfter: rootCaExpiration,
|
||||||
|
signingAlgorithm: alg,
|
||||||
|
keys: rootCaKeys,
|
||||||
|
extensions: [
|
||||||
|
// eslint-disable-next-line no-bitwise
|
||||||
|
new x509.KeyUsagesExtension(x509.KeyUsageFlags.keyCertSign | x509.KeyUsageFlags.cRLSign, true),
|
||||||
|
await x509.SubjectKeyIdentifierExtension.create(rootCaKeys.publicKey)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// generate intermediate server CA
|
||||||
|
const serverIntermediateCaSerialNumber = createSerialNumber();
|
||||||
|
const serverIntermediateCaIssuedAt = new Date();
|
||||||
|
const serverIntermediateCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
|
||||||
|
const serverIntermediateCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
||||||
|
const serverIntermediateCaSkObj = KeyObject.from(serverIntermediateCaKeys.privateKey);
|
||||||
|
|
||||||
|
const serverIntermediateCaCert = await x509.X509CertificateGenerator.create({
|
||||||
|
serialNumber: serverIntermediateCaSerialNumber,
|
||||||
|
subject: `CN=KMIP Server Intermediate CA,OU=${actorOrgId}`,
|
||||||
|
issuer: rootCaCert.subject,
|
||||||
|
notBefore: serverIntermediateCaIssuedAt,
|
||||||
|
notAfter: serverIntermediateCaExpiration,
|
||||||
|
signingKey: rootCaKeys.privateKey,
|
||||||
|
publicKey: serverIntermediateCaKeys.publicKey,
|
||||||
|
signingAlgorithm: alg,
|
||||||
|
extensions: [
|
||||||
|
new x509.KeyUsagesExtension(
|
||||||
|
// eslint-disable-next-line no-bitwise
|
||||||
|
x509.KeyUsageFlags.keyCertSign |
|
||||||
|
x509.KeyUsageFlags.cRLSign |
|
||||||
|
x509.KeyUsageFlags.digitalSignature |
|
||||||
|
x509.KeyUsageFlags.keyEncipherment,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
new x509.BasicConstraintsExtension(true, 0, true),
|
||||||
|
await x509.AuthorityKeyIdentifierExtension.create(rootCaCert, false),
|
||||||
|
await x509.SubjectKeyIdentifierExtension.create(serverIntermediateCaKeys.publicKey)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// generate intermediate client CA
|
||||||
|
const clientIntermediateCaSerialNumber = createSerialNumber();
|
||||||
|
const clientIntermediateCaIssuedAt = new Date();
|
||||||
|
const clientIntermediateCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
|
||||||
|
const clientIntermediateCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
||||||
|
const clientIntermediateCaSkObj = KeyObject.from(clientIntermediateCaKeys.privateKey);
|
||||||
|
|
||||||
|
const clientIntermediateCaCert = await x509.X509CertificateGenerator.create({
|
||||||
|
serialNumber: clientIntermediateCaSerialNumber,
|
||||||
|
subject: `CN=KMIP Client Intermediate CA,OU=${actorOrgId}`,
|
||||||
|
issuer: rootCaCert.subject,
|
||||||
|
notBefore: clientIntermediateCaIssuedAt,
|
||||||
|
notAfter: clientIntermediateCaExpiration,
|
||||||
|
signingKey: rootCaKeys.privateKey,
|
||||||
|
publicKey: clientIntermediateCaKeys.publicKey,
|
||||||
|
signingAlgorithm: alg,
|
||||||
|
extensions: [
|
||||||
|
new x509.KeyUsagesExtension(
|
||||||
|
// eslint-disable-next-line no-bitwise
|
||||||
|
x509.KeyUsageFlags.keyCertSign |
|
||||||
|
x509.KeyUsageFlags.cRLSign |
|
||||||
|
x509.KeyUsageFlags.digitalSignature |
|
||||||
|
x509.KeyUsageFlags.keyEncipherment,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
new x509.BasicConstraintsExtension(true, 0, true),
|
||||||
|
await x509.AuthorityKeyIdentifierExtension.create(rootCaCert, false),
|
||||||
|
await x509.SubjectKeyIdentifierExtension.create(clientIntermediateCaKeys.publicKey)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const { encryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.Organization,
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
await kmipOrgConfigDAL.create({
|
||||||
|
orgId: actorOrgId,
|
||||||
|
caKeyAlgorithm,
|
||||||
|
rootCaIssuedAt,
|
||||||
|
rootCaExpiration,
|
||||||
|
rootCaSerialNumber,
|
||||||
|
encryptedRootCaCertificate: encryptor({ plainText: Buffer.from(rootCaCert.rawData) }).cipherTextBlob,
|
||||||
|
encryptedRootCaPrivateKey: encryptor({
|
||||||
|
plainText: rootCaSkObj.export({
|
||||||
|
type: "pkcs8",
|
||||||
|
format: "der"
|
||||||
|
})
|
||||||
|
}).cipherTextBlob,
|
||||||
|
serverIntermediateCaIssuedAt,
|
||||||
|
serverIntermediateCaExpiration,
|
||||||
|
serverIntermediateCaSerialNumber,
|
||||||
|
encryptedServerIntermediateCaCertificate: encryptor({
|
||||||
|
plainText: Buffer.from(new Uint8Array(serverIntermediateCaCert.rawData))
|
||||||
|
}).cipherTextBlob,
|
||||||
|
encryptedServerIntermediateCaChain: encryptor({ plainText: Buffer.from(rootCaCert.toString("pem")) })
|
||||||
|
.cipherTextBlob,
|
||||||
|
encryptedServerIntermediateCaPrivateKey: encryptor({
|
||||||
|
plainText: serverIntermediateCaSkObj.export({
|
||||||
|
type: "pkcs8",
|
||||||
|
format: "der"
|
||||||
|
})
|
||||||
|
}).cipherTextBlob,
|
||||||
|
clientIntermediateCaIssuedAt,
|
||||||
|
clientIntermediateCaExpiration,
|
||||||
|
clientIntermediateCaSerialNumber,
|
||||||
|
encryptedClientIntermediateCaCertificate: encryptor({
|
||||||
|
plainText: Buffer.from(new Uint8Array(clientIntermediateCaCert.rawData))
|
||||||
|
}).cipherTextBlob,
|
||||||
|
encryptedClientIntermediateCaChain: encryptor({ plainText: Buffer.from(rootCaCert.toString("pem")) })
|
||||||
|
.cipherTextBlob,
|
||||||
|
encryptedClientIntermediateCaPrivateKey: encryptor({
|
||||||
|
plainText: clientIntermediateCaSkObj.export({
|
||||||
|
type: "pkcs8",
|
||||||
|
format: "der"
|
||||||
|
})
|
||||||
|
}).cipherTextBlob
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
publicKey: parsedCertificate.publicKey.toString("pem"),
|
serverCertificateChain: `${serverIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim(),
|
||||||
keyAlgorithm: serverCert.keyAlgorithm as CertKeyAlgorithm
|
clientCertificateChain: `${clientIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOrgKmip = async ({ actorOrgId }: TGetOrgKmipDTO) => {
|
||||||
|
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!kmipConfig) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "KMIP has not been configured for the organization"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.Organization,
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
const rootCaCert = new x509.X509Certificate(decryptor({ cipherTextBlob: kmipConfig.encryptedRootCaCertificate }));
|
||||||
|
const serverIntermediateCaCert = new x509.X509Certificate(
|
||||||
|
decryptor({ cipherTextBlob: kmipConfig.encryptedServerIntermediateCaCertificate })
|
||||||
|
);
|
||||||
|
|
||||||
|
const clientIntermediateCaCert = new x509.X509Certificate(
|
||||||
|
decryptor({ cipherTextBlob: kmipConfig.encryptedClientIntermediateCaCertificate })
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
serverCertificateChain: `${serverIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim(),
|
||||||
|
clientCertificateChain: `${clientIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateOrgKmipServerCertificate = async ({
|
||||||
|
actorOrgId,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
ttl,
|
||||||
|
commonName,
|
||||||
|
altNames,
|
||||||
|
keyAlgorithm
|
||||||
|
}: TGenerateOrgKmipServerCertificateDTO) => {
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Setup, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
|
const kmipOrgConfig = await kmipOrgConfigDAL.findOne({
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!kmipOrgConfig) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "KMIP has not been configured for the organization"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { decryptor, encryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.Organization,
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
const caCertObj = new x509.X509Certificate(
|
||||||
|
decryptor({ cipherTextBlob: kmipOrgConfig.encryptedServerIntermediateCaCertificate })
|
||||||
|
);
|
||||||
|
|
||||||
|
const notBeforeDate = new Date();
|
||||||
|
const notAfterDate = new Date(new Date().getTime() + ms(ttl));
|
||||||
|
|
||||||
|
const caCertNotBeforeDate = new Date(caCertObj.notBefore);
|
||||||
|
const caCertNotAfterDate = new Date(caCertObj.notAfter);
|
||||||
|
|
||||||
|
// check not before constraint
|
||||||
|
if (notBeforeDate < caCertNotBeforeDate) {
|
||||||
|
throw new BadRequestError({ message: "notBefore date is before CA certificate's notBefore date" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notBeforeDate > notAfterDate) throw new BadRequestError({ message: "notBefore date is after notAfter date" });
|
||||||
|
|
||||||
|
// check not after constraint
|
||||||
|
if (notAfterDate > caCertNotAfterDate) {
|
||||||
|
throw new BadRequestError({ message: "notAfter date is after CA certificate's notAfter date" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const alg = keyAlgorithmToAlgCfg(keyAlgorithm);
|
||||||
|
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
||||||
|
|
||||||
|
const extensions: x509.Extension[] = [
|
||||||
|
new x509.BasicConstraintsExtension(false),
|
||||||
|
await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false),
|
||||||
|
await x509.SubjectKeyIdentifierExtension.create(leafKeys.publicKey),
|
||||||
|
new x509.CertificatePolicyExtension(["2.5.29.32.0"]), // anyPolicy
|
||||||
|
new x509.KeyUsagesExtension(
|
||||||
|
// eslint-disable-next-line no-bitwise
|
||||||
|
x509.KeyUsageFlags[CertKeyUsage.DIGITAL_SIGNATURE] | x509.KeyUsageFlags[CertKeyUsage.KEY_ENCIPHERMENT],
|
||||||
|
true
|
||||||
|
),
|
||||||
|
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.SERVER_AUTH]], true)
|
||||||
|
];
|
||||||
|
|
||||||
|
const altNamesArray: {
|
||||||
|
type: "email" | "dns" | "ip";
|
||||||
|
value: string;
|
||||||
|
}[] = altNames
|
||||||
|
.split(",")
|
||||||
|
.map((name) => name.trim())
|
||||||
|
.map((altName) => {
|
||||||
|
// check if the altName is a valid hostname
|
||||||
|
if (hostnameRegex.test(altName)) {
|
||||||
|
return {
|
||||||
|
type: "dns",
|
||||||
|
value: altName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the altName is a valid IP
|
||||||
|
if (isValidIp(altName)) {
|
||||||
|
return {
|
||||||
|
type: "ip",
|
||||||
|
value: altName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Invalid altName: ${altName}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const altNamesExtension = new x509.SubjectAlternativeNameExtension(altNamesArray, false);
|
||||||
|
extensions.push(altNamesExtension);
|
||||||
|
|
||||||
|
const caAlg = keyAlgorithmToAlgCfg(kmipOrgConfig.caKeyAlgorithm as CertKeyAlgorithm);
|
||||||
|
|
||||||
|
const decryptedCaCertChain = decryptor({
|
||||||
|
cipherTextBlob: kmipOrgConfig.encryptedServerIntermediateCaChain
|
||||||
|
}).toString("utf-8");
|
||||||
|
|
||||||
|
const caSkObj = crypto.createPrivateKey({
|
||||||
|
key: decryptor({ cipherTextBlob: kmipOrgConfig.encryptedServerIntermediateCaPrivateKey }),
|
||||||
|
format: "der",
|
||||||
|
type: "pkcs8"
|
||||||
|
});
|
||||||
|
|
||||||
|
const caPrivateKey = await crypto.subtle.importKey(
|
||||||
|
"pkcs8",
|
||||||
|
caSkObj.export({ format: "der", type: "pkcs8" }),
|
||||||
|
caAlg,
|
||||||
|
true,
|
||||||
|
["sign"]
|
||||||
|
);
|
||||||
|
|
||||||
|
const serialNumber = createSerialNumber();
|
||||||
|
const leafCert = await x509.X509CertificateGenerator.create({
|
||||||
|
serialNumber,
|
||||||
|
subject: `CN=${commonName}`,
|
||||||
|
issuer: caCertObj.subject,
|
||||||
|
notBefore: notBeforeDate,
|
||||||
|
notAfter: notAfterDate,
|
||||||
|
signingKey: caPrivateKey,
|
||||||
|
publicKey: leafKeys.publicKey,
|
||||||
|
signingAlgorithm: alg,
|
||||||
|
extensions
|
||||||
|
});
|
||||||
|
|
||||||
|
const skLeafObj = KeyObject.from(leafKeys.privateKey);
|
||||||
|
const certificateChain = `${caCertObj.toString("pem")}\n${decryptedCaCertChain}`.trim();
|
||||||
|
|
||||||
|
await kmipOrgServerCertificateDAL.create({
|
||||||
|
orgId: actorOrgId,
|
||||||
|
keyAlgorithm,
|
||||||
|
issuedAt: notBeforeDate,
|
||||||
|
expiration: notAfterDate,
|
||||||
|
serialNumber,
|
||||||
|
commonName,
|
||||||
|
altNames,
|
||||||
|
encryptedCertificate: encryptor({ plainText: Buffer.from(new Uint8Array(leafCert.rawData)) }).cipherTextBlob,
|
||||||
|
encryptedChain: encryptor({ plainText: Buffer.from(certificateChain) }).cipherTextBlob
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
serialNumber,
|
||||||
|
privateKey: skLeafObj.export({ format: "pem", type: "pkcs8" }) as string,
|
||||||
|
certificate: leafCert.toString("pem"),
|
||||||
|
certificateChain
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -355,6 +694,8 @@ export const kmipServiceFactory = ({
|
|||||||
getKmipClient,
|
getKmipClient,
|
||||||
listKmipClientsByProjectId,
|
listKmipClientsByProjectId,
|
||||||
createKmipClientCertificate,
|
createKmipClientCertificate,
|
||||||
getServerCertificateBySerialNumber
|
setupOrgKmip,
|
||||||
|
generateOrgKmipServerCertificate,
|
||||||
|
getOrgKmip
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
|
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
|
||||||
import { OrderByDirection, TProjectPermission } from "@app/lib/types";
|
import { OrderByDirection, TOrgPermission, TProjectPermission } from "@app/lib/types";
|
||||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
||||||
|
|
||||||
import { KmipPermission } from "./kmip-enum";
|
import { KmipPermission } from "./kmip-enum";
|
||||||
@@ -79,3 +79,16 @@ export type TKmipRegisterDTO = {
|
|||||||
key: string;
|
key: string;
|
||||||
algorithm: SymmetricEncryption;
|
algorithm: SymmetricEncryption;
|
||||||
} & KmipOperationBaseDTO;
|
} & KmipOperationBaseDTO;
|
||||||
|
|
||||||
|
export type TSetupOrgKmipDTO = {
|
||||||
|
caKeyAlgorithm: CertKeyAlgorithm;
|
||||||
|
} & Omit<TOrgPermission, "orgId">;
|
||||||
|
|
||||||
|
export type TGetOrgKmipDTO = Omit<TOrgPermission, "orgId">;
|
||||||
|
|
||||||
|
export type TGenerateOrgKmipServerCertificateDTO = {
|
||||||
|
commonName: string;
|
||||||
|
altNames: string;
|
||||||
|
keyAlgorithm: CertKeyAlgorithm;
|
||||||
|
ttl: string;
|
||||||
|
} & Omit<TOrgPermission, "orgId">;
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ export enum OrgPermissionAppConnectionActions {
|
|||||||
Connect = "connect"
|
Connect = "connect"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrgPermissionKmipActions {
|
||||||
|
Proxy = "proxy",
|
||||||
|
Setup = "setup"
|
||||||
|
}
|
||||||
|
|
||||||
export enum OrgPermissionAdminConsoleAction {
|
export enum OrgPermissionAdminConsoleAction {
|
||||||
AccessAllProjects = "access-all-projects"
|
AccessAllProjects = "access-all-projects"
|
||||||
}
|
}
|
||||||
@@ -44,7 +49,8 @@ export enum OrgPermissionSubjects {
|
|||||||
AdminConsole = "organization-admin-console",
|
AdminConsole = "organization-admin-console",
|
||||||
AuditLogs = "audit-logs",
|
AuditLogs = "audit-logs",
|
||||||
ProjectTemplates = "project-templates",
|
ProjectTemplates = "project-templates",
|
||||||
AppConnections = "app-connections"
|
AppConnections = "app-connections",
|
||||||
|
Kmip = "kmip"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppConnectionSubjectFields = {
|
export type AppConnectionSubjectFields = {
|
||||||
@@ -74,7 +80,8 @@ export type OrgPermissionSet =
|
|||||||
| (ForcedSubject<OrgPermissionSubjects.AppConnections> & AppConnectionSubjectFields)
|
| (ForcedSubject<OrgPermissionSubjects.AppConnections> & AppConnectionSubjectFields)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole];
|
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
|
||||||
|
| [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip];
|
||||||
|
|
||||||
const AppConnectionConditionSchema = z
|
const AppConnectionConditionSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -167,6 +174,12 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
|
|||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionAdminConsoleAction).describe(
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionAdminConsoleAction).describe(
|
||||||
"Describe what action an entity can take."
|
"Describe what action an entity can take."
|
||||||
)
|
)
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Kmip).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionKmipActions).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
)
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -253,6 +266,8 @@ const buildAdminPermission = () => {
|
|||||||
|
|
||||||
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
|
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
|
||||||
|
|
||||||
|
can(OrgPermissionKmipActions.Setup, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ import { identityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/servic
|
|||||||
import { identityProjectAdditionalPrivilegeV2ServiceFactory } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service";
|
import { identityProjectAdditionalPrivilegeV2ServiceFactory } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service";
|
||||||
import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal";
|
import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal";
|
||||||
import { kmipClientDALFactory } from "@app/ee/services/kmip/kmip-client-dal";
|
import { kmipClientDALFactory } from "@app/ee/services/kmip/kmip-client-dal";
|
||||||
import { kmipInstanceConfigDALFactory } from "@app/ee/services/kmip/kmip-instance-config-dal";
|
|
||||||
import { kmipInstanceServerCertificateDALFactory } from "@app/ee/services/kmip/kmip-instance-server-certificate-dal";
|
|
||||||
import { kmipOperationServiceFactory } from "@app/ee/services/kmip/kmip-operation-service";
|
import { kmipOperationServiceFactory } from "@app/ee/services/kmip/kmip-operation-service";
|
||||||
|
import { kmipOrgConfigDALFactory } from "@app/ee/services/kmip/kmip-org-config-dal";
|
||||||
|
import { kmipOrgServerCertificateDALFactory } from "@app/ee/services/kmip/kmip-org-server-certificate-dal";
|
||||||
import { kmipServiceFactory } from "@app/ee/services/kmip/kmip-service";
|
import { kmipServiceFactory } from "@app/ee/services/kmip/kmip-service";
|
||||||
import { ldapConfigDALFactory } from "@app/ee/services/ldap-config/ldap-config-dal";
|
import { ldapConfigDALFactory } from "@app/ee/services/ldap-config/ldap-config-dal";
|
||||||
import { ldapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
import { ldapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
||||||
@@ -388,8 +388,8 @@ export const registerRoutes = async (
|
|||||||
const resourceMetadataDAL = resourceMetadataDALFactory(db);
|
const resourceMetadataDAL = resourceMetadataDALFactory(db);
|
||||||
const kmipClientDAL = kmipClientDALFactory(db);
|
const kmipClientDAL = kmipClientDALFactory(db);
|
||||||
const kmipClientCertificateDAL = kmipClientCertificateDALFactory(db);
|
const kmipClientCertificateDAL = kmipClientCertificateDALFactory(db);
|
||||||
const kmipInstanceConfigDAL = kmipInstanceConfigDALFactory(db);
|
const kmipOrgConfigDAL = kmipOrgConfigDALFactory(db);
|
||||||
const kmipInstanceServerCertificateDAL = kmipInstanceServerCertificateDALFactory(db);
|
const kmipOrgServerCertificateDAL = kmipOrgServerCertificateDALFactory(db);
|
||||||
|
|
||||||
const permissionService = permissionServiceFactory({
|
const permissionService = permissionServiceFactory({
|
||||||
permissionDAL,
|
permissionDAL,
|
||||||
@@ -630,9 +630,7 @@ export const registerRoutes = async (
|
|||||||
orgService,
|
orgService,
|
||||||
keyStore,
|
keyStore,
|
||||||
licenseService,
|
licenseService,
|
||||||
kmsService,
|
kmsService
|
||||||
kmipInstanceConfigDAL,
|
|
||||||
kmipInstanceServerCertificateDAL
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const orgAdminService = orgAdminServiceFactory({
|
const orgAdminService = orgAdminServiceFactory({
|
||||||
@@ -1434,9 +1432,9 @@ export const registerRoutes = async (
|
|||||||
kmipClientDAL,
|
kmipClientDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
kmipClientCertificateDAL,
|
kmipClientCertificateDAL,
|
||||||
kmipInstanceConfigDAL,
|
kmipOrgConfigDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
kmipInstanceServerCertificateDAL
|
kmipOrgServerCertificateDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const kmipOperationService = kmipOperationServiceFactory({
|
const kmipOperationService = kmipOperationServiceFactory({
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { OrganizationsSchema, SuperAdminSchema, UsersSchema } from "@app/db/schemas";
|
import { OrganizationsSchema, SuperAdminSchema, UsersSchema } from "@app/db/schemas";
|
||||||
@@ -8,8 +7,6 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
|||||||
import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
|
import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
|
||||||
import { validateAltNamesField } from "@app/services/certificate-authority/certificate-authority-validators";
|
|
||||||
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
@@ -319,91 +316,4 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "POST",
|
|
||||||
url: "/kmip",
|
|
||||||
config: {
|
|
||||||
rateLimit: writeLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
body: z.object({
|
|
||||||
caKeyAlgorithm: z.nativeEnum(CertKeyAlgorithm)
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
serverCertificateChain: z.string(),
|
|
||||||
clientCertificateChain: z.string()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: (req, res, done) => {
|
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
|
||||||
verifySuperAdmin(req, res, done);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handler: async (req) => {
|
|
||||||
return server.services.superAdmin.setupInstanceKmip({
|
|
||||||
...req.body
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "GET",
|
|
||||||
url: "/kmip",
|
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
serverCertificateChain: z.string(),
|
|
||||||
clientCertificateChain: z.string()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: (req, res, done) => {
|
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
|
||||||
verifySuperAdmin(req, res, done);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handler: async () => {
|
|
||||||
return server.services.superAdmin.getInstanceKmip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "POST",
|
|
||||||
url: "/kmip/server-certificates",
|
|
||||||
config: {
|
|
||||||
rateLimit: writeLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
body: z.object({
|
|
||||||
commonName: z.string().trim().min(1),
|
|
||||||
altNames: validateAltNamesField,
|
|
||||||
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
|
|
||||||
ttl: z.string().refine((val) => ms(val) > 0, "TTL must be a positive number")
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
serialNumber: z.string(),
|
|
||||||
certificateChain: z.string(),
|
|
||||||
certificate: z.string(),
|
|
||||||
privateKey: z.string()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: (req, res, done) => {
|
|
||||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
|
||||||
verifySuperAdmin(req, res, done);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handler: async (req) => {
|
|
||||||
return server.services.superAdmin.generateInstanceKmipServerCertificate({
|
|
||||||
...req.body
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,24 +1,15 @@
|
|||||||
import * as x509 from "@peculiar/x509";
|
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import crypto, { KeyObject } from "crypto";
|
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
||||||
import { TKmipInstanceConfigDALFactory } from "@app/ee/services/kmip/kmip-instance-config-dal";
|
|
||||||
import { TKmipInstanceServerCertificateDALFactory } from "@app/ee/services/kmip/kmip-instance-server-certificate-dal";
|
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||||
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||||
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { isValidIp } from "@app/lib/ip";
|
|
||||||
|
|
||||||
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
||||||
import { AuthMethod } from "../auth/auth-type";
|
import { AuthMethod } from "../auth/auth-type";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "../certificate/certificate-types";
|
|
||||||
import { createSerialNumber, keyAlgorithmToAlgCfg } from "../certificate-authority/certificate-authority-fns";
|
|
||||||
import { hostnameRegex } from "../certificate-authority/certificate-authority-validators";
|
|
||||||
import { KMS_ROOT_CONFIG_UUID } from "../kms/kms-fns";
|
import { KMS_ROOT_CONFIG_UUID } from "../kms/kms-fns";
|
||||||
import { TKmsRootConfigDALFactory } from "../kms/kms-root-config-dal";
|
import { TKmsRootConfigDALFactory } from "../kms/kms-root-config-dal";
|
||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
@@ -28,13 +19,7 @@ import { TUserDALFactory } from "../user/user-dal";
|
|||||||
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
||||||
import { UserAliasType } from "../user-alias/user-alias-types";
|
import { UserAliasType } from "../user-alias/user-alias-types";
|
||||||
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
||||||
import {
|
import { LoginMethod, TAdminGetUsersDTO, TAdminSignUpDTO } from "./super-admin-types";
|
||||||
LoginMethod,
|
|
||||||
TAdminGetUsersDTO,
|
|
||||||
TAdminSignUpDTO,
|
|
||||||
TGenerateInstanceKmipServerCertificateDTO,
|
|
||||||
TSetupInstanceKmipDTO
|
|
||||||
} from "./super-admin-types";
|
|
||||||
|
|
||||||
type TSuperAdminServiceFactoryDep = {
|
type TSuperAdminServiceFactoryDep = {
|
||||||
serverCfgDAL: TSuperAdminDALFactory;
|
serverCfgDAL: TSuperAdminDALFactory;
|
||||||
@@ -46,8 +31,6 @@ type TSuperAdminServiceFactoryDep = {
|
|||||||
orgService: Pick<TOrgServiceFactory, "createOrganization">;
|
orgService: Pick<TOrgServiceFactory, "createOrganization">;
|
||||||
keyStore: Pick<TKeyStoreFactory, "getItem" | "setItemWithExpiry" | "deleteItem">;
|
keyStore: Pick<TKeyStoreFactory, "getItem" | "setItemWithExpiry" | "deleteItem">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures">;
|
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures">;
|
||||||
kmipInstanceConfigDAL: TKmipInstanceConfigDALFactory;
|
|
||||||
kmipInstanceServerCertificateDAL: TKmipInstanceServerCertificateDALFactory;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFactory>;
|
export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFactory>;
|
||||||
@@ -74,9 +57,7 @@ export const superAdminServiceFactory = ({
|
|||||||
keyStore,
|
keyStore,
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
licenseService,
|
licenseService
|
||||||
kmipInstanceConfigDAL,
|
|
||||||
kmipInstanceServerCertificateDAL
|
|
||||||
}: TSuperAdminServiceFactoryDep) => {
|
}: TSuperAdminServiceFactoryDep) => {
|
||||||
const initServerCfg = async () => {
|
const initServerCfg = async () => {
|
||||||
// TODO(akhilmhdh): bad pattern time less change this later to me itself
|
// TODO(akhilmhdh): bad pattern time less change this later to me itself
|
||||||
@@ -388,309 +369,6 @@ export const superAdminServiceFactory = ({
|
|||||||
await kmsService.updateEncryptionStrategy(strategy);
|
await kmsService.updateEncryptionStrategy(strategy);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setupInstanceKmip = async ({ caKeyAlgorithm }: TSetupInstanceKmipDTO) => {
|
|
||||||
const kmipInstanceConfig = await kmipInstanceConfigDAL.findById(ADMIN_CONFIG_DB_UUID);
|
|
||||||
if (kmipInstanceConfig) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "KMIP has already been configured for the instance"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const alg = keyAlgorithmToAlgCfg(caKeyAlgorithm);
|
|
||||||
|
|
||||||
// generate root CA
|
|
||||||
const rootCaSerialNumber = createSerialNumber();
|
|
||||||
const rootCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
|
||||||
const rootCaSkObj = KeyObject.from(rootCaKeys.privateKey);
|
|
||||||
const rootCaIssuedAt = new Date();
|
|
||||||
const rootCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 20));
|
|
||||||
|
|
||||||
const rootCaCert = await x509.X509CertificateGenerator.createSelfSigned({
|
|
||||||
name: "CN=KMIP Root CA",
|
|
||||||
serialNumber: rootCaSerialNumber,
|
|
||||||
notBefore: rootCaIssuedAt,
|
|
||||||
notAfter: rootCaExpiration,
|
|
||||||
signingAlgorithm: alg,
|
|
||||||
keys: rootCaKeys,
|
|
||||||
extensions: [
|
|
||||||
// eslint-disable-next-line no-bitwise
|
|
||||||
new x509.KeyUsagesExtension(x509.KeyUsageFlags.keyCertSign | x509.KeyUsageFlags.cRLSign, true),
|
|
||||||
await x509.SubjectKeyIdentifierExtension.create(rootCaKeys.publicKey)
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// generate intermediate server CA
|
|
||||||
const serverIntermediateCaSerialNumber = createSerialNumber();
|
|
||||||
const serverIntermediateCaIssuedAt = new Date();
|
|
||||||
const serverIntermediateCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
|
|
||||||
const serverIntermediateCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
|
||||||
const serverIntermediateCaSkObj = KeyObject.from(serverIntermediateCaKeys.privateKey);
|
|
||||||
|
|
||||||
const serverIntermediateCaCert = await x509.X509CertificateGenerator.create({
|
|
||||||
serialNumber: serverIntermediateCaSerialNumber,
|
|
||||||
subject: "CN=KMIP Server Intermediate CA",
|
|
||||||
issuer: rootCaCert.subject,
|
|
||||||
notBefore: serverIntermediateCaIssuedAt,
|
|
||||||
notAfter: serverIntermediateCaExpiration,
|
|
||||||
signingKey: rootCaKeys.privateKey,
|
|
||||||
publicKey: serverIntermediateCaKeys.publicKey,
|
|
||||||
signingAlgorithm: alg,
|
|
||||||
extensions: [
|
|
||||||
new x509.KeyUsagesExtension(
|
|
||||||
// eslint-disable-next-line no-bitwise
|
|
||||||
x509.KeyUsageFlags.keyCertSign |
|
|
||||||
x509.KeyUsageFlags.cRLSign |
|
|
||||||
x509.KeyUsageFlags.digitalSignature |
|
|
||||||
x509.KeyUsageFlags.keyEncipherment,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
new x509.BasicConstraintsExtension(true, 0, true),
|
|
||||||
await x509.AuthorityKeyIdentifierExtension.create(rootCaCert, false),
|
|
||||||
await x509.SubjectKeyIdentifierExtension.create(serverIntermediateCaKeys.publicKey)
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// generate intermediate client CA
|
|
||||||
const clientIntermediateCaSerialNumber = createSerialNumber();
|
|
||||||
const clientIntermediateCaIssuedAt = new Date();
|
|
||||||
const clientIntermediateCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
|
|
||||||
const clientIntermediateCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
|
||||||
const clientIntermediateCaSkObj = KeyObject.from(clientIntermediateCaKeys.privateKey);
|
|
||||||
|
|
||||||
const clientIntermediateCaCert = await x509.X509CertificateGenerator.create({
|
|
||||||
serialNumber: clientIntermediateCaSerialNumber,
|
|
||||||
subject: "CN=KMIP Client Intermediate CA",
|
|
||||||
issuer: rootCaCert.subject,
|
|
||||||
notBefore: clientIntermediateCaIssuedAt,
|
|
||||||
notAfter: clientIntermediateCaExpiration,
|
|
||||||
signingKey: rootCaKeys.privateKey,
|
|
||||||
publicKey: clientIntermediateCaKeys.publicKey,
|
|
||||||
signingAlgorithm: alg,
|
|
||||||
extensions: [
|
|
||||||
new x509.KeyUsagesExtension(
|
|
||||||
// eslint-disable-next-line no-bitwise
|
|
||||||
x509.KeyUsageFlags.keyCertSign |
|
|
||||||
x509.KeyUsageFlags.cRLSign |
|
|
||||||
x509.KeyUsageFlags.digitalSignature |
|
|
||||||
x509.KeyUsageFlags.keyEncipherment,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
new x509.BasicConstraintsExtension(true, 0, true),
|
|
||||||
await x509.AuthorityKeyIdentifierExtension.create(rootCaCert, false),
|
|
||||||
await x509.SubjectKeyIdentifierExtension.create(clientIntermediateCaKeys.publicKey)
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const encryptWithRoot = kmsService.encryptWithRootKey();
|
|
||||||
|
|
||||||
await kmipInstanceConfigDAL.create({
|
|
||||||
// @ts-expect-error id is kept as fixed for idempotence and to avoid race condition
|
|
||||||
id: ADMIN_CONFIG_DB_UUID,
|
|
||||||
caKeyAlgorithm,
|
|
||||||
rootCaIssuedAt,
|
|
||||||
rootCaExpiration,
|
|
||||||
rootCaSerialNumber,
|
|
||||||
encryptedRootCaCertificate: encryptWithRoot(Buffer.from(rootCaCert.rawData)),
|
|
||||||
encryptedRootCaPrivateKey: encryptWithRoot(
|
|
||||||
rootCaSkObj.export({
|
|
||||||
type: "pkcs8",
|
|
||||||
format: "der"
|
|
||||||
})
|
|
||||||
),
|
|
||||||
serverIntermediateCaIssuedAt,
|
|
||||||
serverIntermediateCaExpiration,
|
|
||||||
serverIntermediateCaSerialNumber,
|
|
||||||
encryptedServerIntermediateCaCertificate: encryptWithRoot(
|
|
||||||
Buffer.from(new Uint8Array(serverIntermediateCaCert.rawData))
|
|
||||||
),
|
|
||||||
encryptedServerIntermediateCaChain: encryptWithRoot(Buffer.from(rootCaCert.toString("pem"))),
|
|
||||||
encryptedServerIntermediateCaPrivateKey: encryptWithRoot(
|
|
||||||
serverIntermediateCaSkObj.export({
|
|
||||||
type: "pkcs8",
|
|
||||||
format: "der"
|
|
||||||
})
|
|
||||||
),
|
|
||||||
clientIntermediateCaIssuedAt,
|
|
||||||
clientIntermediateCaExpiration,
|
|
||||||
clientIntermediateCaSerialNumber,
|
|
||||||
encryptedClientIntermediateCaCertificate: encryptWithRoot(
|
|
||||||
Buffer.from(new Uint8Array(clientIntermediateCaCert.rawData))
|
|
||||||
),
|
|
||||||
encryptedClientIntermediateCaChain: encryptWithRoot(Buffer.from(rootCaCert.toString("pem"))),
|
|
||||||
encryptedClientIntermediateCaPrivateKey: encryptWithRoot(
|
|
||||||
clientIntermediateCaSkObj.export({
|
|
||||||
type: "pkcs8",
|
|
||||||
format: "der"
|
|
||||||
})
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
serverCertificateChain: `${serverIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim(),
|
|
||||||
clientCertificateChain: `${clientIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getInstanceKmip = async () => {
|
|
||||||
const kmipInstanceConfig = await kmipInstanceConfigDAL.findById(ADMIN_CONFIG_DB_UUID);
|
|
||||||
if (!kmipInstanceConfig) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "KMIP has not been configured for the instance"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const decryptWithRoot = kmsService.decryptWithRootKey();
|
|
||||||
const rootCaCert = new x509.X509Certificate(decryptWithRoot(kmipInstanceConfig.encryptedRootCaCertificate));
|
|
||||||
const serverIntermediateCaCert = new x509.X509Certificate(
|
|
||||||
decryptWithRoot(kmipInstanceConfig.encryptedServerIntermediateCaCertificate)
|
|
||||||
);
|
|
||||||
const clientIntermediateCaCert = new x509.X509Certificate(
|
|
||||||
decryptWithRoot(kmipInstanceConfig.encryptedClientIntermediateCaCertificate)
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
serverCertificateChain: `${serverIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim(),
|
|
||||||
clientCertificateChain: `${clientIntermediateCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateInstanceKmipServerCertificate = async ({
|
|
||||||
ttl,
|
|
||||||
commonName,
|
|
||||||
altNames,
|
|
||||||
keyAlgorithm
|
|
||||||
}: TGenerateInstanceKmipServerCertificateDTO) => {
|
|
||||||
const kmipInstanceConfig = await kmipInstanceConfigDAL.findById(ADMIN_CONFIG_DB_UUID);
|
|
||||||
if (!kmipInstanceConfig) {
|
|
||||||
throw new InternalServerError({
|
|
||||||
message: "KMIP has not been configured for the instance"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const decryptWithRoot = kmsService.decryptWithRootKey();
|
|
||||||
const caCertObj = new x509.X509Certificate(
|
|
||||||
decryptWithRoot(kmipInstanceConfig.encryptedServerIntermediateCaCertificate)
|
|
||||||
);
|
|
||||||
|
|
||||||
const notBeforeDate = new Date();
|
|
||||||
const notAfterDate = new Date(new Date().getTime() + ms(ttl));
|
|
||||||
|
|
||||||
const caCertNotBeforeDate = new Date(caCertObj.notBefore);
|
|
||||||
const caCertNotAfterDate = new Date(caCertObj.notAfter);
|
|
||||||
|
|
||||||
// check not before constraint
|
|
||||||
if (notBeforeDate < caCertNotBeforeDate) {
|
|
||||||
throw new BadRequestError({ message: "notBefore date is before CA certificate's notBefore date" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notBeforeDate > notAfterDate) throw new BadRequestError({ message: "notBefore date is after notAfter date" });
|
|
||||||
|
|
||||||
// check not after constraint
|
|
||||||
if (notAfterDate > caCertNotAfterDate) {
|
|
||||||
throw new BadRequestError({ message: "notAfter date is after CA certificate's notAfter date" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const alg = keyAlgorithmToAlgCfg(keyAlgorithm);
|
|
||||||
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
|
||||||
|
|
||||||
const extensions: x509.Extension[] = [
|
|
||||||
new x509.BasicConstraintsExtension(false),
|
|
||||||
await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false),
|
|
||||||
await x509.SubjectKeyIdentifierExtension.create(leafKeys.publicKey),
|
|
||||||
new x509.CertificatePolicyExtension(["2.5.29.32.0"]), // anyPolicy
|
|
||||||
new x509.KeyUsagesExtension(
|
|
||||||
// eslint-disable-next-line no-bitwise
|
|
||||||
x509.KeyUsageFlags[CertKeyUsage.DIGITAL_SIGNATURE] | x509.KeyUsageFlags[CertKeyUsage.KEY_ENCIPHERMENT],
|
|
||||||
true
|
|
||||||
),
|
|
||||||
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.SERVER_AUTH]], true)
|
|
||||||
];
|
|
||||||
|
|
||||||
const altNamesArray: {
|
|
||||||
type: "email" | "dns" | "ip";
|
|
||||||
value: string;
|
|
||||||
}[] = altNames
|
|
||||||
.split(",")
|
|
||||||
.map((name) => name.trim())
|
|
||||||
.map((altName) => {
|
|
||||||
// check if the altName is a valid hostname
|
|
||||||
if (hostnameRegex.test(altName)) {
|
|
||||||
return {
|
|
||||||
type: "dns",
|
|
||||||
value: altName
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the altName is a valid IP
|
|
||||||
if (isValidIp(altName)) {
|
|
||||||
return {
|
|
||||||
type: "ip",
|
|
||||||
value: altName
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Invalid altName: ${altName}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
const altNamesExtension = new x509.SubjectAlternativeNameExtension(altNamesArray, false);
|
|
||||||
extensions.push(altNamesExtension);
|
|
||||||
|
|
||||||
const caAlg = keyAlgorithmToAlgCfg(kmipInstanceConfig.caKeyAlgorithm as CertKeyAlgorithm);
|
|
||||||
|
|
||||||
const decryptedCaCertChain = decryptWithRoot(kmipInstanceConfig.encryptedServerIntermediateCaChain).toString(
|
|
||||||
"utf-8"
|
|
||||||
);
|
|
||||||
|
|
||||||
const caSkObj = crypto.createPrivateKey({
|
|
||||||
key: decryptWithRoot(kmipInstanceConfig.encryptedServerIntermediateCaPrivateKey),
|
|
||||||
format: "der",
|
|
||||||
type: "pkcs8"
|
|
||||||
});
|
|
||||||
|
|
||||||
const caPrivateKey = await crypto.subtle.importKey(
|
|
||||||
"pkcs8",
|
|
||||||
caSkObj.export({ format: "der", type: "pkcs8" }),
|
|
||||||
caAlg,
|
|
||||||
true,
|
|
||||||
["sign"]
|
|
||||||
);
|
|
||||||
|
|
||||||
const serialNumber = createSerialNumber();
|
|
||||||
const leafCert = await x509.X509CertificateGenerator.create({
|
|
||||||
serialNumber,
|
|
||||||
subject: `CN=${commonName}`,
|
|
||||||
issuer: caCertObj.subject,
|
|
||||||
notBefore: notBeforeDate,
|
|
||||||
notAfter: notAfterDate,
|
|
||||||
signingKey: caPrivateKey,
|
|
||||||
publicKey: leafKeys.publicKey,
|
|
||||||
signingAlgorithm: alg,
|
|
||||||
extensions
|
|
||||||
});
|
|
||||||
|
|
||||||
const encryptWithRoot = kmsService.encryptWithRootKey();
|
|
||||||
const skLeafObj = KeyObject.from(leafKeys.privateKey);
|
|
||||||
const certificateChain = `${caCertObj.toString("pem")}\n${decryptedCaCertChain}`.trim();
|
|
||||||
|
|
||||||
await kmipInstanceServerCertificateDAL.create({
|
|
||||||
keyAlgorithm,
|
|
||||||
issuedAt: notBeforeDate,
|
|
||||||
expiration: notAfterDate,
|
|
||||||
serialNumber,
|
|
||||||
commonName,
|
|
||||||
altNames,
|
|
||||||
encryptedCertificate: encryptWithRoot(Buffer.from(new Uint8Array(leafCert.rawData))),
|
|
||||||
encryptedChain: encryptWithRoot(Buffer.from(certificateChain))
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
serialNumber,
|
|
||||||
privateKey: skLeafObj.export({ format: "pem", type: "pkcs8" }) as string,
|
|
||||||
certificate: leafCert.toString("pem"),
|
|
||||||
certificateChain
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initServerCfg,
|
initServerCfg,
|
||||||
updateServerCfg,
|
updateServerCfg,
|
||||||
@@ -699,9 +377,6 @@ export const superAdminServiceFactory = ({
|
|||||||
deleteUser,
|
deleteUser,
|
||||||
getAdminSlackConfig,
|
getAdminSlackConfig,
|
||||||
updateRootEncryptionStrategy,
|
updateRootEncryptionStrategy,
|
||||||
getConfiguredEncryptionStrategies,
|
getConfiguredEncryptionStrategies
|
||||||
setupInstanceKmip,
|
|
||||||
getInstanceKmip,
|
|
||||||
generateInstanceKmipServerCertificate
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { CertKeyAlgorithm } from "../certificate/certificate-types";
|
|
||||||
|
|
||||||
export type TAdminSignUpDTO = {
|
export type TAdminSignUpDTO = {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
@@ -33,14 +31,3 @@ export enum LoginMethod {
|
|||||||
LDAP = "ldap",
|
LDAP = "ldap",
|
||||||
OIDC = "oidc"
|
OIDC = "oidc"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TSetupInstanceKmipDTO = {
|
|
||||||
caKeyAlgorithm: CertKeyAlgorithm;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGenerateInstanceKmipServerCertificateDTO = {
|
|
||||||
commonName: string;
|
|
||||||
altNames: string;
|
|
||||||
keyAlgorithm: CertKeyAlgorithm;
|
|
||||||
ttl: string;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export enum OrgPermissionSubjects {
|
|||||||
AdminConsole = "organization-admin-console",
|
AdminConsole = "organization-admin-console",
|
||||||
AuditLogs = "audit-logs",
|
AuditLogs = "audit-logs",
|
||||||
ProjectTemplates = "project-templates",
|
ProjectTemplates = "project-templates",
|
||||||
AppConnections = "app-connections"
|
AppConnections = "app-connections",
|
||||||
|
Kmip = "kmip"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum OrgPermissionAdminConsoleAction {
|
export enum OrgPermissionAdminConsoleAction {
|
||||||
@@ -39,6 +40,11 @@ export enum OrgPermissionAppConnectionActions {
|
|||||||
Connect = "connect"
|
Connect = "connect"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrgPermissionKmipActions {
|
||||||
|
Proxy = "proxy",
|
||||||
|
Setup = "setup"
|
||||||
|
}
|
||||||
|
|
||||||
export type AppConnectionSubjectFields = {
|
export type AppConnectionSubjectFields = {
|
||||||
connectionId: string;
|
connectionId: string;
|
||||||
};
|
};
|
||||||
@@ -61,7 +67,8 @@ export type OrgPermissionSet =
|
|||||||
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
|
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
|
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
|
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
|
||||||
| [OrgPermissionAppConnectionActions, OrgPermissionSubjects.AppConnections];
|
| [OrgPermissionAppConnectionActions, OrgPermissionSubjects.AppConnections]
|
||||||
|
| [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip];
|
||||||
// TODO(scott): add back once org UI refactored
|
// TODO(scott): add back once org UI refactored
|
||||||
// | [
|
// | [
|
||||||
// OrgPermissionAppConnectionActions,
|
// OrgPermissionAppConnectionActions,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
export {
|
export {
|
||||||
useAdminDeleteUser,
|
useAdminDeleteUser,
|
||||||
useCreateAdminUser,
|
useCreateAdminUser,
|
||||||
useSetupInstanceKmip,
|
|
||||||
useUpdateAdminSlackConfig,
|
useUpdateAdminSlackConfig,
|
||||||
useUpdateServerConfig,
|
useUpdateServerConfig,
|
||||||
useUpdateServerEncryptionStrategy
|
useUpdateServerEncryptionStrategy
|
||||||
@@ -9,7 +8,6 @@ export {
|
|||||||
export {
|
export {
|
||||||
useAdminGetUsers,
|
useAdminGetUsers,
|
||||||
useGetAdminSlackConfig,
|
useGetAdminSlackConfig,
|
||||||
useGetInstanceKmipConfig,
|
|
||||||
useGetServerConfig,
|
useGetServerConfig,
|
||||||
useGetServerRootKmsEncryptionDetails
|
useGetServerRootKmsEncryptionDetails
|
||||||
} from "./queries";
|
} from "./queries";
|
||||||
|
|||||||
@@ -7,12 +7,9 @@ import { User } from "../users/types";
|
|||||||
import { adminQueryKeys, adminStandaloneKeys } from "./queries";
|
import { adminQueryKeys, adminStandaloneKeys } from "./queries";
|
||||||
import {
|
import {
|
||||||
AdminSlackConfig,
|
AdminSlackConfig,
|
||||||
InstanceKmipServerCert,
|
|
||||||
RootKeyEncryptionStrategy,
|
RootKeyEncryptionStrategy,
|
||||||
TCreateAdminUserDTO,
|
TCreateAdminUserDTO,
|
||||||
TGenerateInstanceKmipServerCertDTO,
|
|
||||||
TServerConfig,
|
TServerConfig,
|
||||||
TSetupInstanceKmipDTO,
|
|
||||||
TUpdateAdminSlackConfigDTO
|
TUpdateAdminSlackConfigDTO
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
@@ -101,26 +98,3 @@ export const useUpdateServerEncryptionStrategy = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSetupInstanceKmip = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
return useMutation({
|
|
||||||
mutationFn: async (payload: TSetupInstanceKmipDTO) => {
|
|
||||||
await apiRequest.post("/api/v1/admin/kmip", payload);
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: adminQueryKeys.getInstanceKmip() });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGenerateInstanceKmipServerCert = () => {
|
|
||||||
return useMutation({
|
|
||||||
mutationFn: async (payload: TGenerateInstanceKmipServerCertDTO) => {
|
|
||||||
return apiRequest.post<InstanceKmipServerCert>(
|
|
||||||
"/api/v1/admin/kmip/server-certificates",
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { User } from "../types";
|
|||||||
import {
|
import {
|
||||||
AdminGetUsersFilters,
|
AdminGetUsersFilters,
|
||||||
AdminSlackConfig,
|
AdminSlackConfig,
|
||||||
InstanceKmipConfig,
|
|
||||||
TGetServerRootKmsEncryptionDetails,
|
TGetServerRootKmsEncryptionDetails,
|
||||||
TServerConfig
|
TServerConfig
|
||||||
} from "./types";
|
} from "./types";
|
||||||
@@ -95,14 +94,3 @@ export const useGetServerRootKmsEncryptionDetails = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetInstanceKmipConfig = () => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: adminQueryKeys.getInstanceKmip(),
|
|
||||||
queryFn: async () => {
|
|
||||||
const { data } = await apiRequest.get<InstanceKmipConfig>("/api/v1/admin/kmip");
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { CertKeyAlgorithm } from "../certificates/enums";
|
|
||||||
|
|
||||||
export enum LoginMethod {
|
export enum LoginMethod {
|
||||||
EMAIL = "email",
|
EMAIL = "email",
|
||||||
GOOGLE = "google",
|
GOOGLE = "google",
|
||||||
@@ -64,30 +62,7 @@ export type TGetServerRootKmsEncryptionDetails = {
|
|||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InstanceKmipConfig = {
|
|
||||||
serverCertificateChain: string;
|
|
||||||
clientCertificateChain: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum RootKeyEncryptionStrategy {
|
export enum RootKeyEncryptionStrategy {
|
||||||
Software = "SOFTWARE",
|
Software = "SOFTWARE",
|
||||||
HSM = "HSM"
|
HSM = "HSM"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TSetupInstanceKmipDTO = {
|
|
||||||
caKeyAlgorithm: CertKeyAlgorithm;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGenerateInstanceKmipServerCertDTO = {
|
|
||||||
commonName: string;
|
|
||||||
keyAlgorithm: CertKeyAlgorithm;
|
|
||||||
altNames: string;
|
|
||||||
ttl: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InstanceKmipServerCert = {
|
|
||||||
serialNumber: string;
|
|
||||||
certificate: string;
|
|
||||||
certificateChain: string;
|
|
||||||
privateKey: string;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ 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,
|
||||||
TUpdateKmipClient
|
TUpdateKmipClient
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
@@ -75,3 +78,23 @@ export const useGenerateKmipClientCertificate = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSetupOrgKmip = (orgId: string) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async (payload: TSetupOrgKmipDTO) => {
|
||||||
|
await apiRequest.post("/api/v1/kmip", payload);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: kmipKeys.getOrgKmip(orgId) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGenerateOrgKmipServerCert = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async (payload: TGenerateOrgKmipServerCertDTO) => {
|
||||||
|
return apiRequest.post<OrgKmipServerCert>("/api/v1/kmip/server-certificates", payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -3,11 +3,17 @@ import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
|||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { OrderByDirection } from "../generic/types";
|
import { OrderByDirection } from "../generic/types";
|
||||||
import { KmipClientOrderBy, TListProjectKmipClientsDTO, TProjectKmipClientList } from "./types";
|
import {
|
||||||
|
KmipClientOrderBy,
|
||||||
|
OrgKmipConfig,
|
||||||
|
TListProjectKmipClientsDTO,
|
||||||
|
TProjectKmipClientList
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
export const kmipKeys = {
|
export const kmipKeys = {
|
||||||
getKmipClientsByProjectId: ({ projectId, ...filters }: TListProjectKmipClientsDTO) =>
|
getKmipClientsByProjectId: ({ projectId, ...filters }: TListProjectKmipClientsDTO) =>
|
||||||
[projectId, filters] as const
|
[projectId, filters] as const,
|
||||||
|
getOrgKmip: (orgId: string) => [{ orgId }, "org-kmip-config"] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetKmipClientsByProjectId = (
|
export const useGetKmipClientsByProjectId = (
|
||||||
@@ -50,3 +56,14 @@ export const useGetKmipClientsByProjectId = (
|
|||||||
...options
|
...options
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetOrgKmipConfig = (orgId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: kmipKeys.getOrgKmip(orgId),
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await apiRequest.get<OrgKmipConfig>("/api/v1/kmip");
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -63,3 +63,26 @@ export type TListProjectKmipClientsDTO = {
|
|||||||
export enum KmipClientOrderBy {
|
export enum KmipClientOrderBy {
|
||||||
Name = "name"
|
Name = "name"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OrgKmipConfig = {
|
||||||
|
serverCertificateChain: string;
|
||||||
|
clientCertificateChain: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TSetupOrgKmipDTO = {
|
||||||
|
caKeyAlgorithm: CertKeyAlgorithm;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGenerateOrgKmipServerCertDTO = {
|
||||||
|
commonName: string;
|
||||||
|
keyAlgorithm: CertKeyAlgorithm;
|
||||||
|
altNames: string;
|
||||||
|
ttl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OrgKmipServerCert = {
|
||||||
|
serialNumber: string;
|
||||||
|
certificate: string;
|
||||||
|
certificateChain: string;
|
||||||
|
privateKey: string;
|
||||||
|
};
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import {
|
|||||||
import { AuthPanel } from "./components/AuthPanel";
|
import { AuthPanel } from "./components/AuthPanel";
|
||||||
import { EncryptionPanel } from "./components/EncryptionPanel";
|
import { EncryptionPanel } from "./components/EncryptionPanel";
|
||||||
import { IntegrationPanel } from "./components/IntegrationPanel";
|
import { IntegrationPanel } from "./components/IntegrationPanel";
|
||||||
import { KmipPanel } from "./components/KmipPanel";
|
|
||||||
import { RateLimitPanel } from "./components/RateLimitPanel";
|
import { RateLimitPanel } from "./components/RateLimitPanel";
|
||||||
import { UserPanel } from "./components/UserPanel";
|
import { UserPanel } from "./components/UserPanel";
|
||||||
|
|
||||||
@@ -151,7 +150,6 @@ export const OverviewPage = () => {
|
|||||||
<Tab value={TabSections.RateLimit}>Rate Limit</Tab>
|
<Tab value={TabSections.RateLimit}>Rate Limit</Tab>
|
||||||
<Tab value={TabSections.Integrations}>Integrations</Tab>
|
<Tab value={TabSections.Integrations}>Integrations</Tab>
|
||||||
<Tab value={TabSections.Users}>Users</Tab>
|
<Tab value={TabSections.Users}>Users</Tab>
|
||||||
<Tab value={TabSections.Kmip}>KMIP</Tab>
|
|
||||||
</div>
|
</div>
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanel value={TabSections.Settings}>
|
<TabPanel value={TabSections.Settings}>
|
||||||
@@ -350,9 +348,6 @@ export const OverviewPage = () => {
|
|||||||
<TabPanel value={TabSections.Users}>
|
<TabPanel value={TabSections.Users}>
|
||||||
<UserPanel />
|
<UserPanel />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value={TabSections.Kmip}>
|
|
||||||
<KmipPanel />
|
|
||||||
</TabPanel>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+13
-2
@@ -2,7 +2,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { OrgPermissionSubjects } from "@app/context";
|
import { OrgPermissionSubjects } from "@app/context";
|
||||||
import { OrgPermissionAppConnectionActions } from "@app/context/OrgPermissionContext/types";
|
import {
|
||||||
|
OrgPermissionAppConnectionActions,
|
||||||
|
OrgPermissionKmipActions
|
||||||
|
} from "@app/context/OrgPermissionContext/types";
|
||||||
import { TPermission } from "@app/hooks/api/roles/types";
|
import { TPermission } from "@app/hooks/api/roles/types";
|
||||||
|
|
||||||
const generalPermissionSchema = z
|
const generalPermissionSchema = z
|
||||||
@@ -24,6 +27,13 @@ const appConnectionsPermissionSchema = z
|
|||||||
})
|
})
|
||||||
.optional();
|
.optional();
|
||||||
|
|
||||||
|
const kmipPermissionSchema = z
|
||||||
|
.object({
|
||||||
|
[OrgPermissionKmipActions.Proxy]: z.boolean().optional(),
|
||||||
|
[OrgPermissionKmipActions.Setup]: z.boolean().optional()
|
||||||
|
})
|
||||||
|
.optional();
|
||||||
|
|
||||||
const adminConsolePermissionSchmea = z
|
const adminConsolePermissionSchmea = z
|
||||||
.object({
|
.object({
|
||||||
"access-all-projects": z.boolean().optional()
|
"access-all-projects": z.boolean().optional()
|
||||||
@@ -61,7 +71,8 @@ export const formSchema = z.object({
|
|||||||
"organization-admin-console": adminConsolePermissionSchmea,
|
"organization-admin-console": adminConsolePermissionSchmea,
|
||||||
[OrgPermissionSubjects.Kms]: generalPermissionSchema,
|
[OrgPermissionSubjects.Kms]: generalPermissionSchema,
|
||||||
[OrgPermissionSubjects.ProjectTemplates]: generalPermissionSchema,
|
[OrgPermissionSubjects.ProjectTemplates]: generalPermissionSchema,
|
||||||
"app-connections": appConnectionsPermissionSchema
|
"app-connections": appConnectionsPermissionSchema,
|
||||||
|
kmip: kmipPermissionSchema
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
});
|
});
|
||||||
|
|||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
import { useEffect, useMemo } from "react";
|
||||||
|
import { Control, Controller, UseFormSetValue, useWatch } from "react-hook-form";
|
||||||
|
import { faChevronDown, faChevronRight } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { Checkbox, Select, SelectItem, Td, Tr } from "@app/components/v2";
|
||||||
|
import { useToggle } from "@app/hooks";
|
||||||
|
|
||||||
|
import { TFormSchema } from "../OrgRoleModifySection.utils";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isEditable: boolean;
|
||||||
|
setValue: UseFormSetValue<TFormSchema>;
|
||||||
|
control: Control<TFormSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Permission {
|
||||||
|
NoAccess = "no-access",
|
||||||
|
Custom = "custom"
|
||||||
|
}
|
||||||
|
|
||||||
|
const PERMISSION_ACTIONS = [
|
||||||
|
{ action: "proxy", label: "Proxy KMIP requests" },
|
||||||
|
{ action: "setup", label: "Setup KMIP" }
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const OrgPermissionKmipRow = ({ isEditable, control, setValue }: Props) => {
|
||||||
|
const [isRowExpanded, setIsRowExpanded] = useToggle();
|
||||||
|
const [isCustom, setIsCustom] = useToggle();
|
||||||
|
|
||||||
|
const rule = useWatch({
|
||||||
|
control,
|
||||||
|
name: "permissions.kmip"
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedPermissionCategory = useMemo(() => {
|
||||||
|
if (rule?.proxy || rule?.setup) {
|
||||||
|
return Permission.Custom;
|
||||||
|
}
|
||||||
|
return Permission.NoAccess;
|
||||||
|
}, [rule, isCustom]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedPermissionCategory === Permission.Custom) setIsCustom.on();
|
||||||
|
else setIsCustom.off();
|
||||||
|
}, [selectedPermissionCategory]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const isRowCustom = selectedPermissionCategory === Permission.Custom;
|
||||||
|
if (isRowCustom) {
|
||||||
|
setIsRowExpanded.on();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handlePermissionChange = (val: Permission) => {
|
||||||
|
if (!val) return;
|
||||||
|
if (val === Permission.Custom) {
|
||||||
|
setIsRowExpanded.on();
|
||||||
|
setIsCustom.on();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setIsCustom.off();
|
||||||
|
|
||||||
|
if (val === Permission.NoAccess) {
|
||||||
|
setValue("permissions.kmip", { proxy: false, setup: false }, { shouldDirty: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Tr
|
||||||
|
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
|
||||||
|
onClick={() => setIsRowExpanded.toggle()}
|
||||||
|
>
|
||||||
|
<Td>
|
||||||
|
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
|
||||||
|
</Td>
|
||||||
|
<Td>KMIP</Td>
|
||||||
|
<Td>
|
||||||
|
<Select
|
||||||
|
value={selectedPermissionCategory}
|
||||||
|
className="w-40 bg-mineshaft-600"
|
||||||
|
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
|
||||||
|
onValueChange={handlePermissionChange}
|
||||||
|
isDisabled={!isEditable}
|
||||||
|
>
|
||||||
|
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
|
||||||
|
<SelectItem value={Permission.Custom}>Custom</SelectItem>
|
||||||
|
</Select>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
{isRowExpanded && (
|
||||||
|
<Tr>
|
||||||
|
<Td
|
||||||
|
colSpan={3}
|
||||||
|
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
|
||||||
|
>
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
{PERMISSION_ACTIONS.map(({ action, label }) => {
|
||||||
|
return (
|
||||||
|
<Controller
|
||||||
|
name={`permissions.kmip.${action}`}
|
||||||
|
key={`permissions.kmip.${action}`}
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<Checkbox
|
||||||
|
isChecked={field.value}
|
||||||
|
onCheckedChange={(e) => {
|
||||||
|
if (!isEditable) {
|
||||||
|
createNotification({
|
||||||
|
type: "error",
|
||||||
|
text: "Failed to update default role"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
field.onChange(e);
|
||||||
|
}}
|
||||||
|
id={`permissions.kmip.${action}`}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Checkbox>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
+6
@@ -14,6 +14,7 @@ import {
|
|||||||
TFormSchema
|
TFormSchema
|
||||||
} from "../OrgRoleModifySection.utils";
|
} from "../OrgRoleModifySection.utils";
|
||||||
import { OrgPermissionAdminConsoleRow } from "./OrgPermissionAdminConsoleRow";
|
import { OrgPermissionAdminConsoleRow } from "./OrgPermissionAdminConsoleRow";
|
||||||
|
import { OrgPermissionKmipRow } from "./OrgPermissionKmipRow";
|
||||||
import { OrgRoleWorkspaceRow } from "./OrgRoleWorkspaceRow";
|
import { OrgRoleWorkspaceRow } from "./OrgRoleWorkspaceRow";
|
||||||
import { RolePermissionRow } from "./RolePermissionRow";
|
import { RolePermissionRow } from "./RolePermissionRow";
|
||||||
|
|
||||||
@@ -180,6 +181,11 @@ export const RolePermissionsSection = ({ roleId }: Props) => {
|
|||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
isEditable={isCustomRole}
|
isEditable={isCustomRole}
|
||||||
/>
|
/>
|
||||||
|
<OrgPermissionKmipRow
|
||||||
|
control={control}
|
||||||
|
setValue={setValue}
|
||||||
|
isEditable={isCustomRole}
|
||||||
|
/>
|
||||||
</TBody>
|
</TBody>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
|||||||
+32
-25
@@ -18,26 +18,30 @@ import {
|
|||||||
TextArea,
|
TextArea,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
|
import { useOrganization } 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 { useGetInstanceKmipConfig, useSetupInstanceKmip } from "@app/hooks/api";
|
|
||||||
import { useGenerateInstanceKmipServerCert } from "@app/hooks/api/admin/mutation";
|
|
||||||
import { InstanceKmipConfig } from "@app/hooks/api/admin/types";
|
|
||||||
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 {
|
||||||
|
useGenerateOrgKmipServerCert,
|
||||||
|
useGetOrgKmipConfig,
|
||||||
|
useSetupOrgKmip
|
||||||
|
} from "@app/hooks/api/kmip";
|
||||||
|
import { OrgKmipConfig } from "@app/hooks/api/kmip/types";
|
||||||
import { CertificateContent } from "@app/pages/cert-manager/CertificatesPage/components/CertificatesTab/components/CertificateContent";
|
import { CertificateContent } from "@app/pages/cert-manager/CertificatesPage/components/CertificatesTab/components/CertificateContent";
|
||||||
|
|
||||||
const kmipInstanceConfigFormSchema = z.object({
|
const orgConfigFormSchema = z.object({
|
||||||
caKeyAlgorithm: z.nativeEnum(CertKeyAlgorithm)
|
caKeyAlgorithm: z.nativeEnum(CertKeyAlgorithm)
|
||||||
});
|
});
|
||||||
|
|
||||||
type TKmipInstanceConfigForm = z.infer<typeof kmipInstanceConfigFormSchema>;
|
type TKmipOrgConfigForm = z.infer<typeof orgConfigFormSchema>;
|
||||||
|
|
||||||
const KmipInstanceConfigSection = ({
|
const OrgConfigSection = ({
|
||||||
kmipConfig,
|
kmipConfig,
|
||||||
isKmipConfigLoading
|
isKmipConfigLoading
|
||||||
}: {
|
}: {
|
||||||
kmipConfig?: InstanceKmipConfig;
|
kmipConfig?: OrgKmipConfig;
|
||||||
isKmipConfigLoading: boolean;
|
isKmipConfigLoading: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
||||||
@@ -47,13 +51,15 @@ const KmipInstanceConfigSection = ({
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
control,
|
control,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<TKmipInstanceConfigForm>({
|
} = useForm<TKmipOrgConfigForm>({
|
||||||
resolver: zodResolver(kmipInstanceConfigFormSchema)
|
resolver: zodResolver(orgConfigFormSchema)
|
||||||
});
|
});
|
||||||
const { mutateAsync: setupInstanceKmip } = useSetupInstanceKmip();
|
|
||||||
|
|
||||||
const onFormSubmit = async (formData: TKmipInstanceConfigForm) => {
|
const { currentOrg } = useOrganization();
|
||||||
await setupInstanceKmip(formData);
|
const { mutateAsync: setupOrgKmip } = useSetupOrgKmip(currentOrg.id);
|
||||||
|
|
||||||
|
const onFormSubmit = async (formData: TKmipOrgConfigForm) => {
|
||||||
|
await setupOrgKmip(formData);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
type: "success",
|
type: "success",
|
||||||
@@ -171,7 +177,7 @@ const KmipInstanceConfigSection = ({
|
|||||||
)}
|
)}
|
||||||
{!isKmipConfigLoading && !kmipConfig && (
|
{!isKmipConfigLoading && !kmipConfig && (
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<div>KMIP has not yet been configured for the instance.</div>
|
<div>KMIP has not yet been configured for the organization.</div>
|
||||||
<Button
|
<Button
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -187,7 +193,7 @@ const KmipInstanceConfigSection = ({
|
|||||||
isOpen={popUp.configureKmip.isOpen}
|
isOpen={popUp.configureKmip.isOpen}
|
||||||
onOpenChange={(state) => handlePopUpToggle("configureKmip", state)}
|
onOpenChange={(state) => handlePopUpToggle("configureKmip", state)}
|
||||||
>
|
>
|
||||||
<ModalContent title="Configure KMIP for the instance">
|
<ModalContent title="Configure KMIP for the organization">
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -242,14 +248,14 @@ const KmipInstanceConfigSection = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const kmipInstanceServerCertFormSchema = z.object({
|
const orgServerCertFormSchema = z.object({
|
||||||
commonName: z.string(),
|
commonName: z.string(),
|
||||||
altNames: z.string(),
|
altNames: z.string(),
|
||||||
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
|
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
|
||||||
ttl: z.string()
|
ttl: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
type TKmipInstanceServerCertForm = z.infer<typeof kmipInstanceServerCertFormSchema>;
|
type TOrgServerCertForm = z.infer<typeof orgServerCertFormSchema>;
|
||||||
|
|
||||||
export const KmipServerConfigSection = () => {
|
export const KmipServerConfigSection = () => {
|
||||||
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
||||||
@@ -268,13 +274,13 @@ export const KmipServerConfigSection = () => {
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
control,
|
control,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<TKmipInstanceServerCertForm>({
|
} = useForm<TOrgServerCertForm>({
|
||||||
resolver: zodResolver(kmipInstanceServerCertFormSchema)
|
resolver: zodResolver(orgServerCertFormSchema)
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: generateKmipServerCert } = useGenerateInstanceKmipServerCert();
|
const { mutateAsync: generateKmipServerCert } = useGenerateOrgKmipServerCert();
|
||||||
|
|
||||||
const onFormSubmit = async (formData: TKmipInstanceServerCertForm) => {
|
const onFormSubmit = async (formData: TOrgServerCertForm) => {
|
||||||
const { data: certificate } = await generateKmipServerCert(formData);
|
const { data: certificate } = await generateKmipServerCert(formData);
|
||||||
handlePopUpOpen("showCertificate", certificate);
|
handlePopUpOpen("showCertificate", certificate);
|
||||||
|
|
||||||
@@ -304,7 +310,7 @@ export const KmipServerConfigSection = () => {
|
|||||||
isOpen={popUp.configureKmipServerCert.isOpen}
|
isOpen={popUp.configureKmipServerCert.isOpen}
|
||||||
onOpenChange={(state) => handlePopUpToggle("configureKmipServerCert", state)}
|
onOpenChange={(state) => handlePopUpToggle("configureKmipServerCert", state)}
|
||||||
>
|
>
|
||||||
<ModalContent title="Configure KMIP for the instance">
|
<ModalContent title="Configure KMIP for the organization">
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -402,7 +408,7 @@ export const KmipServerConfigSection = () => {
|
|||||||
isOpen={popUp.showCertificate.isOpen}
|
isOpen={popUp.showCertificate.isOpen}
|
||||||
onOpenChange={(state) => handlePopUpToggle("showCertificate", state)}
|
onOpenChange={(state) => handlePopUpToggle("showCertificate", state)}
|
||||||
>
|
>
|
||||||
<ModalContent title="Configure KMIP for the instance">
|
<ModalContent title="Configure KMIP for the organization">
|
||||||
<CertificateContent {...certificateData} />
|
<CertificateContent {...certificateData} />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -410,12 +416,13 @@ export const KmipServerConfigSection = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const KmipPanel = () => {
|
export const KmipTab = () => {
|
||||||
const { data: kmipConfig, isPending } = useGetInstanceKmipConfig();
|
const { currentOrg } = useOrganization();
|
||||||
|
const { data: kmipConfig, isPending } = useGetOrgKmipConfig(currentOrg.id);
|
||||||
|
|
||||||
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">
|
||||||
<KmipInstanceConfigSection kmipConfig={kmipConfig} isKmipConfigLoading={isPending} />
|
<OrgConfigSection kmipConfig={kmipConfig} isKmipConfigLoading={isPending} />
|
||||||
{kmipConfig && <KmipServerConfigSection />}
|
{kmipConfig && <KmipServerConfigSection />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -7,6 +7,7 @@ import { ROUTE_PATHS } from "@app/const/routes";
|
|||||||
import { AppConnectionsTab } from "../AppConnectionsTab";
|
import { AppConnectionsTab } from "../AppConnectionsTab";
|
||||||
import { AuditLogStreamsTab } from "../AuditLogStreamTab";
|
import { AuditLogStreamsTab } from "../AuditLogStreamTab";
|
||||||
import { ImportTab } from "../ImportTab";
|
import { ImportTab } from "../ImportTab";
|
||||||
|
import { KmipTab } from "../KmipTab/OrgKmipTab";
|
||||||
import { OrgAuthTab } from "../OrgAuthTab";
|
import { OrgAuthTab } from "../OrgAuthTab";
|
||||||
import { OrgEncryptionTab } from "../OrgEncryptionTab";
|
import { OrgEncryptionTab } from "../OrgEncryptionTab";
|
||||||
import { OrgGeneralTab } from "../OrgGeneralTab";
|
import { OrgGeneralTab } from "../OrgGeneralTab";
|
||||||
@@ -29,7 +30,8 @@ export const OrgTabGroup = () => {
|
|||||||
{ name: "App Connections", key: "app-connections", component: AppConnectionsTab },
|
{ name: "App Connections", key: "app-connections", component: AppConnectionsTab },
|
||||||
{ name: "Audit Log Streams", key: "tag-audit-log-streams", component: AuditLogStreamsTab },
|
{ name: "Audit Log Streams", key: "tag-audit-log-streams", component: AuditLogStreamsTab },
|
||||||
{ name: "Import", key: "tab-import", component: ImportTab },
|
{ name: "Import", key: "tab-import", component: ImportTab },
|
||||||
{ name: "Project Templates", key: "project-templates", component: ProjectTemplatesTab }
|
{ name: "Project Templates", key: "project-templates", component: ProjectTemplatesTab },
|
||||||
|
{ name: "KMIP", key: "kmip", component: KmipTab }
|
||||||
];
|
];
|
||||||
|
|
||||||
const [selectedTab, setSelectedTab] = useState(search.selectedTab || tabs[0].key);
|
const [selectedTab, setSelectedTab] = useState(search.selectedTab || tabs[0].key);
|
||||||
|
|||||||
Reference in New Issue
Block a user