From 79fa21a6586ae38ecaed04c75e1b7919358709ff Mon Sep 17 00:00:00 2001 From: drhaynes Date: Mon, 22 Jan 2024 10:44:30 +0000 Subject: [PATCH] Add EncryptedEnvironmentVariable --- Sources/Config/ConfigurationFile.swift | 2 +- Sources/Config/Property.swift | 24 ++++++++++++++----- .../ConfigurationPropertyTests.swift | 19 +++++++++++++-- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Sources/Config/ConfigurationFile.swift b/Sources/Config/ConfigurationFile.swift index 1f7768a..f84c49e 100644 --- a/Sources/Config/ConfigurationFile.swift +++ b/Sources/Config/ConfigurationFile.swift @@ -100,7 +100,7 @@ func parseNextProperty(properties: [String: Property], pair: (key: String, value var copy = properties if let typeHint = PropertyType(rawValue: typeHintValue) { switch typeHint { - case .string, .url, .encrypted, .encryptionKey, .colour, .image, .regex, .environmentVariable: + case .string, .url, .encrypted, .encryptionKey, .colour, .image, .regex, .environmentVariable, .encryptedEnvironmentVariable: copy[pair.key] = ConfigurationProperty(key: pair.key, typeHint: typeHintValue, dict: dict, patterns: patterns) case .optionalString: copy[pair.key] = ConfigurationProperty(key: pair.key, typeHint: typeHintValue, dict: dict, patterns: patterns) diff --git a/Sources/Config/Property.swift b/Sources/Config/Property.swift index 94ec07d..49f573f 100644 --- a/Sources/Config/Property.swift +++ b/Sources/Config/Property.swift @@ -71,10 +71,11 @@ enum PropertyType: String { case dynamicColour = "DynamicColour" case dynamicColourReference = "DynamicColourReference" case environmentVariable = "EnvironmentVariable" + case encryptedEnvironmentVariable = "EncryptedEnvironmentVariable" var typeName: String { switch self { - case .encrypted, .encryptionKey: + case .encrypted, .encryptionKey, .encryptedEnvironmentVariable: return "[UInt8]" case .dictionary: return "[String: Any]" @@ -154,17 +155,28 @@ enum PropertyType: String { case .dynamicColourReference: return dynamicColourReferenceValue(for: value as? [String: String]) case .environmentVariable: - guard let environmentVariable = value as? String, - let rawValue = getenv(environmentVariable), - let stringValue = String(utf8String: rawValue) else { - fatalError("Missing environment variable \(value)") + return "#\"\(environmentVariable(for: value as? String))\"#" + case .encryptedEnvironmentVariable: + let valueString = environmentVariable(for: value as? String) + guard let key = key else { fatalError("No encryption key present to encrypt value") } + guard let encryptedString = valueString.encrypt(key: Array(key.utf8), iv: Array(iv.hash.utf8)) else { + fatalError("Unable to encrypt \(value) with key") } - return "#\"\(stringValue)\"#" + return byteArrayOutput(from: encryptedString) default: return "\(value)" } } + private func environmentVariable(for value: String?) -> String { + guard let environmentVariableName = value, + let rawValue = getenv(environmentVariableName), + let stringValue = String(utf8String: rawValue) else { + fatalError("Missing environment variable \(value ?? "")") + } + return stringValue + } + private func colourValue(for value: String?) -> String { guard let value = value else { return "No colour provided" } let string = value.replacingOccurrences(of: "#", with: "") diff --git a/Tests/ConfigTests/ConfigurationPropertyTests.swift b/Tests/ConfigTests/ConfigurationPropertyTests.swift index 76275ff..79788b9 100644 --- a/Tests/ConfigTests/ConfigurationPropertyTests.swift +++ b/Tests/ConfigTests/ConfigurationPropertyTests.swift @@ -348,14 +348,24 @@ class ConfigurationPropertyTests: XCTestCase { } func testItCanWriteAnEnvironmentVariableProperty() throws { - // Set an environment variable using the libc API - setenv("SOMETHING", "testValue", 1) + when(environmentVariableNamed: "SOMETHING", hasValue: "testValue") let property = ConfigurationProperty(key: "test", typeHint: "EnvironmentVariable", dict: ["defaultValue": "SOMETHING"]) let expectedValue = ##" static let test: String = #"testValue"#"## let actualValue = try whenTheDeclarationIsWritten(for: property) expect(actualValue).to(equal(expectedValue)) } + func testItCanWriteAnEncryptedEnvironmentVariableProperty() throws { + // Note: this test doesn't test the encryption itself, that test will be written elsewhere + when(environmentVariableNamed: "SOMETHING_SECRET", hasValue: "secretValue") + let property = ConfigurationProperty(key: "test", + typeHint: "EncryptedEnvironmentVariable", + dict: ["defaultValue": "SOMETHING_SECRET"]) + let expectedValue = " static let test: [UInt8] = [" + let actualValue = try whenTheDeclarationIsWritten(for: property, encryptionKey: "testKey") + expect(actualValue).to(beginWith(expectedValue)) + } + func testItCanUseCommonPatternsForOverrides() throws { let dict: [String: Any] = [ "defaultValue": "test value", @@ -368,4 +378,9 @@ class ConfigurationPropertyTests: XCTestCase { let actualValue = try whenTheDeclarationIsWritten(for: property, scheme: "gary") expect(actualValue).to(equal(expectedValue)) } + + private func when(environmentVariableNamed environmentVariableName: String, hasValue value: String) { + // Set an environment variable using the libc API + setenv(environmentVariableName, value, 1) + } }