|
6 | 6 | */
|
7 | 7 |
|
8 | 8 | import { localize } from 'vscode-nls'
|
9 |
| -import { SectionName, SharedCredentialsKeys, StaticCredentialsProfileData } from './types' |
| 9 | +import { CredentialsData, CredentialsKey, SectionName, SharedCredentialsKeys } from './types' |
10 | 10 | import { ToolkitError } from '../shared/errors'
|
11 | 11 | import { profileExists } from './sharedCredentials'
|
| 12 | +import { getLogger } from '../shared/logger' |
| 13 | + |
| 14 | +/** credentials keys and their associated error message, if they exists */ |
| 15 | +type CredentialsErrors = CredentialsData |
12 | 16 |
|
13 | 17 | /**
|
14 |
| - * The format validators for shared credentials keys. |
| 18 | + * A function that validates a credential value |
15 | 19 | *
|
16 |
| - * A format validator validates the format of the data, |
17 |
| - * but not the validity of the content. |
| 20 | + * @returns An error message string if there is an error, otherwise undefined. |
18 | 21 | */
|
19 |
| -export const CredentialsKeyFormatValidators = { |
20 |
| - [SharedCredentialsKeys.AWS_ACCESS_KEY_ID]: getAccessKeyIdFormatError, |
21 |
| - [SharedCredentialsKeys.AWS_SECRET_ACCESS_KEY]: getSecretAccessKeyFormatError, |
22 |
| -} as const |
| 22 | +type GetCredentialError = (key: CredentialsKey, value: string | undefined) => string | undefined |
23 | 23 |
|
24 | 24 | /**
|
25 |
| - * Holds the error for each key of static credentials data, |
26 |
| - * if it exists. This allows the user to get all the errors |
27 |
| - * at once. |
| 25 | + * Validates all credentials values from the given input |
| 26 | + * |
| 27 | + * @returns Returns the same shape as the input, but the value of each |
| 28 | + * key is an error message if the original value is invalid, |
| 29 | + * otherwise undefined. |
| 30 | + * |
| 31 | + * If there are no errors at all, undefined is returned |
28 | 32 | */
|
29 |
| -export type StaticCredentialsErrorResult = { |
30 |
| - [k in keyof StaticCredentialsProfileData]: string | undefined |
| 33 | +export function getCredentialsErrors( |
| 34 | + data: CredentialsData, |
| 35 | + validateFunc: GetCredentialError = getCredentialError |
| 36 | +): CredentialsErrors | undefined { |
| 37 | + const errors: CredentialsData = {} |
| 38 | + Object.entries(data).forEach(([key, value]) => { |
| 39 | + if (!isCredentialsKey(key)) { |
| 40 | + return |
| 41 | + } |
| 42 | + errors[key] = validateFunc(key, value) |
| 43 | + }) |
| 44 | + if (Object.keys(errors).length === 0) { |
| 45 | + return |
| 46 | + } |
| 47 | + return errors |
31 | 48 | }
|
32 | 49 |
|
33 |
| -export function getStaticCredentialsDataErrors( |
34 |
| - data: StaticCredentialsProfileData |
35 |
| -): StaticCredentialsErrorResult | undefined { |
36 |
| - const accessKeyIdError = CredentialsKeyFormatValidators[SharedCredentialsKeys.AWS_ACCESS_KEY_ID]( |
37 |
| - data[SharedCredentialsKeys.AWS_ACCESS_KEY_ID] |
38 |
| - ) |
39 |
| - const secretAccessKeyError = CredentialsKeyFormatValidators[SharedCredentialsKeys.AWS_SECRET_ACCESS_KEY]( |
40 |
| - data[SharedCredentialsKeys.AWS_SECRET_ACCESS_KEY] |
41 |
| - ) |
42 |
| - |
43 |
| - if (accessKeyIdError === undefined && secretAccessKeyError === undefined) { |
44 |
| - return undefined |
| 50 | +export const getCredentialError: GetCredentialError = (key: CredentialsKey, value: string | undefined) => { |
| 51 | + const emptyError = getCredentialEmptyError(key, value) |
| 52 | + if (emptyError) { |
| 53 | + return emptyError |
45 | 54 | }
|
46 | 55 |
|
47 |
| - return { |
48 |
| - [SharedCredentialsKeys.AWS_ACCESS_KEY_ID]: accessKeyIdError, |
49 |
| - [SharedCredentialsKeys.AWS_SECRET_ACCESS_KEY]: secretAccessKeyError, |
| 56 | + // If value is allowed to be empty, no need to validate anything further |
| 57 | + if (!value) { |
| 58 | + return |
50 | 59 | }
|
51 |
| -} |
52 | 60 |
|
53 |
| -const accessKeyPattern = /[\w]{16,128}/ |
| 61 | + return getCredentialFormatError(key, value) |
| 62 | +} |
54 | 63 |
|
55 |
| -function getAccessKeyIdFormatError(awsAccessKeyId: string | undefined): string | undefined { |
56 |
| - if (awsAccessKeyId === undefined) { |
57 |
| - return undefined |
| 64 | +/** |
| 65 | + * Validates the format of a credential value. |
| 66 | + * |
| 67 | + * This function assumes there is a value to evaluate, if not |
| 68 | + * it returns no error. |
| 69 | + */ |
| 70 | +export const getCredentialFormatError: GetCredentialError = (key, value) => { |
| 71 | + if (!value) { |
| 72 | + /** Empty values should be validated in {@link getCredentialEmptyError} */ |
| 73 | + getLogger().debug('getCredentialFormatError() called with empty value for key "%s"', key) |
| 74 | + return |
58 | 75 | }
|
59 | 76 |
|
60 |
| - if (awsAccessKeyId === '') { |
61 |
| - return localize('AWS.credentials.error.emptyAccessKey', 'Access key must not be empty') |
62 |
| - } |
63 |
| - if (!accessKeyPattern.test(awsAccessKeyId)) { |
64 |
| - return localize( |
65 |
| - 'AWS.credentials.error.emptyAccessKey', |
66 |
| - 'Access key must be alphanumeric and between 16 and 128 characters' |
67 |
| - ) |
| 77 | + switch (key) { |
| 78 | + case SharedCredentialsKeys.AWS_ACCESS_KEY_ID: { |
| 79 | + const accessKeyPattern = /[\w]{16,128}/ |
| 80 | + if (!accessKeyPattern.test(value)) { |
| 81 | + return localize( |
| 82 | + 'AWS.credentials.error.invalidAccessKeyFormat', |
| 83 | + 'Access key must be alphanumeric and between 16 and 128 characters' |
| 84 | + ) |
| 85 | + } |
| 86 | + return |
| 87 | + } |
| 88 | + case SharedCredentialsKeys.AWS_SECRET_ACCESS_KEY: |
| 89 | + return |
| 90 | + default: |
| 91 | + throw new ToolkitError(`Unsupported key in getCredentialFormatError(): "${key}"`) |
68 | 92 | }
|
69 | 93 | }
|
70 | 94 |
|
71 |
| -function getSecretAccessKeyFormatError(awsSecretAccessKey: string | undefined): string | undefined { |
72 |
| - if (awsSecretAccessKey === undefined) { |
73 |
| - return undefined |
74 |
| - } |
75 |
| - |
76 |
| - if (awsSecretAccessKey === '') { |
77 |
| - return localize('AWS.credentials.error.emptySecretKey', 'Secret key must not be empty') |
| 95 | +export const getCredentialEmptyError: GetCredentialError = (key: CredentialsKey, value: string | undefined) => { |
| 96 | + switch (key) { |
| 97 | + case SharedCredentialsKeys.AWS_ACCESS_KEY_ID: |
| 98 | + case SharedCredentialsKeys.AWS_SECRET_ACCESS_KEY: |
| 99 | + if (value) { |
| 100 | + return undefined |
| 101 | + } |
| 102 | + return 'Cannot be empty.' |
| 103 | + default: |
| 104 | + throw new ToolkitError(`Unsupported key in getCredentialEmptyError(): "${key}"`) |
78 | 105 | }
|
79 | 106 | }
|
80 | 107 |
|
81 | 108 | /** To be used as a sanity check to validate all core parts of a credentials profile */
|
82 |
| -export async function validateCredentialsProfile(profileName: SectionName, profileData: StaticCredentialsProfileData) { |
83 |
| - if (await profileExists(profileName)) { |
84 |
| - throw new ToolkitError(`Credentials profile "${profileName}" already exists`) |
85 |
| - } |
| 109 | +export async function throwOnInvalidCredentials(profileName: SectionName, data: CredentialsData) { |
| 110 | + await validateProfileName(profileName) |
86 | 111 |
|
87 |
| - const credentialsDataErrors = getStaticCredentialsDataErrors(profileData) |
| 112 | + const credentialsDataErrors = getCredentialsErrors(data) |
88 | 113 | if (credentialsDataErrors !== undefined) {
|
89 | 114 | throw new ToolkitError(`Errors in credentials data: ${credentialsDataErrors}`, {
|
90 | 115 | code: 'InvalidCredentialsData',
|
91 | 116 | details: credentialsDataErrors,
|
92 | 117 | })
|
93 | 118 | }
|
94 | 119 | }
|
| 120 | + |
| 121 | +async function validateProfileName(profileName: SectionName) { |
| 122 | + if (await profileExists(profileName)) { |
| 123 | + throw new ToolkitError(`Credentials profile "${profileName}" already exists`) |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +// All shared credentials keys |
| 128 | +const sharedCredentialsKeysSet = new Set(Object.values(SharedCredentialsKeys)) |
| 129 | + |
| 130 | +export function isCredentialsKey(key: string): key is CredentialsKey { |
| 131 | + return sharedCredentialsKeysSet.has(key as CredentialsKey) |
| 132 | +} |
0 commit comments