← All changes
|
includes/sdk/google/google/auth/src/CredentialSource/AwsNativeSource.php
+54
-43
1.2.7
→
1.4.2
View file →
| @@ -14,14 +14,14 @@ | ||
| 14 | 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | 15 | * See the License for the specific language governing permissions and |
| 16 | 16 | * limitations under the License. |
| 17 | 17 | */ |
| 18 | -namespace Dudlewebs\WPMCS\Google\Auth\CredentialSource; | |
| 18 | +namespace Dudlewebs\WPMCS\GCP\Google\Auth\CredentialSource; | |
| 19 | 19 | |
| 20 | -use Dudlewebs\WPMCS\Google\Auth\ExternalAccountCredentialSourceInterface; | |
| 21 | -use Dudlewebs\WPMCS\Google\Auth\HttpHandler\HttpClientCache; | |
| 22 | -use Dudlewebs\WPMCS\Google\Auth\HttpHandler\HttpHandlerFactory; | |
| 23 | -use Dudlewebs\WPMCS\GuzzleHttp\Psr7\Request; | |
| 20 | +use Dudlewebs\WPMCS\GCP\Google\Auth\ExternalAccountCredentialSourceInterface; | |
| 21 | +use Dudlewebs\WPMCS\GCP\Google\Auth\HttpHandler\HttpClientCache; | |
| 22 | +use Dudlewebs\WPMCS\GCP\Google\Auth\HttpHandler\HttpHandlerFactory; | |
| 23 | +use Dudlewebs\WPMCS\GCP\GuzzleHttp\Psr7\Request; | |
| 24 | 24 | /** |
| 25 | 25 | * Authenticates requests using AWS credentials. |
| 26 | 26 | */ |
| 27 | 27 | class AwsNativeSource implements ExternalAccountCredentialSourceInterface |
| @@ -43,9 +43,9 @@ | ||
| 43 | 43 | * key and security token needed to sign the GetCallerIdentity request. |
| 44 | 44 | * @param string|null $imdsv2SessionTokenUrl Presence of this URL enforces the auth libraries to fetch a Session |
| 45 | 45 | * Token from AWS. This field is required for EC2 instances using IMDSv2. |
| 46 | 46 | */ |
| 47 | - public function __construct(string $audience, string $regionalCredVerificationUrl, string $regionUrl = null, string $securityCredentialsUrl = null, string $imdsv2SessionTokenUrl = null) | |
| 47 | + public function __construct(string $audience, string $regionalCredVerificationUrl, ?string $regionUrl = null, ?string $securityCredentialsUrl = null, ?string $imdsv2SessionTokenUrl = null) | |
| 48 | 48 | { |
| 49 | 49 | $this->audience = $audience; |
| 50 | 50 | $this->regionalCredVerificationUrl = $regionalCredVerificationUrl; |
| 51 | 51 | $this->regionUrl = $regionUrl; |
| @@ -51,11 +51,11 @@ | ||
| 51 | 51 | $this->regionUrl = $regionUrl; |
| 52 | 52 | $this->securityCredentialsUrl = $securityCredentialsUrl; |
| 53 | 53 | $this->imdsv2SessionTokenUrl = $imdsv2SessionTokenUrl; |
| 54 | 54 | } |
| 55 | - public function fetchSubjectToken(callable $httpHandler = null): string | |
| 55 | + public function fetchSubjectToken(?callable $httpHandler = null) : string | |
| 56 | 56 | { |
| 57 | - if (is_null($httpHandler)) { | |
| 57 | + if (\is_null($httpHandler)) { | |
| 58 | 58 | $httpHandler = HttpHandlerFactory::build(HttpClientCache::getHttpClient()); |
| 59 | 59 | } |
| 60 | 60 | $headers = []; |
| 61 | 61 | if ($this->imdsv2SessionTokenUrl) { |
| @@ -60,22 +60,22 @@ | ||
| 60 | 60 | $headers = []; |
| 61 | 61 | if ($this->imdsv2SessionTokenUrl) { |
| 62 | 62 | $headers = ['X-aws-ec2-metadata-token' => self::getImdsV2SessionToken($this->imdsv2SessionTokenUrl, $httpHandler)]; |
| 63 | 63 | } |
| 64 | - if (!$signingVars = self::getSigningVarsFromEnv()) { | |
| 64 | + if (!($signingVars = self::getSigningVarsFromEnv())) { | |
| 65 | 65 | if (!$this->securityCredentialsUrl) { |
| 66 | 66 | throw new \LogicException('Unable to get credentials from ENV, and no security credentials URL provided'); |
| 67 | 67 | } |
| 68 | 68 | $signingVars = self::getSigningVarsFromUrl($httpHandler, $this->securityCredentialsUrl, self::getRoleName($httpHandler, $this->securityCredentialsUrl, $headers), $headers); |
| 69 | 69 | } |
| 70 | - if (!$region = self::getRegionFromEnv()) { | |
| 70 | + if (!($region = self::getRegionFromEnv())) { | |
| 71 | 71 | if (!$this->regionUrl) { |
| 72 | 72 | throw new \LogicException('Unable to get region from ENV, and no region URL provided'); |
| 73 | 73 | } |
| 74 | 74 | $region = self::getRegionFromUrl($httpHandler, $this->regionUrl, $headers); |
| 75 | 75 | } |
| 76 | - $url = str_replace('{region}', $region, $this->regionalCredVerificationUrl); | |
| 77 | - $host = parse_url($url)['host'] ?? ''; | |
| 76 | + $url = \str_replace('{region}', $region, $this->regionalCredVerificationUrl); | |
| 77 | + $host = \parse_url($url)['host'] ?? ''; | |
| 78 | 78 | // From here we use the signing vars to create the signed request to receive a token |
| 79 | 79 | [$accessKeyId, $secretAccessKey, $securityToken] = $signingVars; |
| 80 | 80 | $headers = self::getSignedRequestHeaders($region, $host, $accessKeyId, $secretAccessKey, $securityToken); |
| 81 | 81 | // Inject x-goog-cloud-target-resource into header |
| @@ -80,16 +80,16 @@ | ||
| 80 | 80 | $headers = self::getSignedRequestHeaders($region, $host, $accessKeyId, $secretAccessKey, $securityToken); |
| 81 | 81 | // Inject x-goog-cloud-target-resource into header |
| 82 | 82 | $headers['x-goog-cloud-target-resource'] = $this->audience; |
| 83 | 83 | // Format headers as they're expected in the subject token |
| 84 | - $formattedHeaders = array_map(fn($k, $v) => ['key' => $k, 'value' => $v], array_keys($headers), $headers); | |
| 84 | + $formattedHeaders = \array_map(fn($k, $v) => ['key' => $k, 'value' => $v], \array_keys($headers), $headers); | |
| 85 | 85 | $request = ['headers' => $formattedHeaders, 'method' => 'POST', 'url' => $url]; |
| 86 | - return urlencode(json_encode($request) ?: ''); | |
| 86 | + return \urlencode(\json_encode($request) ?: ''); | |
| 87 | 87 | } |
| 88 | 88 | /** |
| 89 | 89 | * @internal |
| 90 | 90 | */ |
| 91 | - public static function getImdsV2SessionToken(string $imdsV2Url, callable $httpHandler): string | |
| 91 | + public static function getImdsV2SessionToken(string $imdsV2Url, callable $httpHandler) : string | |
| 92 | 92 | { |
| 93 | 93 | $headers = ['X-aws-ec2-metadata-token-ttl-seconds' => '21600']; |
| 94 | 94 | $request = new Request('PUT', $imdsV2Url, $headers); |
| 95 | 95 | $response = $httpHandler($request); |
| @@ -101,21 +101,21 @@ | ||
| 101 | 101 | * @internal |
| 102 | 102 | * |
| 103 | 103 | * @return array<string, string> |
| 104 | 104 | */ |
| 105 | - public static function getSignedRequestHeaders(string $region, string $host, string $accessKeyId, string $secretAccessKey, ?string $securityToken): array | |
| 105 | + public static function getSignedRequestHeaders(string $region, string $host, string $accessKeyId, string $secretAccessKey, ?string $securityToken) : array | |
| 106 | 106 | { |
| 107 | 107 | $service = 'sts'; |
| 108 | 108 | # Create a date for headers and the credential string in ISO-8601 format |
| 109 | - $amzdate = gmdate('Ymd\THis\Z'); | |
| 110 | - $datestamp = gmdate('Ymd'); | |
| 109 | + $amzdate = \gmdate('Ymd\\THis\\Z'); | |
| 110 | + $datestamp = \gmdate('Ymd'); | |
| 111 | 111 | # Date w/o time, used in credential scope |
| 112 | 112 | # Create the canonical headers and signed headers. Header names |
| 113 | 113 | # must be trimmed and lowercase, and sorted in code point order from |
| 114 | 114 | # low to high. Note that there is a trailing \n. |
| 115 | - $canonicalHeaders = sprintf("host:%s\nx-amz-date:%s\n", $host, $amzdate); | |
| 115 | + $canonicalHeaders = \sprintf("host:%s\nx-amz-date:%s\n", $host, $amzdate); | |
| 116 | 116 | if ($securityToken) { |
| 117 | - $canonicalHeaders .= sprintf("x-amz-security-token:%s\n", $securityToken); | |
| 117 | + $canonicalHeaders .= \sprintf("x-amz-security-token:%s\n", $securityToken); | |
| 118 | 118 | } |
| 119 | 119 | # Step 5: Create the list of signed headers. This lists the headers |
| 120 | 120 | # in the canonicalHeaders list, delimited with ";" and in alpha order. |
| 121 | 121 | # Note: The request can include any headers; $canonicalHeaders and |
| @@ -126,11 +126,11 @@ | ||
| 126 | 126 | $signedHeaders .= ';x-amz-security-token'; |
| 127 | 127 | } |
| 128 | 128 | # Step 6: Create payload hash (hash of the request body content). For GET |
| 129 | 129 | # requests, the payload is an empty string (""). |
| 130 | - $payloadHash = hash('sha256', ''); | |
| 130 | + $payloadHash = \hash('sha256', ''); | |
| 131 | 131 | # Step 7: Combine elements to create canonical request |
| 132 | - $canonicalRequest = implode("\n", [ | |
| 132 | + $canonicalRequest = \implode("\n", [ | |
| 133 | 133 | 'POST', |
| 134 | 134 | // method |
| 135 | 135 | '/', |
| 136 | 136 | // canonical URL |
| @@ -143,21 +143,21 @@ | ||
| 143 | 143 | # ************* TASK 2: CREATE THE STRING TO SIGN************* |
| 144 | 144 | # Match the algorithm to the hashing algorithm you use, either SHA-1 or |
| 145 | 145 | # SHA-256 (recommended) |
| 146 | 146 | $algorithm = 'AWS4-HMAC-SHA256'; |
| 147 | - $scope = implode('/', [$datestamp, $region, $service, 'aws4_request']); | |
| 148 | - $stringToSign = implode("\n", [$algorithm, $amzdate, $scope, hash('sha256', $canonicalRequest)]); | |
| 147 | + $scope = \implode('/', [$datestamp, $region, $service, 'aws4_request']); | |
| 148 | + $stringToSign = \implode("\n", [$algorithm, $amzdate, $scope, \hash('sha256', $canonicalRequest)]); | |
| 149 | 149 | # ************* TASK 3: CALCULATE THE SIGNATURE ************* |
| 150 | 150 | # Create the signing key using the function defined above. |
| 151 | 151 | // (done above) |
| 152 | 152 | $signingKey = self::getSignatureKey($secretAccessKey, $datestamp, $region, $service); |
| 153 | 153 | # Sign the string_to_sign using the signing_key |
| 154 | - $signature = bin2hex(self::hmacSign($signingKey, $stringToSign)); | |
| 154 | + $signature = \bin2hex(self::hmacSign($signingKey, $stringToSign)); | |
| 155 | 155 | # ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST ************* |
| 156 | 156 | # The signing information can be either in a query string value or in |
| 157 | 157 | # a header named Authorization. This code shows how to use a header. |
| 158 | 158 | # Create authorization header and add to request headers |
| 159 | - $authorizationHeader = sprintf('%s Credential=%s/%s, SignedHeaders=%s, Signature=%s', $algorithm, $accessKeyId, $scope, $signedHeaders, $signature); | |
| 159 | + $authorizationHeader = \sprintf('%s Credential=%s/%s, SignedHeaders=%s, Signature=%s', $algorithm, $accessKeyId, $scope, $signedHeaders, $signature); | |
| 160 | 160 | # The request can include any headers, but MUST include "host", "x-amz-date", |
| 161 | 161 | # and (for this scenario) "Authorization". "host" and "x-amz-date" must |
| 162 | 162 | # be included in the canonical_headers and signed_headers, as noted |
| 163 | 163 | # earlier. Order here is not significant. |
| @@ -169,13 +169,13 @@ | ||
| 169 | 169 | } |
| 170 | 170 | /** |
| 171 | 171 | * @internal |
| 172 | 172 | */ |
| 173 | - public static function getRegionFromEnv(): ?string | |
| 173 | + public static function getRegionFromEnv() : ?string | |
| 174 | 174 | { |
| 175 | - $region = getenv('AWS_REGION'); | |
| 175 | + $region = \getenv('AWS_REGION'); | |
| 176 | 176 | if (empty($region)) { |
| 177 | - $region = getenv('AWS_DEFAULT_REGION'); | |
| 177 | + $region = \getenv('AWS_DEFAULT_REGION'); | |
| 178 | 178 | } |
| 179 | 179 | return $region ?: null; |
| 180 | 180 | } |
| 181 | 181 | /** |
| @@ -184,9 +184,9 @@ | ||
| 184 | 184 | * @param callable $httpHandler |
| 185 | 185 | * @param string $regionUrl |
| 186 | 186 | * @param array<string, string|string[]> $headers Request headers to send in with the request. |
| 187 | 187 | */ |
| 188 | - public static function getRegionFromUrl(callable $httpHandler, string $regionUrl, array $headers): string | |
| 188 | + public static function getRegionFromUrl(callable $httpHandler, string $regionUrl, array $headers) : string | |
| 189 | 189 | { |
| 190 | 190 | // get the region/zone from the region URL |
| 191 | 191 | $regionRequest = new Request('GET', $regionUrl, $headers); |
| 192 | 192 | $regionResponse = $httpHandler($regionRequest); |
| @@ -191,9 +191,9 @@ | ||
| 191 | 191 | $regionRequest = new Request('GET', $regionUrl, $headers); |
| 192 | 192 | $regionResponse = $httpHandler($regionRequest); |
| 193 | 193 | // Remove last character. For example, if us-east-2b is returned, |
| 194 | 194 | // the region would be us-east-2. |
| 195 | - return substr((string) $regionResponse->getBody(), 0, -1); | |
| 195 | + return \substr((string) $regionResponse->getBody(), 0, -1); | |
| 196 | 196 | } |
| 197 | 197 | /** |
| 198 | 198 | * @internal |
| 199 | 199 | * |
| @@ -200,9 +200,9 @@ | ||
| 200 | 200 | * @param callable $httpHandler |
| 201 | 201 | * @param string $securityCredentialsUrl |
| 202 | 202 | * @param array<string, string|string[]> $headers Request headers to send in with the request. |
| 203 | 203 | */ |
| 204 | - public static function getRoleName(callable $httpHandler, string $securityCredentialsUrl, array $headers): string | |
| 204 | + public static function getRoleName(callable $httpHandler, string $securityCredentialsUrl, array $headers) : string | |
| 205 | 205 | { |
| 206 | 206 | // Get the AWS role name |
| 207 | 207 | $roleRequest = new Request('GET', $securityCredentialsUrl, $headers); |
| 208 | 208 | $roleResponse = $httpHandler($roleRequest); |
| @@ -216,14 +216,14 @@ | ||
| 216 | 216 | * @param string $securityCredentialsUrl |
| 217 | 217 | * @param array<string, string|string[]> $headers Request headers to send in with the request. |
| 218 | 218 | * @return array{string, string, ?string} |
| 219 | 219 | */ |
| 220 | - public static function getSigningVarsFromUrl(callable $httpHandler, string $securityCredentialsUrl, string $roleName, array $headers): array | |
| 220 | + public static function getSigningVarsFromUrl(callable $httpHandler, string $securityCredentialsUrl, string $roleName, array $headers) : array | |
| 221 | 221 | { |
| 222 | 222 | // Get the AWS credentials |
| 223 | 223 | $credsRequest = new Request('GET', $securityCredentialsUrl . '/' . $roleName, $headers); |
| 224 | 224 | $credsResponse = $httpHandler($credsRequest); |
| 225 | - $awsCreds = json_decode((string) $credsResponse->getBody(), \true); | |
| 225 | + $awsCreds = \json_decode((string) $credsResponse->getBody(), \true); | |
| 226 | 226 | return [ |
| 227 | 227 | $awsCreds['AccessKeyId'], |
| 228 | 228 | // accessKeyId |
| 229 | 229 | $awsCreds['SecretAccessKey'], |
| @@ -235,32 +235,43 @@ | ||
| 235 | 235 | * @internal |
| 236 | 236 | * |
| 237 | 237 | * @return array{string, string, ?string} |
| 238 | 238 | */ |
| 239 | - public static function getSigningVarsFromEnv(): ?array | |
| 239 | + public static function getSigningVarsFromEnv() : ?array | |
| 240 | 240 | { |
| 241 | - $accessKeyId = getenv('AWS_ACCESS_KEY_ID'); | |
| 242 | - $secretAccessKey = getenv('AWS_SECRET_ACCESS_KEY'); | |
| 241 | + $accessKeyId = \getenv('AWS_ACCESS_KEY_ID'); | |
| 242 | + $secretAccessKey = \getenv('AWS_SECRET_ACCESS_KEY'); | |
| 243 | 243 | if ($accessKeyId && $secretAccessKey) { |
| 244 | - return [$accessKeyId, $secretAccessKey, getenv('AWS_SESSION_TOKEN') ?: null]; | |
| 244 | + return [$accessKeyId, $secretAccessKey, \getenv('AWS_SESSION_TOKEN') ?: null]; | |
| 245 | 245 | } |
| 246 | 246 | return null; |
| 247 | 247 | } |
| 248 | 248 | /** |
| 249 | + * Gets the unique key for caching | |
| 250 | + * For AwsNativeSource the values are: | |
| 251 | + * Imdsv2SessionTokenUrl.SecurityCredentialsUrl.RegionUrl.RegionalCredVerificationUrl | |
| 252 | + * | |
| 253 | + * @return string | |
| 254 | + */ | |
| 255 | + public function getCacheKey() : string | |
| 256 | + { | |
| 257 | + return ($this->imdsv2SessionTokenUrl ?? '') . '.' . ($this->securityCredentialsUrl ?? '') . '.' . $this->regionUrl . '.' . $this->regionalCredVerificationUrl; | |
| 258 | + } | |
| 259 | + /** | |
| 249 | 260 | * Return HMAC hash in binary string |
| 250 | 261 | */ |
| 251 | - private static function hmacSign(string $key, string $msg): string | |
| 262 | + private static function hmacSign(string $key, string $msg) : string | |
| 252 | 263 | { |
| 253 | - return hash_hmac('sha256', self::utf8Encode($msg), $key, \true); | |
| 264 | + return \hash_hmac('sha256', self::utf8Encode($msg), $key, \true); | |
| 254 | 265 | } |
| 255 | 266 | /** |
| 256 | 267 | * @TODO add a fallback when mbstring is not available |
| 257 | 268 | */ |
| 258 | - private static function utf8Encode(string $string): string | |
| 269 | + private static function utf8Encode(string $string) : string | |
| 259 | 270 | { |
| 260 | - return mb_convert_encoding($string, 'UTF-8', 'ISO-8859-1'); | |
| 271 | + return (string) \mb_convert_encoding($string, 'UTF-8', 'ISO-8859-1'); | |
| 261 | 272 | } |
| 262 | - private static function getSignatureKey(string $key, string $dateStamp, string $regionName, string $serviceName): string | |
| 273 | + private static function getSignatureKey(string $key, string $dateStamp, string $regionName, string $serviceName) : string | |
| 263 | 274 | { |
| 264 | 275 | $kDate = self::hmacSign(self::utf8Encode('AWS4' . $key), $dateStamp); |
| 265 | 276 | $kRegion = self::hmacSign($kDate, $regionName); |
| 266 | 277 | $kService = self::hmacSign($kRegion, $serviceName); |