diff --git a/README.md b/README.md index d5dd1d39..7da2e658 100644 --- a/README.md +++ b/README.md @@ -343,17 +343,19 @@ For environment variables, each environment comprises a set of key value pairs d "local": { "version": "v2", "host": "localhost", - "token": "test token" + "token": "test token", + "secret": "devSecret" }, "production": { "host": "example.com", - "token": "product token" + "token": "product token", + "secret" : "prodSecret" } } ``` A sample usage in `http` file for above environment variables is listed below, note that if you switch to _local_ environment, the `version` would be _v2_, if you change to _production_ environment, the `version` would be _v1_ which is inherited from the _$shared_ environment: ```http -GET https://{{host}}/api/{{version}comments/1 HTTP/1.1 +GET https://{{host}}/api/{{version}}comments/1 HTTP/1.1 Authorization: {{token}} ``` @@ -368,6 +370,7 @@ File variables can be defined in a separate request block only filled with varia @host = {{hostname}}:{{port}} @contentType = application/json @createdAt = {{$datetime iso8601}} +@modifiedBy = {{$processEnv USERNAME}} ### @@ -382,7 +385,8 @@ Content-Type: {{contentType}} { "content": "foo bar", - "created_at": {{createdAt}} + "created_at": {{createdAt}}, + "modified_by": {{modifiedBy}} } ``` @@ -451,6 +455,49 @@ System variables provide a pre-defined set of variables that can be used in any `aud:`: Optional. Target Azure AD app id (aka client id) or domain the token should be created for (aka audience or resource). Default: Domain of the REST endpoint. * `{{$guid}}`: Add a RFC 4122 v4 UUID +* `{{$processEnv [%]envVarName}}`: Allows the resolution of a local machine environment variable to a string value. A typical use case is for secret keys that you don't want to commit to source control. +For example: Define a shell environment variable in `.bashrc` or similar on windows + ```bash + export DEVSECRET="XlII3JUaEZldVg=" + export PRODSECRET="qMTkleUgjclRoRmV1WA==" + export USERNAME="sameUsernameInDevAndProd" + ``` + and with extension setting environment variables. + ```json + "rest-client.environmentVariables": { + "$shared": { + "version": "v1" + }, + "local": { + "version": "v2", + "host": "localhost", + "secretKey": "DEVSECRET" + }, + "production": { + "host": "example.com", + "secretKey" : "PRODSECRET" + } + } + ``` + + You can refer directly to the key (e.g. ```PRODSECRET```) in the script, for example if running in the production environment + ```http + ### Lookup PRODSECRET from local machine environment + GET https://{{host}}/{{version}}/values/item1?user={{$processEnv USERNAME}} + Authorization: {{$processEnv PRODSECRET}} + ``` + or, it can be rewritten to indirectly refer to the key using an extension environment setting (e.g. ```%secret```) to be environment independent using the optional ```%``` modifier. + ```http + ### Use secretKey from extension environment settings to determine + ### which local machine environment variable to use + GET https://{{host}}/{{version}}/values/item1?user={{$processEnv USERNAME}} + Authorization: {{$processEnv %secret}} + ``` + `envVarName`: Mandatory. Specifies the local machine environment variable + + `%`: Optional. If specified, treats envVarName as an extension setting environment variable, and uses the value of that for the lookup. + + * `{{$randomInt min max}}`: Returns a random integer between min (included) and max (excluded) * `{{$timestamp [offset option]}}`: Add UTC timestamp of now. You can even specify any date time based on current time in the format `{{$timestamp number option}}`, e.g., to represent 3 hours ago, simply `{{$timestamp -3 h}}`; to represent the day after tomorrow, simply `{{$timestamp 2 d}}`. * `{{$datetime rfc1123|iso8601|"custom format"|'custom format' [offset option]}}`: Add a datetime string in either _ISO8601_, _RFC1123_ or a custom display format. You can even specify a date time relative to the current date similar to `timestamp` like: `{{$datetime iso8601 1 y}}` to represent a year later in _ISO8601_ format. If specifying a custom format, wrap it in single or double quotes like: `{{$datetime "DD-MM-YYYY" 1 y}}`. The date is formatted using moment.js, read [here](https://momentjs.com/docs/#/parsing/string-format/) for information on format strings. diff --git a/package-lock.json b/package-lock.json index db23a12a..c3a50265 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7976,4 +7976,4 @@ "integrity": "sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=" } } -} +} \ No newline at end of file diff --git a/src/common/constants.ts b/src/common/constants.ts index d15d262a..27a749ca 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -27,6 +27,8 @@ export const GuidVariableName = "$guid"; export const GuidVariableDescription = "Add a RFC 4122 v4 UUID"; export const RandomIntVariableName = "$randomInt"; export const RandomIntDescription = "Returns a random integer between min (included) and max (excluded)"; +export const ProcessEnvVariableName = "$processEnv"; +export const ProcessEnvDescription = "Returns the value of process environment variable or '' if not found "; export const AzureActiveDirectoryVariableName = "$aadToken"; export const AzureActiveDirectoryDescription = "Prompts to sign in to Azure AD and adds the token to the request"; diff --git a/src/models/httpVariableResolveResult.ts b/src/models/httpVariableResolveResult.ts index 2620264e..3046fea3 100644 --- a/src/models/httpVariableResolveResult.ts +++ b/src/models/httpVariableResolveResult.ts @@ -31,6 +31,7 @@ export const enum ResolveWarningMessage { IncorrectHeaderName = 'No value is resolved for given header name', IncorrectJSONPath = 'No value is resolved for given JSONPath', IncorrectRandomIntegerVariableFormat = 'RandomInt system variable should follow format "{{$randomInt minInteger maxInteger}}"', + IncorrectProcessEnvVariableFormat = 'ProcessEnv system variable should follow format "{{$processEnv envVarName}}"', IncorrectTimestampVariableFormat = 'Timestamp system variable should follow format "{{$timestamp [integer y|Q|M|w|d|h|m|s|ms]}}"', IncorrectXPath = 'No value is resolved for given XPath', UnsupportedBodyContentType = 'Only JSON response/request body is supported to query the result', diff --git a/src/utils/httpVariableProviders/systemVariableProvider.ts b/src/utils/httpVariableProviders/systemVariableProvider.ts index 025a5a76..58f0ae83 100644 --- a/src/utils/httpVariableProviders/systemVariableProvider.ts +++ b/src/utils/httpVariableProviders/systemVariableProvider.ts @@ -9,6 +9,7 @@ import { ResolveErrorMessage, ResolveWarningMessage } from '../../models/httpVar import { VariableType } from '../../models/variableType'; import { AadTokenCache } from '../aadTokenCache'; import { HttpClient } from '../httpClient'; +import { EnvironmentVariableProvider } from './environmentVariableProvider'; import { HttpVariable, HttpVariableContext, HttpVariableProvider } from './httpVariableProvider'; const uuidv4 = require('uuid/v4'); @@ -20,15 +21,16 @@ export class SystemVariableProvider implements HttpVariableProvider { private readonly clipboard: Clipboard; private readonly resolveFuncs: Map = new Map(); - private readonly timestampRegex: RegExp = new RegExp(`\\${Constants.TimeStampVariableName}(?:\\s(\\-?\\d+)\\s(y|Q|M|w|d|h|m|s|ms))?`); private readonly datetimeRegex: RegExp = new RegExp(`\\${Constants.DateTimeVariableName}\\s(rfc1123|iso8601|\'.+\'|\".+\")(?:\\s(\\-?\\d+)\\s(y|Q|M|w|d|h|m|s|ms))?`); private readonly randomIntegerRegex: RegExp = new RegExp(`\\${Constants.RandomIntVariableName}\\s(\\-?\\d+)\\s(\\-?\\d+)`); + private readonly processEnvRegex: RegExp = new RegExp(`\\${Constants.ProcessEnvVariableName}\\s(\\%)?(\\w+)`); private readonly requestUrlRegex: RegExp = /^(?:[^\s]+\s+)([^:]*:\/\/\/?[^/\s]*\/?)/; private readonly aadRegex: RegExp = new RegExp(`\\s*\\${Constants.AzureActiveDirectoryVariableName}(\\s+(${Constants.AzureActiveDirectoryForceNewOption}))?(\\s+(ppe|public|cn|de|us))?(\\s+([^\\.]+\\.[^\\}\\s]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}))?(\\s+aud:([^\\.]+\\.[^\\}\\s]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}))?\\s*`); + private readonly innerSettingsEnvironmentVariableProvider: HttpVariableProvider = EnvironmentVariableProvider.Instance; private static _instance: SystemVariableProvider; public static get Instance(): SystemVariableProvider { @@ -45,6 +47,7 @@ export class SystemVariableProvider implements HttpVariableProvider { this.registerDateTimeVariable(); this.registerGuidVariable(); this.registerRandomIntVariable(); + this.registerProcessEnvVariable(); this.registerAadTokenVariable(); } @@ -129,6 +132,41 @@ export class SystemVariableProvider implements HttpVariableProvider { }); } + private async resolveSettingsEnvironmentVariable (name: string) { + let document = null; + let context = null; + if (await this.innerSettingsEnvironmentVariableProvider.has(document, name, context)) { + const { value, error, warning } = await this.innerSettingsEnvironmentVariableProvider.get(document, name, context); + if (!error && !warning) { + return value.toString(); + } else { + return name; + } + } else { + return name; + } + } + + private registerProcessEnvVariable() { + this.resolveFuncs.set(Constants.ProcessEnvVariableName, async name => { + const groups = this.processEnvRegex.exec(name); + if (groups !== null && groups.length === 3 ) { + const [, refToggle, environmentVarName] = groups; + let processEnvName = environmentVarName; + if (refToggle !== undefined) { + processEnvName = await this.resolveSettingsEnvironmentVariable(environmentVarName); + } + let envValue = process.env[processEnvName]; + if (envValue !== undefined) { + return { value: envValue.toString()}; + } else { + return { value: ''}; + } + } + return { warning: ResolveWarningMessage.IncorrectProcessEnvVariableFormat }; + }); + } + private registerAadTokenVariable() { this.resolveFuncs.set(Constants.AzureActiveDirectoryVariableName, (name, context) => { // get target app from URL