s3util.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. var AWS = require('../core');
  2. var regionUtil = require('../region_config');
  3. var s3util = {
  4. /**
  5. * @api private
  6. */
  7. isArnInParam: function isArnInParam(req, paramName) {
  8. var inputShape = (req.service.api.operations[req.operation] || {}).input || {};
  9. var inputMembers = inputShape.members || {};
  10. if (!req.params[paramName] || !inputMembers[paramName]) return false;
  11. return AWS.util.ARN.validate(req.params[paramName]);
  12. },
  13. /**
  14. * Validate service component from ARN supplied in Bucket parameter
  15. */
  16. validateArnService: function validateArnService(req) {
  17. var parsedArn = req._parsedArn;
  18. if (parsedArn.service !== 's3'
  19. && parsedArn.service !== 's3-outposts'
  20. && parsedArn.service !== 's3-object-lambda') {
  21. throw AWS.util.error(new Error(), {
  22. code: 'InvalidARN',
  23. message: 'expect \'s3\' or \'s3-outposts\' or \'s3-object-lambda\' in ARN service component'
  24. });
  25. }
  26. },
  27. /**
  28. * Validate account ID from ARN supplied in Bucket parameter is a valid account
  29. */
  30. validateArnAccount: function validateArnAccount(req) {
  31. var parsedArn = req._parsedArn;
  32. if (!/[0-9]{12}/.exec(parsedArn.accountId)) {
  33. throw AWS.util.error(new Error(), {
  34. code: 'InvalidARN',
  35. message: 'ARN accountID does not match regex "[0-9]{12}"'
  36. });
  37. }
  38. },
  39. /**
  40. * Validate ARN supplied in Bucket parameter is a valid access point ARN
  41. */
  42. validateS3AccessPointArn: function validateS3AccessPointArn(req) {
  43. var parsedArn = req._parsedArn;
  44. //can be ':' or '/'
  45. var delimiter = parsedArn.resource['accesspoint'.length];
  46. if (parsedArn.resource.split(delimiter).length !== 2) {
  47. throw AWS.util.error(new Error(), {
  48. code: 'InvalidARN',
  49. message: 'Access Point ARN should have one resource accesspoint/{accesspointName}'
  50. });
  51. }
  52. var accessPoint = parsedArn.resource.split(delimiter)[1];
  53. var accessPointPrefix = accessPoint + '-' + parsedArn.accountId;
  54. if (!s3util.dnsCompatibleBucketName(accessPointPrefix) || accessPointPrefix.match(/\./)) {
  55. throw AWS.util.error(new Error(), {
  56. code: 'InvalidARN',
  57. message: 'Access point resource in ARN is not DNS compatible. Got ' + accessPoint
  58. });
  59. }
  60. //set parsed valid access point
  61. req._parsedArn.accessPoint = accessPoint;
  62. },
  63. /**
  64. * Validate Outposts ARN supplied in Bucket parameter is a valid outposts ARN
  65. */
  66. validateOutpostsArn: function validateOutpostsArn(req) {
  67. var parsedArn = req._parsedArn;
  68. if (
  69. parsedArn.resource.indexOf('outpost:') !== 0 &&
  70. parsedArn.resource.indexOf('outpost/') !== 0
  71. ) {
  72. throw AWS.util.error(new Error(), {
  73. code: 'InvalidARN',
  74. message: 'ARN resource should begin with \'outpost/\''
  75. });
  76. }
  77. //can be ':' or '/'
  78. var delimiter = parsedArn.resource['outpost'.length];
  79. var outpostId = parsedArn.resource.split(delimiter)[1];
  80. var dnsHostRegex = new RegExp(/^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/);
  81. if (!dnsHostRegex.test(outpostId)) {
  82. throw AWS.util.error(new Error(), {
  83. code: 'InvalidARN',
  84. message: 'Outpost resource in ARN is not DNS compatible. Got ' + outpostId
  85. });
  86. }
  87. req._parsedArn.outpostId = outpostId;
  88. },
  89. /**
  90. * Validate Outposts ARN supplied in Bucket parameter is a valid outposts ARN
  91. */
  92. validateOutpostsAccessPointArn: function validateOutpostsAccessPointArn(req) {
  93. var parsedArn = req._parsedArn;
  94. //can be ':' or '/'
  95. var delimiter = parsedArn.resource['outpost'.length];
  96. if (parsedArn.resource.split(delimiter).length !== 4) {
  97. throw AWS.util.error(new Error(), {
  98. code: 'InvalidARN',
  99. message: 'Outposts ARN should have two resources outpost/{outpostId}/accesspoint/{accesspointName}'
  100. });
  101. }
  102. var accessPoint = parsedArn.resource.split(delimiter)[3];
  103. var accessPointPrefix = accessPoint + '-' + parsedArn.accountId;
  104. if (!s3util.dnsCompatibleBucketName(accessPointPrefix) || accessPointPrefix.match(/\./)) {
  105. throw AWS.util.error(new Error(), {
  106. code: 'InvalidARN',
  107. message: 'Access point resource in ARN is not DNS compatible. Got ' + accessPoint
  108. });
  109. }
  110. //set parsed valid access point
  111. req._parsedArn.accessPoint = accessPoint;
  112. },
  113. /**
  114. * Validate region field in ARN supplied in Bucket parameter is a valid region
  115. */
  116. validateArnRegion: function validateArnRegion(req, options) {
  117. if (options === undefined) {
  118. options = {};
  119. }
  120. var useArnRegion = s3util.loadUseArnRegionConfig(req);
  121. var regionFromArn = req._parsedArn.region;
  122. var clientRegion = req.service.config.region;
  123. var useFipsEndpoint = req.service.config.useFipsEndpoint;
  124. var allowFipsEndpoint = options.allowFipsEndpoint || false;
  125. if (!regionFromArn) {
  126. var message = 'ARN region is empty';
  127. if (req._parsedArn.service === 's3') {
  128. message = message + '\nYou may want to use multi-regional ARN. The feature is not supported in current SDK. ' +
  129. 'You should consider switching to V3(https://github.com/aws/aws-sdk-js-v3).';
  130. }
  131. throw AWS.util.error(new Error(), {
  132. code: 'InvalidARN',
  133. message: message
  134. });
  135. }
  136. if (useFipsEndpoint && !allowFipsEndpoint) {
  137. throw AWS.util.error(new Error(), {
  138. code: 'InvalidConfiguration',
  139. message: 'ARN endpoint is not compatible with FIPS region'
  140. });
  141. }
  142. if (regionFromArn.indexOf('fips') >= 0) {
  143. throw AWS.util.error(new Error(), {
  144. code: 'InvalidConfiguration',
  145. message: 'FIPS region not allowed in ARN'
  146. });
  147. }
  148. if (!useArnRegion && regionFromArn !== clientRegion) {
  149. throw AWS.util.error(new Error(), {
  150. code: 'InvalidConfiguration',
  151. message: 'Configured region conflicts with access point region'
  152. });
  153. } else if (
  154. useArnRegion &&
  155. regionUtil.getEndpointSuffix(regionFromArn) !== regionUtil.getEndpointSuffix(clientRegion)
  156. ) {
  157. throw AWS.util.error(new Error(), {
  158. code: 'InvalidConfiguration',
  159. message: 'Configured region and access point region not in same partition'
  160. });
  161. }
  162. if (req.service.config.useAccelerateEndpoint) {
  163. throw AWS.util.error(new Error(), {
  164. code: 'InvalidConfiguration',
  165. message: 'useAccelerateEndpoint config is not supported with access point ARN'
  166. });
  167. }
  168. if (req._parsedArn.service === 's3-outposts' && req.service.config.useDualstackEndpoint) {
  169. throw AWS.util.error(new Error(), {
  170. code: 'InvalidConfiguration',
  171. message: 'Dualstack is not supported with outposts access point ARN'
  172. });
  173. }
  174. },
  175. loadUseArnRegionConfig: function loadUseArnRegionConfig(req) {
  176. var envName = 'AWS_S3_USE_ARN_REGION';
  177. var configName = 's3_use_arn_region';
  178. var useArnRegion = true;
  179. var originalConfig = req.service._originalConfig || {};
  180. if (req.service.config.s3UseArnRegion !== undefined) {
  181. return req.service.config.s3UseArnRegion;
  182. } else if (originalConfig.s3UseArnRegion !== undefined) {
  183. useArnRegion = originalConfig.s3UseArnRegion === true;
  184. } else if (AWS.util.isNode()) {
  185. //load from environmental variable AWS_USE_ARN_REGION
  186. if (process.env[envName]) {
  187. var value = process.env[envName].trim().toLowerCase();
  188. if (['false', 'true'].indexOf(value) < 0) {
  189. throw AWS.util.error(new Error(), {
  190. code: 'InvalidConfiguration',
  191. message: envName + ' only accepts true or false. Got ' + process.env[envName],
  192. retryable: false
  193. });
  194. }
  195. useArnRegion = value === 'true';
  196. } else { //load from shared config property use_arn_region
  197. var profiles = {};
  198. var profile = {};
  199. try {
  200. profiles = AWS.util.getProfilesFromSharedConfig(AWS.util.iniLoader);
  201. profile = profiles[process.env.AWS_PROFILE || AWS.util.defaultProfile];
  202. } catch (e) {}
  203. if (profile[configName]) {
  204. if (['false', 'true'].indexOf(profile[configName].trim().toLowerCase()) < 0) {
  205. throw AWS.util.error(new Error(), {
  206. code: 'InvalidConfiguration',
  207. message: configName + ' only accepts true or false. Got ' + profile[configName],
  208. retryable: false
  209. });
  210. }
  211. useArnRegion = profile[configName].trim().toLowerCase() === 'true';
  212. }
  213. }
  214. }
  215. req.service.config.s3UseArnRegion = useArnRegion;
  216. return useArnRegion;
  217. },
  218. /**
  219. * Validations before URI can be populated
  220. */
  221. validatePopulateUriFromArn: function validatePopulateUriFromArn(req) {
  222. if (req.service._originalConfig && req.service._originalConfig.endpoint) {
  223. throw AWS.util.error(new Error(), {
  224. code: 'InvalidConfiguration',
  225. message: 'Custom endpoint is not compatible with access point ARN'
  226. });
  227. }
  228. if (req.service.config.s3ForcePathStyle) {
  229. throw AWS.util.error(new Error(), {
  230. code: 'InvalidConfiguration',
  231. message: 'Cannot construct path-style endpoint with access point'
  232. });
  233. }
  234. },
  235. /**
  236. * Returns true if the bucket name is DNS compatible. Buckets created
  237. * outside of the classic region MUST be DNS compatible.
  238. *
  239. * @api private
  240. */
  241. dnsCompatibleBucketName: function dnsCompatibleBucketName(bucketName) {
  242. var b = bucketName;
  243. var domain = new RegExp(/^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$/);
  244. var ipAddress = new RegExp(/(\d+\.){3}\d+/);
  245. var dots = new RegExp(/\.\./);
  246. return (b.match(domain) && !b.match(ipAddress) && !b.match(dots)) ? true : false;
  247. },
  248. };
  249. /**
  250. * @api private
  251. */
  252. module.exports = s3util;