process_credentials.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. var AWS = require('../core');
  2. var proc = require('child_process');
  3. var iniLoader = AWS.util.iniLoader;
  4. /**
  5. * Represents credentials loaded from shared credentials file
  6. * (defaulting to ~/.aws/credentials or defined by the
  7. * `AWS_SHARED_CREDENTIALS_FILE` environment variable).
  8. *
  9. * ## Using process credentials
  10. *
  11. * The credentials file can specify a credential provider that executes
  12. * a given process and attempts to read its stdout to recieve a JSON payload
  13. * containing the credentials:
  14. *
  15. * [default]
  16. * credential_process = /usr/bin/credential_proc
  17. *
  18. * Automatically handles refreshing credentials if an Expiration time is
  19. * provided in the credentials payload. Credentials supplied in the same profile
  20. * will take precedence over the credential_process.
  21. *
  22. * Sourcing credentials from an external process can potentially be dangerous,
  23. * so proceed with caution. Other credential providers should be preferred if
  24. * at all possible. If using this option, you should make sure that the shared
  25. * credentials file is as locked down as possible using security best practices
  26. * for your operating system.
  27. *
  28. * ## Using custom profiles
  29. *
  30. * The SDK supports loading credentials for separate profiles. This can be done
  31. * in two ways:
  32. *
  33. * 1. Set the `AWS_PROFILE` environment variable in your process prior to
  34. * loading the SDK.
  35. * 2. Directly load the AWS.ProcessCredentials provider:
  36. *
  37. * ```javascript
  38. * var creds = new AWS.ProcessCredentials({profile: 'myprofile'});
  39. * AWS.config.credentials = creds;
  40. * ```
  41. *
  42. * @!macro nobrowser
  43. */
  44. AWS.ProcessCredentials = AWS.util.inherit(AWS.Credentials, {
  45. /**
  46. * Creates a new ProcessCredentials object.
  47. *
  48. * @param options [map] a set of options
  49. * @option options profile [String] (AWS_PROFILE env var or 'default')
  50. * the name of the profile to load.
  51. * @option options filename [String] ('~/.aws/credentials' or defined by
  52. * AWS_SHARED_CREDENTIALS_FILE process env var)
  53. * the filename to use when loading credentials.
  54. * @option options callback [Function] (err) Credentials are eagerly loaded
  55. * by the constructor. When the callback is called with no error, the
  56. * credentials have been loaded successfully.
  57. */
  58. constructor: function ProcessCredentials(options) {
  59. AWS.Credentials.call(this);
  60. options = options || {};
  61. this.filename = options.filename;
  62. this.profile = options.profile || process.env.AWS_PROFILE || AWS.util.defaultProfile;
  63. this.get(options.callback || AWS.util.fn.noop);
  64. },
  65. /**
  66. * @api private
  67. */
  68. load: function load(callback) {
  69. var self = this;
  70. try {
  71. var profiles = AWS.util.getProfilesFromSharedConfig(iniLoader, this.filename);
  72. var profile = profiles[this.profile] || {};
  73. if (Object.keys(profile).length === 0) {
  74. throw AWS.util.error(
  75. new Error('Profile ' + this.profile + ' not found'),
  76. { code: 'ProcessCredentialsProviderFailure' }
  77. );
  78. }
  79. if (profile['credential_process']) {
  80. this.loadViaCredentialProcess(profile, function(err, data) {
  81. if (err) {
  82. callback(err, null);
  83. } else {
  84. self.expired = false;
  85. self.accessKeyId = data.AccessKeyId;
  86. self.secretAccessKey = data.SecretAccessKey;
  87. self.sessionToken = data.SessionToken;
  88. if (data.Expiration) {
  89. self.expireTime = new Date(data.Expiration);
  90. }
  91. callback(null);
  92. }
  93. });
  94. } else {
  95. throw AWS.util.error(
  96. new Error('Profile ' + this.profile + ' did not include credential process'),
  97. { code: 'ProcessCredentialsProviderFailure' }
  98. );
  99. }
  100. } catch (err) {
  101. callback(err);
  102. }
  103. },
  104. /**
  105. * Executes the credential_process and retrieves
  106. * credentials from the output
  107. * @api private
  108. * @param profile [map] credentials profile
  109. * @throws ProcessCredentialsProviderFailure
  110. */
  111. loadViaCredentialProcess: function loadViaCredentialProcess(profile, callback) {
  112. proc.exec(profile['credential_process'], { env: process.env }, function(err, stdOut, stdErr) {
  113. if (err) {
  114. callback(AWS.util.error(
  115. new Error('credential_process returned error'),
  116. { code: 'ProcessCredentialsProviderFailure'}
  117. ), null);
  118. } else {
  119. try {
  120. var credData = JSON.parse(stdOut);
  121. if (credData.Expiration) {
  122. var currentTime = AWS.util.date.getDate();
  123. var expireTime = new Date(credData.Expiration);
  124. if (expireTime < currentTime) {
  125. throw Error('credential_process returned expired credentials');
  126. }
  127. }
  128. if (credData.Version !== 1) {
  129. throw Error('credential_process does not return Version == 1');
  130. }
  131. callback(null, credData);
  132. } catch (err) {
  133. callback(AWS.util.error(
  134. new Error(err.message),
  135. { code: 'ProcessCredentialsProviderFailure'}
  136. ), null);
  137. }
  138. }
  139. });
  140. },
  141. /**
  142. * Loads the credentials from the credential process
  143. *
  144. * @callback callback function(err)
  145. * Called after the credential process has been executed. When this
  146. * callback is called with no error, it means that the credentials
  147. * information has been loaded into the object (as the `accessKeyId`,
  148. * `secretAccessKey`, and `sessionToken` properties).
  149. * @param err [Error] if an error occurred, this value will be filled
  150. * @see get
  151. */
  152. refresh: function refresh(callback) {
  153. iniLoader.clearCachedFiles();
  154. this.coalesceRefresh(callback || AWS.util.fn.callback);
  155. }
  156. });