Skip to content

Commit 5ce1233

Browse files
authored
Merge pull request #1391 from jeskew/jans510-Add-AWS_CREDENTIAL_PROFILES_FILE-Environment-Variable
Add support for loading shared config
2 parents 57a3e04 + 7528c83 commit 5ce1233

10 files changed

+517
-68
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "EnvironmentVariable",
4+
"description": "Load config from ~/.aws/config if AWS_SDK_LOAD_CONFIG is set"
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "EnvironmentVariable",
4+
"description": "Add support for specifying the location of the shared config file via the AWS_CONFIG_FILE environment variable. This variable is only honored if AWS_SDK_LOAD_CONFIG is set to a truthy value."
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "EnvironmentVariable",
4+
"description": "Add support for the AWS_SHARED_CREDENTIALS_FILE environment variable if AWS_SDK_LOAD_CONFIG is set"
5+
}

lib/credentials/shared_ini_file_credentials.js

+37-40
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
var AWS = require('../core');
22
var path = require('path');
3+
var SharedIniFile = require('../shared_ini');
34
var STS = require('../../clients/sts');
45

56
/**
67
* Represents credentials loaded from shared credentials file
7-
* (defaulting to ~/.aws/credentials).
8+
* (defaulting to ~/.aws/credentials or defined by the
9+
* `AWS_SHARED_CREDENTIALS_FILE` environment variable).
810
*
911
* ## Using the shared credentials file
1012
*
@@ -39,8 +41,9 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
3941
* @param options [map] a set of options
4042
* @option options profile [String] (AWS_PROFILE env var or 'default')
4143
* the name of the profile to load.
42-
* @option options filename [String] ('~/.aws/credentials') the filename
43-
* to use when loading credentials.
44+
* @option options filename [String] ('~/.aws/credentials' or defined by
45+
* AWS_SHARED_CREDENTIALS_FILE process env var)
46+
* the filename to use when loading credentials.
4447
* @option options disableAssumeRole [Boolean] (false) True to disable
4548
* support for profiles that assume an IAM role. If true, and an assume
4649
* role profile is selected, an error is raised.
@@ -51,8 +54,8 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
5154
options = options || {};
5255

5356
this.filename = options.filename;
54-
this.profile = options.profile || process.env.AWS_PROFILE || 'default';
55-
this.disableAssumeRole = !!options.disableAssumeRole;
57+
this.profile = options.profile || process.env.AWS_PROFILE || AWS.util.defaultProfile;
58+
this.disableAssumeRole = Boolean(options.disableAssumeRole);
5659
this.get(function() {});
5760
},
5861

@@ -70,19 +73,35 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
7073
refresh: function refresh(callback) {
7174
if (!callback) callback = function(err) { if (err) throw err; };
7275
try {
73-
if (!this.filename) this.loadDefaultFilename();
74-
var creds = AWS.util.ini.parse(AWS.util.readFileSync(this.filename));
75-
var profile = creds[this.profile];
76+
var profiles = {};
77+
var i, availableProfiles;
78+
if (process.env[AWS.util.configOptInEnv]) {
79+
var config = new SharedIniFile({
80+
isConfig: true,
81+
filename: process.env[AWS.util.sharedConfigFileEnv]
82+
});
83+
for (i = 0, availableProfiles = config.getProfiles(); i < availableProfiles.length; i++) {
84+
profiles[availableProfiles[i]] = config.getProfile(availableProfiles[i]);
85+
}
86+
}
87+
var creds = new SharedIniFile({
88+
filename: this.filename ||
89+
(process.env[AWS.util.configOptInEnv] && process.env[AWS.util.sharedCredentialsFileEnv])
90+
});
91+
for (i = 0, availableProfiles = creds.getProfiles(); i < availableProfiles.length; i++) {
92+
profiles[availableProfiles[i]] = creds.getProfile(availableProfiles[i]);
93+
}
94+
var profile = profiles[this.profile] || {};
7695

77-
if (typeof profile !== 'object') {
96+
if (Object.keys(profile).length === 0) {
7897
throw AWS.util.error(
79-
new Error('Profile ' + this.profile + ' not found in ' + this.filename),
98+
new Error('Profile ' + this.profile + ' not found'),
8099
{ code: 'SharedIniFileCredentialsProviderFailure' }
81100
);
82101
}
83102

84103
if (profile['role_arn']) {
85-
this.loadRoleProfile(creds, profile, callback);
104+
this.loadRoleProfile(profiles, profile, callback);
86105
return;
87106
}
88107

@@ -92,8 +111,7 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
92111

93112
if (!this.accessKeyId || !this.secretAccessKey) {
94113
throw AWS.util.error(
95-
new Error('Credentials not set in ' + this.filename +
96-
' using profile ' + this.profile),
114+
new Error('Credentials not set for profile ' + this.profile),
97115
{ code: 'SharedIniFileCredentialsProviderFailure' }
98116
);
99117
}
@@ -111,8 +129,8 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
111129
if (this.disableAssumeRole) {
112130
throw AWS.util.error(
113131
new Error('Role assumption profiles are disabled. ' +
114-
'Failed to load profile ' + this.profile + ' from ' +
115-
this.filename),
132+
'Failed to load profile ' + this.profile +
133+
' from ' + creds.filename),
116134
{ code: 'SharedIniFileCredentialsProviderFailure' }
117135
);
118136
}
@@ -125,8 +143,7 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
125143

126144
if (!sourceProfileName) {
127145
throw AWS.util.error(
128-
new Error('source_profile is not set in ' + this.filename +
129-
' using profile ' + this.profile),
146+
new Error('source_profile is not set using profile ' + this.profile),
130147
{ code: 'SharedIniFileCredentialsProviderFailure' }
131148
);
132149
}
@@ -135,9 +152,8 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
135152

136153
if (typeof sourceProfile !== 'object') {
137154
throw AWS.util.error(
138-
new Error('source_profile ' + sourceProfileName + ' set in ' +
139-
this.filename + ' using profile ' + this.profile +
140-
' does not exist'),
155+
new Error('source_profile ' + sourceProfileName + ' using profile '
156+
+ this.profile + ' does not exist'),
141157
{ code: 'SharedIniFileCredentialsProviderFailure' }
142158
);
143159
}
@@ -153,8 +169,7 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
153169
if (!sourceCredentials.accessKeyId || !sourceCredentials.secretAccessKey) {
154170
throw AWS.util.error(
155171
new Error('Credentials not set in source_profile ' +
156-
sourceProfileName + ' set in ' + this.filename +
157-
' using profile ' + this.profile),
172+
sourceProfileName + ' using profile ' + this.profile),
158173
{ code: 'SharedIniFileCredentialsProviderFailure' }
159174
);
160175
}
@@ -184,23 +199,5 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
184199
self.expireTime = data.Credentials.Expiration;
185200
callback();
186201
});
187-
},
188-
189-
/**
190-
* @api private
191-
*/
192-
loadDefaultFilename: function loadDefaultFilename() {
193-
var env = process.env;
194-
var home = env.HOME ||
195-
env.USERPROFILE ||
196-
(env.HOMEPATH ? ((env.HOMEDRIVE || 'C:/') + env.HOMEPATH) : null);
197-
if (!home) {
198-
throw AWS.util.error(
199-
new Error('Cannot load credentials, HOME path not set'),
200-
{ code: 'SharedIniFileCredentialsProviderFailure' }
201-
);
202-
}
203-
204-
this.filename = path.join(home, '.aws', 'credentials');
205202
}
206203
});

