All files / src/client-side-encryption/providers index.ts

100% Statements 17/17
100% Branches 10/10
100% Functions 2/2
100% Lines 17/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208    404x 404x 404x                                                                                                                                                                                                                                                                                                                                           404x       1427x 1427x 590x   837x                   404x       295x   295x 215x     291x 8x     287x 68x   247x    
import type { Binary } from '../../bson';
import { type AWSCredentialProvider } from '../../cmap/auth/aws_temporary_credentials';
import { loadAWSCredentials } from './aws';
import { loadAzureCredentials } from './azure';
import { loadGCPCredentials } from './gcp';
 
/**
 * @public
 *
 * A data key provider.  Allowed values:
 *
 * - aws, gcp, local, kmip or azure
 * - (`mongodb-client-encryption>=6.0.1` only) a named key, in the form of:
 *    `aws:<name>`, `gcp:<name>`, `local:<name>`, `kmip:<name>`, `azure:<name>`
 *  where `name` is an alphanumeric string, underscores allowed.
 */
export type ClientEncryptionDataKeyProvider = keyof KMSProviders;
 
/** @public */
export interface AWSKMSProviderConfiguration {
  /**
   * The access key used for the AWS KMS provider
   */
  accessKeyId: string;
 
  /**
   * The secret access key used for the AWS KMS provider
   */
  secretAccessKey: string;
 
  /**
   * An optional AWS session token that will be used as the
   * X-Amz-Security-Token header for AWS requests.
   */
  sessionToken?: string;
}
 
/** @public */
export interface LocalKMSProviderConfiguration {
  /**
   * The master key used to encrypt/decrypt data keys.
   * A 96-byte long Buffer or base64 encoded string.
   */
  key: Binary | Uint8Array | string;
}
 
/** @public */
export interface KMIPKMSProviderConfiguration {
  /**
   * The output endpoint string.
   * The endpoint consists of a hostname and port separated by a colon.
   * E.g. "example.com:123". A port is always present.
   */
  endpoint?: string;
}
 
/** @public */
export type AzureKMSProviderConfiguration =
  | {
      /**
       * The tenant ID identifies the organization for the account
       */
      tenantId: string;
 
      /**
       * The client ID to authenticate a registered application
       */
      clientId: string;
 
      /**
       * The client secret to authenticate a registered application
       */
      clientSecret: string;
 
      /**
       * If present, a host with optional port. E.g. "example.com" or "example.com:443".
       * This is optional, and only needed if customer is using a non-commercial Azure instance
       * (e.g. a government or China account, which use different URLs).
       * Defaults to "login.microsoftonline.com"
       */
      identityPlatformEndpoint?: string | undefined;
    }
  | {
      /**
       * If present, an access token to authenticate with Azure.
       */
      accessToken: string;
    };
 
/** @public */
export type GCPKMSProviderConfiguration =
  | {
      /**
       * The service account email to authenticate
       */
      email: string;
 
      /**
       * A PKCS#8 encrypted key. This can either be a base64 string or a binary representation
       */
      privateKey: string | Buffer;
 
      /**
       * If present, a host with optional port. E.g. "example.com" or "example.com:443".
       * Defaults to "oauth2.googleapis.com"
       */
      endpoint?: string | undefined;
    }
  | {
      /**
       * If present, an access token to authenticate with GCP.
       */
      accessToken: string;
    };
 
/**
 * @public
 * Configuration options for custom credential providers for KMS requests.
 */
export interface CredentialProviders {
  /* A custom AWS credential provider */
  aws?: AWSCredentialProvider;
}
 
/**
 * @public
 * Configuration options that are used by specific KMS providers during key generation, encryption, and decryption.
 *
 * Named KMS providers _are not supported_ for automatic KMS credential fetching.
 */
export interface KMSProviders {
  /**
   * Configuration options for using 'aws' as your KMS provider
   */
  aws?: AWSKMSProviderConfiguration | Record<string, never>;
  [key: `aws:${string}`]: AWSKMSProviderConfiguration;
 
  /**
   * Configuration options for using 'local' as your KMS provider
   */
  local?: LocalKMSProviderConfiguration;
  [key: `local:${string}`]: LocalKMSProviderConfiguration;
 
  /**
   * Configuration options for using 'kmip' as your KMS provider
   */
  kmip?: KMIPKMSProviderConfiguration;
  [key: `kmip:${string}`]: KMIPKMSProviderConfiguration;
 
  /**
   * Configuration options for using 'azure' as your KMS provider
   */
  azure?: AzureKMSProviderConfiguration | Record<string, never>;
  [key: `azure:${string}`]: AzureKMSProviderConfiguration;
 
  /**
   * Configuration options for using 'gcp' as your KMS provider
   */
  gcp?: GCPKMSProviderConfiguration | Record<string, never>;
  [key: `gcp:${string}`]: GCPKMSProviderConfiguration;
}
 
/**
 * Auto credential fetching should only occur when the provider is defined on the kmsProviders map
 * and the settings are an empty object.
 *
 * This is distinct from a nullish provider key.
 *
 * @internal - exposed for testing purposes only
 */
export function isEmptyCredentials(
  providerName: ClientEncryptionDataKeyProvider,
  kmsProviders: KMSProviders
): boolean {
  const provider = kmsProviders[providerName];
  if (provider == null) {
    return false;
  }
  return typeof provider === 'object' && Object.keys(provider).length === 0;
}
 
/**
 * Load cloud provider credentials for the user provided KMS providers.
 * Credentials will only attempt to get loaded if they do not exist
 * and no existing credentials will get overwritten.
 *
 * @internal
 */
export async function refreshKMSCredentials(
  kmsProviders: KMSProviders,
  credentialProviders?: CredentialProviders
): Promise<KMSProviders> {
  let finalKMSProviders = kmsProviders;
 
  if (isEmptyCredentials('aws', kmsProviders)) {
    finalKMSProviders = await loadAWSCredentials(finalKMSProviders, credentialProviders?.aws);
  }
 
  if (isEmptyCredentials('gcp', kmsProviders)) {
    finalKMSProviders = await loadGCPCredentials(finalKMSProviders);
  }
 
  if (isEmptyCredentials('azure', kmsProviders)) {
    finalKMSProviders = await loadAzureCredentials(finalKMSProviders);
  }
  return finalKMSProviders;
}