← All changes
|
includes/sdk/google/google/auth/src/CredentialsLoader.php
+71
-43
1.2.12
→
1.4.1
View file →
| @@ -14,15 +14,15 @@ | ||
| 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; | |
| 18 | +namespace Dudlewebs\WPMCS\GCP\Google\Auth; | |
| 19 | 19 | |
| 20 | -use Dudlewebs\WPMCS\Google\Auth\Credentials\ExternalAccountCredentials; | |
| 21 | -use Dudlewebs\WPMCS\Google\Auth\Credentials\ImpersonatedServiceAccountCredentials; | |
| 22 | -use Dudlewebs\WPMCS\Google\Auth\Credentials\InsecureCredentials; | |
| 23 | -use Dudlewebs\WPMCS\Google\Auth\Credentials\ServiceAccountCredentials; | |
| 24 | -use Dudlewebs\WPMCS\Google\Auth\Credentials\UserRefreshCredentials; | |
| 20 | +use Dudlewebs\WPMCS\GCP\Google\Auth\Credentials\ExternalAccountCredentials; | |
| 21 | +use Dudlewebs\WPMCS\GCP\Google\Auth\Credentials\ImpersonatedServiceAccountCredentials; | |
| 22 | +use Dudlewebs\WPMCS\GCP\Google\Auth\Credentials\InsecureCredentials; | |
| 23 | +use Dudlewebs\WPMCS\GCP\Google\Auth\Credentials\ServiceAccountCredentials; | |
| 24 | +use Dudlewebs\WPMCS\GCP\Google\Auth\Credentials\UserRefreshCredentials; | |
| 25 | 25 | use RuntimeException; |
| 26 | 26 | use UnexpectedValueException; |
| 27 | 27 | /** |
| 28 | 28 | * CredentialsLoader contains the behaviour used to locate and find default |
| @@ -53,9 +53,9 @@ | ||
| 53 | 53 | * @return bool |
| 54 | 54 | */ |
| 55 | 55 | private static function isOnWindows() |
| 56 | 56 | { |
| 57 | - return strtoupper(substr(\PHP_OS, 0, 3)) === 'WIN'; | |
| 57 | + return \strtoupper(\substr(\PHP_OS, 0, 3)) === 'WIN'; | |
| 58 | 58 | } |
| 59 | 59 | /** |
| 60 | 60 | * Load a JSON key from the path specified in the environment. |
| 61 | 61 | * |
| @@ -66,18 +66,18 @@ | ||
| 66 | 66 | * @return array<mixed>|null JSON key | null |
| 67 | 67 | */ |
| 68 | 68 | public static function fromEnv() |
| 69 | 69 | { |
| 70 | - $path = getenv(self::ENV_VAR); | |
| 70 | + $path = self::getEnv(self::ENV_VAR); | |
| 71 | 71 | if (empty($path)) { |
| 72 | 72 | return null; |
| 73 | 73 | } |
| 74 | - if (!file_exists($path)) { | |
| 74 | + if (!\file_exists($path)) { | |
| 75 | 75 | $cause = 'file ' . $path . ' does not exist'; |
| 76 | 76 | throw new \DomainException(self::unableToReadEnv($cause)); |
| 77 | 77 | } |
| 78 | - $jsonKey = file_get_contents($path); | |
| 79 | - return json_decode((string) $jsonKey, \true); | |
| 78 | + $jsonKey = \file_get_contents($path); | |
| 79 | + return \json_decode((string) $jsonKey, \true); | |
| 80 | 80 | } |
| 81 | 81 | /** |
| 82 | 82 | * Load a JSON key from a well known path. |
| 83 | 83 | * |
| @@ -92,35 +92,60 @@ | ||
| 92 | 92 | */ |
| 93 | 93 | public static function fromWellKnownFile() |
| 94 | 94 | { |
| 95 | 95 | $rootEnv = self::isOnWindows() ? 'APPDATA' : 'HOME'; |
| 96 | - $path = [getenv($rootEnv)]; | |
| 96 | + $path = [self::getEnv($rootEnv)]; | |
| 97 | 97 | if (!self::isOnWindows()) { |
| 98 | 98 | $path[] = self::NON_WINDOWS_WELL_KNOWN_PATH_BASE; |
| 99 | 99 | } |
| 100 | 100 | $path[] = self::WELL_KNOWN_PATH; |
| 101 | - $path = implode(\DIRECTORY_SEPARATOR, $path); | |
| 102 | - if (!file_exists($path)) { | |
| 101 | + $path = \implode(\DIRECTORY_SEPARATOR, $path); | |
| 102 | + if (!\file_exists($path)) { | |
| 103 | 103 | return null; |
| 104 | 104 | } |
| 105 | - $jsonKey = file_get_contents($path); | |
| 106 | - return json_decode((string) $jsonKey, \true); | |
| 105 | + $jsonKey = \file_get_contents($path); | |
| 106 | + return \json_decode((string) $jsonKey, \true); | |
| 107 | 107 | } |
| 108 | 108 | /** |
| 109 | 109 | * Create a new Credentials instance. |
| 110 | 110 | * |
| 111 | - * @param string|string[] $scope the scope of the access request, expressed | |
| 112 | - * either as an Array or as a space-delimited String. | |
| 113 | - * @param array<mixed> $jsonKey the JSON credentials. | |
| 114 | - * @param string|string[] $defaultScope The default scope to use if no | |
| 115 | - * user-defined scopes exist, expressed either as an Array or as a | |
| 116 | - * space-delimited string. | |
| 111 | + * @deprecated This method is being deprecated because of a potential security risk. | |
| 117 | 112 | * |
| 113 | + * This method does not validate the credential configuration. The security | |
| 114 | + * risk occurs when a credential configuration is accepted from a source | |
| 115 | + * that is not under your control and used without validation on your side. | |
| 116 | + * | |
| 117 | + * If you know that you will be loading credential configurations of a | |
| 118 | + * specific type, it is recommended to use a credential-type-specific | |
| 119 | + * method. | |
| 120 | + * This will ensure that an unexpected credential type with potential for | |
| 121 | + * malicious intent is not loaded unintentionally. You might still have to do | |
| 122 | + * validation for certain credential types. Please follow the recommendation | |
| 123 | + * for that method. For example, if you want to load only service accounts, | |
| 124 | + * you can create the {@see ServiceAccountCredentials} explicitly: | |
| 125 | + * | |
| 126 | + * ``` | |
| 127 | + * use Google\Auth\Credentials\ServiceAccountCredentials; | |
| 128 | + * $creds = new ServiceAccountCredentials($scopes, $json); | |
| 129 | + * ``` | |
| 130 | + * | |
| 131 | + * If you are loading your credential configuration from an untrusted source and have | |
| 132 | + * not mitigated the risks (e.g. by validating the configuration yourself), make | |
| 133 | + * these changes as soon as possible to prevent security risks to your environment. | |
| 134 | + * | |
| 135 | + * Regardless of the method used, it is always your responsibility to validate | |
| 136 | + * configurations received from external sources. | |
| 137 | + * | |
| 138 | + * @see https://cloud.google.com/docs/authentication/external/externally-sourced-credentials | |
| 139 | + * | |
| 140 | + * @param string|string[] $scope | |
| 141 | + * @param array<mixed> $jsonKey | |
| 142 | + * @param string|string[] $defaultScope | |
| 118 | 143 | * @return ServiceAccountCredentials|UserRefreshCredentials|ImpersonatedServiceAccountCredentials|ExternalAccountCredentials |
| 119 | 144 | */ |
| 120 | 145 | public static function makeCredentials($scope, array $jsonKey, $defaultScope = null) |
| 121 | 146 | { |
| 122 | - if (!array_key_exists('type', $jsonKey)) { | |
| 147 | + if (!\array_key_exists('type', $jsonKey)) { | |
| 123 | 148 | throw new \InvalidArgumentException('json key is missing the type field'); |
| 124 | 149 | } |
| 125 | 150 | if ($jsonKey['type'] == 'service_account') { |
| 126 | 151 | // Do not pass $defaultScope to ServiceAccountCredentials |
| @@ -130,10 +155,9 @@ | ||
| 130 | 155 | $anyScope = $scope ?: $defaultScope; |
| 131 | 156 | return new UserRefreshCredentials($anyScope, $jsonKey); |
| 132 | 157 | } |
| 133 | 158 | if ($jsonKey['type'] == 'impersonated_service_account') { |
| 134 | - $anyScope = $scope ?: $defaultScope; | |
| 135 | - return new ImpersonatedServiceAccountCredentials($anyScope, $jsonKey); | |
| 159 | + return new ImpersonatedServiceAccountCredentials($scope, $jsonKey, null, $defaultScope); | |
| 136 | 160 | } |
| 137 | 161 | if ($jsonKey['type'] == 'external_account') { |
| 138 | 162 | $anyScope = $scope ?: $defaultScope; |
| 139 | 163 | return new ExternalAccountCredentials($anyScope, $jsonKey); |
| @@ -144,18 +168,18 @@ | ||
| 144 | 168 | * Create an authorized HTTP Client from an instance of FetchAuthTokenInterface. |
| 145 | 169 | * |
| 146 | 170 | * @param FetchAuthTokenInterface $fetcher is used to fetch the auth token |
| 147 | 171 | * @param array<mixed> $httpClientOptions (optional) Array of request options to apply. |
| 148 | - * @param callable $httpHandler (optional) http client to fetch the token. | |
| 149 | - * @param callable $tokenCallback (optional) function to be called when a new token is fetched. | |
| 172 | + * @param callable|null $httpHandler (optional) http client to fetch the token. | |
| 173 | + * @param callable|null $tokenCallback (optional) function to be called when a new token is fetched. | |
| 150 | 174 | * @return \GuzzleHttp\Client |
| 151 | 175 | */ |
| 152 | - public static function makeHttpClient(FetchAuthTokenInterface $fetcher, array $httpClientOptions = [], callable $httpHandler = null, callable $tokenCallback = null) | |
| 176 | + public static function makeHttpClient(FetchAuthTokenInterface $fetcher, array $httpClientOptions = [], ?callable $httpHandler = null, ?callable $tokenCallback = null) | |
| 153 | 177 | { |
| 154 | 178 | $middleware = new Middleware\AuthTokenMiddleware($fetcher, $httpHandler, $tokenCallback); |
| 155 | - $stack = \Dudlewebs\WPMCS\GuzzleHttp\HandlerStack::create(); | |
| 179 | + $stack = \Dudlewebs\WPMCS\GCP\GuzzleHttp\HandlerStack::create(); | |
| 156 | 180 | $stack->push($middleware); |
| 157 | - return new \Dudlewebs\WPMCS\GuzzleHttp\Client(['handler' => $stack, 'auth' => 'google_auth'] + $httpClientOptions); | |
| 181 | + return new \Dudlewebs\WPMCS\GCP\GuzzleHttp\Client(['handler' => $stack, 'auth' => 'google_auth'] + $httpClientOptions); | |
| 158 | 182 | } |
| 159 | 183 | /** |
| 160 | 184 | * Create a new instance of InsecureCredentials. |
| 161 | 185 | * |
| @@ -173,9 +197,9 @@ | ||
| 173 | 197 | * @return string|null |
| 174 | 198 | */ |
| 175 | 199 | public static function quotaProjectFromEnv() |
| 176 | 200 | { |
| 177 | - return getenv(self::QUOTA_PROJECT_ENV_VAR) ?: null; | |
| 201 | + return self::getEnv(self::QUOTA_PROJECT_ENV_VAR) ?: null; | |
| 178 | 202 | } |
| 179 | 203 | /** |
| 180 | 204 | * Gets a callable which returns the default device certification. |
| 181 | 205 | * |
| @@ -183,17 +207,17 @@ | ||
| 183 | 207 | * @return callable|null |
| 184 | 208 | */ |
| 185 | 209 | public static function getDefaultClientCertSource() |
| 186 | 210 | { |
| 187 | - if (!$clientCertSourceJson = self::loadDefaultClientCertSourceFile()) { | |
| 211 | + if (!($clientCertSourceJson = self::loadDefaultClientCertSourceFile())) { | |
| 188 | 212 | return null; |
| 189 | 213 | } |
| 190 | 214 | $clientCertSourceCmd = $clientCertSourceJson['cert_provider_command']; |
| 191 | - return function () use ($clientCertSourceCmd) { | |
| 192 | - $cmd = array_map('escapeshellarg', $clientCertSourceCmd); | |
| 193 | - exec(implode(' ', $cmd), $output, $returnVar); | |
| 215 | + return function () use($clientCertSourceCmd) { | |
| 216 | + $cmd = \array_map('escapeshellarg', $clientCertSourceCmd); | |
| 217 | + \exec(\implode(' ', $cmd), $output, $returnVar); | |
| 194 | 218 | if (0 === $returnVar) { |
| 195 | - return implode(\PHP_EOL, $output); | |
| 219 | + return \implode(\PHP_EOL, $output); | |
| 196 | 220 | } |
| 197 | 221 | throw new RuntimeException('"cert_provider_command" failed with a nonzero exit code'); |
| 198 | 222 | }; |
| 199 | 223 | } |
| @@ -203,9 +227,9 @@ | ||
| 203 | 227 | * @return bool |
| 204 | 228 | */ |
| 205 | 229 | public static function shouldLoadClientCertSource() |
| 206 | 230 | { |
| 207 | - return filter_var(getenv(self::MTLS_CERT_ENV_VAR), \FILTER_VALIDATE_BOOLEAN); | |
| 231 | + return \filter_var(self::getEnv(self::MTLS_CERT_ENV_VAR), \FILTER_VALIDATE_BOOLEAN); | |
| 208 | 232 | } |
| 209 | 233 | /** |
| 210 | 234 | * @return array{cert_provider_command:string[]}|null |
| 211 | 235 | */ |
| @@ -211,14 +235,14 @@ | ||
| 211 | 235 | */ |
| 212 | 236 | private static function loadDefaultClientCertSourceFile() |
| 213 | 237 | { |
| 214 | 238 | $rootEnv = self::isOnWindows() ? 'APPDATA' : 'HOME'; |
| 215 | - $path = sprintf('%s/%s', getenv($rootEnv), self::MTLS_WELL_KNOWN_PATH); | |
| 216 | - if (!file_exists($path)) { | |
| 239 | + $path = \sprintf('%s/%s', self::getEnv($rootEnv), self::MTLS_WELL_KNOWN_PATH); | |
| 240 | + if (!\file_exists($path)) { | |
| 217 | 241 | return null; |
| 218 | 242 | } |
| 219 | - $jsonKey = file_get_contents($path); | |
| 220 | - $clientCertSourceJson = json_decode((string) $jsonKey, \true); | |
| 243 | + $jsonKey = \file_get_contents($path); | |
| 244 | + $clientCertSourceJson = \json_decode((string) $jsonKey, \true); | |
| 221 | 245 | if (!$clientCertSourceJson) { |
| 222 | 246 | throw new UnexpectedValueException('Invalid client cert source JSON'); |
| 223 | 247 | } |
| 224 | 248 | if (!isset($clientCertSourceJson['cert_provider_command'])) { |
| @@ -223,9 +247,9 @@ | ||
| 223 | 247 | } |
| 224 | 248 | if (!isset($clientCertSourceJson['cert_provider_command'])) { |
| 225 | 249 | throw new UnexpectedValueException('cert source requires "cert_provider_command"'); |
| 226 | 250 | } |
| 227 | - if (!is_array($clientCertSourceJson['cert_provider_command'])) { | |
| 251 | + if (!\is_array($clientCertSourceJson['cert_provider_command'])) { | |
| 228 | 252 | throw new UnexpectedValueException('cert source expects "cert_provider_command" to be an array'); |
| 229 | 253 | } |
| 230 | 254 | return $clientCertSourceJson; |
| 231 | 255 | } |
| @@ -234,9 +258,13 @@ | ||
| 234 | 258 | * for all credential types which do not support universe domain. |
| 235 | 259 | * |
| 236 | 260 | * @return string |
| 237 | 261 | */ |
| 238 | - public function getUniverseDomain(): string | |
| 262 | + public function getUniverseDomain() : string | |
| 239 | 263 | { |
| 240 | 264 | return self::DEFAULT_UNIVERSE_DOMAIN; |
| 265 | + } | |
| 266 | + private static function getEnv(string $env) : mixed | |
| 267 | + { | |
| 268 | + return \getenv($env) ?: $_ENV[$env] ?? null; | |
| 241 | 269 | } |
| 242 | 270 | } |