diff --git a/lib/Github/Exception/SsoRequiredException.php b/lib/Github/Exception/SsoRequiredException.php new file mode 100644 index 00000000000..6a47eea7738 --- /dev/null +++ b/lib/Github/Exception/SsoRequiredException.php @@ -0,0 +1,32 @@ +url = $url; + + parent::__construct('Resource protected by organization SAML enforcement. You must grant your personal token access to this organization.', $code, $previous); + } + + /** + * @return string + */ + public function getUrl() + { + return $this->url; + } +} diff --git a/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php b/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php index 60382074a40..b16ec620308 100644 --- a/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php +++ b/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php @@ -5,6 +5,7 @@ use Github\Exception\ApiLimitExceedException; use Github\Exception\ErrorException; use Github\Exception\RuntimeException; +use Github\Exception\SsoRequiredException; use Github\Exception\TwoFactorAuthenticationRequiredException; use Github\Exception\ValidationFailedException; use Github\HttpClient\Message\ResponseMediator; @@ -103,6 +104,16 @@ public function doHandleRequest(RequestInterface $request, callable $next, calla throw new RuntimeException(implode(', ', $errors), 502); } + if ((403 === $response->getStatusCode()) && $response->hasHeader('X-GitHub-SSO') && 0 === strpos((string) ResponseMediator::getHeader($response, 'X-GitHub-SSO'), 'required;')) { + // The header will look something like this: + // required; url=https://github.com/orgs/octodocs-test/sso?authorization_request=AZSCKtL4U8yX1H3sCQIVnVgmjmon5fWxks5YrqhJgah0b2tlbl9pZM4EuMz4 + // So we strip out the first 14 characters, leaving only the URL. + // @see https://developer.github.com/v3/auth/#authenticating-for-saml-sso + $url = substr((string) ResponseMediator::getHeader($response, 'X-GitHub-SSO'), 14); + + throw new SsoRequiredException($url); + } + throw new RuntimeException(isset($content['message']) ? $content['message'] : $content, $response->getStatusCode()); }); } diff --git a/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php b/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php index 32be44ce1d5..7011e1fe6bb 100644 --- a/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php +++ b/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php @@ -136,6 +136,16 @@ public static function responseProvider() ), 'exception' => new \Github\Exception\RuntimeException('Something went wrong with executing your query', 502), ], + 'Sso required Response' => [ + 'response' => new Response( + 403, + [ + 'Content-Type' => 'application/json', + 'X-GitHub-SSO' => 'required; url=https://github.com/orgs/octodocs-test/sso?authorization_request=AZSCKtL4U8yX1H3sCQIVnVgmjmon5fWxks5YrqhJgah0b2tlbl9pZM4EuMz4', + ] + ), + 'exception' => new \Github\Exception\SsoRequiredException('https://github.com/orgs/octodocs-test/sso?authorization_request=AZSCKtL4U8yX1H3sCQIVnVgmjmon5fWxks5YrqhJgah0b2tlbl9pZM4EuMz4'), + ], 'Default handling' => [ 'response' => new Response( 555,