mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 18:27:19 +00:00
feat: integrated with github app connection to gateway and some adjustments
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
import { 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";
|
||||||
@@ -21,7 +21,18 @@ export const registerProxyRouter = async (server: FastifyZodProvider) => {
|
|||||||
name: z.string()
|
name: z.string()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.any()
|
200: z.object({
|
||||||
|
pki: z.object({
|
||||||
|
serverCertificate: z.string(),
|
||||||
|
serverPrivateKey: z.string(),
|
||||||
|
clientCertificateChain: z.string()
|
||||||
|
}),
|
||||||
|
ssh: z.object({
|
||||||
|
serverCertificate: z.string(),
|
||||||
|
serverPrivateKey: z.string(),
|
||||||
|
clientCAPublicKey: z.string()
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: (req, _, next) => {
|
onRequest: (req, _, next) => {
|
||||||
@@ -59,6 +70,10 @@ export const registerProxyRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Org proxy registration is not yet supported"
|
||||||
|
});
|
||||||
|
|
||||||
return server.services.proxy.registerProxy({
|
return server.services.proxy.registerProxy({
|
||||||
...req.body,
|
...req.body,
|
||||||
identityId: req.permission.id,
|
identityId: req.permission.id,
|
||||||
|
|||||||
@@ -1,9 +1,19 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
|
import { GatewaysV2Schema } from "@app/db/schemas";
|
||||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
import { 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";
|
||||||
|
|
||||||
|
const SanitizedGatewayV2Schema = GatewaysV2Schema.pick({
|
||||||
|
id: true,
|
||||||
|
identityId: true,
|
||||||
|
name: true,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true,
|
||||||
|
heartbeat: true
|
||||||
|
});
|
||||||
|
|
||||||
export const registerGatewayV2Router = async (server: FastifyZodProvider) => {
|
export const registerGatewayV2Router = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -14,7 +24,20 @@ export const registerGatewayV2Router = async (server: FastifyZodProvider) => {
|
|||||||
name: z.string()
|
name: z.string()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.any()
|
200: z.object({
|
||||||
|
gatewayId: z.string(),
|
||||||
|
proxyIp: z.string(),
|
||||||
|
pki: z.object({
|
||||||
|
serverCertificate: z.string(),
|
||||||
|
serverPrivateKey: z.string(),
|
||||||
|
clientCertificateChain: z.string()
|
||||||
|
}),
|
||||||
|
ssh: z.object({
|
||||||
|
clientCertificate: z.string(),
|
||||||
|
clientPrivateKey: z.string(),
|
||||||
|
serverCAPublicKey: z.string()
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
@@ -23,6 +46,7 @@ export const registerGatewayV2Router = async (server: FastifyZodProvider) => {
|
|||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
proxyName: req.body.proxyName,
|
proxyName: req.body.proxyName,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
name: req.body.name
|
name: req.body.name
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,7 +82,12 @@ export const registerGatewayV2Router = async (server: FastifyZodProvider) => {
|
|||||||
url: "/",
|
url: "/",
|
||||||
schema: {
|
schema: {
|
||||||
response: {
|
response: {
|
||||||
200: z.any()
|
200: SanitizedGatewayV2Schema.extend({
|
||||||
|
identity: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.string()
|
||||||
|
})
|
||||||
|
}).array()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
@@ -82,7 +111,7 @@ export const registerGatewayV2Router = async (server: FastifyZodProvider) => {
|
|||||||
id: z.string()
|
id: z.string()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.any()
|
200: SanitizedGatewayV2Schema
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.JWT]),
|
||||||
@@ -91,7 +120,7 @@ export const registerGatewayV2Router = async (server: FastifyZodProvider) => {
|
|||||||
orgPermission: req.permission,
|
orgPermission: req.permission,
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
});
|
});
|
||||||
return { gateway };
|
return gateway;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import net from "node:net";
|
import net from "node:net";
|
||||||
|
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
|
|
||||||
import { TProxies } from "@app/db/schemas";
|
import { TProxies } from "@app/db/schemas";
|
||||||
@@ -9,7 +10,7 @@ import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
|||||||
import { GatewayProxyProtocol } from "@app/lib/gateway/types";
|
import { GatewayProxyProtocol } from "@app/lib/gateway/types";
|
||||||
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||||
import { constructPemChainFromCerts } from "@app/services/certificate/certificate-fns";
|
import { constructPemChainFromCerts } from "@app/services/certificate/certificate-fns";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import {
|
import {
|
||||||
@@ -20,6 +21,8 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
|||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
|
import { OrgPermissionGatewayActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
|
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
||||||
import { TProxyDALFactory } from "../proxy/proxy-dal";
|
import { TProxyDALFactory } from "../proxy/proxy-dal";
|
||||||
import { isInstanceProxy } from "../proxy/proxy-fns";
|
import { isInstanceProxy } from "../proxy/proxy-fns";
|
||||||
import { TProxyServiceFactory } from "../proxy/proxy-service";
|
import { TProxyServiceFactory } from "../proxy/proxy-service";
|
||||||
@@ -34,6 +37,7 @@ type TGatewayV2ServiceFactoryDep = {
|
|||||||
proxyService: TProxyServiceFactory;
|
proxyService: TProxyServiceFactory;
|
||||||
gatewayV2DAL: TGatewayV2DALFactory;
|
gatewayV2DAL: TGatewayV2DALFactory;
|
||||||
proxyDAL: TProxyDALFactory;
|
proxyDAL: TProxyDALFactory;
|
||||||
|
permissionService: TPermissionServiceFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGatewayV2ServiceFactory = ReturnType<typeof gatewayV2ServiceFactory>;
|
export type TGatewayV2ServiceFactory = ReturnType<typeof gatewayV2ServiceFactory>;
|
||||||
@@ -44,8 +48,32 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
proxyService,
|
proxyService,
|
||||||
gatewayV2DAL,
|
gatewayV2DAL,
|
||||||
proxyDAL
|
proxyDAL,
|
||||||
|
permissionService
|
||||||
}: TGatewayV2ServiceFactoryDep) => {
|
}: TGatewayV2ServiceFactoryDep) => {
|
||||||
|
const $validateIdentityAccessToGateway = async (orgId: string, actorId: string, actorAuthMethod: ActorAuthMethod) => {
|
||||||
|
const orgLicensePlan = await licenseService.getPlan(orgId);
|
||||||
|
if (!orgLicensePlan.gateway) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Gateway operation failed due to organization plan restrictions. Please upgrade your instance to Infisical's Enterprise plan."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
ActorType.IDENTITY,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
orgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionGatewayActions.CreateGateways,
|
||||||
|
OrgPermissionSubjects.Gateway
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const $getOrgCAs = async (orgId: string) => {
|
const $getOrgCAs = async (orgId: string) => {
|
||||||
const { encryptor: orgKmsEncryptor, decryptor: orgKmsDecryptor } = await kmsService.createCipherPairWithDataKey({
|
const { encryptor: orgKmsEncryptor, decryptor: orgKmsDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
type: KmsDataKey.Organization,
|
type: KmsDataKey.Organization,
|
||||||
@@ -217,20 +245,18 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const listGateways = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
|
const listGateways = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
|
||||||
// const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
// orgPermission.type,
|
orgPermission.type,
|
||||||
// orgPermission.id,
|
orgPermission.id,
|
||||||
// orgPermission.orgId,
|
orgPermission.orgId,
|
||||||
// orgPermission.authMethod,
|
orgPermission.authMethod,
|
||||||
// orgPermission.orgId
|
orgPermission.orgId
|
||||||
// );
|
);
|
||||||
// ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
// OrgPermissionGatewayActions.ListGateways,
|
|
||||||
// OrgPermissionSubjects.Gateway
|
|
||||||
// );
|
|
||||||
|
|
||||||
const orgGatewayConfig = await orgGatewayConfigV2DAL.findOne({ orgId: orgPermission.orgId });
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
if (!orgGatewayConfig) return [];
|
OrgPermissionGatewayActions.ListGateways,
|
||||||
|
OrgPermissionSubjects.Gateway
|
||||||
|
);
|
||||||
|
|
||||||
const gateways = await gatewayV2DAL.find({
|
const gateways = await gatewayV2DAL.find({
|
||||||
orgId: orgPermission.orgId
|
orgId: orgPermission.orgId
|
||||||
@@ -389,14 +415,17 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
const registerGateway = async ({
|
const registerGateway = async ({
|
||||||
orgId,
|
orgId,
|
||||||
actorId,
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
proxyName,
|
proxyName,
|
||||||
name
|
name
|
||||||
}: {
|
}: {
|
||||||
orgId: string;
|
orgId: string;
|
||||||
actorId: string;
|
actorId: string;
|
||||||
|
actorAuthMethod: ActorAuthMethod;
|
||||||
proxyName: string;
|
proxyName: string;
|
||||||
name: string;
|
name: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
await $validateIdentityAccessToGateway(orgId, actorId, actorAuthMethod);
|
||||||
const orgCAs = await $getOrgCAs(orgId);
|
const orgCAs = await $getOrgCAs(orgId);
|
||||||
|
|
||||||
let proxy: TProxies;
|
let proxy: TProxies;
|
||||||
@@ -407,7 +436,7 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!proxy) {
|
if (!proxy) {
|
||||||
throw new Error("Proxy not found");
|
throw new NotFoundError({ message: `Proxy ${proxyName} not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const [gateway] = await gatewayV2DAL.upsert(
|
const [gateway] = await gatewayV2DAL.upsert(
|
||||||
@@ -499,6 +528,8 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const heartbeat = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
|
const heartbeat = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
|
||||||
|
await $validateIdentityAccessToGateway(orgPermission.orgId, orgPermission.id, orgPermission.authMethod);
|
||||||
|
|
||||||
const gateway = await gatewayV2DAL.findOne({
|
const gateway = await gatewayV2DAL.findOne({
|
||||||
orgId: orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
identityId: orgPermission.id
|
identityId: orgPermission.id
|
||||||
@@ -587,19 +618,25 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteGatewayById = async ({ orgPermission, id }: { orgPermission: OrgServiceActor; id: string }) => {
|
const deleteGatewayById = async ({ orgPermission, id }: { orgPermission: OrgServiceActor; id: string }) => {
|
||||||
// const { permission } = await permissionService.getOrgPermission(
|
const gateway = await gatewayV2DAL.findOne({ id, orgId: orgPermission.orgId });
|
||||||
// orgPermission.type,
|
if (!gateway) {
|
||||||
// orgPermission.id,
|
throw new NotFoundError({ message: `Gateway ${id} not found` });
|
||||||
// orgPermission.orgId,
|
}
|
||||||
// orgPermission.authMethod,
|
|
||||||
// orgPermission.orgId
|
|
||||||
// );
|
|
||||||
// ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
// OrgPermissionGatewayActions.DeleteGateways,
|
|
||||||
// OrgPermissionSubjects.Gateway
|
|
||||||
// );
|
|
||||||
|
|
||||||
return gatewayV2DAL.deleteById(id);
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
orgPermission.type,
|
||||||
|
orgPermission.id,
|
||||||
|
gateway.orgId,
|
||||||
|
orgPermission.authMethod,
|
||||||
|
orgPermission.orgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionGatewayActions.DeleteGateways,
|
||||||
|
OrgPermissionSubjects.Gateway
|
||||||
|
);
|
||||||
|
|
||||||
|
return gatewayV2DAL.deleteById(gateway.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -799,7 +799,7 @@ export const proxyServiceFactory = ({
|
|||||||
const proxyClientSshCert = await createSshCert({
|
const proxyClientSshCert = await createSshCert({
|
||||||
caPrivateKey: instanceCAs.instanceProxySshServerCaPrivateKey.toString("utf8"),
|
caPrivateKey: instanceCAs.instanceProxySshServerCaPrivateKey.toString("utf8"),
|
||||||
clientPublicKey: proxyClientSshPublicKey,
|
clientPublicKey: proxyClientSshPublicKey,
|
||||||
keyId: `proxy-client-${proxy.id}`,
|
keyId: `client-${proxyName}`,
|
||||||
principals: [gatewayId],
|
principals: [gatewayId],
|
||||||
certType: SshCertType.USER,
|
certType: SshCertType.USER,
|
||||||
requestedTtl: "30d"
|
requestedTtl: "30d"
|
||||||
@@ -898,7 +898,6 @@ export const proxyServiceFactory = ({
|
|||||||
const isOrgProxy = identityId && orgId;
|
const isOrgProxy = identityId && orgId;
|
||||||
|
|
||||||
if (isOrgProxy) {
|
if (isOrgProxy) {
|
||||||
// organization proxy
|
|
||||||
if (isInstanceProxy(name)) {
|
if (isInstanceProxy(name)) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Org proxy name cannot start with 'infisical-'. This is reserved for internal use."
|
message: "Org proxy name cannot start with 'infisical-'. This is reserved for internal use."
|
||||||
@@ -935,8 +934,7 @@ export const proxyServiceFactory = ({
|
|||||||
return existingProxy;
|
return existingProxy;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// instance proxy
|
if (!isInstanceProxy(name)) {
|
||||||
if (!name.startsWith("infisical-")) {
|
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Instance proxy name must start with 'infisical-'."
|
message: "Instance proxy name must start with 'infisical-'."
|
||||||
});
|
});
|
||||||
@@ -952,7 +950,7 @@ export const proxyServiceFactory = ({
|
|||||||
|
|
||||||
if (existingProxy && existingProxy.ip !== ip) {
|
if (existingProxy && existingProxy.ip !== ip) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Instance proxy with this name already exists"
|
message: "Instance proxy with this name already exists with a different IP address"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1071,6 +1071,23 @@ export const registerRoutes = async (
|
|||||||
keyStore
|
keyStore
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const proxyService = proxyServiceFactory({
|
||||||
|
instanceProxyConfigDAL,
|
||||||
|
orgProxyConfigDAL,
|
||||||
|
proxyDAL,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
|
const gatewayV2Service = gatewayV2ServiceFactory({
|
||||||
|
kmsService,
|
||||||
|
licenseService,
|
||||||
|
proxyService,
|
||||||
|
orgGatewayConfigV2DAL,
|
||||||
|
gatewayV2DAL,
|
||||||
|
proxyDAL,
|
||||||
|
permissionService
|
||||||
|
});
|
||||||
|
|
||||||
const secretSyncQueue = secretSyncQueueFactory({
|
const secretSyncQueue = secretSyncQueueFactory({
|
||||||
queueService,
|
queueService,
|
||||||
secretSyncDAL,
|
secretSyncDAL,
|
||||||
@@ -1095,7 +1112,8 @@ export const registerRoutes = async (
|
|||||||
resourceMetadataDAL,
|
resourceMetadataDAL,
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretQueueService = secretQueueFactory({
|
const secretQueueService = secretQueueFactory({
|
||||||
@@ -1463,22 +1481,6 @@ export const registerRoutes = async (
|
|||||||
smtpService
|
smtpService
|
||||||
});
|
});
|
||||||
|
|
||||||
const proxyService = proxyServiceFactory({
|
|
||||||
instanceProxyConfigDAL,
|
|
||||||
orgProxyConfigDAL,
|
|
||||||
proxyDAL,
|
|
||||||
kmsService
|
|
||||||
});
|
|
||||||
|
|
||||||
const gatewayV2Service = gatewayV2ServiceFactory({
|
|
||||||
kmsService,
|
|
||||||
licenseService,
|
|
||||||
proxyService,
|
|
||||||
orgGatewayConfigV2DAL,
|
|
||||||
gatewayV2DAL,
|
|
||||||
proxyDAL
|
|
||||||
});
|
|
||||||
|
|
||||||
const identityService = identityServiceFactory({
|
const identityService = identityServiceFactory({
|
||||||
permissionService,
|
permissionService,
|
||||||
identityDAL,
|
identityDAL,
|
||||||
|
|||||||
@@ -597,7 +597,7 @@ export const appConnectionServiceFactory = ({
|
|||||||
deleteAppConnection,
|
deleteAppConnection,
|
||||||
connectAppConnectionById,
|
connectAppConnectionById,
|
||||||
listAvailableAppConnectionsForUser,
|
listAvailableAppConnectionsForUser,
|
||||||
github: githubConnectionService(connectAppConnectionById, gatewayService),
|
github: githubConnectionService(connectAppConnectionById, gatewayService, gatewayV2Service),
|
||||||
githubRadar: githubRadarConnectionService(connectAppConnectionById),
|
githubRadar: githubRadarConnectionService(connectAppConnectionById),
|
||||||
gcp: gcpConnectionService(connectAppConnectionById),
|
gcp: gcpConnectionService(connectAppConnectionById),
|
||||||
databricks: databricksConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
databricks: databricksConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import RE2 from "re2";
|
|||||||
|
|
||||||
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
|
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { request as httpRequest } from "@app/lib/config/request";
|
import { request as httpRequest } from "@app/lib/config/request";
|
||||||
import { BadRequestError, ForbiddenRequestError, InternalServerError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, InternalServerError } from "@app/lib/errors";
|
||||||
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
||||||
|
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||||
import { getAppConnectionMethodName } from "@app/services/app-connection/app-connection-fns";
|
import { getAppConnectionMethodName } from "@app/services/app-connection/app-connection-fns";
|
||||||
@@ -50,6 +52,7 @@ export const getGitHubInstanceApiUrl = async (config: {
|
|||||||
export const requestWithGitHubGateway = async <T>(
|
export const requestWithGitHubGateway = async <T>(
|
||||||
appConnection: { gatewayId?: string | null },
|
appConnection: { gatewayId?: string | null },
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
|
||||||
requestConfig: AxiosRequestConfig
|
requestConfig: AxiosRequestConfig
|
||||||
): Promise<AxiosResponse<T>> => {
|
): Promise<AxiosResponse<T>> => {
|
||||||
const { gatewayId } = appConnection;
|
const { gatewayId } = appConnection;
|
||||||
@@ -64,6 +67,52 @@ export const requestWithGitHubGateway = async <T>(
|
|||||||
await blockLocalAndPrivateIpAddresses(url.toString());
|
await blockLocalAndPrivateIpAddresses(url.toString());
|
||||||
|
|
||||||
const [targetHost] = await verifyHostInputValidity(url.host, true);
|
const [targetHost] = await verifyHostInputValidity(url.host, true);
|
||||||
|
const gatewayConnectionDetails = await gatewayV2Service.getPlatformConnectionDetailsByGatewayId({
|
||||||
|
gatewayId,
|
||||||
|
targetHost,
|
||||||
|
targetPort: 443
|
||||||
|
});
|
||||||
|
|
||||||
|
if (gatewayConnectionDetails) {
|
||||||
|
return withGatewayV2Proxy(
|
||||||
|
async (proxyPort) => {
|
||||||
|
const httpsAgent = new https.Agent({
|
||||||
|
servername: targetHost
|
||||||
|
});
|
||||||
|
|
||||||
|
url.protocol = "https:";
|
||||||
|
url.host = `localhost:${proxyPort}`;
|
||||||
|
|
||||||
|
const finalRequestConfig: AxiosRequestConfig = {
|
||||||
|
...requestConfig,
|
||||||
|
url: url.toString(),
|
||||||
|
httpsAgent,
|
||||||
|
headers: {
|
||||||
|
...requestConfig.headers,
|
||||||
|
Host: targetHost
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await httpRequest.request(finalRequestConfig);
|
||||||
|
} catch (error) {
|
||||||
|
const axiosError = error as AxiosError;
|
||||||
|
logger.error(
|
||||||
|
{ message: axiosError.message, data: axiosError.response?.data },
|
||||||
|
"Error during GitHub gateway request:"
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: GatewayProxyProtocol.Tcp,
|
||||||
|
proxyIp: gatewayConnectionDetails.proxyIp,
|
||||||
|
gateway: gatewayConnectionDetails.gateway,
|
||||||
|
proxy: gatewayConnectionDetails.proxy
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(gatewayId);
|
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(gatewayId);
|
||||||
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
||||||
|
|
||||||
@@ -168,6 +217,7 @@ function extractNextPageUrl(linkHeader: string | undefined): string | null {
|
|||||||
export const makePaginatedGitHubRequest = async <T, R = T[]>(
|
export const makePaginatedGitHubRequest = async <T, R = T[]>(
|
||||||
appConnection: TGitHubConnection,
|
appConnection: TGitHubConnection,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
|
||||||
path: string,
|
path: string,
|
||||||
dataMapper?: (data: R) => T[]
|
dataMapper?: (data: R) => T[]
|
||||||
): Promise<T[]> => {
|
): Promise<T[]> => {
|
||||||
@@ -184,15 +234,20 @@ export const makePaginatedGitHubRequest = async <T, R = T[]>(
|
|||||||
const maxIterations = 1000;
|
const maxIterations = 1000;
|
||||||
|
|
||||||
// Make initial request to get link header
|
// Make initial request to get link header
|
||||||
const firstResponse: AxiosResponse<R> = await requestWithGitHubGateway<R>(appConnection, gatewayService, {
|
const firstResponse: AxiosResponse<R> = await requestWithGitHubGateway<R>(
|
||||||
url: initialUrlObj.toString(),
|
appConnection,
|
||||||
method: "GET",
|
gatewayService,
|
||||||
headers: {
|
gatewayV2Service,
|
||||||
Accept: "application/vnd.github+json",
|
{
|
||||||
Authorization: `Bearer ${token}`,
|
url: initialUrlObj.toString(),
|
||||||
"X-GitHub-Api-Version": "2022-11-28"
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/vnd.github+json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const firstPageItems = dataMapper ? dataMapper(firstResponse.data) : (firstResponse.data as unknown as T[]);
|
const firstPageItems = dataMapper ? dataMapper(firstResponse.data) : (firstResponse.data as unknown as T[]);
|
||||||
results = results.concat(firstPageItems);
|
results = results.concat(firstPageItems);
|
||||||
@@ -212,7 +267,7 @@ export const makePaginatedGitHubRequest = async <T, R = T[]>(
|
|||||||
pageUrlObj.searchParams.set("page", pageNum.toString());
|
pageUrlObj.searchParams.set("page", pageNum.toString());
|
||||||
|
|
||||||
pageRequests.push(
|
pageRequests.push(
|
||||||
requestWithGitHubGateway<R>(appConnection, gatewayService, {
|
requestWithGitHubGateway<R>(appConnection, gatewayService, gatewayV2Service, {
|
||||||
url: pageUrlObj.toString(),
|
url: pageUrlObj.toString(),
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -236,15 +291,20 @@ export const makePaginatedGitHubRequest = async <T, R = T[]>(
|
|||||||
|
|
||||||
while (url && i < maxIterations) {
|
while (url && i < maxIterations) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const response: AxiosResponse<R> = await requestWithGitHubGateway<R>(appConnection, gatewayService, {
|
const response: AxiosResponse<R> = await requestWithGitHubGateway<R>(
|
||||||
url,
|
appConnection,
|
||||||
method: "GET",
|
gatewayService,
|
||||||
headers: {
|
gatewayV2Service,
|
||||||
Accept: "application/vnd.github+json",
|
{
|
||||||
Authorization: `Bearer ${token}`,
|
url,
|
||||||
"X-GitHub-Api-Version": "2022-11-28"
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/vnd.github+json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const items = dataMapper ? dataMapper(response.data) : (response.data as unknown as T[]);
|
const items = dataMapper ? dataMapper(response.data) : (response.data as unknown as T[]);
|
||||||
results = results.concat(items);
|
results = results.concat(items);
|
||||||
@@ -283,30 +343,39 @@ type GitHubEnvironment = {
|
|||||||
|
|
||||||
export const getGitHubRepositories = async (
|
export const getGitHubRepositories = async (
|
||||||
appConnection: TGitHubConnection,
|
appConnection: TGitHubConnection,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
if (appConnection.method === GitHubConnectionMethod.App) {
|
if (appConnection.method === GitHubConnectionMethod.App) {
|
||||||
return makePaginatedGitHubRequest<GitHubRepository, { repositories: GitHubRepository[] }>(
|
return makePaginatedGitHubRequest<GitHubRepository, { repositories: GitHubRepository[] }>(
|
||||||
appConnection,
|
appConnection,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
|
gatewayV2Service,
|
||||||
"/installation/repositories",
|
"/installation/repositories",
|
||||||
(data) => data.repositories
|
(data) => data.repositories
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const repos = await makePaginatedGitHubRequest<GitHubRepository>(appConnection, gatewayService, "/user/repos");
|
const repos = await makePaginatedGitHubRequest<GitHubRepository>(
|
||||||
|
appConnection,
|
||||||
|
gatewayService,
|
||||||
|
gatewayV2Service,
|
||||||
|
"/user/repos"
|
||||||
|
);
|
||||||
|
|
||||||
return repos.filter((repo) => repo.permissions?.admin);
|
return repos.filter((repo) => repo.permissions?.admin);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getGitHubOrganizations = async (
|
export const getGitHubOrganizations = async (
|
||||||
appConnection: TGitHubConnection,
|
appConnection: TGitHubConnection,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
if (appConnection.method === GitHubConnectionMethod.App) {
|
if (appConnection.method === GitHubConnectionMethod.App) {
|
||||||
const installationRepositories = await makePaginatedGitHubRequest<
|
const installationRepositories = await makePaginatedGitHubRequest<
|
||||||
GitHubRepository,
|
GitHubRepository,
|
||||||
{ repositories: GitHubRepository[] }
|
{ repositories: GitHubRepository[] }
|
||||||
>(appConnection, gatewayService, "/installation/repositories", (data) => data.repositories);
|
>(appConnection, gatewayService, gatewayV2Service, "/installation/repositories", (data) => data.repositories);
|
||||||
|
|
||||||
const organizationMap: Record<string, GitHubOrganization> = {};
|
const organizationMap: Record<string, GitHubOrganization> = {};
|
||||||
installationRepositories.forEach((repo) => {
|
installationRepositories.forEach((repo) => {
|
||||||
@@ -318,12 +387,13 @@ export const getGitHubOrganizations = async (
|
|||||||
return Object.values(organizationMap);
|
return Object.values(organizationMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
return makePaginatedGitHubRequest<GitHubOrganization>(appConnection, gatewayService, "/user/orgs");
|
return makePaginatedGitHubRequest<GitHubOrganization>(appConnection, gatewayService, gatewayV2Service, "/user/orgs");
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getGitHubEnvironments = async (
|
export const getGitHubEnvironments = async (
|
||||||
appConnection: TGitHubConnection,
|
appConnection: TGitHubConnection,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
|
||||||
owner: string,
|
owner: string,
|
||||||
repo: string
|
repo: string
|
||||||
) => {
|
) => {
|
||||||
@@ -331,6 +401,7 @@ export const getGitHubEnvironments = async (
|
|||||||
return await makePaginatedGitHubRequest<GitHubEnvironment, { environments: GitHubEnvironment[] }>(
|
return await makePaginatedGitHubRequest<GitHubEnvironment, { environments: GitHubEnvironment[] }>(
|
||||||
appConnection,
|
appConnection,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
|
gatewayV2Service,
|
||||||
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/environments`,
|
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/environments`,
|
||||||
(data) => data.environments
|
(data) => data.environments
|
||||||
);
|
);
|
||||||
@@ -358,7 +429,8 @@ export function isGithubErrorResponse(data: GithubTokenRespData): data is Github
|
|||||||
|
|
||||||
export const validateGitHubConnectionCredentials = async (
|
export const validateGitHubConnectionCredentials = async (
|
||||||
config: TGitHubConnectionConfig,
|
config: TGitHubConnectionConfig,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
const { credentials, method } = config;
|
const { credentials, method } = config;
|
||||||
const {
|
const {
|
||||||
@@ -394,7 +466,7 @@ export const validateGitHubConnectionCredentials = async (
|
|||||||
const host = credentials.host || "github.com";
|
const host = credentials.host || "github.com";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tokenResp = await requestWithGitHubGateway<GithubTokenRespData>(config, gatewayService, {
|
tokenResp = await requestWithGitHubGateway<GithubTokenRespData>(config, gatewayService, gatewayV2Service, {
|
||||||
url: `https://${host}/login/oauth/access_token`,
|
url: `https://${host}/login/oauth/access_token`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: {
|
data: {
|
||||||
@@ -446,7 +518,7 @@ export const validateGitHubConnectionCredentials = async (
|
|||||||
id: number;
|
id: number;
|
||||||
};
|
};
|
||||||
}[];
|
}[];
|
||||||
}>(config, gatewayService, {
|
}>(config, gatewayService, gatewayV2Service, {
|
||||||
url: `https://${await getGitHubInstanceApiUrl(config)}/user/installations`,
|
url: `https://${await getGitHubInstanceApiUrl(config)}/user/installations`,
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import {
|
import {
|
||||||
@@ -22,12 +23,13 @@ type TListGitHubEnvironmentsDTO = {
|
|||||||
|
|
||||||
export const githubConnectionService = (
|
export const githubConnectionService = (
|
||||||
getAppConnection: TGetAppConnectionFunc,
|
getAppConnection: TGetAppConnectionFunc,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
const listRepositories = async (connectionId: string, actor: OrgServiceActor) => {
|
const listRepositories = async (connectionId: string, actor: OrgServiceActor) => {
|
||||||
const appConnection = await getAppConnection(AppConnection.GitHub, connectionId, actor);
|
const appConnection = await getAppConnection(AppConnection.GitHub, connectionId, actor);
|
||||||
|
|
||||||
const repositories = await getGitHubRepositories(appConnection, gatewayService);
|
const repositories = await getGitHubRepositories(appConnection, gatewayService, gatewayV2Service);
|
||||||
|
|
||||||
return repositories;
|
return repositories;
|
||||||
};
|
};
|
||||||
@@ -35,7 +37,7 @@ export const githubConnectionService = (
|
|||||||
const listOrganizations = async (connectionId: string, actor: OrgServiceActor) => {
|
const listOrganizations = async (connectionId: string, actor: OrgServiceActor) => {
|
||||||
const appConnection = await getAppConnection(AppConnection.GitHub, connectionId, actor);
|
const appConnection = await getAppConnection(AppConnection.GitHub, connectionId, actor);
|
||||||
|
|
||||||
const organizations = await getGitHubOrganizations(appConnection, gatewayService);
|
const organizations = await getGitHubOrganizations(appConnection, gatewayService, gatewayV2Service);
|
||||||
|
|
||||||
return organizations;
|
return organizations;
|
||||||
};
|
};
|
||||||
@@ -46,7 +48,7 @@ export const githubConnectionService = (
|
|||||||
) => {
|
) => {
|
||||||
const appConnection = await getAppConnection(AppConnection.GitHub, connectionId, actor);
|
const appConnection = await getAppConnection(AppConnection.GitHub, connectionId, actor);
|
||||||
|
|
||||||
const environments = await getGitHubEnvironments(appConnection, gatewayService, owner, repo);
|
const environments = await getGitHubEnvironments(appConnection, gatewayService, gatewayV2Service, owner, repo);
|
||||||
|
|
||||||
return environments;
|
return environments;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import sodium from "libsodium-wrappers";
|
import sodium from "libsodium-wrappers";
|
||||||
|
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import {
|
import {
|
||||||
getGitHubAppAuthToken,
|
getGitHubAppAuthToken,
|
||||||
getGitHubInstanceApiUrl,
|
getGitHubInstanceApiUrl,
|
||||||
@@ -20,7 +21,8 @@ import { TGitHubPublicKey, TGitHubSecret, TGitHubSecretPayload, TGitHubSyncWithC
|
|||||||
|
|
||||||
const getEncryptedSecrets = async (
|
const getEncryptedSecrets = async (
|
||||||
secretSync: TGitHubSyncWithCredentials,
|
secretSync: TGitHubSyncWithCredentials,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
const { destinationConfig, connection } = secretSync;
|
const { destinationConfig, connection } = secretSync;
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@ const getEncryptedSecrets = async (
|
|||||||
return makePaginatedGitHubRequest<TGitHubSecret, { secrets: TGitHubSecret[] }>(
|
return makePaginatedGitHubRequest<TGitHubSecret, { secrets: TGitHubSecret[] }>(
|
||||||
connection,
|
connection,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
|
gatewayV2Service,
|
||||||
path,
|
path,
|
||||||
(data) => data.secrets
|
(data) => data.secrets
|
||||||
);
|
);
|
||||||
@@ -52,6 +55,7 @@ const getEncryptedSecrets = async (
|
|||||||
const getPublicKey = async (
|
const getPublicKey = async (
|
||||||
secretSync: TGitHubSyncWithCredentials,
|
secretSync: TGitHubSyncWithCredentials,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
|
||||||
token: string
|
token: string
|
||||||
) => {
|
) => {
|
||||||
const { destinationConfig, connection } = secretSync;
|
const { destinationConfig, connection } = secretSync;
|
||||||
@@ -73,7 +77,7 @@ const getPublicKey = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await requestWithGitHubGateway<TGitHubPublicKey>(connection, gatewayService, {
|
const response = await requestWithGitHubGateway<TGitHubPublicKey>(connection, gatewayService, gatewayV2Service, {
|
||||||
url: `https://${await getGitHubInstanceApiUrl(connection)}${path}`,
|
url: `https://${await getGitHubInstanceApiUrl(connection)}${path}`,
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -89,6 +93,7 @@ const getPublicKey = async (
|
|||||||
const deleteSecret = async (
|
const deleteSecret = async (
|
||||||
secretSync: TGitHubSyncWithCredentials,
|
secretSync: TGitHubSyncWithCredentials,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
|
||||||
token: string,
|
token: string,
|
||||||
encryptedSecret: TGitHubSecret
|
encryptedSecret: TGitHubSecret
|
||||||
) => {
|
) => {
|
||||||
@@ -111,7 +116,7 @@ const deleteSecret = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await requestWithGitHubGateway(connection, gatewayService, {
|
await requestWithGitHubGateway(connection, gatewayService, gatewayV2Service, {
|
||||||
url: `https://${await getGitHubInstanceApiUrl(connection)}${path}`,
|
url: `https://${await getGitHubInstanceApiUrl(connection)}${path}`,
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -125,6 +130,7 @@ const deleteSecret = async (
|
|||||||
const putSecret = async (
|
const putSecret = async (
|
||||||
secretSync: TGitHubSyncWithCredentials,
|
secretSync: TGitHubSyncWithCredentials,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
|
||||||
token: string,
|
token: string,
|
||||||
payload: TGitHubSecretPayload
|
payload: TGitHubSecretPayload
|
||||||
) => {
|
) => {
|
||||||
@@ -157,7 +163,7 @@ const putSecret = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await requestWithGitHubGateway(connection, gatewayService, {
|
await requestWithGitHubGateway(connection, gatewayService, gatewayV2Service, {
|
||||||
url: `https://${await getGitHubInstanceApiUrl(connection)}${path}`,
|
url: `https://${await getGitHubInstanceApiUrl(connection)}${path}`,
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -173,7 +179,8 @@ export const GithubSyncFns = {
|
|||||||
syncSecrets: async (
|
syncSecrets: async (
|
||||||
secretSync: TGitHubSyncWithCredentials,
|
secretSync: TGitHubSyncWithCredentials,
|
||||||
ogSecretMap: TSecretMap,
|
ogSecretMap: TSecretMap,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
const secretMap = Object.fromEntries(Object.entries(ogSecretMap).map(([i, v]) => [i.toUpperCase(), v]));
|
const secretMap = Object.fromEntries(Object.entries(ogSecretMap).map(([i, v]) => [i.toUpperCase(), v]));
|
||||||
|
|
||||||
@@ -209,8 +216,8 @@ export const GithubSyncFns = {
|
|||||||
? connection.credentials.accessToken
|
? connection.credentials.accessToken
|
||||||
: await getGitHubAppAuthToken(connection);
|
: await getGitHubAppAuthToken(connection);
|
||||||
|
|
||||||
const encryptedSecrets = await getEncryptedSecrets(secretSync, gatewayService);
|
const encryptedSecrets = await getEncryptedSecrets(secretSync, gatewayService, gatewayV2Service);
|
||||||
const publicKey = await getPublicKey(secretSync, gatewayService, token);
|
const publicKey = await getPublicKey(secretSync, gatewayService, gatewayV2Service, token);
|
||||||
|
|
||||||
await sodium.ready;
|
await sodium.ready;
|
||||||
for await (const key of Object.keys(secretMap)) {
|
for await (const key of Object.keys(secretMap)) {
|
||||||
@@ -225,7 +232,7 @@ export const GithubSyncFns = {
|
|||||||
const encryptedSecretValue = sodium.to_base64(encryptedBytes, sodium.base64_variants.ORIGINAL);
|
const encryptedSecretValue = sodium.to_base64(encryptedBytes, sodium.base64_variants.ORIGINAL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await putSecret(secretSync, gatewayService, token, {
|
await putSecret(secretSync, gatewayService, gatewayV2Service, token, {
|
||||||
secret_name: key,
|
secret_name: key,
|
||||||
encrypted_value: encryptedSecretValue,
|
encrypted_value: encryptedSecretValue,
|
||||||
key_id: publicKey.key_id
|
key_id: publicKey.key_id
|
||||||
@@ -246,7 +253,7 @@ export const GithubSyncFns = {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!(encryptedSecret.name in secretMap)) {
|
if (!(encryptedSecret.name in secretMap)) {
|
||||||
await deleteSecret(secretSync, gatewayService, token, encryptedSecret);
|
await deleteSecret(secretSync, gatewayService, gatewayV2Service, token, encryptedSecret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -256,7 +263,8 @@ export const GithubSyncFns = {
|
|||||||
removeSecrets: async (
|
removeSecrets: async (
|
||||||
secretSync: TGitHubSyncWithCredentials,
|
secretSync: TGitHubSyncWithCredentials,
|
||||||
ogSecretMap: TSecretMap,
|
ogSecretMap: TSecretMap,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
const secretMap = Object.fromEntries(Object.entries(ogSecretMap).map(([i, v]) => [i.toUpperCase(), v]));
|
const secretMap = Object.fromEntries(Object.entries(ogSecretMap).map(([i, v]) => [i.toUpperCase(), v]));
|
||||||
|
|
||||||
@@ -266,11 +274,11 @@ export const GithubSyncFns = {
|
|||||||
? connection.credentials.accessToken
|
? connection.credentials.accessToken
|
||||||
: await getGitHubAppAuthToken(connection);
|
: await getGitHubAppAuthToken(connection);
|
||||||
|
|
||||||
const encryptedSecrets = await getEncryptedSecrets(secretSync, gatewayService);
|
const encryptedSecrets = await getEncryptedSecrets(secretSync, gatewayService, gatewayV2Service);
|
||||||
|
|
||||||
for await (const encryptedSecret of encryptedSecrets) {
|
for await (const encryptedSecret of encryptedSecrets) {
|
||||||
if (encryptedSecret.name in secretMap) {
|
if (encryptedSecret.name in secretMap) {
|
||||||
await deleteSecret(secretSync, gatewayService, token, encryptedSecret);
|
await deleteSecret(secretSync, gatewayService, gatewayV2Service, token, encryptedSecret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { AxiosError } from "axios";
|
|||||||
import handlebars from "handlebars";
|
import handlebars from "handlebars";
|
||||||
|
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { OCI_VAULT_SYNC_LIST_OPTION, OCIVaultSyncFns } from "@app/ee/services/secret-sync/oci-vault";
|
import { OCI_VAULT_SYNC_LIST_OPTION, OCIVaultSyncFns } from "@app/ee/services/secret-sync/oci-vault";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
@@ -101,6 +102,7 @@ type TSyncSecretDeps = {
|
|||||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">;
|
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add schema to secret keys
|
// Add schema to secret keys
|
||||||
@@ -195,7 +197,7 @@ export const SecretSyncFns = {
|
|||||||
syncSecrets: (
|
syncSecrets: (
|
||||||
secretSync: TSecretSyncWithCredentials,
|
secretSync: TSecretSyncWithCredentials,
|
||||||
secretMap: TSecretMap,
|
secretMap: TSecretMap,
|
||||||
{ kmsService, appConnectionDAL, gatewayService }: TSyncSecretDeps
|
{ kmsService, appConnectionDAL, gatewayService, gatewayV2Service }: TSyncSecretDeps
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const schemaSecretMap = addSchema(secretMap, secretSync.environment?.slug || "", secretSync.syncOptions.keySchema);
|
const schemaSecretMap = addSchema(secretMap, secretSync.environment?.slug || "", secretSync.syncOptions.keySchema);
|
||||||
|
|
||||||
@@ -205,7 +207,7 @@ export const SecretSyncFns = {
|
|||||||
case SecretSync.AWSSecretsManager:
|
case SecretSync.AWSSecretsManager:
|
||||||
return AwsSecretsManagerSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
return AwsSecretsManagerSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||||
case SecretSync.GitHub:
|
case SecretSync.GitHub:
|
||||||
return GithubSyncFns.syncSecrets(secretSync, schemaSecretMap, gatewayService);
|
return GithubSyncFns.syncSecrets(secretSync, schemaSecretMap, gatewayService, gatewayV2Service);
|
||||||
case SecretSync.GCPSecretManager:
|
case SecretSync.GCPSecretManager:
|
||||||
return GcpSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
return GcpSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||||
case SecretSync.AzureKeyVault:
|
case SecretSync.AzureKeyVault:
|
||||||
@@ -404,7 +406,7 @@ export const SecretSyncFns = {
|
|||||||
removeSecrets: (
|
removeSecrets: (
|
||||||
secretSync: TSecretSyncWithCredentials,
|
secretSync: TSecretSyncWithCredentials,
|
||||||
secretMap: TSecretMap,
|
secretMap: TSecretMap,
|
||||||
{ kmsService, appConnectionDAL, gatewayService }: TSyncSecretDeps
|
{ kmsService, appConnectionDAL, gatewayService, gatewayV2Service }: TSyncSecretDeps
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const schemaSecretMap = addSchema(secretMap, secretSync.environment?.slug || "", secretSync.syncOptions.keySchema);
|
const schemaSecretMap = addSchema(secretMap, secretSync.environment?.slug || "", secretSync.syncOptions.keySchema);
|
||||||
|
|
||||||
@@ -414,7 +416,7 @@ export const SecretSyncFns = {
|
|||||||
case SecretSync.AWSSecretsManager:
|
case SecretSync.AWSSecretsManager:
|
||||||
return AwsSecretsManagerSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
return AwsSecretsManagerSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||||
case SecretSync.GitHub:
|
case SecretSync.GitHub:
|
||||||
return GithubSyncFns.removeSecrets(secretSync, schemaSecretMap, gatewayService);
|
return GithubSyncFns.removeSecrets(secretSync, schemaSecretMap, gatewayService, gatewayV2Service);
|
||||||
case SecretSync.GCPSecretManager:
|
case SecretSync.GCPSecretManager:
|
||||||
return GcpSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
return GcpSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||||
case SecretSync.AzureKeyVault:
|
case SecretSync.AzureKeyVault:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Job } from "bullmq";
|
|||||||
import { ProjectMembershipRole, SecretType } from "@app/db/schemas";
|
import { ProjectMembershipRole, SecretType } from "@app/db/schemas";
|
||||||
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
|
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
@@ -98,6 +99,7 @@ type TSecretSyncQueueFactoryDep = {
|
|||||||
folderCommitService: Pick<TFolderCommitServiceFactory, "createCommit">;
|
folderCommitService: Pick<TFolderCommitServiceFactory, "createCommit">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SecretSyncActionJob = Job<
|
type SecretSyncActionJob = Job<
|
||||||
@@ -141,7 +143,8 @@ export const secretSyncQueueFactory = ({
|
|||||||
resourceMetadataDAL,
|
resourceMetadataDAL,
|
||||||
folderCommitService,
|
folderCommitService,
|
||||||
licenseService,
|
licenseService,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
}: TSecretSyncQueueFactoryDep) => {
|
}: TSecretSyncQueueFactoryDep) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
@@ -357,7 +360,8 @@ export const secretSyncQueueFactory = ({
|
|||||||
const importedSecrets = await SecretSyncFns.getSecrets(secretSync, {
|
const importedSecrets = await SecretSyncFns.getSecrets(secretSync, {
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!Object.keys(importedSecrets).length) return {};
|
if (!Object.keys(importedSecrets).length) return {};
|
||||||
@@ -486,7 +490,8 @@ export const secretSyncQueueFactory = ({
|
|||||||
await SecretSyncFns.syncSecrets(secretSyncWithCredentials, secretMap, {
|
await SecretSyncFns.syncSecrets(secretSyncWithCredentials, secretMap, {
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
});
|
});
|
||||||
|
|
||||||
isSynced = true;
|
isSynced = true;
|
||||||
@@ -736,7 +741,8 @@ export const secretSyncQueueFactory = ({
|
|||||||
{
|
{
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user