s3control.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. var AWS = require('../core');
  2. var s3util = require('./s3util');
  3. var regionUtil = require('../region_config');
  4. AWS.util.update(AWS.S3Control.prototype, {
  5. /**
  6. * @api private
  7. */
  8. setupRequestListeners: function setupRequestListeners(request) {
  9. request.addListener('extractError', this.extractHostId);
  10. request.addListener('extractData', this.extractHostId);
  11. request.addListener('validate', this.validateAccountId);
  12. var isArnInBucket = s3util.isArnInParam(request, 'Bucket');
  13. var isArnInName = s3util.isArnInParam(request, 'Name');
  14. if (isArnInBucket) {
  15. request._parsedArn = AWS.util.ARN.parse(request.params['Bucket']);
  16. request.addListener('validate', this.validateOutpostsBucketArn);
  17. request.addListener('validate', s3util.validateOutpostsArn);
  18. request.addListener('afterBuild', this.addOutpostIdHeader);
  19. } else if (isArnInName) {
  20. request._parsedArn = AWS.util.ARN.parse(request.params['Name']);
  21. request.addListener('validate', s3util.validateOutpostsAccessPointArn);
  22. request.addListener('validate', s3util.validateOutpostsArn);
  23. request.addListener('afterBuild', this.addOutpostIdHeader);
  24. }
  25. if (isArnInBucket || isArnInName) {
  26. request.addListener('validate', this.validateArnRegion);
  27. request.addListener('validate', this.validateArnAccountWithParams, true);
  28. request.addListener('validate', s3util.validateArnAccount);
  29. request.addListener('validate', s3util.validateArnService);
  30. request.addListener('build', this.populateParamFromArn, true);
  31. request.addListener('build', this.populateUriFromArn);
  32. request.addListener('build', s3util.validatePopulateUriFromArn);
  33. }
  34. if (request.params.OutpostId &&
  35. (request.operation === 'createBucket' ||
  36. request.operation === 'listRegionalBuckets')) {
  37. request.addListener('build', this.populateEndpointForOutpostId);
  38. }
  39. },
  40. /**
  41. * Adds outpostId header
  42. */
  43. addOutpostIdHeader: function addOutpostIdHeader(req) {
  44. req.httpRequest.headers['x-amz-outpost-id'] = req._parsedArn.outpostId;
  45. },
  46. /**
  47. * Validate Outposts ARN supplied in Bucket parameter is a valid bucket name
  48. */
  49. validateOutpostsBucketArn: function validateOutpostsBucketArn(req) {
  50. var parsedArn = req._parsedArn;
  51. //can be ':' or '/'
  52. var delimiter = parsedArn.resource['outpost'.length];
  53. if (parsedArn.resource.split(delimiter).length !== 4) {
  54. throw AWS.util.error(new Error(), {
  55. code: 'InvalidARN',
  56. message: 'Bucket ARN should have two resources outpost/{outpostId}/bucket/{accesspointName}'
  57. });
  58. }
  59. var bucket = parsedArn.resource.split(delimiter)[3];
  60. if (!s3util.dnsCompatibleBucketName(bucket) || bucket.match(/\./)) {
  61. throw AWS.util.error(new Error(), {
  62. code: 'InvalidARN',
  63. message: 'Bucket ARN is not DNS compatible. Got ' + bucket
  64. });
  65. }
  66. //set parsed valid bucket
  67. req._parsedArn.bucket = bucket;
  68. },
  69. /**
  70. * @api private
  71. */
  72. populateParamFromArn: function populateParamFromArn(req) {
  73. var parsedArn = req._parsedArn;
  74. if (s3util.isArnInParam(req, 'Bucket')) {
  75. req.params.Bucket = parsedArn.bucket;
  76. } else if (s3util.isArnInParam(req, 'Name')) {
  77. req.params.Name = parsedArn.accessPoint;
  78. }
  79. },
  80. /**
  81. * Populate URI according to the ARN
  82. */
  83. populateUriFromArn: function populateUriFromArn(req) {
  84. var parsedArn = req._parsedArn;
  85. var endpoint = req.httpRequest.endpoint;
  86. var useArnRegion = req.service.config.s3UseArnRegion;
  87. var useFipsEndpoint = req.service.config.useFipsEndpoint;
  88. endpoint.hostname = [
  89. 's3-outposts' + (useFipsEndpoint ? '-fips': ''),
  90. useArnRegion ? parsedArn.region : req.service.config.region,
  91. 'amazonaws.com'
  92. ].join('.');
  93. endpoint.host = endpoint.hostname;
  94. },
  95. /**
  96. * @api private
  97. */
  98. populateEndpointForOutpostId: function populateEndpointForOutpostId(req) {
  99. var endpoint = req.httpRequest.endpoint;
  100. var useFipsEndpoint = req.service.config.useFipsEndpoint;
  101. endpoint.hostname = [
  102. 's3-outposts' + (useFipsEndpoint ? '-fips': ''),
  103. req.service.config.region,
  104. 'amazonaws.com'
  105. ].join('.');
  106. endpoint.host = endpoint.hostname;
  107. },
  108. /**
  109. * @api private
  110. */
  111. extractHostId: function(response) {
  112. var hostId = response.httpResponse.headers ? response.httpResponse.headers['x-amz-id-2'] : null;
  113. response.extendedRequestId = hostId;
  114. if (response.error) {
  115. response.error.extendedRequestId = hostId;
  116. }
  117. },
  118. /**
  119. * @api private
  120. */
  121. validateArnRegion: function validateArnRegion(req) {
  122. s3util.validateArnRegion(req, { allowFipsEndpoint: true });
  123. },
  124. /**
  125. * @api private
  126. */
  127. validateArnAccountWithParams: function validateArnAccountWithParams(req) {
  128. var params = req.params;
  129. var inputModel = req.service.api.operations[req.operation].input;
  130. if (inputModel.members.AccountId) {
  131. var parsedArn = req._parsedArn;
  132. if (parsedArn.accountId) {
  133. if (params.AccountId) {
  134. if (params.AccountId !== parsedArn.accountId) {
  135. throw AWS.util.error(
  136. new Error(),
  137. {code: 'ValidationError', message: 'AccountId in ARN and request params should be same.'}
  138. );
  139. }
  140. } else {
  141. // Store accountId from ARN in params
  142. params.AccountId = parsedArn.accountId;
  143. }
  144. }
  145. }
  146. },
  147. /**
  148. * @api private
  149. */
  150. validateAccountId: function(request) {
  151. var params = request.params;
  152. if (!Object.prototype.hasOwnProperty.call(params, 'AccountId')) return;
  153. var accountId = params.AccountId;
  154. //validate type
  155. if (typeof accountId !== 'string') {
  156. throw AWS.util.error(
  157. new Error(),
  158. {code: 'ValidationError', message: 'AccountId must be a string.'}
  159. );
  160. }
  161. //validate length
  162. if (accountId.length < 1 || accountId.length > 63) {
  163. throw AWS.util.error(
  164. new Error(),
  165. {code: 'ValidationError', message: 'AccountId length should be between 1 to 63 characters, inclusive.'}
  166. );
  167. }
  168. //validate pattern
  169. var hostPattern = /^[a-zA-Z0-9]{1}$|^[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]$/;
  170. if (!hostPattern.test(accountId)) {
  171. throw AWS.util.error(new Error(),
  172. {code: 'ValidationError', message: 'AccountId should be hostname compatible. AccountId: ' + accountId});
  173. }
  174. },
  175. /**
  176. * @api private
  177. */
  178. getSigningName: function getSigningName(req) {
  179. var _super = AWS.Service.prototype.getSigningName;
  180. if (req && req._parsedArn && req._parsedArn.service) {
  181. return req._parsedArn.service;
  182. } else if (req.params.OutpostId &&
  183. (req.operation === 'createBucket' ||
  184. req.operation === 'listRegionalBuckets')) {
  185. return 's3-outposts';
  186. } else {
  187. return _super.call(this, req);
  188. }
  189. },
  190. });