lib/node_loader.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ require('./credentials/environment_credentials');
2626
require('./credentials/file_system_credentials');
2727
require('./credentials/shared_ini_file_credentials');
2828

29+
var SharedIniFile = require('./shared_ini');
30+
2931
// Setup default chain providers
3032
// If this changes, please update documentation for
3133
// AWS.CredentialProviderChain.defaultProviders in
@@ -59,7 +61,22 @@ AWS.util.update(AWS.Config.prototype.keys, {
5961
return new AWS.CredentialProviderChain();
6062
},
6163
region: function() {
62-
return process.env.AWS_REGION || process.env.AMAZON_REGION;
64+
var env = process.env;
65+
var region = env.AWS_REGION || env.AMAZON_REGION;
66+
if (env[AWS.util.configOptInEnv]) {
67+
var toCheck = [
68+
{filename: env[AWS.util.sharedCredentialsFileEnv]},
69+
{isConfig: true, filename: env[AWS.util.sharedConfigFileEnv]}
70+
];
71+
while (!region && toCheck.length) {
72+
var configFile = new SharedIniFile(toCheck.shift());
73+
var profile = configFile.getProfile(
74+
env.AWS_PROFILE || AWS.util.defaultProfile
75+
);
76+
region = profile && profile.region;
77+
}
78+
}
79+
return region;
6380
}
6481
});
6582

