From 66ecddc58474dd189bcdb61fbe202aa03734f921 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Wed, 19 Nov 2025 18:14:32 -0800 Subject: [PATCH] Implement more stuff --- .../app-connection/app-connection-fns.ts | 14 ++- .../app-connection/app-connection-types.ts | 12 +- .../dns-made-easy-connection-enum.ts | 2 +- .../dns-made-easy-connection-fns.ts | 104 ++++++++++-------- .../dns-made-easy-connection-schema.ts | 6 +- .../dns-made-easy-connection-types.ts | 8 +- .../integration-auth/integration-list.ts | 4 +- 7 files changed, 94 insertions(+), 56 deletions(-) diff --git a/backend/src/services/app-connection/app-connection-fns.ts b/backend/src/services/app-connection/app-connection-fns.ts index 57a219c71..5b03dc903 100644 --- a/backend/src/services/app-connection/app-connection-fns.ts +++ b/backend/src/services/app-connection/app-connection-fns.ts @@ -11,8 +11,8 @@ import { validateOCIConnectionCredentials } from "@app/ee/services/app-connections/oci"; import { getOracleDBConnectionListItem, OracleDBConnectionMethod } from "@app/ee/services/app-connections/oracledb"; -import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service"; import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service"; +import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { SECRET_ROTATION_CONNECTION_MAP } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-maps"; import { SECRET_SCANNING_DATA_SOURCE_CONNECTION_MAP } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-maps"; @@ -78,11 +78,6 @@ import { getCloudflareConnectionListItem, validateCloudflareConnectionCredentials } from "./cloudflare/cloudflare-connection-fns"; -import { DNSMadeEasyConnectionMethod } from "./dns-made-easy/dns-made-easy-connection-enum"; -import { - getDNSMadeEasyConnectionListItem, - validateDNSMadeEasyConnectionCredentials -} from "./dns-made-easy/dns-made-easy-connection-fns"; import { DatabricksConnectionMethod, getDatabricksConnectionListItem, @@ -93,6 +88,11 @@ import { getDigitalOceanConnectionListItem, validateDigitalOceanConnectionCredentials } from "./digital-ocean"; +import { DNSMadeEasyConnectionMethod } from "./dns-made-easy/dns-made-easy-connection-enum"; +import { + getDNSMadeEasyConnectionListItem, + validateDNSMadeEasyConnectionCredentials +} from "./dns-made-easy/dns-made-easy-connection-fns"; import { FlyioConnectionMethod, getFlyioConnectionListItem, validateFlyioConnectionCredentials } from "./flyio"; import { GcpConnectionMethod, getGcpConnectionListItem, validateGcpConnectionCredentials } from "./gcp"; import { getGitHubConnectionListItem, GitHubConnectionMethod, validateGitHubConnectionCredentials } from "./github"; @@ -402,6 +402,8 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) => case OktaConnectionMethod.ApiToken: case LaravelForgeConnectionMethod.ApiToken: return "API Token"; + case DNSMadeEasyConnectionMethod.APIKeySecret: + return "API Key & Secret"; case PostgresConnectionMethod.UsernameAndPassword: case MsSqlConnectionMethod.UsernameAndPassword: case MySqlConnectionMethod.UsernameAndPassword: diff --git a/backend/src/services/app-connection/app-connection-types.ts b/backend/src/services/app-connection/app-connection-types.ts index e1bdb8132..e26df87f5 100644 --- a/backend/src/services/app-connection/app-connection-types.ts +++ b/backend/src/services/app-connection/app-connection-types.ts @@ -15,8 +15,8 @@ import { TOracleDBConnectionInput, TValidateOracleDBConnectionCredentialsSchema } from "@app/ee/services/app-connections/oracledb"; -import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service"; import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service"; +import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service"; import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal"; import { TSqlConnectionConfig } from "@app/services/app-connection/shared/sql/sql-connection-types"; import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; @@ -106,6 +106,12 @@ import { TDigitalOceanConnectionInput, TValidateDigitalOceanCredentialsSchema } from "./digital-ocean"; +import { + TDNSMadeEasyConnection, + TDNSMadeEasyConnectionConfig, + TDNSMadeEasyConnectionInput, + TValidateDNSMadeEasyConnectionCredentialsSchema +} from "./dns-made-easy/dns-made-easy-connection-types"; import { TFlyioConnection, TFlyioConnectionConfig, @@ -279,6 +285,7 @@ export type TAppConnection = { id: string } & ( | TGitLabConnection | TCloudflareConnection | TBitbucketConnection + | TDNSMadeEasyConnection | TZabbixConnection | TRailwayConnection | TChecklyConnection @@ -328,6 +335,7 @@ export type TAppConnectionInput = { id: string } & ( | TGitLabConnectionInput | TCloudflareConnectionInput | TBitbucketConnectionInput + | TDNSMadeEasyConnectionInput | TZabbixConnectionInput | TRailwayConnectionInput | TChecklyConnectionInput @@ -395,6 +403,7 @@ export type TAppConnectionConfig = | TGitLabConnectionConfig | TCloudflareConnectionConfig | TBitbucketConnectionConfig + | TDNSMadeEasyConnectionConfig | TZabbixConnectionConfig | TRailwayConnectionConfig | TChecklyConnectionConfig @@ -439,6 +448,7 @@ export type TValidateAppConnectionCredentialsSchema = | TValidateGitLabConnectionCredentialsSchema | TValidateCloudflareConnectionCredentialsSchema | TValidateBitbucketConnectionCredentialsSchema + | TValidateDNSMadeEasyConnectionCredentialsSchema | TValidateZabbixConnectionCredentialsSchema | TValidateRailwayConnectionCredentialsSchema | TValidateChecklyConnectionCredentialsSchema diff --git a/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-enum.ts b/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-enum.ts index 8ee08985f..6d782b4bb 100644 --- a/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-enum.ts +++ b/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-enum.ts @@ -1,3 +1,3 @@ export enum DNSMadeEasyConnectionMethod { - APIKey = "api-key" + APIKeySecret = "api-key-secret" } diff --git a/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-fns.ts b/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-fns.ts index 1d571224c..b50e47a03 100644 --- a/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-fns.ts +++ b/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-fns.ts @@ -1,6 +1,7 @@ import { AxiosError } from "axios"; import { request } from "@app/lib/config/request"; +import { crypto } from "@app/lib/crypto/cryptography"; import { BadRequestError } from "@app/lib/errors"; import { AppConnection } from "@app/services/app-connection/app-connection-enums"; import { IntegrationUrls } from "@app/services/integration-auth/integration-list"; @@ -12,11 +13,42 @@ import { TDNSMadeEasyZone } from "./dns-made-easy-connection-types"; +interface DNSMadeEasyApiResponse { + totalRecords: number; + totalPages: number; + data: Array<{ + id: number; + name: string; + [key: string]: unknown; + }>; + page: number; +} + +export const makeDNSMadeEasyAuthHeaders = ( + apiKey: string, + apiSecret: string, + currentDate: Date = new Date() +): Record => { + // Format date as "Day, DD Mon YYYY HH:MM:SS GMT" (e.g., "Mon, 01 Jan 2024 12:00:00 GMT") + const requestDate = currentDate.toUTCString(); + + // Generate HMAC-SHA1 signature + const hmac = crypto.nativeCrypto.createHmac("sha1", apiSecret); + hmac.update(requestDate); + const hmacSignature = hmac.digest("hex"); + + return { + "x-dnsme-apiKey": apiKey, + "x-dnsme-hmac": hmacSignature, + "x-dnsme-requestDate": requestDate + }; +}; + export const getDNSMadeEasyConnectionListItem = () => { return { name: "DNS Made Easy" as const, app: AppConnection.DNSMadeEasy as const, - methods: Object.values(DNSMadeEasyConnectionMethod) as [DNSMadeEasyConnectionMethod.APIKey] + methods: Object.values(DNSMadeEasyConnectionMethod) as [DNSMadeEasyConnectionMethod.APIKeySecret] }; }; @@ -27,7 +59,7 @@ export const listDNSMadeEasyZones = async (appConnection: TDNSMadeEasyConnection // Authentication: Use API key and secret from appConnection.credentials // Return format: Array of { id: string, name: string } - if (appConnection.method !== DNSMadeEasyConnectionMethod.APIKey) { + if (appConnection.method !== DNSMadeEasyConnectionMethod.APIKeySecret) { throw new Error("Unsupported DNS Made Easy connection method"); } @@ -35,56 +67,42 @@ export const listDNSMadeEasyZones = async (appConnection: TDNSMadeEasyConnection credentials: { apiKey, apiSecret } } = appConnection; - // TODO: Make API request to DNS Made Easy - // const { data } = await request.get<{ data: { id: number; name: string }[] }>( - // `${IntegrationUrls.DNS_MADE_EASY_API_URL}/V2.0/dns/managed`, - // { - // headers: { - // "x-dnsme-apiKey": apiKey, - // "x-dnsme-hmac": generateHMAC(apiSecret, ...), - // "x-dnsme-requestDate": requestDate, - // Accept: "application/json" - // } - // } - // ); - - // TODO: Transform response data to match TDNSMadeEasyZone format - // return data.data.map((zone) => ({ - // name: zone.name, - // id: zone.id.toString() - // })); - - throw new Error("Not implemented: listDNSMadeEasyZones"); + try { + // TODO: + const allZones: TDNSMadeEasyZone[] = []; + return allZones; + } catch (error: unknown) { + if (error instanceof AxiosError) { + throw new BadRequestError({ + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + message: `Failed to list DNS Made Easy zones: ${error.response?.data?.error?.[0] || error.message || "Unknown error"}` + }); + } + throw new BadRequestError({ + message: "Unable to list DNS Made Easy zones" + }); + } }; export const validateDNSMadeEasyConnectionCredentials = async (config: TDNSMadeEasyConnectionConfig) => { - // TODO: Implement DNS Made Easy credentials validation - // This should call the DNS Made Easy API to validate the API key and secret - // Example API endpoint: GET https://api.dnsmadeeasy.com/V2.0/account - // Authentication: Use API key and secret from config.credentials - - if (config.method !== DNSMadeEasyConnectionMethod.APIKey) { + if (config.method !== DNSMadeEasyConnectionMethod.APIKeySecret) { throw new Error("Unsupported DNS Made Easy connection method"); } const { apiKey, apiSecret } = config.credentials; try { - // TODO: Make API request to validate credentials - // const resp = await request.get(`${IntegrationUrls.DNS_MADE_EASY_API_URL}/V2.0/account`, { - // headers: { - // "x-dnsme-apiKey": apiKey, - // "x-dnsme-hmac": generateHMAC(apiSecret, ...), - // "x-dnsme-requestDate": requestDate, - // Accept: "application/json" - // } - // }); - // TODO: Validate response - // if (resp.data === null || !resp.data.id) { - // throw new BadRequestError({ - // message: "Unable to validate connection: Invalid API credentials provided." - // }); - // } + const resp = await request.get(`${IntegrationUrls.DNS_MADE_EASY_API_URL}/V2.0/dns/managed/`, { + headers: { + ...makeDNSMadeEasyAuthHeaders(apiKey, apiSecret), + Accept: "application/json" + } + }); + if (resp.status !== 200) { + throw new BadRequestError({ + message: "Unable to validate connection: Invalid API credentials provided." + }); + } } catch (error: unknown) { if (error instanceof AxiosError) { throw new BadRequestError({ diff --git a/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-schema.ts b/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-schema.ts index eb39b6235..d5ed6d3cb 100644 --- a/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-schema.ts +++ b/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-schema.ts @@ -21,13 +21,13 @@ const BaseDNSMadeEasyConnectionSchema = BaseAppConnectionSchema.extend({ }); export const DNSMadeEasyConnectionSchema = BaseDNSMadeEasyConnectionSchema.extend({ - method: z.literal(DNSMadeEasyConnectionMethod.APIKey), + method: z.literal(DNSMadeEasyConnectionMethod.APIKeySecret), credentials: DNSMadeEasyConnectionApiKeyCredentialsSchema }); export const SanitizedDNSMadeEasyConnectionSchema = z.discriminatedUnion("method", [ BaseDNSMadeEasyConnectionSchema.extend({ - method: z.literal(DNSMadeEasyConnectionMethod.APIKey), + method: z.literal(DNSMadeEasyConnectionMethod.APIKeySecret), credentials: DNSMadeEasyConnectionApiKeyCredentialsSchema.pick({ apiKey: true }) }).describe(JSON.stringify({ title: `${APP_CONNECTION_NAME_MAP[AppConnection.DNSMadeEasy]} (API Key)` })) ]); @@ -35,7 +35,7 @@ export const SanitizedDNSMadeEasyConnectionSchema = z.discriminatedUnion("method export const ValidateDNSMadeEasyConnectionCredentialsSchema = z.discriminatedUnion("method", [ z.object({ method: z - .literal(DNSMadeEasyConnectionMethod.APIKey) + .literal(DNSMadeEasyConnectionMethod.APIKeySecret) .describe(AppConnections.CREATE(AppConnection.DNSMadeEasy).method), credentials: DNSMadeEasyConnectionApiKeyCredentialsSchema.describe( AppConnections.CREATE(AppConnection.DNSMadeEasy).credentials diff --git a/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-types.ts b/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-types.ts index dabb72771..eff96f6f9 100644 --- a/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-types.ts +++ b/backend/src/services/app-connection/dns-made-easy/dns-made-easy-connection-types.ts @@ -3,7 +3,11 @@ import z from "zod"; import { DiscriminativePick } from "@app/lib/types"; import { AppConnection } from "../app-connection-enums"; -import { DNSMadeEasyConnectionSchema, CreateDNSMadeEasyConnectionSchema } from "./dns-made-easy-connection-schema"; +import { + CreateDNSMadeEasyConnectionSchema, + DNSMadeEasyConnectionSchema, + ValidateDNSMadeEasyConnectionCredentialsSchema +} from "./dns-made-easy-connection-schema"; export type TDNSMadeEasyConnection = z.infer; @@ -11,6 +15,8 @@ export type TDNSMadeEasyConnectionInput = z.infer {