-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2559 from srchase/credential-process
implement credential_process on ProcessCredentials provider
- Loading branch information
Showing
8 changed files
with
418 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"type": "feature", | ||
"category": "Credentials", | ||
"description": "enables use of credentials_process for sourcing credentials from an external process https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import {Credentials} from '../credentials'; | ||
import {HTTPOptions} from '../config'; | ||
export class ProcessCredentials extends Credentials { | ||
/** | ||
* Creates a new ProcessCredentials object. | ||
*/ | ||
constructor(options?: ProcessCredentialsOptions); | ||
} | ||
|
||
interface ProcessCredentialsOptions { | ||
profile?: string | ||
filename?: string | ||
httpOptions?: HTTPOptions | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
var AWS = require('../core'); | ||
var proc = require('child_process'); | ||
var iniLoader = AWS.util.iniLoader; | ||
|
||
/** | ||
* Represents credentials loaded from shared credentials file | ||
* (defaulting to ~/.aws/credentials or defined by the | ||
* `AWS_SHARED_CREDENTIALS_FILE` environment variable). | ||
* | ||
* ## Using process credentials | ||
* | ||
* The credentials file can specify a credential provider that executes | ||
* a given process and attempts to read its stdout to recieve a JSON payload | ||
* containing the credentials: | ||
* | ||
* [default] | ||
* credential_process = /usr/bin/credential_proc | ||
* | ||
* Automatically handles refreshing credentials if an Expiration time is | ||
* provided in the credentials payload. Credentials supplied in the same profile | ||
* will take precedence over the credential_process. | ||
* | ||
* Sourcing credentials from an external process can potentially be dangerous, | ||
* so proceed with caution. Other credential providers should be preferred if | ||
* at all possible. If using this option, you should make sure that the shared | ||
* credentials file is as locked down as possible using security best practices | ||
* for your operating system. | ||
* | ||
* ## Using custom profiles | ||
* | ||
* The SDK supports loading credentials for separate profiles. This can be done | ||
* in two ways: | ||
* | ||
* 1. Set the `AWS_PROFILE` environment variable in your process prior to | ||
* loading the SDK. | ||
* 2. Directly load the AWS.ProcessCredentials provider: | ||
* | ||
* ```javascript | ||
* var creds = new AWS.ProcessCredentials({profile: 'myprofile'}); | ||
* AWS.config.credentials = creds; | ||
* ``` | ||
* | ||
* @!macro nobrowser | ||
*/ | ||
AWS.ProcessCredentials = AWS.util.inherit(AWS.Credentials, { | ||
/** | ||
* Creates a new ProcessCredentials object. | ||
* | ||
* @param options [map] a set of options | ||
* @option options profile [String] (AWS_PROFILE env var or 'default') | ||
* the name of the profile to load. | ||
* @option options filename [String] ('~/.aws/credentials' or defined by | ||
* AWS_SHARED_CREDENTIALS_FILE process env var) | ||
* the filename to use when loading credentials. | ||
* @option options callback [Function] (err) Credentials are eagerly loaded | ||
* by the constructor. When the callback is called with no error, the | ||
* credentials have been loaded successfully. | ||
*/ | ||
constructor: function ProcessCredentials(options) { | ||
AWS.Credentials.call(this); | ||
|
||
options = options || {}; | ||
|
||
this.filename = options.filename; | ||
this.profile = options.profile || process.env.AWS_PROFILE || AWS.util.defaultProfile; | ||
this.get(options.callback || AWS.util.fn.noop); | ||
}, | ||
|
||
/** | ||
* @api private | ||
*/ | ||
load: function load(callback) { | ||
var self = this; | ||
try { | ||
var profiles = {}; | ||
var profilesFromConfig = {}; | ||
if (process.env[AWS.util.configOptInEnv]) { | ||
var profilesFromConfig = iniLoader.loadFrom({ | ||
isConfig: true, | ||
filename: process.env[AWS.util.sharedConfigFileEnv] | ||
}); | ||
} | ||
var profilesFromCreds = iniLoader.loadFrom({ | ||
filename: this.filename || | ||
(process.env[AWS.util.configOptInEnv] && process.env[AWS.util.sharedCredentialsFileEnv]) | ||
}); | ||
for (var i = 0, profileNames = Object.keys(profilesFromCreds); i < profileNames.length; i++) { | ||
profiles[profileNames[i]] = profilesFromCreds[profileNames[i]]; | ||
} | ||
// load after profilesFromCreds to prefer profilesFromConfig | ||
for (var i = 0, profileNames = Object.keys(profilesFromConfig); i < profileNames.length; i++) { | ||
profiles[profileNames[i]] = profilesFromConfig[profileNames[i]]; | ||
} | ||
var profile = profiles[this.profile] || {}; | ||
|
||
if (Object.keys(profile).length === 0) { | ||
throw AWS.util.error( | ||
new Error('Profile ' + this.profile + ' not found'), | ||
{ code: 'ProcessCredentialsProviderFailure' } | ||
); | ||
} | ||
|
||
if (profile['credential_process']) { | ||
this.loadViaCredentialProcess(profile, function(err, data) { | ||
if (err) { | ||
callback(err, null); | ||
} else { | ||
self.expired = false; | ||
self.accessKeyId = data.AccessKeyId; | ||
self.secretAccessKey = data.SecretAccessKey; | ||
self.sessionToken = data.SessionToken; | ||
if (data.Expiration) { | ||
self.expireTime = new Date(data.Expiration); | ||
} | ||
callback(null); | ||
} | ||
}); | ||
} else { | ||
throw AWS.util.error( | ||
new Error('Profile ' + this.profile + ' did not include credential process'), | ||
{ code: 'ProcessCredentialsProviderFailure' } | ||
); | ||
} | ||
} catch (err) { | ||
callback(err); | ||
} | ||
}, | ||
|
||
/** | ||
* Executes the credential_process and retrieves | ||
* credentials from the output | ||
* @api private | ||
* @param profile [map] credentials profile | ||
* @throws ProcessCredentialsProviderFailure | ||
*/ | ||
loadViaCredentialProcess: function loadViaCredentialProcess(profile, callback) { | ||
proc.exec(profile['credential_process'], function(err, stdOut, stdErr) { | ||
if (err) { | ||
callback(AWS.util.error( | ||
new Error('credential_process returned error'), | ||
{ code: 'ProcessCredentialsProviderFailure'} | ||
), null); | ||
} else { | ||
try { | ||
var credData = JSON.parse(stdOut); | ||
if (credData.Expiration) { | ||
var currentTime = AWS.util.date.getDate(); | ||
var expireTime = new Date(credData.Expiration); | ||
if (expireTime < currentTime) { | ||
throw Error('credential_process returned expired credentials'); | ||
} | ||
} | ||
|
||
if (credData.Version !== 1) { | ||
throw Error('credential_process does not return Version == 1'); | ||
} | ||
callback(null, credData); | ||
} catch (err) { | ||
callback(AWS.util.error( | ||
new Error(err.message), | ||
{ code: 'ProcessCredentialsProviderFailure'} | ||
), null); | ||
} | ||
} | ||
}); | ||
}, | ||
|
||
/** | ||
* Loads the credentials from the credential process | ||
* | ||
* @callback callback function(err) | ||
* Called after the credential process has been executed. When this | ||
* callback is called with no error, it means that the credentials | ||
* information has been loaded into the object (as the `accessKeyId`, | ||
* `secretAccessKey`, and `sessionToken` properties). | ||
* @param err [Error] if an error occurred, this value will be filled | ||
* @see get | ||
*/ | ||
refresh: function refresh(callback) { | ||
this.coalesceRefresh(callback || AWS.util.fn.callback); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.