mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 20:27:43 +00:00
feat: switched to projecet gateway, updated icon, cli
This commit is contained in:
Vendored
+8
@@ -206,6 +206,9 @@ import {
|
|||||||
TProjectEnvironments,
|
TProjectEnvironments,
|
||||||
TProjectEnvironmentsInsert,
|
TProjectEnvironmentsInsert,
|
||||||
TProjectEnvironmentsUpdate,
|
TProjectEnvironmentsUpdate,
|
||||||
|
TProjectGateways,
|
||||||
|
TProjectGatewaysInsert,
|
||||||
|
TProjectGatewaysUpdate,
|
||||||
TProjectKeys,
|
TProjectKeys,
|
||||||
TProjectKeysInsert,
|
TProjectKeysInsert,
|
||||||
TProjectKeysUpdate,
|
TProjectKeysUpdate,
|
||||||
@@ -937,6 +940,11 @@ declare module "knex/types/tables" {
|
|||||||
TKmipClientCertificatesUpdate
|
TKmipClientCertificatesUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.Gateway]: KnexOriginal.CompositeTableType<TGateways, TGatewaysInsert, TGatewaysUpdate>;
|
[TableName.Gateway]: KnexOriginal.CompositeTableType<TGateways, TGatewaysInsert, TGatewaysUpdate>;
|
||||||
|
[TableName.ProjectGateway]: KnexOriginal.CompositeTableType<
|
||||||
|
TProjectGateways,
|
||||||
|
TProjectGatewaysInsert,
|
||||||
|
TProjectGatewaysUpdate
|
||||||
|
>;
|
||||||
[TableName.OrgGatewayConfig]: KnexOriginal.CompositeTableType<
|
[TableName.OrgGatewayConfig]: KnexOriginal.CompositeTableType<
|
||||||
TOrgGatewayConfig,
|
TOrgGatewayConfig,
|
||||||
TOrgGatewayConfigInsert,
|
TOrgGatewayConfigInsert,
|
||||||
|
|||||||
@@ -68,13 +68,29 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
await createOnUpdateTrigger(knex, TableName.Gateway);
|
await createOnUpdateTrigger(knex, TableName.Gateway);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.ProjectGateway))) {
|
||||||
|
await knex.schema.createTable(TableName.ProjectGateway, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
|
||||||
|
t.string("projectId").notNullable();
|
||||||
|
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("gatewayId").notNullable();
|
||||||
|
t.foreign("gatewayId").references("id").inTable(TableName.Gateway).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.ProjectGateway);
|
||||||
|
}
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.DynamicSecret)) {
|
if (await knex.schema.hasTable(TableName.DynamicSecret)) {
|
||||||
const doesGatewayColExist = await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId");
|
const doesGatewayColExist = await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId");
|
||||||
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
||||||
// not setting a foreign constraint so that cascade effects are not triggered
|
// not setting a foreign constraint so that cascade effects are not triggered
|
||||||
if (!doesGatewayColExist) {
|
if (!doesGatewayColExist) {
|
||||||
t.uuid("gatewayId");
|
t.uuid("projectGatewayId");
|
||||||
t.foreign("gatewayId").references("id").inTable(TableName.Identity);
|
t.foreign("projectGatewayId").references("id").inTable(TableName.ProjectGateway);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const DynamicSecretsSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
encryptedInput: zodBuffer,
|
encryptedInput: zodBuffer,
|
||||||
gatewayId: z.string().uuid().nullable().optional()
|
projectGatewayId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export * from "./pki-collection-items";
|
|||||||
export * from "./pki-collections";
|
export * from "./pki-collections";
|
||||||
export * from "./project-bots";
|
export * from "./project-bots";
|
||||||
export * from "./project-environments";
|
export * from "./project-environments";
|
||||||
|
export * from "./project-gateways";
|
||||||
export * from "./project-keys";
|
export * from "./project-keys";
|
||||||
export * from "./project-memberships";
|
export * from "./project-memberships";
|
||||||
export * from "./project-roles";
|
export * from "./project-roles";
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ export enum TableName {
|
|||||||
// Gateway
|
// Gateway
|
||||||
OrgGatewayConfig = "org_gateway_config",
|
OrgGatewayConfig = "org_gateway_config",
|
||||||
Gateway = "gateways",
|
Gateway = "gateways",
|
||||||
|
ProjectGateway = "project_gateways",
|
||||||
// junction tables with tags
|
// junction tables with tags
|
||||||
SecretV2JnTag = "secret_v2_tag_junction",
|
SecretV2JnTag = "secret_v2_tag_junction",
|
||||||
JnSecretTag = "secret_tag_junction",
|
JnSecretTag = "secret_tag_junction",
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const ProjectGatewaysSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
projectId: z.string(),
|
||||||
|
gatewayId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TProjectGateways = z.infer<typeof ProjectGatewaysSchema>;
|
||||||
|
export type TProjectGatewaysInsert = Omit<z.input<typeof ProjectGatewaysSchema>, TImmutableDBKeys>;
|
||||||
|
export type TProjectGatewaysUpdate = Partial<Omit<z.input<typeof ProjectGatewaysSchema>, TImmutableDBKeys>>;
|
||||||
@@ -17,6 +17,7 @@ const SanitizedGatewaySchema = GatewaysSchema.pick({
|
|||||||
serialNumber: true,
|
serialNumber: true,
|
||||||
heartbeat: true
|
heartbeat: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const isValidRelayAddress = (relayAddress: string) => {
|
const isValidRelayAddress = (relayAddress: string) => {
|
||||||
const [ip, port] = relayAddress.split(":");
|
const [ip, port] = relayAddress.split(":");
|
||||||
return isValidIp(ip) && Number(port) <= 65535 && Number(port) >= 40000;
|
return isValidIp(ip) && Number(port) <= 65535 && Number(port) >= 40000;
|
||||||
@@ -120,21 +121,20 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
|||||||
identity: z.object({
|
identity: z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
id: z.string()
|
id: z.string()
|
||||||
})
|
}),
|
||||||
|
projects: z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.string(),
|
||||||
|
slug: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
}).array()
|
}).array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
if (req.query.projectId) {
|
|
||||||
const gateways = await server.services.gateway.getProjectGateways({
|
|
||||||
projectId: req.query.projectId,
|
|
||||||
projectPermission: req.permission
|
|
||||||
});
|
|
||||||
return { gateways };
|
|
||||||
}
|
|
||||||
|
|
||||||
const gateways = await server.services.gateway.listGateways({
|
const gateways = await server.services.gateway.listGateways({
|
||||||
orgPermission: req.permission
|
orgPermission: req.permission
|
||||||
});
|
});
|
||||||
@@ -142,6 +142,38 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/projects/:projectId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
projectId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
gateways: SanitizedGatewaySchema.extend({
|
||||||
|
identity: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.string()
|
||||||
|
}),
|
||||||
|
projectGatewayId: z.string()
|
||||||
|
}).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const gateways = await server.services.gateway.getProjectGateways({
|
||||||
|
projectId: req.params.projectId,
|
||||||
|
projectPermission: req.permission
|
||||||
|
});
|
||||||
|
return { gateways };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:id",
|
url: "/:id",
|
||||||
@@ -184,7 +216,8 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
|||||||
id: z.string()
|
id: z.string()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
name: slugSchema({ field: "name" }).optional()
|
name: slugSchema({ field: "name" }).optional(),
|
||||||
|
projectIds: z.string().array().optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -197,7 +230,8 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
|||||||
const gateway = await server.services.gateway.updateGatewayById({
|
const gateway = await server.services.gateway.updateGatewayById({
|
||||||
orgPermission: req.permission,
|
orgPermission: req.permission,
|
||||||
id: req.params.id,
|
id: req.params.id,
|
||||||
name: req.body.name
|
name: req.body.name,
|
||||||
|
projectIds: req.body.projectIds
|
||||||
});
|
});
|
||||||
return { gateway };
|
return { gateway };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-fold
|
|||||||
|
|
||||||
import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal";
|
import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal";
|
||||||
import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/dynamic-secret-lease-queue";
|
import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/dynamic-secret-lease-queue";
|
||||||
import { TGatewayDALFactory } from "../gateway/gateway-dal";
|
import { TProjectGatewayDALFactory } from "../gateway/project-gateway-dal";
|
||||||
import { TOrgGatewayConfigDALFactory } from "../gateway/org-gateway-config-dal";
|
|
||||||
import { TDynamicSecretDALFactory } from "./dynamic-secret-dal";
|
import { TDynamicSecretDALFactory } from "./dynamic-secret-dal";
|
||||||
import {
|
import {
|
||||||
DynamicSecretStatus,
|
DynamicSecretStatus,
|
||||||
@@ -46,8 +45,7 @@ type TDynamicSecretServiceFactoryDep = {
|
|||||||
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
|
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
gatewayDAL: Pick<TGatewayDALFactory, "findOne">;
|
projectGatewayDAL: Pick<TProjectGatewayDALFactory, "findOne">;
|
||||||
orgGatewayConfigDAL: Pick<TOrgGatewayConfigDALFactory, "findOne">;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDynamicSecretServiceFactory = ReturnType<typeof dynamicSecretServiceFactory>;
|
export type TDynamicSecretServiceFactory = ReturnType<typeof dynamicSecretServiceFactory>;
|
||||||
@@ -62,8 +60,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
dynamicSecretQueueService,
|
dynamicSecretQueueService,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
gatewayDAL,
|
projectGatewayDAL
|
||||||
orgGatewayConfigDAL
|
|
||||||
}: TDynamicSecretServiceFactoryDep) => {
|
}: TDynamicSecretServiceFactoryDep) => {
|
||||||
const create = async ({
|
const create = async ({
|
||||||
path,
|
path,
|
||||||
@@ -115,20 +112,15 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
const inputs = await selectedProvider.validateProviderInputs(provider.inputs);
|
const inputs = await selectedProvider.validateProviderInputs(provider.inputs);
|
||||||
|
|
||||||
let selectedGatewayId: string | null = null;
|
let selectedGatewayId: string | null = null;
|
||||||
if (inputs && typeof inputs === "object" && "gatewayId" in inputs && inputs.gatewayId) {
|
if (inputs && typeof inputs === "object" && "projectGatewayId" in inputs && inputs.projectGatewayId) {
|
||||||
const gatewayId = inputs.gatewayId as string;
|
const projectGatewayId = inputs.projectGatewayId as string;
|
||||||
|
|
||||||
const orgGateway = await orgGatewayConfigDAL.findOne({ orgId: actorOrgId });
|
const projectGateway = await projectGatewayDAL.findOne({ id: projectGatewayId, projectId });
|
||||||
if (!orgGateway)
|
if (!projectGateway)
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Gateway with ${gatewayId} not found`
|
message: `Project gateway with ${projectGatewayId} not found`
|
||||||
});
|
});
|
||||||
const gateway = await gatewayDAL.findOne({ id: gatewayId, orgGatewayRootCaId: orgGateway.id });
|
selectedGatewayId = projectGateway.id;
|
||||||
if (!gateway)
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Gateway with ${gatewayId} not found`
|
|
||||||
});
|
|
||||||
selectedGatewayId = gateway.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isConnected = await selectedProvider.validateConnection(provider.inputs);
|
const isConnected = await selectedProvider.validateConnection(provider.inputs);
|
||||||
@@ -147,7 +139,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
defaultTTL,
|
defaultTTL,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
name,
|
name,
|
||||||
gatewayId: selectedGatewayId
|
projectGatewayId: selectedGatewayId
|
||||||
});
|
});
|
||||||
return dynamicSecretCfg;
|
return dynamicSecretCfg;
|
||||||
};
|
};
|
||||||
@@ -220,20 +212,20 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
const updatedInput = await selectedProvider.validateProviderInputs(newInput);
|
const updatedInput = await selectedProvider.validateProviderInputs(newInput);
|
||||||
|
|
||||||
let selectedGatewayId: string | null = null;
|
let selectedGatewayId: string | null = null;
|
||||||
if (updatedInput && typeof updatedInput === "object" && "gatewayId" in updatedInput && updatedInput?.gatewayId) {
|
if (
|
||||||
const gatewayId = updatedInput.gatewayId as string;
|
updatedInput &&
|
||||||
|
typeof updatedInput === "object" &&
|
||||||
|
"projectGatewayId" in updatedInput &&
|
||||||
|
updatedInput?.projectGatewayId
|
||||||
|
) {
|
||||||
|
const projectGatewayId = updatedInput.projectGatewayId as string;
|
||||||
|
|
||||||
const orgGateway = await orgGatewayConfigDAL.findOne({ orgId: actorOrgId });
|
const projectGateway = await projectGatewayDAL.findOne({ id: projectGatewayId, projectId });
|
||||||
if (!orgGateway)
|
if (!projectGateway)
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Gateway with ${gatewayId} not found`
|
message: `Project gateway with ${projectGatewayId} not found`
|
||||||
});
|
});
|
||||||
const gateway = await gatewayDAL.findOne({ id: gatewayId, orgGatewayRootCaId: orgGateway.id });
|
selectedGatewayId = projectGateway.id;
|
||||||
if (!gateway)
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Gateway with ${gatewayId} not found`
|
|
||||||
});
|
|
||||||
selectedGatewayId = gateway.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isConnected = await selectedProvider.validateConnection(newInput);
|
const isConnected = await selectedProvider.validateConnection(newInput);
|
||||||
@@ -246,7 +238,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
name: newName ?? name,
|
name: newName ?? name,
|
||||||
status: null,
|
status: null,
|
||||||
statusDetails: null,
|
statusDetails: null,
|
||||||
gatewayId: selectedGatewayId
|
projectGatewayId: selectedGatewayId
|
||||||
});
|
});
|
||||||
|
|
||||||
return updatedDynamicCfg;
|
return updatedDynamicCfg;
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export const DynamicSecretSqlDBSchema = z.object({
|
|||||||
revocationStatement: z.string().trim(),
|
revocationStatement: z.string().trim(),
|
||||||
renewStatement: z.string().trim().optional(),
|
renewStatement: z.string().trim().optional(),
|
||||||
ca: z.string().optional(),
|
ca: z.string().optional(),
|
||||||
gatewayId: z.string().nullable().optional()
|
projectGatewayId: z.string().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DynamicSecretCassandraSchema = z.object({
|
export const DynamicSecretCassandraSchema = z.object({
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ type TSqlDatabaseProviderDTO = {
|
|||||||
export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO): TDynamicProviderFns => {
|
export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
|
||||||
verifyHostInputValidity(providerInputs.host, Boolean(providerInputs.gatewayId));
|
verifyHostInputValidity(providerInputs.host, Boolean(providerInputs.projectGatewayId));
|
||||||
return providerInputs;
|
return providerInputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>,
|
providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>,
|
||||||
gatewayCallback: (port: number) => Promise<void>
|
gatewayCallback: (port: number) => Promise<void>
|
||||||
) => {
|
) => {
|
||||||
const relayDetails = await gatewayService.fnGetGatewayClientTls(providerInputs.gatewayId as string);
|
const relayDetails = await gatewayService.fnGetGatewayClientTls(providerInputs.projectGatewayId as string);
|
||||||
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
||||||
await withGatewayProxy(
|
await withGatewayProxy(
|
||||||
async (port) => {
|
async (port) => {
|
||||||
@@ -105,7 +105,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
await db.destroy();
|
await db.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (providerInputs.gatewayId) {
|
if (providerInputs.projectGatewayId) {
|
||||||
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
||||||
} else {
|
} else {
|
||||||
await gatewayCallback();
|
await gatewayCallback();
|
||||||
@@ -138,7 +138,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
});
|
});
|
||||||
await db.destroy();
|
await db.destroy();
|
||||||
};
|
};
|
||||||
if (providerInputs.gatewayId) {
|
if (providerInputs.projectGatewayId) {
|
||||||
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
||||||
} else {
|
} else {
|
||||||
await gatewayCallback();
|
await gatewayCallback();
|
||||||
@@ -163,7 +163,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
|
|
||||||
await db.destroy();
|
await db.destroy();
|
||||||
};
|
};
|
||||||
if (providerInputs.gatewayId) {
|
if (providerInputs.projectGatewayId) {
|
||||||
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
||||||
} else {
|
} else {
|
||||||
await gatewayCallback();
|
await gatewayCallback();
|
||||||
@@ -198,7 +198,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
|
|
||||||
await db.destroy();
|
await db.destroy();
|
||||||
};
|
};
|
||||||
if (providerInputs.gatewayId) {
|
if (providerInputs.projectGatewayId) {
|
||||||
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
||||||
} else {
|
} else {
|
||||||
await gatewayCallback();
|
await gatewayCallback();
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TGateways } from "@app/db/schemas";
|
import { GatewaysSchema, TableName, TGateways } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
import {
|
||||||
|
buildFindFilter,
|
||||||
|
ormify,
|
||||||
|
selectAllTableCols,
|
||||||
|
sqlNestRelationships,
|
||||||
|
TFindFilter,
|
||||||
|
TFindOpt
|
||||||
|
} from "@app/lib/knex";
|
||||||
|
|
||||||
export type TGatewayDALFactory = ReturnType<typeof gatewayDALFactory>;
|
export type TGatewayDALFactory = ReturnType<typeof gatewayDALFactory>;
|
||||||
|
|
||||||
@@ -16,8 +23,15 @@ export const gatewayDALFactory = (db: TDbClient) => {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
.where(buildFindFilter(filter))
|
.where(buildFindFilter(filter))
|
||||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
||||||
|
.leftJoin(TableName.ProjectGateway, `${TableName.ProjectGateway}.gatewayId`, `${TableName.Gateway}.id`)
|
||||||
|
.leftJoin(TableName.Project, `${TableName.Project}.id`, `${TableName.ProjectGateway}.projectId`)
|
||||||
.select(selectAllTableCols(TableName.Gateway))
|
.select(selectAllTableCols(TableName.Gateway))
|
||||||
.select(db.ref("name").withSchema(TableName.Identity).as("identityName"));
|
.select(
|
||||||
|
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
||||||
|
db.ref("name").withSchema(TableName.Project).as("projectName"),
|
||||||
|
db.ref("slug").withSchema(TableName.Project).as("projectSlug"),
|
||||||
|
db.ref("id").withSchema(TableName.Project).as("projectId")
|
||||||
|
);
|
||||||
if (limit) void query.limit(limit);
|
if (limit) void query.limit(limit);
|
||||||
if (offset) void query.offset(offset);
|
if (offset) void query.offset(offset);
|
||||||
if (sort) {
|
if (sort) {
|
||||||
@@ -25,7 +39,25 @@ export const gatewayDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const docs = await query;
|
const docs = await query;
|
||||||
return docs.map((el) => ({ ...el, identity: { id: el.identityId, name: el.identityName } }));
|
return sqlNestRelationships({
|
||||||
|
data: docs,
|
||||||
|
key: "id",
|
||||||
|
parentMapper: (data) => ({
|
||||||
|
...GatewaysSchema.parse(data),
|
||||||
|
identity: { id: data.identityId, name: data.identityName }
|
||||||
|
}),
|
||||||
|
childrenMapper: [
|
||||||
|
{
|
||||||
|
key: "projectId",
|
||||||
|
label: "projects" as const,
|
||||||
|
mapper: ({ projectId, projectName, projectSlug }) => ({
|
||||||
|
id: projectId,
|
||||||
|
name: projectName,
|
||||||
|
slug: projectSlug
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: `${TableName.Gateway}: Find` });
|
throw new DatabaseError({ error, name: `${TableName.Gateway}: Find` });
|
||||||
}
|
}
|
||||||
@@ -35,14 +67,14 @@ export const gatewayDALFactory = (db: TDbClient) => {
|
|||||||
try {
|
try {
|
||||||
const query = (tx || db)(TableName.Gateway)
|
const query = (tx || db)(TableName.Gateway)
|
||||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
||||||
.join(
|
.join(TableName.ProjectGateway, `${TableName.ProjectGateway}.gatewayId`, `${TableName.Gateway}.id`)
|
||||||
TableName.IdentityProjectMembership,
|
|
||||||
`${TableName.Identity}.id`,
|
|
||||||
`${TableName.IdentityProjectMembership}.identityId`
|
|
||||||
)
|
|
||||||
.select(selectAllTableCols(TableName.Gateway))
|
.select(selectAllTableCols(TableName.Gateway))
|
||||||
.select(db.ref("name").withSchema(TableName.Identity).as("identityName"))
|
.select(
|
||||||
.where({ [`${TableName.IdentityProjectMembership}.projectId` as "projectId"]: projectId });
|
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
||||||
|
db.ref("id").withSchema(TableName.ProjectGateway).as("projectGatewayId")
|
||||||
|
)
|
||||||
|
.where({ [`${TableName.ProjectGateway}.projectId` as "projectId"]: projectId });
|
||||||
|
|
||||||
const docs = await query;
|
const docs = await query;
|
||||||
return docs.map((el) => ({ ...el, identity: { id: el.identityId, name: el.identityName } }));
|
return docs.map((el) => ({ ...el, identity: { id: el.identityId, name: el.identityName } }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
|
|
||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -23,7 +23,6 @@ import { KmsDataKey } from "@app/services/kms/kms-types";
|
|||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionGatewayActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionGatewayActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
|
||||||
import { TGatewayDALFactory } from "./gateway-dal";
|
import { TGatewayDALFactory } from "./gateway-dal";
|
||||||
import {
|
import {
|
||||||
TExchangeAllocatedRelayAddressDTO,
|
TExchangeAllocatedRelayAddressDTO,
|
||||||
@@ -34,9 +33,11 @@ import {
|
|||||||
TUpdateGatewayByIdDTO
|
TUpdateGatewayByIdDTO
|
||||||
} from "./gateway-types";
|
} from "./gateway-types";
|
||||||
import { TOrgGatewayConfigDALFactory } from "./org-gateway-config-dal";
|
import { TOrgGatewayConfigDALFactory } from "./org-gateway-config-dal";
|
||||||
|
import { TProjectGatewayDALFactory } from "./project-gateway-dal";
|
||||||
|
|
||||||
type TGatewayServiceFactoryDep = {
|
type TGatewayServiceFactoryDep = {
|
||||||
gatewayDAL: TGatewayDALFactory;
|
gatewayDAL: TGatewayDALFactory;
|
||||||
|
projectGatewayDAL: TProjectGatewayDALFactory;
|
||||||
orgGatewayConfigDAL: Pick<TOrgGatewayConfigDALFactory, "findOne" | "create" | "transaction" | "findById">;
|
orgGatewayConfigDAL: Pick<TOrgGatewayConfigDALFactory, "findOne" | "create" | "transaction" | "findById">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures" | "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures" | "getPlan">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey" | "decryptWithRootKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey" | "decryptWithRootKey">;
|
||||||
@@ -56,7 +57,8 @@ export const gatewayServiceFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
permissionService,
|
permissionService,
|
||||||
orgGatewayConfigDAL,
|
orgGatewayConfigDAL,
|
||||||
keyStore
|
keyStore,
|
||||||
|
projectGatewayDAL
|
||||||
}: TGatewayServiceFactoryDep) => {
|
}: TGatewayServiceFactoryDep) => {
|
||||||
const $validateOrgAccessToGateway = async (orgId: string, actorId: string, actorAuthMethod: ActorAuthMethod) => {
|
const $validateOrgAccessToGateway = async (orgId: string, actorId: string, actorAuthMethod: ActorAuthMethod) => {
|
||||||
if (!licenseService.onPremFeatures.gateway) {
|
if (!licenseService.onPremFeatures.gateway) {
|
||||||
@@ -410,7 +412,7 @@ export const gatewayServiceFactory = ({
|
|||||||
}).cipherTextBlob,
|
}).cipherTextBlob,
|
||||||
identityId,
|
identityId,
|
||||||
orgGatewayRootCaId: orgGatewayConfig.id,
|
orgGatewayRootCaId: orgGatewayConfig.id,
|
||||||
name: `gateway-${alphaNumericNanoId(6)}`
|
name: `gateway-${alphaNumericNanoId(6).toLowerCase()}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -520,7 +522,7 @@ export const gatewayServiceFactory = ({
|
|||||||
return gateway;
|
return gateway;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateGatewayById = async ({ orgPermission, id, name }: TUpdateGatewayByIdDTO) => {
|
const updateGatewayById = async ({ orgPermission, id, name, projectIds }: TUpdateGatewayByIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
orgPermission.type,
|
orgPermission.type,
|
||||||
orgPermission.id,
|
orgPermission.id,
|
||||||
@@ -537,6 +539,16 @@ export const gatewayServiceFactory = ({
|
|||||||
|
|
||||||
const [gateway] = await gatewayDAL.update({ id, orgGatewayRootCaId: orgGatewayConfig.id }, { name });
|
const [gateway] = await gatewayDAL.update({ id, orgGatewayRootCaId: orgGatewayConfig.id }, { name });
|
||||||
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${id} not found.` });
|
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${id} not found.` });
|
||||||
|
if (projectIds) {
|
||||||
|
await projectGatewayDAL.transaction(async (tx) => {
|
||||||
|
await projectGatewayDAL.delete({ gatewayId: gateway.id }, tx);
|
||||||
|
await projectGatewayDAL.insertMany(
|
||||||
|
projectIds.map((el) => ({ gatewayId: gateway.id, projectId: el })),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return gateway;
|
return gateway;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -561,7 +573,7 @@ export const gatewayServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getProjectGateways = async ({ projectId, projectPermission }: TGetProjectGatewayByIdDTO) => {
|
const getProjectGateways = async ({ projectId, projectPermission }: TGetProjectGatewayByIdDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
await permissionService.getProjectPermission({
|
||||||
projectId,
|
projectId,
|
||||||
actor: projectPermission.type,
|
actor: projectPermission.type,
|
||||||
actorId: projectPermission.id,
|
actorId: projectPermission.id,
|
||||||
@@ -571,18 +583,16 @@ export const gatewayServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const gateways = await gatewayDAL.findByProjectId(projectId);
|
const gateways = await gatewayDAL.findByProjectId(projectId);
|
||||||
const allowedGateways = gateways.filter((el) =>
|
return gateways;
|
||||||
permission.can(
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: el.identityId })
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return allowedGateways;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// this has no permission check and used for dynamic secrets directly
|
// this has no permission check and used for dynamic secrets directly
|
||||||
// assumes permission check is already done
|
// assumes permission check is already done
|
||||||
const fnGetGatewayClientTls = async (gatewayId: string) => {
|
const fnGetGatewayClientTls = async (projectGatewayId: string) => {
|
||||||
|
const projectGateway = await projectGatewayDAL.findById(projectGatewayId);
|
||||||
|
if (!projectGateway) throw new NotFoundError({ message: `Project gateway with ID ${projectGatewayId} not found.` });
|
||||||
|
|
||||||
|
const { gatewayId } = projectGateway;
|
||||||
const gateway = await gatewayDAL.findById(gatewayId);
|
const gateway = await gatewayDAL.findById(gatewayId);
|
||||||
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${gatewayId} not found.` });
|
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${gatewayId} not found.` });
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export type TGetGatewayByIdDTO = {
|
|||||||
export type TUpdateGatewayByIdDTO = {
|
export type TUpdateGatewayByIdDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
projectIds?: string[];
|
||||||
orgPermission: OrgServiceActor;
|
orgPermission: OrgServiceActor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TProjectGatewayDALFactory = ReturnType<typeof projectGatewayDALFactory>;
|
||||||
|
|
||||||
|
export const projectGatewayDALFactory = (db: TDbClient) => {
|
||||||
|
const orm = ormify(db, TableName.ProjectGateway);
|
||||||
|
return orm;
|
||||||
|
};
|
||||||
@@ -30,6 +30,7 @@ import { externalKmsServiceFactory } from "@app/ee/services/external-kms/externa
|
|||||||
import { gatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
import { gatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
||||||
import { gatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { gatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
import { orgGatewayConfigDALFactory } from "@app/ee/services/gateway/org-gateway-config-dal";
|
import { orgGatewayConfigDALFactory } from "@app/ee/services/gateway/org-gateway-config-dal";
|
||||||
|
import { projectGatewayDALFactory } from "@app/ee/services/gateway/project-gateway-dal";
|
||||||
import { groupDALFactory } from "@app/ee/services/group/group-dal";
|
import { groupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { groupServiceFactory } from "@app/ee/services/group/group-service";
|
import { groupServiceFactory } from "@app/ee/services/group/group-service";
|
||||||
import { userGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
import { userGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||||
@@ -398,6 +399,7 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const orgGatewayConfigDAL = orgGatewayConfigDALFactory(db);
|
const orgGatewayConfigDAL = orgGatewayConfigDALFactory(db);
|
||||||
const gatewayDAL = gatewayDALFactory(db);
|
const gatewayDAL = gatewayDALFactory(db);
|
||||||
|
const projectGatewayDAL = projectGatewayDALFactory(db);
|
||||||
|
|
||||||
const permissionService = permissionServiceFactory({
|
const permissionService = permissionServiceFactory({
|
||||||
permissionDAL,
|
permissionDAL,
|
||||||
@@ -1312,7 +1314,8 @@ export const registerRoutes = async (
|
|||||||
kmsService,
|
kmsService,
|
||||||
licenseService,
|
licenseService,
|
||||||
orgGatewayConfigDAL,
|
orgGatewayConfigDAL,
|
||||||
keyStore
|
keyStore,
|
||||||
|
projectGatewayDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const dynamicSecretProviders = buildDynamicSecretProviders({
|
const dynamicSecretProviders = buildDynamicSecretProviders({
|
||||||
@@ -1336,8 +1339,7 @@ export const registerRoutes = async (
|
|||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
kmsService,
|
kmsService,
|
||||||
gatewayDAL,
|
projectGatewayDAL
|
||||||
orgGatewayConfigDAL
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const dynamicSecretLeaseService = dynamicSecretLeaseServiceFactory({
|
const dynamicSecretLeaseService = dynamicSecretLeaseServiceFactory({
|
||||||
|
|||||||
@@ -52,12 +52,32 @@ You can easily deploy a gateway using the Infisical CLI by following these steps
|
|||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Verification">
|
<Step title="Verify Gateway Deployment">
|
||||||
1. Check the gateway log to have **Gateway started successfully**
|
1. Check gateway deployment status:
|
||||||
|
- Look for the message "**Gateway started successfully**" in the gateway logs
|
||||||
|
- This confirms your gateway is running correctly
|
||||||
|
|
||||||
2. Navigate to **Gateways** list page in Org Access Control
|
2. Verify gateway registration:
|
||||||

|
- Open your Infisical dashboard
|
||||||
</Step>
|
- Navigate to **Organization Access Control**
|
||||||
|
- Select the **Gateways** tab
|
||||||
|
- Your newly deployed gateway should appear in the list
|
||||||
|

|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Link Gateway to Projects">
|
||||||
|
1. Access gateway settings:
|
||||||
|
- Find your gateway in the list
|
||||||
|
- Click the options menu (⋮)
|
||||||
|
- Select **Edit Details**
|
||||||
|

|
||||||
|
|
||||||
|
2. Configure project access:
|
||||||
|
- In the edit modal, you'll see a list of available projects
|
||||||
|
- Select the projects you want to grant gateway access to
|
||||||
|
- Click Save to apply your changes
|
||||||
|

|
||||||
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Using Your Gateway
|
## Using Your Gateway
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 338 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 365 KiB |
File diff suppressed because one or more lines are too long
@@ -12,7 +12,7 @@ export const useDeleteGatewayById = () => {
|
|||||||
return apiRequest.delete(`/api/v1/gateways/${id}`);
|
return apiRequest.delete(`/api/v1/gateways/${id}`);
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(gatewaysQueryKeys.list({}));
|
queryClient.invalidateQueries(gatewaysQueryKeys.list());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -20,11 +20,11 @@ export const useDeleteGatewayById = () => {
|
|||||||
export const useUpdateGatewayById = () => {
|
export const useUpdateGatewayById = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: ({ id, name }: TUpdateGatewayDTO) => {
|
mutationFn: ({ id, name, projectIds }: TUpdateGatewayDTO) => {
|
||||||
return apiRequest.patch(`/api/v1/gateways/${id}`, { name });
|
return apiRequest.patch(`/api/v1/gateways/${id}`, { name, projectIds });
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(gatewaysQueryKeys.list({}));
|
queryClient.invalidateQueries(gatewaysQueryKeys.list());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,22 +2,31 @@ import { queryOptions } from "@tanstack/react-query";
|
|||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { TGateway, TListGatewayDTO } from "./types";
|
import { TGateway, TListProjectGatewayDTO, TProjectGateway } from "./types";
|
||||||
|
|
||||||
export const gatewaysQueryKeys = {
|
export const gatewaysQueryKeys = {
|
||||||
allKey: () => ["gateways"],
|
allKey: () => ["gateways"],
|
||||||
listKey: ({ projectId }: TListGatewayDTO) => [
|
listKey: () => [...gatewaysQueryKeys.allKey(), "list"],
|
||||||
|
list: () =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: gatewaysQueryKeys.listKey(),
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await apiRequest.get<{ gateways: TGateway[] }>("/api/v1/gateways");
|
||||||
|
return data.gateways;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
listProjectGatewayKey: ({ projectId }: TListProjectGatewayDTO) => [
|
||||||
...gatewaysQueryKeys.allKey(),
|
...gatewaysQueryKeys.allKey(),
|
||||||
"list",
|
"list",
|
||||||
{ projectId }
|
{ projectId }
|
||||||
],
|
],
|
||||||
list: ({ projectId }: TListGatewayDTO = {}) =>
|
listProjectGateways: ({ projectId }: TListProjectGatewayDTO) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: gatewaysQueryKeys.listKey({ projectId }),
|
queryKey: gatewaysQueryKeys.listProjectGatewayKey({ projectId }),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await apiRequest.get<{ gateways: TGateway[] }>("/api/v1/gateways", {
|
const { data } = await apiRequest.get<{ gateways: TProjectGateway[] }>(
|
||||||
params: { projectId }
|
`/api/v1/gateways/projects/${projectId}`
|
||||||
});
|
);
|
||||||
return data.gateways;
|
return data.gateways;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,17 +11,39 @@ export type TGateway = {
|
|||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
projects: {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TProjectGateway = {
|
||||||
|
id: string;
|
||||||
|
identityId: string;
|
||||||
|
name: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
issuedAt: string;
|
||||||
|
serialNumber: string;
|
||||||
|
heartbeat: string;
|
||||||
|
projectGatewayId: string;
|
||||||
|
identity: {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateGatewayDTO = {
|
export type TUpdateGatewayDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
projectIds?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteGatewayDTO = {
|
export type TDeleteGatewayDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TListGatewayDTO = {
|
export type TListProjectGatewayDTO = {
|
||||||
projectId?: string;
|
projectId: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export const OrganizationLayout = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
<Link to="/organization/gateways">
|
<Link to="/organization/gateways">
|
||||||
{({ isActive }) => (
|
{({ isActive }) => (
|
||||||
<MenuItem isSelected={isActive} icon="jigsaw-puzzle">
|
<MenuItem isSelected={isActive} icon="gateway" iconMode="reverse">
|
||||||
Gateways
|
Gateways
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
Table,
|
Table,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
TableSkeleton,
|
TableSkeleton,
|
||||||
|
Tag,
|
||||||
TBody,
|
TBody,
|
||||||
Td,
|
Td,
|
||||||
Th,
|
Th,
|
||||||
@@ -127,6 +128,7 @@ export const GatewayListPage = withPermission(
|
|||||||
<Tr>
|
<Tr>
|
||||||
<Th className="w-1/3">Name</Th>
|
<Th className="w-1/3">Name</Th>
|
||||||
<Th>Cert Issued At</Th>
|
<Th>Cert Issued At</Th>
|
||||||
|
<Th>Projects</Th>
|
||||||
<Th>Identity</Th>
|
<Th>Identity</Th>
|
||||||
<Th>
|
<Th>
|
||||||
Health Check
|
Health Check
|
||||||
@@ -149,6 +151,13 @@ export const GatewayListPage = withPermission(
|
|||||||
<Tr key={el.id}>
|
<Tr key={el.id}>
|
||||||
<Td>{el.name}</Td>
|
<Td>{el.name}</Td>
|
||||||
<Td>{format(new Date(el.issuedAt), "yyyy-MM-dd hh:mm:ss aaa")}</Td>
|
<Td>{format(new Date(el.issuedAt), "yyyy-MM-dd hh:mm:ss aaa")}</Td>
|
||||||
|
<Td>
|
||||||
|
{el.projects.map((projectDetails) => (
|
||||||
|
<Tag key={projectDetails.id} size="xs">
|
||||||
|
{projectDetails.name}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</Td>
|
||||||
<Td>{el.identity.name}</Td>
|
<Td>{el.identity.name}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{el.heartbeat
|
{el.heartbeat
|
||||||
|
|||||||
+43
-5
@@ -3,9 +3,10 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Input } from "@app/components/v2";
|
import { Button, FilterableSelect, FormControl, Input } from "@app/components/v2";
|
||||||
import { useUpdateGatewayById } from "@app/hooks/api";
|
import { useGetUserWorkspaces, useUpdateGatewayById } from "@app/hooks/api";
|
||||||
import { TGateway } from "@app/hooks/api/gateways/types";
|
import { TGateway } from "@app/hooks/api/gateways/types";
|
||||||
|
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
gatewayDetails: TGateway;
|
gatewayDetails: TGateway;
|
||||||
@@ -13,7 +14,13 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
name: z.string()
|
name: z.string(),
|
||||||
|
projects: z
|
||||||
|
.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FormData = z.infer<typeof schema>;
|
export type FormData = z.infer<typeof schema>;
|
||||||
@@ -31,13 +38,20 @@ export const EditGatewayDetailsModal = ({ gatewayDetails, onClose }: Props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const updateGatewayById = useUpdateGatewayById();
|
const updateGatewayById = useUpdateGatewayById();
|
||||||
|
// when gateway goes to other products switch to all
|
||||||
|
const { data: secretManagerWorkspaces, isLoading: isSecretManagerLoading } = useGetUserWorkspaces(
|
||||||
|
{
|
||||||
|
type: ProjectType.SecretManager
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const onFormSubmit = ({ name }: FormData) => {
|
const onFormSubmit = ({ name, projects }: FormData) => {
|
||||||
if (isSubmitting) return;
|
if (isSubmitting) return;
|
||||||
updateGatewayById.mutate(
|
updateGatewayById.mutate(
|
||||||
{
|
{
|
||||||
id: gatewayDetails.id,
|
id: gatewayDetails.id,
|
||||||
name
|
name,
|
||||||
|
projectIds: projects.map((el) => el.id)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -62,6 +76,30 @@ export const EditGatewayDetailsModal = ({ gatewayDetails, onClose }: Props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="projects"
|
||||||
|
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
className="w-full"
|
||||||
|
label="Projects"
|
||||||
|
tooltipText="Select the project that you wish to assign to the gateway."
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={secretManagerWorkspaces}
|
||||||
|
placeholder="Select projects..."
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
isMulti
|
||||||
|
isLoading={isSecretManagerLoading}
|
||||||
|
getOptionValue={(option) => option.id}
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<div className="mt-4 flex items-center">
|
<div className="mt-4 flex items-center">
|
||||||
<Button className="mr-4" size="sm" type="submit" isLoading={isSubmitting}>
|
<Button className="mr-4" size="sm" type="submit" isLoading={isSubmitting}>
|
||||||
Update
|
Update
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ type Props = {
|
|||||||
title: string;
|
title: string;
|
||||||
formName: keyof Omit<
|
formName: keyof Omit<
|
||||||
Exclude<TFormSchema["permissions"], undefined>,
|
Exclude<TFormSchema["permissions"], undefined>,
|
||||||
"workspace" | "organization-admin-console" | "kmip"
|
"workspace" | "organization-admin-console" | "kmip" | "gateway"
|
||||||
>;
|
>;
|
||||||
setValue: UseFormSetValue<TFormSchema>;
|
setValue: UseFormSetValue<TFormSchema>;
|
||||||
control: Control<TFormSchema>;
|
control: Control<TFormSchema>;
|
||||||
|
|||||||
+1
-1
@@ -143,7 +143,7 @@ export const SqlDatabaseInputForm = ({
|
|||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
const { data: projectGateways, isPending: isProjectGatewaysLoading } = useQuery(
|
const { data: projectGateways, isPending: isProjectGatewaysLoading } = useQuery(
|
||||||
gatewaysQueryKeys.list({ projectId: currentWorkspace.id })
|
gatewaysQueryKeys.listProjectGateways({ projectId: currentWorkspace.id })
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||||
|
|||||||
+13
-9
@@ -36,7 +36,7 @@ const formSchema = z.object({
|
|||||||
revocationStatement: z.string().min(1),
|
revocationStatement: z.string().min(1),
|
||||||
renewStatement: z.string().optional(),
|
renewStatement: z.string().optional(),
|
||||||
ca: z.string().optional(),
|
ca: z.string().optional(),
|
||||||
gatewayId: z.string().optional()
|
projectGatewayId: z.string().optional()
|
||||||
})
|
})
|
||||||
.partial(),
|
.partial(),
|
||||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||||
@@ -100,12 +100,13 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
});
|
});
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const { data: projectGateways, isPending: isProjectGatewaysLoading } = useQuery(
|
const { data: projectGateways, isPending: isProjectGatewaysLoading } = useQuery(
|
||||||
gatewaysQueryKeys.list({ projectId: currentWorkspace.id })
|
gatewaysQueryKeys.listProjectGateways({ projectId: currentWorkspace.id })
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateDynamicSecret = useUpdateDynamicSecret();
|
const updateDynamicSecret = useUpdateDynamicSecret();
|
||||||
const selectedGatewayId = watch("inputs.gatewayId");
|
const selectedProjectGatewayId = watch("inputs.projectGatewayId");
|
||||||
const isGatewayInActive = projectGateways?.findIndex((el) => el.id === selectedGatewayId) === -1;
|
const isGatewayInActive =
|
||||||
|
projectGateways?.findIndex((el) => el.projectGatewayId === selectedProjectGatewayId) === -1;
|
||||||
|
|
||||||
const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => {
|
const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
@@ -119,7 +120,10 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
data: {
|
data: {
|
||||||
maxTTL: maxTTL || undefined,
|
maxTTL: maxTTL || undefined,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
inputs: { ...inputs, gatewayId: isGatewayInActive ? null : inputs.gatewayId },
|
inputs: {
|
||||||
|
...inputs,
|
||||||
|
projectGatewayId: isGatewayInActive ? null : inputs.projectGatewayId
|
||||||
|
},
|
||||||
newName: newName === dynamicSecret.name ? undefined : newName
|
newName: newName === dynamicSecret.name ? undefined : newName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -189,14 +193,14 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
<div>
|
<div>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="inputs.gatewayId"
|
name="inputs.projectGatewayId"
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
isError={Boolean(error?.message) || isGatewayInActive}
|
isError={Boolean(error?.message) || isGatewayInActive}
|
||||||
errorText={
|
errorText={
|
||||||
isGatewayInActive && selectedGatewayId
|
isGatewayInActive && selectedProjectGatewayId
|
||||||
? `Gateway ${selectedGatewayId} is removed`
|
? `Project Gateway ${selectedProjectGatewayId} is removed`
|
||||||
: error?.message
|
: error?.message
|
||||||
}
|
}
|
||||||
label="Gateway"
|
label="Gateway"
|
||||||
@@ -215,7 +219,7 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
Internet Gateway
|
Internet Gateway
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
{projectGateways?.map((el) => (
|
{projectGateways?.map((el) => (
|
||||||
<SelectItem value={el.id} key={el.id}>
|
<SelectItem value={el.projectGatewayId} key={el.id}>
|
||||||
{el.name}
|
{el.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user