Partition.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <?php
  2. namespace Aws\Endpoint;
  3. use ArrayAccess;
  4. use Aws\HasDataTrait;
  5. use Aws\Sts\RegionalEndpoints\ConfigurationProvider;
  6. use Aws\S3\RegionalEndpoint\ConfigurationProvider as S3ConfigurationProvider;
  7. use InvalidArgumentException as Iae;
  8. /**
  9. * Default implementation of an AWS partition.
  10. */
  11. final class Partition implements ArrayAccess, PartitionInterface
  12. {
  13. use HasDataTrait;
  14. private $stsLegacyGlobalRegions = [
  15. 'ap-northeast-1',
  16. 'ap-south-1',
  17. 'ap-southeast-1',
  18. 'ap-southeast-2',
  19. 'aws-global',
  20. 'ca-central-1',
  21. 'eu-central-1',
  22. 'eu-north-1',
  23. 'eu-west-1',
  24. 'eu-west-2',
  25. 'eu-west-3',
  26. 'sa-east-1',
  27. 'us-east-1',
  28. 'us-east-2',
  29. 'us-west-1',
  30. 'us-west-2',
  31. ];
  32. /**
  33. * The partition constructor accepts the following options:
  34. *
  35. * - `partition`: (string, required) The partition name as specified in an
  36. * ARN (e.g., `aws`)
  37. * - `partitionName`: (string) The human readable name of the partition
  38. * (e.g., "AWS Standard")
  39. * - `dnsSuffix`: (string, required) The DNS suffix of the partition. This
  40. * value is used to determine how endpoints in the partition are resolved.
  41. * - `regionRegex`: (string) A PCRE regular expression that specifies the
  42. * pattern that region names in the endpoint adhere to.
  43. * - `regions`: (array, required) A map of the regions in the partition.
  44. * Each key is the region as present in a hostname (e.g., `us-east-1`),
  45. * and each value is a structure containing region information.
  46. * - `defaults`: (array) A map of default key value pairs to apply to each
  47. * endpoint of the partition. Any value in an `endpoint` definition will
  48. * supersede any values specified in `defaults`.
  49. * - `services`: (array, required) A map of service endpoint prefix name
  50. * (the value found in a hostname) to information about the service.
  51. *
  52. * @param array $definition
  53. *
  54. * @throws Iae if any required options are missing
  55. */
  56. public function __construct(array $definition)
  57. {
  58. foreach (['partition', 'regions', 'services', 'dnsSuffix'] as $key) {
  59. if (!isset($definition[$key])) {
  60. throw new Iae("Partition missing required $key field");
  61. }
  62. }
  63. $this->data = $definition;
  64. }
  65. public function getName()
  66. {
  67. return $this->data['partition'];
  68. }
  69. /**
  70. * @internal
  71. * @return mixed
  72. */
  73. public function getDnsSuffix()
  74. {
  75. return $this->data['dnsSuffix'];
  76. }
  77. public function isRegionMatch($region, $service)
  78. {
  79. if (isset($this->data['regions'][$region])
  80. || isset($this->data['services'][$service]['endpoints'][$region])
  81. ) {
  82. return true;
  83. }
  84. if (isset($this->data['regionRegex'])) {
  85. return (bool) preg_match(
  86. "@{$this->data['regionRegex']}@",
  87. $region
  88. );
  89. }
  90. return false;
  91. }
  92. public function getAvailableEndpoints(
  93. $service,
  94. $allowNonRegionalEndpoints = false
  95. ) {
  96. if ($this->isServicePartitionGlobal($service)) {
  97. return [$this->getPartitionEndpoint($service)];
  98. }
  99. if (isset($this->data['services'][$service]['endpoints'])) {
  100. $serviceRegions = array_keys(
  101. $this->data['services'][$service]['endpoints']
  102. );
  103. return $allowNonRegionalEndpoints
  104. ? $serviceRegions
  105. : array_intersect($serviceRegions, array_keys(
  106. $this->data['regions']
  107. ));
  108. }
  109. return [];
  110. }
  111. public function __invoke(array $args = [])
  112. {
  113. $service = isset($args['service']) ? $args['service'] : '';
  114. $region = isset($args['region']) ? $args['region'] : '';
  115. $scheme = isset($args['scheme']) ? $args['scheme'] : 'https';
  116. $options = isset($args['options']) ? $args['options'] : [];
  117. $data = $this->getEndpointData($service, $region, $options);
  118. return [
  119. 'endpoint' => "{$scheme}://" . $this->formatEndpoint(
  120. isset($data['hostname']) ? $data['hostname'] : '',
  121. $service,
  122. $region
  123. ),
  124. 'signatureVersion' => $this->getSignatureVersion($data),
  125. 'signingRegion' => isset($data['credentialScope']['region'])
  126. ? $data['credentialScope']['region']
  127. : $region,
  128. 'signingName' => isset($data['credentialScope']['service'])
  129. ? $data['credentialScope']['service']
  130. : $service,
  131. ];
  132. }
  133. private function getEndpointData($service, $region, $options)
  134. {
  135. $resolved = $this->resolveRegion($service, $region, $options);
  136. $data = isset($this->data['services'][$service]['endpoints'][$resolved])
  137. ? $this->data['services'][$service]['endpoints'][$resolved]
  138. : [];
  139. $data += isset($this->data['services'][$service]['defaults'])
  140. ? $this->data['services'][$service]['defaults']
  141. : [];
  142. $data += isset($this->data['defaults'])
  143. ? $this->data['defaults']
  144. : [];
  145. return $data;
  146. }
  147. private function getSignatureVersion(array $data)
  148. {
  149. static $supportedBySdk = [
  150. 's3v4',
  151. 'v4',
  152. 'anonymous',
  153. ];
  154. $possibilities = array_intersect(
  155. $supportedBySdk,
  156. isset($data['signatureVersions'])
  157. ? $data['signatureVersions']
  158. : ['v4']
  159. );
  160. return array_shift($possibilities);
  161. }
  162. private function resolveRegion($service, $region, $options)
  163. {
  164. if ($this->isServicePartitionGlobal($service)
  165. || $this->isStsLegacyEndpointUsed($service, $region, $options)
  166. || $this->isS3LegacyEndpointUsed($service, $region, $options)
  167. ) {
  168. return $this->getPartitionEndpoint($service);
  169. }
  170. return $region;
  171. }
  172. private function isServicePartitionGlobal($service)
  173. {
  174. return isset($this->data['services'][$service]['isRegionalized'])
  175. && false === $this->data['services'][$service]['isRegionalized']
  176. && isset($this->data['services'][$service]['partitionEndpoint']);
  177. }
  178. /**
  179. * STS legacy endpoints used for valid regions unless option is explicitly
  180. * set to 'regional'
  181. *
  182. * @param string $service
  183. * @param string $region
  184. * @param array $options
  185. * @return bool
  186. */
  187. private function isStsLegacyEndpointUsed($service, $region, $options)
  188. {
  189. return $service === 'sts'
  190. && in_array($region, $this->stsLegacyGlobalRegions)
  191. && (empty($options['sts_regional_endpoints'])
  192. || ConfigurationProvider::unwrap(
  193. $options['sts_regional_endpoints']
  194. )->getEndpointsType() !== 'regional'
  195. );
  196. }
  197. /**
  198. * S3 legacy us-east-1 endpoint used for valid regions unless option is explicitly
  199. * set to 'regional'
  200. *
  201. * @param string $service
  202. * @param string $region
  203. * @param array $options
  204. * @return bool
  205. */
  206. private function isS3LegacyEndpointUsed($service, $region, $options)
  207. {
  208. return $service === 's3'
  209. && $region === 'us-east-1'
  210. && (empty($options['s3_us_east_1_regional_endpoint'])
  211. || S3ConfigurationProvider::unwrap(
  212. $options['s3_us_east_1_regional_endpoint']
  213. )->getEndpointsType() !== 'regional'
  214. );
  215. }
  216. private function getPartitionEndpoint($service)
  217. {
  218. return $this->data['services'][$service]['partitionEndpoint'];
  219. }
  220. private function formatEndpoint($template, $service, $region)
  221. {
  222. return strtr($template, [
  223. '{service}' => $service,
  224. '{region}' => $region,
  225. '{dnsSuffix}' => $this->data['dnsSuffix'],
  226. ]);
  227. }
  228. }