Repository: aws/aws-sdk-js
Pull request #2698 (2019-06-05 22:14:04)

Refresh ini based credentials
When a credential expires it must be refreshed.
SharedIniFileCredentials could not refresh credentials because iniLoader
caches parsed config files. I added a `refresh` option to iniLoader and
used it in SharedIniFileCredentials and ProcessCredentials.

ProcessCredentials is a little weird, it reads ini files and built a
map of profiles, just like the SharedIniFileCredentials, but it may also
execute the `credentials_process` and read credentials from it's stdout.
I added `refresh: true` to it's iniLoader calls because
ProcessCredentials may be found in the ini files, not the exec'ed
process.

The tests in credentials.spec.js do not return fresh credentials
(expired is false but expireTime is null so needsRefresh() is always
true). iniLoader.loadFile is called once when the creds are constructed
and again when `get()` is called and the credentials are refreshed.

<!--
Thank you for your pull request. Please provide a description below.
-->

##### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] `npm run test` passes (i skipped browser tests and react-native
- [x] `.d.ts` file is updated
- [x] changelog is added, `npm run add-change`


AllanFly120: Hey @everett1992 

The iniLoader exposes a method to clear the cached files: https://github.com/aws/aws-sdk-js/blob/33b429b005249b6a5afbb7e48db0b87ba4cc2089/lib/shared-ini/ini-loader.js#L34, so that the next credential refresh will load the credentials from files again. The issue is that this method is supposed to be called explicitly. I think a better way to go is clear the cached credential files when the credentials is expired, and reload them.
everett1992: A single expired credential will remove all ini files from the cache if `clearCachedFiles()` is used. There are costs to adding option to interfaces, but there are also advantages, in this case improved cache performance due to targeted expiration.

However, I'm happy to use `clearCachedFiles()` if you insist. 
https://github.com/aws/aws-sdk-js/blob/master/lib/credentials/shared_ini_file_credentials.js#L183

```diff
  refresh: function refresh(callback) {
+   iniLoader.clearCachedFiles();
    this.coalesceRefresh(
      callback || AWS.util.fn.callback,
      this.disableAssumeRole
    );
  },
```


Patch:
diff --git a/.changes/next-release/bugfix-Credentials-5bd1e48a.json b/.changes/next-release/bugfix-Credentials-5bd1e48a.json
new file mode 100644
index 000000000..21253d3e9
--- /dev/null
+++ b/.changes/next-release/bugfix-Credentials-5bd1e48a.json
@@ -0,0 +1,5 @@
+{
+  "type": "bugfix",
+  "category": "Credentials",
+  "description": "Refresh cached ini credentials from disk"
+}
\ No newline at end of file
diff --git a/lib/credentials/process_credentials.js b/lib/credentials/process_credentials.js
index fc7d893dd..ff59350a5 100644
--- a/lib/credentials/process_credentials.js
+++ b/lib/credentials/process_credentials.js
@@ -177,6 +177,7 @@ AWS.ProcessCredentials = AWS.util.inherit(AWS.Credentials, {
    * @see get
    */
   refresh: function refresh(callback) {
+    iniLoader.clearCachedFiles();
     this.coalesceRefresh(callback || AWS.util.fn.callback);
   }
 });
diff --git a/lib/credentials/shared_ini_file_credentials.js b/lib/credentials/shared_ini_file_credentials.js
index 1eb380734..23665150b 100644
--- a/lib/credentials/shared_ini_file_credentials.js
+++ b/lib/credentials/shared_ini_file_credentials.js
@@ -181,6 +181,7 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
    * @see get
    */
   refresh: function refresh(callback) {
+    iniLoader.clearCachedFiles();
     this.coalesceRefresh(
       callback || AWS.util.fn.callback,
       this.disableAssumeRole


Test patch:
diff --git a/test/credentials.spec.js b/test/credentials.spec.js
index 4d6a326f0..758c7c7af 100644
--- a/test/credentials.spec.js
+++ b/test/credentials.spec.js
@@ -523,7 +523,7 @@
           process.env.HOMEPATH = 'homepath';
           creds = new AWS.ProcessCredentials();
           creds.get();
-          expect(AWS.util.readFileSync.calls.length).to.equal(1);
+          expect(AWS.util.readFileSync.calls.length).to.equal(2);
           return expect(AWS.util.readFileSync.calls[0]['arguments'][0]).to.match(/d:[\/\\]homepath[\/\\].aws[\/\\]credentials/);
         });
         it('uses default HOMEDRIVE of C:/', function() {
@@ -531,7 +531,7 @@
           process.env.HOMEPATH = 'homepath';
           creds = new AWS.ProcessCredentials();
           creds.get();
-          expect(AWS.util.readFileSync.calls.length).to.equal(1);
+          expect(AWS.util.readFileSync.calls.length).to.equal(2);
           return expect(AWS.util.readFileSync.calls[0]['arguments'][0]).to.match(/C:[\/\\]homepath[\/\\].aws[\/\\]credentials/);
         });
         it('uses USERPROFILE if HOME is not set', function() {
@@ -539,7 +539,7 @@
           process.env.USERPROFILE = '/userprofile';
           creds = new AWS.ProcessCredentials();
           creds.get();
-          expect(AWS.util.readFileSync.calls.length).to.equal(1);
+          expect(AWS.util.readFileSync.calls.length).to.equal(2);
           return expect(AWS.util.readFileSync.calls[0]['arguments'][0]).to.match(/[\/\\]userprofile[\/\\].aws[\/\\]credentials/);
         });
         return it('can override filename as a constructor argument', function() {
@@ -548,7 +548,7 @@
             filename: '/etc/creds'
           });
           creds.get();
-          expect(AWS.util.readFileSync.calls.length).to.equal(1);
+          expect(AWS.util.readFileSync.calls.length).to.equal(2);
           return expect(AWS.util.readFileSync.calls[0]['arguments'][0]).to.equal('/etc/creds');
         });
       });