diff --git a/src/Provider/Slack.php b/src/Provider/Slack.php index bb65fcb..79186e6 100644 --- a/src/Provider/Slack.php +++ b/src/Provider/Slack.php @@ -46,7 +46,25 @@ public function getBaseAccessTokenUrl(array $params) */ public function getResourceOwnerDetailsUrl(AccessToken $token) { - return "https://slack.com/api/team.info?token=".$token; + $authorizedUser = $this->getAuthorizedUser($token); + + $url = sprintf( + 'https://slack.com/api/users.info?token=%s&user=%s', + $token, + $authorizedUser->getId() + ); + + return $url; + } + + /** + * @param $token + * + * @return string + */ + public function getAuthorizedUserTestUrl($token) + { + return "https://slack.com/api/auth.test?token=".$token; } /** @@ -61,10 +79,7 @@ public function getResourceOwnerDetailsUrl(AccessToken $token) */ protected function checkResponse(ResponseInterface $response, $data) { - if (isset($data['ok']) && $data['ok'] == false) { - $error = isset($error['error']) ? $error['error']: 'Unknown error'; - throw new IdentityProviderException($error, 400, $data); - } + } /** @@ -73,11 +88,11 @@ protected function checkResponse(ResponseInterface $response, $data) * @param array $response * @param AccessToken $token * - * @return SlackTeamResourceOwner + * @return SlackResourceOwner */ protected function createResourceOwner(array $response, AccessToken $token) { - return new SlackTeamResourceOwner($response, null); + return new SlackResourceOwner($response); } /** @@ -87,4 +102,40 @@ protected function getDefaultScopes() { return []; } + + /** + * @param AccessToken $token + * + * @return mixed + */ + public function fetchAuthorizedUserDetails(AccessToken $token) + { + $url = $this->getAuthorizedUserTestUrl($token); + + $request = $this->getAuthenticatedRequest(self::METHOD_GET, $url, $token); + + return $this->getResponse($request); + } + + /** + * @param AccessToken $token + * + * @return SlackAuthorizedUser + */ + public function getAuthorizedUser(AccessToken $token) + { + $response = $this->fetchAuthorizedUserDetails($token); + + return $this->createAuthorizedUser($response); + } + + /** + * @param $response + * + * @return SlackAuthorizedUser + */ + protected function createAuthorizedUser($response) + { + return new SlackAuthorizedUser($response); + } } diff --git a/src/Provider/SlackAuthorizedUser.php b/src/Provider/SlackAuthorizedUser.php new file mode 100644 index 0000000..7a56e3f --- /dev/null +++ b/src/Provider/SlackAuthorizedUser.php @@ -0,0 +1,66 @@ +response = $response; + } + + /** + * Returns the identifier of the authorized resource owner. + * + * @return mixed + */ + public function getId() + { + return $this->response['user_id']; + } + + /** + * Return all of the owner details available as an array. + * + * @return array + */ + public function toArray() + { + return $this->response; + } + + public function getUrl() + { + return $this->response['url'] ?: null; + } + + public function getTeam() + { + return $this->response['team'] ?: null; + } + + public function getUser() + { + return $this->response['user'] ?: null; + } + + public function getTeamId() + { + return $this->response['team_id'] ?: null; + } + + public function getUserId() + { + return $this->response['user_id'] ?: null; + } +} diff --git a/src/Provider/SlackResourceOwner.php b/src/Provider/SlackResourceOwner.php new file mode 100644 index 0000000..7611c37 --- /dev/null +++ b/src/Provider/SlackResourceOwner.php @@ -0,0 +1,133 @@ + + * + * @package AdamPaterson\OAuth2\Client\Provider + */ +class SlackResourceOwner implements ResourceOwnerInterface +{ + + protected $response; + + public function __construct(array $response) + { + $this->response = $response; + } + + /** + * Return all of the owner details available as an array. + * + * @return array + */ + public function toArray() + { + return $this->response; + } + + public function getId() + { + return $this->response['user']['id'] ?: null; + } + + + public function getName() + { + return $this->response['user']['name'] ?: null; + } + + public function isDeleted() + { + return $this->response['user']['deleted'] ?: null; + } + + public function getColor() + { + return $this->response['user']['color'] ?: null; + } + + public function getProfile() + { + return $this->response['user']['profile'] ?: null; + } + + public function getFirstName() + { + return $this->response['user']['profile']['first_name'] ?: null; + } + + public function getLastName() + { + return $this->response['user']['profile']['last_name'] ?: null; + } + + public function getRealName() + { + return $this->response['user']['profile']['real_name'] ?: null; + } + + public function getEmail() + { + return $this->response['user']['profile']['email'] ?: null; + } + + public function getSkype() + { + return $this->response['user']['profile']['skype'] ?: null; + } + + public function getPhone() + { + return $this->response['user']['profile']['phone'] ?: null; + } + + public function getImage24() + { + return $this->response['user']['profile']['image_24'] ?: null; + } + + public function getImage32() + { + return $this->response['user']['profile']['image_32'] ?: null; + } + + public function getImage48() + { + return $this->response['user']['profile']['image_48'] ?: null; + } + + public function getImage72() + { + return $this->response['user']['profile']['image_72'] ?: null; + } + + public function getImage192() + { + return $this->response['user']['profile']['image_192'] ?: null; + } + + public function isAdmin() + { + return $this->response['user']['is_admin'] ?: null; + } + + public function isOwner() + { + return $this->response['user']['is_owner'] ?: null; + } + + public function hasTwoFactorAuthentication() + { + return $this->response['user']['has_2fa'] ?: null; + } + + public function hasFiles() + { + return $this->response['user']['has_files'] ?: null; + } +} diff --git a/src/Provider/SlackTeamResourceOwner.php b/src/Provider/SlackTeamResourceOwner.php deleted file mode 100644 index 0e928a7..0000000 --- a/src/Provider/SlackTeamResourceOwner.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * @package AdamPaterson\OAuth2\Client\Provider - */ -class SlackTeamResourceOwner extends GenericResourceOwner -{ - /** - * @var array - */ - protected $response; - - /** - * SlackResourceOwner constructor. - * - * @param array $response - */ - public function __construct(array $response = array()) - { - $this->response = $response; - } - - /** - * Get team id. - * - * @return null - */ - public function getId() - { - return $this->response['team']['id'] ?: null; - } - - /** - * Get team name. - * - * @return null - */ - public function getName() - { - return $this->response['team']['name'] ?: null; - } - - /** - * Get team domain. - * - * @return null - */ - public function getDomain() - { - return $this->response['team']['domain'] ?: null; - } - - /** - * @return null - */ - public function getEmailDomain() - { - return $this->response['team']['email_domain'] ?: null; - } - - /** - * Get icons. - * - * @return null - */ - public function getIcon() - { - return $this->response['team']['icon'] ?: null; - } - - /** - * @return array - */ - public function toArray() - { - return $this->response; - } -} diff --git a/test/src/Provider/SlackTest.php b/test/src/Provider/SlackTest.php index 58aca0e..98c71ec 100644 --- a/test/src/Provider/SlackTest.php +++ b/test/src/Provider/SlackTest.php @@ -47,9 +47,27 @@ public function testAuthorizationUrl() $this->assertNotNull($this->provider->getState()); } - public function testGetAuthoriztionUrl() + public function testGetResourceOwnerDetailsUrl() { - $url = $this->provider->getAuthorizationUrl(); + $authUser = json_decode('{"ok": true,"url": "https:\/\/myteam.slack.com\/","team": "My Team","user": "cal","team_id": "T12345","user_id": "U12345"}',true); + $token = m::mock('League\OAuth2\Client\Token\AccessToken', [['access_token' => 'mock_access_token']]); + $token->shouldReceive('__toString')->andReturn('mock_access_token'); + + $provider = m::mock('AdamPaterson\OAuth2\Client\Provider\Slack'); + $provider->shouldReceive('getAuthorizedUser')->andReturn($authUser); + $provider->shouldReceive('getResourceOwnerDetailsUrl')->once()->andReturn('https://slack.com/api/users.info?token=mock_access_token&user=U12345'); + + $url = $provider->getResourceOwnerDetailsUrl($token); + $uri = parse_url($url); + + $this->assertEquals('/api/users.info', $uri['path']); + $this->assertEquals('token=mock_access_token&user=U12345', $uri['query']); + } + + public function testGetAuthorizationUrl() + { + $params = []; + $url = $this->provider->getAuthorizationUrl($params); $uri = parse_url($url); $this->assertEquals('/oauth/authorize', $uri['path']); @@ -92,42 +110,160 @@ public function testCheckResponseThrowsIdentityProviderException() } } - public function testGetTeamInfo() + public function testGetAuthorizedUserTestUrl() { - $id = rand(1000,999); - $name = uniqid(); - $domain = uniqid(); - $emailDomain = uniqid(); - $icon = [ - 0 => uniqid(), - 1 => uniqid(), - ]; + $token = m::mock('League\OAuth2\Client\Token\AccessToken', [['access_token' => 'mock_access_token']]); + $token->shouldReceive('__toString')->andReturn('mock_access_token'); + $url = $this->provider->getAuthorizedUserTestUrl($token); + $uri = parse_url($url); + + $this->assertEquals('/api/auth.test', $uri['path']); + $this->assertEquals('token=mock_access_token', $uri['query']); + } + + public function testGetAuthorizedUserDetails() + { + $url = uniqid(); + $team = uniqid(); + $userName = uniqid(); + $teamId = uniqid(); + $userId = uniqid(); $postResponse = m::mock('Psr\Http\Message\ResponseInterface'); - $postResponse->shouldReceive('getBody')->andReturn('{"access_token": "mock_access_token"}'); - $postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); + $postResponse->shouldReceive('getBody')->andReturn('access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&otherKey={1234}'); + $postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'application/x-www-form-urlencoded']); + $postResponse->shouldReceive('getStatusCode')->andReturn(200); - $teamResponse = m::mock('Psr\Http\Message\ResponseInterface'); - $teamResponse->shouldReceive('getBody')->andReturn('{"ok":true,"team":{"id":"'.$id.'","name":"'.$name.'","domain":"'.$domain.'","email_domain":"'.$emailDomain.'","icon":{"0":"'.$icon[0].'","1":"'.$icon[1].'"}}}'); - $teamResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); + $userResponse = m::mock('Psr\Http\Message\ResponseInterface'); + $userResponse->shouldReceive('getBody')->andReturn('{"ok": true,"url": "'.$url.'","user": "'.$userName.'","team": "'.$team.'","team_id": "'.$teamId.'","user_id": "'.$userId.'"}'); + $userResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); + $userResponse->shouldReceive('getStatusCode')->andReturn(200); $client = m::mock('GuzzleHttp\ClientInterface'); $client->shouldReceive('send') ->times(2) - ->andReturn($postResponse, $teamResponse); + ->andReturn($postResponse, $userResponse); + $this->provider->setHttpClient($client); + $token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']); + $user = $this->provider->getAuthorizedUser($token); + + $this->assertEquals($userId, $user->getId()); + $this->assertEquals($url, $user->getUrl()); + $this->assertEquals($url, $user->toArray()['url']); + $this->assertEquals($team, $user->getTeam()); + $this->assertEquals($team, $user->toArray()['team']); + $this->assertEquals($userName, $user->getUser()); + $this->assertEquals($userName, $user->toArray()['user']); + $this->assertEquals($teamId, $user->getTeamId()); + $this->assertEquals($teamId, $user->toArray()['team_id']); + $this->assertEquals($userId, $user->getUserId()); + $this->assertEquals($userId, $user->toArray()['user_id']); + } + + public function testGetResourceOwnerDetails() + { + $id = uniqid(); + $name = uniqid(); + $deleted = false; + $color = uniqid(); + $profile = [ + "first_name" => uniqid(), + "last_name" => uniqid(), + "real_name" => uniqid(), + "email" => uniqid(), + "skype" => uniqid(), + "phone" => uniqid(), + "image_24" => uniqid(), + "image_32" => uniqid(), + "image_48" => uniqid(), + "image_72" => uniqid(), + "image_192" => uniqid() + ]; + + $isAdmin = true; + $isOwner = true; + $has2FA = true; + $hasFiles = true; + + $url = uniqid(); + $userName = uniqid(); + $team = uniqid(); + $teamId = uniqid(); + $userId = uniqid(); + + $accessTokenResponse = m::mock('Psr\Http\Message\ResponseInterface'); + $accessTokenResponse->shouldReceive('getBody')->andReturn('access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&otherKey={1234}'); + $accessTokenResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'application/x-www-form-urlencoded']); + $accessTokenResponse->shouldReceive('getStatusCode')->andReturn(200); + + $authUser = m::mock('Psr\Http\Message\ResponseInterface'); + $authUser->shouldReceive('getBody')->andReturn('{"ok": true,"url": "'.$url.'","user": "'.$userName.'","team": "'.$team.'","team_id": "'.$teamId.'","user_id": "'.$userId.'"}'); + $authUser->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); + $authUser->shouldReceive('getStatusCode')->andReturn(200); + + $authUserResponse = m::mock('Psr\Http\Message\ResponseInterface'); + $authUserResponse->shouldReceive('getBody')->andReturn('{"ok": true,"url": "'.$url.'","team": "'.$team.'","user": "'.$userName.'","team_id": "'.$teamId.'","user_id": "'.$userId.'"}'); + $authUserResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); + $authUserResponse->shouldReceive('getStatusCode')->andReturn(200); + + $userResponse = m::mock('Psr\Http\Message\ResponseInterface'); + $userResponse->shouldReceive('getBody')->andReturn('{"ok": true,"user": {"id": "'.$id.'","name": "'.$name.'","deleted": false,"color": "'.$color.'","profile": {"first_name": "'.$profile["first_name"].'","last_name": "'.$profile["last_name"].'","real_name": "'.$profile["real_name"].'","email": "'.$profile["email"].'","skype": "'.$profile["skype"].'","phone": "'.$profile["phone"].'","image_24": "'.$profile["image_24"].'","image_32": "'.$profile["image_32"].'","image_48": "'.$profile["image_48"].'","image_72": "'.$profile["image_72"].'","image_192": "'.$profile["image_192"].'"},"is_admin": true,"is_owner": true,"has_2fa": true,"has_files": true}}'); + $userResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); + $userResponse->shouldReceive('getStatusCode')->andReturn(200); + + $client = m::mock('GuzzleHttp\ClientInterface'); + $client->shouldReceive('send') + ->times(3) + ->andReturn($accessTokenResponse, $authUserResponse, $userResponse); $this->provider->setHttpClient($client); + $token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']); $user = $this->provider->getResourceOwner($token); + $this->assertEquals($id, $user->getId()); - $this->assertEquals($id, $user->toArray()['team']['id']); + $this->assertEquals($id, $user->toArray()['user']['id']); $this->assertEquals($name, $user->getName()); - $this->assertEquals($name, $user->toArray()['team']['name']); - $this->assertEquals($domain, $user->getDomain()); - $this->assertEquals($domain, $user->toArray()['team']['domain']); - $this->assertEquals($emailDomain, $user->getEmailDomain()); - $this->assertEquals($emailDomain, $user->toArray()['team']['email_domain']); - $this->assertEquals($icon, $user->getIcon()); - $this->assertEquals($icon, $user->toArray()['team']['icon']); + $this->assertEquals($name, $user->toArray()['user']['name']); + $this->assertEquals($deleted, $user->isDeleted()); + $this->assertEquals($deleted, $user->toArray()['user']['deleted']); + $this->assertEquals($color, $user->getColor()); + $this->assertEquals($color, $user->toArray()['user']['color']); + $this->assertEquals($profile, $user->getProfile()); + $this->assertEquals($profile, $user->toArray()['user']['profile']); + + $this->assertEquals($profile['first_name'], $user->getFirstName()); + $this->assertEquals($profile['first_name'], $user->toArray()['user']['profile']['first_name']); + $this->assertEquals($profile['last_name'], $user->getLastName()); + $this->assertEquals($profile['last_name'], $user->toArray()['user']['profile']['last_name']); + $this->assertEquals($profile['real_name'], $user->getRealName()); + $this->assertEquals($profile['real_name'], $user->toArray()['user']['profile']['real_name']); + $this->assertEquals($profile['email'], $user->getEmail()); + $this->assertEquals($profile['email'], $user->toArray()['user']['profile']['email']); + $this->assertEquals($profile['skype'], $user->getSkype()); + $this->assertEquals($profile['skype'], $user->toArray()['user']['profile']['skype']); + $this->assertEquals($profile['phone'], $user->getPhone()); + $this->assertEquals($profile['phone'], $user->toArray()['user']['profile']['phone']); + $this->assertEquals($profile['image_24'], $user->getImage24()); + $this->assertEquals($profile['image_24'], $user->toArray()['user']['profile']['image_24']); + $this->assertEquals($profile['image_32'], $user->getImage32()); + $this->assertEquals($profile['image_32'], $user->toArray()['user']['profile']['image_32']); + $this->assertEquals($profile['image_48'], $user->getImage48()); + $this->assertEquals($profile['image_48'], $user->toArray()['user']['profile']['image_48']); + $this->assertEquals($profile['image_72'], $user->getImage72()); + $this->assertEquals($profile['image_72'], $user->toArray()['user']['profile']['image_72']); + $this->assertEquals($profile['image_192'], $user->getImage192()); + $this->assertEquals($profile['image_192'], $user->toArray()['user']['profile']['image_192']); + + $this->assertEquals($isAdmin, $user->isAdmin()); + $this->assertEquals($isAdmin, $user->toArray()['user']['is_admin']); + $this->assertEquals($isOwner, $user->isOwner()); + $this->assertEquals($isOwner, $user->toArray()['user']['is_owner']); + $this->assertEquals($has2FA, $user->hasTwoFactorAuthentication()); + $this->assertEquals($has2FA, $user->toArray()['user']['has_2fa']); + $this->assertEquals($hasFiles, $user->hasFiles()); + $this->assertEquals($hasFiles, $user->toArray()['user']['has_files']); + + } -} +} \ No newline at end of file