lib/shared_ini.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
var AWS = require('./core');
2+
var os = require('os');
3+
var path = require('path');
4+
5+
/**
6+
* @api private
7+
*/
8+
module.exports = AWS.util.inherit({
9+
constructor: function SharedIniFile(options) {
10+
options = options || {};
11+
12+
this.isConfig = options.isConfig === true;
13+
this.filename = options.filename || this.getDefaultFilepath();
14+
},
15+
16+
ensureFileLoaded: function loadFile() {
17+
if (!this.parsedContents) {
18+
this.parsedContents = AWS.util.ini.parse(
19+
AWS.util.readFileSync(this.filename)
20+
);
21+
}
22+
},
23+
24+
getDefaultFilepath: function getDefaultFilepath() {
25+
return path.join(
26+
this.getHomeDir(),
27+
'.aws',
28+
this.isConfig ? 'config' : 'credentials'
29+
);
30+
},
31+
32+
getHomeDir: function getHomeDir() {
33+
if (typeof os.homedir === 'function') {
34+
return os.homedir();
35+
}
36+
37+
var env = process.env;
38+
var home = env.HOME ||
39+
env.USERPROFILE ||
40+
(env.HOMEPATH ? ((env.HOMEDRIVE || 'C:/') + env.HOMEPATH) : null);
41+
42+
if (home) {
43+
return home;
44+
}
45+
46+
throw AWS.util.error(
47+
new Error('Cannot load credentials, HOME path not set')
48+
);
49+
},
50+
51+
getProfile: function loadProfile(profile) {
52+
this.ensureFileLoaded();
53+
54+
var profileIndex = profile !== AWS.util.defaultProfile && this.isConfig ?
55+
'profile ' + profile : profile;
56+
57+
return this.parsedContents[profileIndex];
58+
},
59+
60+
getProfiles: function loadProfileNames() {
61+
this.ensureFileLoaded();
62+
var isConfig = this.isConfig;
63+
64+
return Object.keys(this.parsedContents).map(function(profileName) {
65+
if (isConfig) {
66+
return profileName.replace(/^profile\s/, '');
67+
}
68+
69+
return profileName;
70+
});
71+
}
72+
});

lib/util.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -892,8 +892,27 @@ var util = {
892892
if (rules.payload && resp.data[rules.payload]) {
893893
resp.data[rules.payload] = resp.data[rules.payload].toString();
894894
}
895-
}
895+
},
896+
897+
/**
898+
* @api private
899+
*/
900+
defaultProfile: 'default',
901+
902+
/**
903+
* @api private
904+
*/
905+
configOptInEnv: 'AWS_SDK_LOAD_CONFIG',
906+
907+
/**
908+
* @api private
909+
*/
910+
sharedCredentialsFileEnv: 'AWS_SHARED_CREDENTIALS_FILE',
896911

912+
/**
913+
* @api private
914+
*/
915+
sharedConfigFileEnv: 'AWS_CONFIG_FILE'
897916
};
898917

899918
module.exports = util;

0 commit comments

Comments
 (0)