diff --git a/packages/old-import/src/mappers/map-integration.ts b/packages/old-import/src/mappers/map-integration.ts index 43c678302..8c24c2adf 100644 --- a/packages/old-import/src/mappers/map-integration.ts +++ b/packages/old-import/src/mappers/map-integration.ts @@ -44,17 +44,75 @@ export const mapAndDecryptIntegrations = ( return []; } + return preparedIntegrations.map(({ type, name, url, properties }) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const kind = mapIntegrationType(type!); + + return { + id: createId(), + name, + url, + kind, + secrets: mapSecrets(properties, encryptionToken, kind), + }; + }); +}; + +const mapSecrets = (properties: PreparedIntegration["properties"], encryptionToken: string, kind: IntegrationKind) => { const key = Buffer.from(encryptionToken, "hex"); - return preparedIntegrations.map(({ type, name, url, properties }) => ({ - id: createId(), - name, - url, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - kind: mapIntegrationType(type!), - secrets: properties.map((property) => ({ - ...property, - value: property.value ? decryptSecretWithKey(property.value as `${string}.${string}`, key) : null, - })), + const decryptedProperties = properties.map((property) => ({ + ...property, + value: property.value ? decryptSecretWithKey(property.value as `${string}.${string}`, key) : null, })); + + return kind === "proxmox" ? mapProxmoxSecrets(decryptedProperties) : decryptedProperties; +}; + +/** + * Proxmox secrets have bee split up from format `user@realm!tokenId=secret` to separate fields + */ +const mapProxmoxSecrets = (decryptedProperties: PreparedIntegration["properties"]) => { + const apiToken = decryptedProperties.find((property) => property.field === "apiKey"); + + if (!apiToken?.value) return []; + + let splitValues = apiToken.value.split("@"); + + if (splitValues.length <= 1) return []; + + const [user, ...rest] = splitValues; + + splitValues = rest.join("@").split("!"); + + if (splitValues.length <= 1) return []; + + const [realm, ...rest2] = splitValues; + + splitValues = rest2.join("!").split("="); + + if (splitValues.length <= 1) return []; + + const [tokenId, ...rest3] = splitValues; + + const secret = rest3.join("="); + + return [ + { + field: "username" as const, + value: user, + }, + { + field: "realm" as const, + value: realm, + }, + { + field: "tokenId" as const, + value: tokenId, + }, + { + field: "apiKey" as const, + value: secret, + }, + ]; }; diff --git a/packages/old-import/src/mappers/test/map-integration.spec.ts b/packages/old-import/src/mappers/test/map-integration.spec.ts new file mode 100644 index 000000000..976da6126 --- /dev/null +++ b/packages/old-import/src/mappers/test/map-integration.spec.ts @@ -0,0 +1,46 @@ +import { describe, expect, test, vi } from "vitest"; + +import * as commonServer from "@homarr/common/server"; + +import type { PreparedIntegration } from "../../prepare/prepare-integrations"; +import { mapAndDecryptIntegrations } from "../map-integration"; + +describe("Map Integrations", () => { + test("should map proxmox integration", () => { + vi.spyOn(commonServer, "decryptSecretWithKey").mockReturnValue("user@realm!tokenId=secret"); + + const proxmoxIntegration: PreparedIntegration = { + type: "proxmox", + name: "Proxmox", + url: "https://proxmox.com", + properties: [ + { + field: "apiKey", + value: "any-encrypted-value", + type: "private", + }, + ], + }; + + const mappedIntegrations = mapAndDecryptIntegrations([proxmoxIntegration], "encryptionToken"); + + expect(mappedIntegrations[0]?.secrets).toEqual([ + { + field: "username", + value: "user", + }, + { + field: "realm", + value: "realm", + }, + { + field: "tokenId", + value: "tokenId", + }, + { + field: "apiKey", + value: "secret", + }, + ]); + }); +});