SecretReader.ts 2.97 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** ***************************************************************
 Copyright (C) 2022 Eclipse Foundation, Inc.

 This program and the accompanying materials are made
 available under the terms of the Eclipse Public License 2.0
 which is available at https://www.eclipse.org/legal/epl-2.0/

  Contributors:
    Martin Lowe <martin.lowe@eclipse-foundation.org>

 SPDX-License-Identifier: EPL-2.0
******************************************************************/

import { getLogger, isNodeErr } from './logger';
import fs from 'fs';
import { Logger } from 'winston';

18
19
export const DEFAULT_FILE_ENCODING: string = 'utf-8';
export const DEFAULT_SECRET_LOCATION: string = '/run/secrets/';
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

export interface SecretReaderConfig {
  root?: string;
  encoding?: string;
}

/**
 * Contains functionality for reading secret files in and returning them
 * to the user. This defaults to the location used in Kubernetes containers
 * for secrets. This can be configured by passing an object with updated values.
 *
 * Multiple secrets can be read using the same reader assuming that they are
 * in the same directory. Additional secrets would need to be read in using a
 * new reader in such a case.
 */
export class SecretReader {
  verbose: boolean = false;
  logger: Logger;
  config: SecretReaderConfig;

  constructor(config: SecretReaderConfig) {
    // set internally and modify for defaults
    this.config = config;
    // set defaults if value is missing
    this.config.root = this.config.root ?? DEFAULT_SECRET_LOCATION;
    this.config.encoding = this.config.encoding ?? DEFAULT_FILE_ENCODING;
    // throws if there is no access
    fs.accessSync(this.config.root, fs.constants.R_OK);
    this.logger = getLogger('info', 'SecretReader');
  }

  readSecret(name: string, encoding: string = this.config.encoding ?? DEFAULT_FILE_ENCODING): string | null {
    if (this.verbose === true) {
      this.logger.debug(`SecretReader:readSecret(name = ${name}, encoding = ${encoding})`);
    }
    var filepath = `${this.config.root}/${name}`;
    try {
      var data = fs.readFileSync(filepath, { encoding: encoding as BufferEncoding });
      let out: string;
      if (data !== undefined && (out = data.trim()) !== '') {
        return out;
      }
    } catch (e) {
      // cast and message with error
      if (isNodeErr(e)) {
        if (e.code === 'ENOENT') {
          this.logger.error(`File at path ${filepath} does not exist`);
        } else if (e.code === 'EACCES') {
          this.logger.error(`File at path ${filepath} cannot be read`);
        } else {
          this.logger.error('An unknown error occurred while reading the secret');
        }
      } else if (typeof e === 'string') {
        this.logger.error('An unknown error occurred while reading the secret');
      }
    }
    return null;
  }
}

/**
 * Get modifiable deep copy of the base configuration for this class.
 */
export function getBaseConfig(): SecretReaderConfig {
  return {
    root: DEFAULT_SECRET_LOCATION,
    encoding: DEFAULT_FILE_ENCODING,
  };
}