feat: add selfmedia access secret distribution
This commit is contained in:
+21
-9
@@ -40,7 +40,7 @@ interface SecretDistributionConfig {
|
||||
version: number;
|
||||
kind: "unidesk-secret-distribution";
|
||||
metadata: { id: string; owner: string; relatedIssues: number[] };
|
||||
sources: { root: string; files: EnvSourceFileConfig[]; externalFiles: RawSourceFileConfig[] };
|
||||
sources: { root: string; files: EnvSourceFileConfig[]; externalFiles: SourceFileConfig[] };
|
||||
targets: DistributionTarget[];
|
||||
kubernetesSecrets: KubernetesSecretConfig[];
|
||||
}
|
||||
@@ -311,7 +311,7 @@ function readSecretDistributionConfig(pathArg: string): SecretDistributionConfig
|
||||
files: arrayOfRecords(sources.files, `${label}.sources.files`).map((item, index) => parseSourceFile(item, `${label}.sources.files[${index}]`)),
|
||||
externalFiles: sources.externalFiles === undefined
|
||||
? []
|
||||
: arrayOfRecords(sources.externalFiles, `${label}.sources.externalFiles`).map((item, index) => parseRawSourceFile(item, `${label}.sources.externalFiles[${index}]`)),
|
||||
: arrayOfRecords(sources.externalFiles, `${label}.sources.externalFiles`).map((item, index) => parseExternalSourceFile(item, `${label}.sources.externalFiles[${index}]`)),
|
||||
},
|
||||
targets: arrayOfRecords(root.targets, `${label}.targets`).map((item, index) => parseTarget(item, `${label}.targets[${index}]`)),
|
||||
kubernetesSecrets: arrayOfRecords(root.kubernetesSecrets, `${label}.kubernetesSecrets`).map((item, index) => parseKubernetesSecret(item, `${label}.kubernetesSecrets[${index}]`)),
|
||||
@@ -320,13 +320,13 @@ function readSecretDistributionConfig(pathArg: string): SecretDistributionConfig
|
||||
return config;
|
||||
}
|
||||
|
||||
function parseSourceFile(record: Record<string, unknown>, path: string): EnvSourceFileConfig {
|
||||
function parseSourceFile(record: Record<string, unknown>, path: string, external = false): EnvSourceFileConfig {
|
||||
const type = stringField(record, "type", path);
|
||||
if (type !== "env") throw new Error(`${path}.type must be env`);
|
||||
const createRaw = record.createIfMissing === undefined ? {} : objectField(record, "createIfMissing", path);
|
||||
const randomBase64UrlRaw = createRaw.randomBase64Url === undefined ? {} : objectField(createRaw, "randomBase64Url", `${path}.createIfMissing`);
|
||||
return {
|
||||
sourceRef: sourceRefField(record, "sourceRef", path),
|
||||
sourceRef: external ? externalSourceRefField(record, "sourceRef", path) : sourceRefField(record, "sourceRef", path),
|
||||
type,
|
||||
requiredKeys: stringArrayField(record, "requiredKeys", path).map((key, index) => envKeyValue(key, `${path}.requiredKeys[${index}]`)),
|
||||
required: true,
|
||||
@@ -339,15 +339,16 @@ function parseSourceFile(record: Record<string, unknown>, path: string): EnvSour
|
||||
};
|
||||
}
|
||||
|
||||
function parseRawSourceFile(record: Record<string, unknown>, path: string): RawSourceFileConfig {
|
||||
function parseExternalSourceFile(record: Record<string, unknown>, path: string): SourceFileConfig {
|
||||
const type = stringField(record, "type", path);
|
||||
if (type === "env") return parseSourceFile(record, path, true);
|
||||
if (type !== "raw-file") throw new Error(`${path}.type must be raw-file`);
|
||||
const createRaw = objectField(record, "createIfMissing", path);
|
||||
const enabled = booleanField(createRaw, "enabled", `${path}.createIfMissing`);
|
||||
const randomHex = createRaw.randomHex === undefined
|
||||
const randomHex: Record<string, number> = createRaw.randomHex === undefined
|
||||
? {}
|
||||
: { contents: randomByteCount(createRaw.randomHex, `${path}.createIfMissing.randomHex`) };
|
||||
const randomBase64Url = createRaw.randomBase64Url === undefined
|
||||
const randomBase64Url: Record<string, { bytes: number; prefix: string }> = createRaw.randomBase64Url === undefined
|
||||
? {}
|
||||
: { contents: randomBase64UrlSpec(createRaw.randomBase64Url, `${path}.createIfMissing.randomBase64Url`) };
|
||||
if (createRaw.values !== undefined) throw new Error(`${path}.createIfMissing.values is not allowed for raw-file sources`);
|
||||
@@ -414,7 +415,8 @@ function validateDistributionConfig(config: SecretDistributionConfig): void {
|
||||
for (const item of secret.data) {
|
||||
const source = sources.get(item.sourceRef);
|
||||
if (source === undefined) throw new Error(`${config.configPath}.kubernetesSecrets.${secret.name} references undeclared sourceRef ${item.sourceRef}`);
|
||||
if (!source.requiredKeys.includes(item.sourceKey)) throw new Error(`${config.configPath}.kubernetesSecrets.${secret.name} maps undeclared source key ${item.sourceRef}.${item.sourceKey}`);
|
||||
const requiredKeys: readonly string[] = source.requiredKeys;
|
||||
if (!requiredKeys.includes(item.sourceKey)) throw new Error(`${config.configPath}.kubernetesSecrets.${secret.name} maps undeclared source key ${item.sourceRef}.${item.sourceKey}`);
|
||||
if (targetKeys.has(item.targetKey)) throw new Error(`${config.configPath}.kubernetesSecrets.${secret.name} maps duplicate target key ${item.targetKey}`);
|
||||
targetKeys.add(item.targetKey);
|
||||
}
|
||||
@@ -429,7 +431,7 @@ function inspectSources(config: SecretDistributionConfig, materialize: boolean,
|
||||
const root = secretRoot(config);
|
||||
const materials = new Map<string, SourceMaterial>();
|
||||
const entries = allSourceFiles(config).filter((source) => selectedRefs.has(source.sourceRef)).map((source) => {
|
||||
const sourcePath = source.type === "env" ? join(root, source.sourceRef) : externalSourcePath(source.sourceRef);
|
||||
const sourcePath = sourcePathFor(root, source);
|
||||
const exists = existsSync(sourcePath);
|
||||
const existingText = exists ? readFileSync(sourcePath, "utf8") : "";
|
||||
const existing = exists ? source.type === "env" ? parseEnvFile(existingText) : { contents: existingText } : {};
|
||||
@@ -502,6 +504,8 @@ function inspectSources(config: SecretDistributionConfig, materialize: boolean,
|
||||
presence,
|
||||
bytes,
|
||||
fingerprint,
|
||||
ownerOnly: true,
|
||||
fileMode: "0600",
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
@@ -517,6 +521,8 @@ function inspectSources(config: SecretDistributionConfig, materialize: boolean,
|
||||
generatedKeys,
|
||||
unmaterializedGeneratedKeys,
|
||||
fingerprint: material.fingerprint,
|
||||
ownerOnly: true,
|
||||
fileMode: "0600",
|
||||
valuesPrinted: false,
|
||||
};
|
||||
});
|
||||
@@ -967,6 +973,12 @@ function externalSourcePath(sourceRef: string): string {
|
||||
throw new Error(`external sourceRef ${sourceRef} must be an absolute path or a ~/ home path`);
|
||||
}
|
||||
|
||||
function sourcePathFor(root: string, source: SourceFileConfig): string {
|
||||
return source.sourceRef.startsWith("~/") || isAbsolute(source.sourceRef)
|
||||
? externalSourcePath(source.sourceRef)
|
||||
: join(root, source.sourceRef);
|
||||
}
|
||||
|
||||
function sourceDataKeyField(obj: Record<string, unknown>, key: string, path: string): string {
|
||||
const value = stringField(obj, key, path);
|
||||
if (!/^[A-Za-z_][A-Za-z0-9._-]*$/u.test(value)) throw new Error(`${path}.${key} must be a source data key`);
|
||||
|
||||
Reference in New Issue
Block a user