Add providers

This commit is contained in:
Romain Neutron
2013-03-15 16:41:38 +01:00
parent 62f5a9d4c8
commit 3a3b9f5ece
13 changed files with 2809 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Authentication\Provider;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
abstract class AbstractProvider implements ProviderInterface
{
protected $generator;
protected $session;
/**
* @param UrlGenerator $generator
*
* @return ProviderInterface
*/
public function setUrlGenerator(UrlGenerator $generator)
{
$this->generator = $generator;
return $this;
}
/**
* @return UrlGenerator
*/
public function getUrlGenerator()
{
return $this->generator;
}
/**
* @param SessionInterface $session
*
* @return ProviderInterface
*/
public function setSession(SessionInterface $session)
{
$this->session = $session;
return $this;
}
/**
* @return SessionInterface
*/
public function getSession()
{
return $this->session;
}
protected function createState()
{
return md5(uniqid(microtime(true), true));
}
}

View File

@@ -0,0 +1,198 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Authentication\Provider\Token\Token;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
use Alchemy\Phrasea\Authentication\Exception\NotAuthenticatedException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class Facebook extends AbstractProvider
{
/** @var \Facebook */
private $facebook;
public function __construct(\Facebook $facebook, UrlGenerator $generator)
{
$this->facebook = $facebook;
$this->generator = $generator;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return 'facebook';
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'Facebook';
}
/**
* {@inheritdoc}
*/
public function authenticate()
{
return new RedirectResponse($this->facebook->getLoginUrl(array(
'scope' => 'email',
'redirect_uri' => $this->generator->generate(
'login_authentication_provider_callback',
array('providerId' => $this->getId()),
UrlGenerator::ABSOLUTE_URL
)
)));
}
/**
* @param \Facebook $facebook
*
* @return Facebook
*/
public function setFacebook(\Facebook $facebook)
{
$this->facebook = $facebook;
return $this;
}
/**
* @return \Facebook
*/
public function getFacebook()
{
return $this->facebook;
}
/**
* {@inheritdoc}
*/
public function getIdentity()
{
try {
$data = $this->facebook->api('/me');
$identity = new Identity();
$identity->set(Identity::PROPERTY_ID, $data['id']);
$identity->set(Identity::PROPERTY_IMAGEURL, sprintf(
'https://graph.facebook.com/%s/picture?return_ssl_resources=1',
$data['username']
));
$identity->set(Identity::PROPERTY_EMAIL, $data['email']);
$identity->set(Identity::PROPERTY_FIRSTNAME, $data['first_name']);
$identity->set(Identity::PROPERTY_LASTNAME, $data['last_name']);
$identity->set(Identity::PROPERTY_USERNAME, $data['username']);
} catch (\FacebookApiException $e) {
throw new RuntimeException('Unable to get profile informations', $e->getCode(), $e);
}
return $identity;
}
/**
* {@inheritdoc}
*/
public function onCallback(Request $request)
{
if (!$this->facebook->getUser()) {
throw new NotAuthenticatedException('Facebook authentication failed');
}
}
/**
* {@inheritdoc}
*/
public function getToken()
{
if (0 >= $this->facebook->getUser()) {
throw new RuntimeException('Provider has not authenticated');
}
return new Token($this, $this->facebook->getUser());
}
/**
* {@inheritdoc}
*/
public function getIconURI()
{
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADEAAAAwCAYAAAC4w'
. 'JK5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0W'
. 'E1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iV'
. 'zVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iY'
. 'WRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExI'
. 'DY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSR'
. 'EYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1ze'
. 'W50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6e'
. 'G1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0U'
. 'mVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZ'
. 'WYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtc'
. 'E1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMjgwMTE3NDA3MjA2ODExO'
. 'EMxNEY3NEJCM0UzNjU4QyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFRjg3O'
. 'UMwQTcxRUMxMUUyQjc5NzlGRUJDNjcwRkVDMyIgeG1wTU06SW5zdGFuY2VJRD0ie'
. 'G1wLmlpZDpFRjg3OUMwOTcxRUMxMUUyQjc5NzlGRUJDNjcwRkVDMyIgeG1wOkNyZ'
. 'WF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtc'
. 'E1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDI4MDExN'
. 'zQwNzIwNjgxMThDMTRGNzRCQjNFMzY1OEMiIHN0UmVmOmRvY3VtZW50SUQ9Inhtc'
. 'C5kaWQ6MDI4MDExNzQwNzIwNjgxMThDMTRGNzRCQjNFMzY1OEMiLz4gPC9yZGY6R'
. 'GVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlb'
. 'mQ9InIiPz59EDiBAAAEEUlEQVR42txaXUgUURQ+szu76pZlYhoJJoYaEgUJRUUUQ'
. 'hBBL0FPUdBjEUFPPfTaSw9B0GsR0UuPkQ8FQdFDVFBKJGaFS6b4r6Vru87fvZ0zM'
. '2uz7o6Ozr02duXszs4MM+e733fuOfdeFc45rPcWg/+gqfShKIr95RoBi3uOo9YYG'
. 'snHyh+r7gXb4TsvP7RX1Wy7r2tmm6GbEEWlMcvsS/e8v3jr0tku/KmjGV4QiQ3Vt'
. 'Q8nx+dapoZ/QTYzD1GMF1TNrmR5wz087EDLoGVVT2wk9RxrmRj6CRND06DPG1EFA'
. 'cnyRBMe1rmnzDwIioEyPWdAdjYHWlaHqI5aHP/IP2y1xIKXCVtODP228INFfNh1O'
. '3gzWor89sopzizEyZht66BVUAiQ36o3ZyAGIP/XionaLSk4sq8B2pq2QnVVCjZWJ'
. 'G256IYFmmbC5/Qk3H3cvVR6oM6PeUGAhShsOTG5IBJqDM6f2gMdB3ZCPKb43pdB7'
. 'QfxpQAEsUB6kxnU8XgMrl04DLub65a9NxdwhCxiguJCJhOnO5oDAbD9CagKHzkxa'
. 'TI6ebQ18P2mxQL5sggE2MhlMdHaWAOVqbKS1wZHZ+DT11HIooQWzo3MrI4JmXKqr'
. '60seX5sag6u3nwKhrk6BRSCMEmHTJqc1Hjporj/xzRoWHCGKsX/apA7eUISE9znu'
. 'QQgzDtLyEkeE35JFGuEUO8sBEG1E5dXO/k+loerEgpBGAgAg4vYENHKkyqoarxgi'
. 'C2ZADFrp8oSxQwhO97RKhAILFmABghLUExcOXcQjh9qXva+Y/ubbFvcevvH4fKNz'
. 'pWB4IKr2LCyzOH8hq802RkIgrKkJQhE2NDKaUYgXxblCbFDbNhCMpPVVpmxBSa7s'
. 'J3x2y7FV8iEPbOzKCYsISDedA/AbEZb+N3SVAPtbfVF96UHp+Hdx8Gi8129Q4F8K'
. 'SoARU6Knr/+Ylu+nTmxtySInm8jcPvBKzF5wmYBkYhiotT8wG8UC/POEjEhbz7h9'
. '9yw71wUE86LuDQQSzHBBDIhswD0eS4XyoRJPeJfMstlQmApbqGmmKTAXjomBAU2d'
. '3uE838QE1xgYNsgZMnJpwzhYuXkSEmanKw1kBNNiJxyXNbMbg2Y4BxZQJMWE34gm'
. 'NCYYHJjYi2GWKpf7JmdJCb85QTimHBWOpi01Q7fjB1yhUWF4jHWMQmt89lb20Q3t'
. 'WBwMnIDHJQdTqdEd9/OMrKT+EXrntTbLL8QRD+s0b4n1zHAhpRYcqGGipqZ83NTk'
. '+kXj2gdAZzNeEshPSqKQuvtm8DZG25E245WA87uZDxKJICz7UtMDKN9RxtTPRdpM'
. 'kw79OP5eTo4O5RqhECYLgMzrp/kr6Z65KS7J8FFSyzQFmuU/kkl72fW9ZVM/yPAA'
. 'LuRXOCVA9oFAAAAAElFTkSuQmCC';
}
/**
* {@inheritdoc}
*
* @return Facebook
*/
public static function create(UrlGenerator $generator, SessionInterface $session, array $options)
{
$config['appId'] = $options['app-id'];
$config['secret'] = $options['secret'];
$facebook = new \Facebook($config);
return new static($facebook, $generator);
}
}

View File

@@ -0,0 +1,271 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\Provider\Token\Token;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
use Alchemy\Phrasea\Authentication\Exception\NotAuthenticatedException;
use Alchemy\Phrasea\Exception\RuntimeException;
use Guzzle\Http\Client as Guzzle;
use Guzzle\Http\ClientInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class Github extends AbstractProvider
{
private $client;
private $key;
private $secret;
public function __construct(UrlGenerator $generator, SessionInterface $session, ClientInterface $client, $key, $secret)
{
$this->generator = $generator;
$this->session = $session;
$this->client = $client;
$this->key = $key;
$this->secret = $secret;
}
/**
* @param ClientInterface $client
*
* @return Github
*/
public function setGuzzleClient(ClientInterface $client)
{
$this->client = $client;
return $this;
}
/**
* @return ClientInterface
*/
public function getGuzzleClient()
{
return $this->client;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return 'github';
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'Github';
}
/**
* {@inheritdoc}
*/
public function authenticate()
{
$state = $this->createState();
$this->session->set('github.provider.state', $state);
return new RedirectResponse('https://github.com/login/oauth/authorize?' . http_build_query(array(
'client_id' => $this->key,
'scope' => 'user,user:email',
'state' => $state,
'redirect_uri' => $this->generator->generate(
'login_authentication_provider_callback',
array('providerId' => $this->getId()),
UrlGenerator::ABSOLUTE_URL
),
), '', '&'));
}
/**
* {@inheritdoc}
*/
public function onCallback(Request $request)
{
if (!$this->session->has('github.provider.state')) {
throw new RuntimeException('Invalid state value ; CSRF try ?');
}
if ($request->query->get('state') !== $this->session->remove('github.provider.state')) {
throw new RuntimeException('Invalid state value ; CSRF try ?');
}
$guzzleRequest = $this->client->post('access_token');
$guzzleRequest->addPostFields(array(
'code' => $request->query->get('code'),
'redirect_uri' => $this->generator->generate(
'login_authentication_provider_callback',
array('providerId' => $this->getId()),
UrlGenerator::ABSOLUTE_URL
),
'client_id' => $this->key,
'client_secret' => $this->secret,
));
$guzzleRequest->setHeader('Accept', 'application/json');
$response = $guzzleRequest->send();
if (200 !== $response->getStatusCode()) {
throw new RuntimeException('Error while getting access_token');
}
$data = json_decode($response->getBody(true), true);
$this->session->remove('github.provider.state');
$this->session->set('github.provider.access_token', $data['access_token']);
$request = $this->client->get('https://api.github.com/user');
$request->getQuery()->add('access_token', $data['access_token']);
$request->setHeader('Accept', 'application/json');
$response = $request->send();
if (200 !== $response->getStatusCode()) {
throw new RuntimeException('Error while retrieving user info');
}
$data = json_decode($response->getBody(true), true);
$this->session->set('github.provider.id', $data['id']);
}
/**
* {@inheritdoc}
*/
public function getToken()
{
if ('' === trim($this->session->get('github.provider.id'))) {
throw new RuntimeException('Github has not authenticated');
}
return new Token($this, $this->session->get('github.provider.id'));
}
/**
* {@inheritdoc}
*/
public function getIdentity()
{
$identity = new Identity();
$request = $this->client->get('https://api.github.com/user');
$request->getQuery()->add('access_token', $this->session->get('github.provider.access_token'));
$request->setHeader('Accept', 'application/json');
$response = $request->send();
if (200 !== $response->getStatusCode()) {
throw new RuntimeException('Error while retrieving user info');
}
$data = json_decode($response->getBody(true), true);
list($firstname, $lastname) = explode(' ', $data['name'], 2);
$identity->set(Identity::PROPERTY_EMAIL, $data['email']);
$identity->set(Identity::PROPERTY_FIRSTNAME, $firstname);
$identity->set(Identity::PROPERTY_ID, $data['id']);
$identity->set(Identity::PROPERTY_IMAGEURL, $data['avatar_url']);
$identity->set(Identity::PROPERTY_LASTNAME, $lastname);
return $identity;
}
/**
* {@inheritdoc}
*/
public function getIconURI()
{
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADEAAAAwCAYAAAC4w'
. 'JK5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MO'
. 'mNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ'
. '2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6b'
. 'WV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgM'
. 'jAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJod'
. 'HRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZ'
. 'XNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZ'
. 'S5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL'
. '3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZ'
. 'G9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZ'
. 'DowMjgwMTE3NDA3MjA2ODExOEMxNEY3NEJCM0UzNjU4QyIgeG1wTU06RG9jdW1lbnRJR'
. 'D0ieG1wLmRpZDowMUNGRURDNTgzRUUxMUUyQjNFMDk5RUI0NTk2RTdBMiIgeG1wTU06S'
. 'W5zdGFuY2VJRD0ieG1wLmlpZDowMUNGRURDNDgzRUUxMUUyQjNFMDk5RUI0NTk2RTdBM'
. 'iIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpI'
. 'j4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ODUwR'
. 'DRBRkIxQjIwNjgxMThDMTRGNzRCQjNFMzY1OEMiIHN0UmVmOmRvY3VtZW50SUQ9Inhtc'
. 'C5kaWQ6MDI4MDExNzQwNzIwNjgxMThDMTRGNzRCQjNFMzY1OEMiLz4gPC9yZGY6RGVzY'
. '3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiP'
. 'z4ThndjAAAG5ElEQVR42tRaaUxUVxT+GB+YIqjEiCwiVBoLGgSRRRHUxjSKVutSfzTGu'
. 'CXUWnFNbGpKXYgLKribaGLin1ZjiwUxbq1xqaICKqKyKJtSwAUdBpBtYKbn3HlDGRiYY'
. 'cRk5pLDzH3v3vPud89yzzlv7LRaLWy9SfzPzs5OfMikIOrT7rs1NU0HYglo7VgSdjoUi'
. 'sKnT8NcXV1P0LURGo3G6nZc3dyc/evJkwvXrlv3nLrNRC0MRg+Cd9xBpVQ+blGrfZsaG'
. '9DSQvetTdVor9+/f3/FP2D0EurVEjUwNkm+LUDQqgWAhvp6aFpbrVL/7SVpEn24yl1eZ'
. 'IseBNtAX1YhlkCrlQIQTauVZBANMjXpQbBN2LP2MBBr9ljy2gYSveE1sxYp2qlTHxvyq'
. 'o6sObJ3tZPa3VCwx2KkNnB2SPKmKzqCkL0urM8rGfFTnQ67/zHoUNjaKS51MhyZbBeEX'
. 'pUskAR7tbuZmfjryhVkP8xBeUUFamtr4ezsDE8PDwQFjsaXU6YgPDSU3IniY0rCMsO+f'
. 'uMfJO7fj6LiYtF3cHCA19ChGEbU2NSE4pIS5BcU4NTp3+E7fDjWr16NSROjPg4InRDMB'
. '6FWq7Fj126cTk4Wuzt92jTMmzMbwUFBsLe3Nxh378EDnElJxcXLl7GCQMyfOxcbf9xgM'
. 'M5iK5djJ/a7g6tevSpVvn2LxoZ6swCsWrceN9PT8ZmvL3bEx8Pv8xEm5+UXPMVPcXEoL'
. 'CpCZEQEDiQl9hhIwNiQ7+kjl4gDwTcKo2atNU3bExIEgNCxY3H00EF86j3MrHmf+njj2'
. 'OFDCAsJEfO37Uwwa54B9YY63bh5C3/8mQIfb2/s37Mbu5L24sKlS5gRPQ0/LF8u4q+ys'
. 'n/R0NgIR8dPMNTTE5Ik4cjRYzh3/jxmz5qFfbt3YcGSpUhOScHkiRMxKSqy91wsTLhY9'
. 'kJ7Dx4UidT2rVvQz8kJz8vK0NTcjDOpZ5GSdg7GchG2Gf315y9eiHnbaP6CRYuJ3wFET'
. 'oiw2Gt1VicToswgN8peiN3lKH9/cc118GADkF2B17dBgwaJeaP8/ASf4pJS3M3ItFidF'
. 'B3VSUP/uqO/r14VY+fN/lr0ldXVyMjK7NHO8UYoVSoxn/lwu3Ltqsln68kMSaDbXXiY8'
. 'wgO5E2CAgNFPzUtDUpldY9AvKmqwlmax/PHEB/ml/PoUS8atonYqbKyEh50AlOGJcY9z'
. 's2zSI9z8/LFfDZ45ldeUWlxzKYwlnR0R7V1dehPoYS+z+eFJa2Fskc9jwH9+6OO+Jp6d'
. 'lees0PsJFI7aLupdDiTV6mhmEg/xtPD3SIQ7kOGtPFQ1dTAyalft881WxKsSqaMys3ND'
. 'RUU3PEZwP3I8REWPThi3Dgxn/kwPw93j94x7DaRydIwRgGjRqKZVCg7J0f0g4MCEREe3'
. 'iMAk6OixDyez3yYX8DIkd0+tz2ZdLFaE7vApyu3FPIurAbX6fSO3/QLncIzhZF2W26xl'
. '/DNnDnYEvdzGz/mwy0qMtJiSXSyCa0Jm+AI1YfipKvXb+Db+fPx4GE2bt+5gw1r1yBm8'
. 'WJk3r+P/YeP4J1S2TbHe9gwLFu0CCHBYzBwwADdo+gZhXRoXqMwnvnxvV6xiY7xkzHi5'
. 'HZFTIw4gXfuScTShQtxLzub8okDyHn8hPz9YwMA3F5QWDKcAj/2Qno+bAtbt+8QNS7mZ'
. '2eGZzTLO+kGaUzuSERYGGZGRyPtwgVs3LwZqyjo25mUhOTU1C5rRYVFxfAhiXBrJAAbN'
. '28RkviKgkbmp/2A2q/UlXcy1dbErsTrqjeUkmahquotEuK3wsXFBXezspCQmGQ0dmK+B'
. 'c+eYWdiIsVfJSIcXxsba9bzepBji+DJrF2RKOLctmkT9h06jHMXLyImdhUmRU6Ay0AXo'
. '+Of5OXh5u10YQO86BlTpxKAlYKP9gMr8AaZXVlJSenLinLUkdfpSbt9NwNHjx8XIbmp5'
. 'u3lhe+WLcP48DCLF/1F9HSDzK6TOllSKBgXForw0BDcJwO/cSsdufl5qHz5CvX19ZQUO'
. 'cLdbQhG+vljIuUM7N04F+nN2lanko0pF9tdC6aIlMlERbjXi3OSsaTIpiuAbaVY2HIZs'
. 'y12smVJ9LB4ZqUFZfnE1mpsWxLQ2sLria5BaBobG8rIh3tZM4pqleoddG9N9S/k26JY7'
. 'rT+dvJUXJNaXSFR3M9hpdbK/pSq6nfHTpw4RWvlYnGzHow+7OCXeP2JhhD5cOrMNS7oX'
. 'vBZywvJVnnxb4nKiUqJXhHVSO0GNEH3lv61fK3OSkGo5DXWymtuNfhZhLxoZ5kc5WvW8'
. 'iMVjaxC9TKAWr1aGfxABbqX27zwvvL3PlYGgqWhliXQLH/X/CfAALHSg9r3u8gEAAAAA'
. 'ElFTkSuQmCC';
}
/**
* {@inheritdoc}
*/
public static function create(UrlGenerator $generator, SessionInterface $session, array $options)
{
if (!isset($options['client-id'])) {
throw new InvalidArgumentException('Missing GitHub client-id parameter');
}
if (!isset($options['client-secret'])) {
throw new InvalidArgumentException('Missing GitHub client-secret parameter');
}
return new Github($generator, $session, new Guzzle('https://github.com/login/oauth'), $options['client-id'], $options['client-secret']);
}
}

View File

@@ -0,0 +1,319 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\Provider\Token\Token;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
use Alchemy\Phrasea\Authentication\Exception\NotAuthenticatedException;
use Alchemy\Phrasea\Exception\RuntimeException;
use Guzzle\Http\Client as Guzzle;
use Guzzle\Http\ClientInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class GooglePlus extends AbstractProvider
{
private $client;
private $guzzle;
private $plus;
public function __construct(UrlGenerator $generator, SessionInterface $session, \Google_Client $google, ClientInterface $guzzle)
{
$this->generator = $generator;
$this->session = $session;
$this->client = $google;
$this->guzzle = $guzzle;
$this->plus = new \Google_PlusService($this->client);
$this->client->setScopes(array(
'https://www.googleapis.com/auth/plus.me',
'https://www.googleapis.com/auth/userinfo.email',
));
$this->client->setRedirectUri(
$this->generator->generate(
'login_authentication_provider_callback', array(
'providerId' => $this->getId(),
), UrlGenerator::ABSOLUTE_URL
)
);
$this->client->setApprovalPrompt("auto");
if ($this->session->has('google-plus.provider.token')) {
$this->client->setAccessToken($this->session->get('google-plus.provider.token'));
}
}
/**
* @param ClientInterface $client
*
* @return Github
*/
public function setGuzzleClient(ClientInterface $client)
{
$this->guzzle = $client;
return $this;
}
/**
* @return ClientInterface
*/
public function getGuzzleClient()
{
return $this->guzzle;
}
/**
* @param \Google_Client $client
*
* @return GooglePlus
*/
public function setGoogleClient(\Google_Client $client)
{
$this->client = $client;
return $this;
}
/**
* @return \Google_Client
*/
public function getGoogleClient()
{
return $this->client;
}
/**
* @param \Google_PlusService $plus
*
* @return GooglePlus
*/
public function setGooglePlusService(\Google_PlusService $plus)
{
$this->plus = $plus;
return $this;
}
/**
* @return \Google_PlusService
*/
public function getGooglePlusService()
{
return $this->plus;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return 'google-plus';
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'Google +';
}
/**
* {@inheritdoc}
*/
public function authenticate()
{
$state = $this->createState();
$this->session->set('google-plus.provider.state', $state);
$this->client->setState($state);
return new RedirectResponse($this->client->createAuthUrl());
}
/**
* {@inheritdoc}
*/
public function onCallback(Request $request)
{
if (!$this->session->has('google-plus.provider.state')) {
throw new RuntimeException('Invalid state value ; CSRF try ?');
}
if ($request->query->get('state') !== $this->session->remove('google-plus.provider.state')) {
throw new RuntimeException('Invalid state value ; CSRF try ?');
}
$this->client->authenticate($request->query->get('code'));
$token = json_decode($this->client->getAccessToken(), true);
$ticket = $this->client->verifyIdToken($token['id_token']);
$this->session->set('google-plus.provider.token', json_encode($token));
$this->session->set('google-plus.provider.id', $ticket->getUserId());
}
/**
* {@inheritdoc}
*/
public function getToken()
{
if (!ctype_digit($this->session->get('google-plus.provider.id'))) {
throw new RuntimeException('Google + has not authenticated');
}
return new Token($this, $this->session->get('google-plus.provider.id'));
}
/**
* {@inheritdoc}
*/
public function getIdentity()
{
$identity = new Identity();
$token = json_decode($this->session->get('google-plus.provider.token'), true);
$request = $this->guzzle->get(sprintf(
'https://www.googleapis.com/oauth2/v1/tokeninfo?%s',
http_build_query(array('access_token' => $token['access_token']), '', '&')
));
$response = $request->send();
if (200 !== $response->getStatusCode()) {
throw new RuntimeException('Error while retrieving user info');
}
try{
$plusData = $this->plus->people->get('me');
} catch (\Google_Exception $e) {
throw new RuntimeException('Error while retrieving user info', $e->getCode(), $e);
}
$data = json_decode($response->getBody(true), true);
$identity->set(Identity::PROPERTY_EMAIL, $data['email']);
$identity->set(Identity::PROPERTY_FIRSTNAME, $plusData['name']['givenName']);
$identity->set(Identity::PROPERTY_ID, $plusData['id']);
$identity->set(Identity::PROPERTY_IMAGEURL, $plusData['image']['url']);
$identity->set(Identity::PROPERTY_LASTNAME, $plusData['name']['familyName']);
return $identity;
}
/**
* {@inheritdoc}
*/
public function getIconURI()
{
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADEAAAAwCAYAAAC4w'
. 'JK5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MO'
. 'mNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ'
. '2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6b'
. 'WV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgM'
. 'jAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJod'
. 'HRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZ'
. 'XNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZ'
. 'S5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL'
. '3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZ'
. 'G9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZ'
. 'DowMjgwMTE3NDA3MjA2ODExOEMxNEY3NEJCM0UzNjU4QyIgeG1wTU06RG9jdW1lbnRJR'
. 'D0ieG1wLmRpZDo2N0M5MkQ3OTcxRUUxMUUyQjc5NzlGRUJDNjcwRkVDMyIgeG1wTU06S'
. 'W5zdGFuY2VJRD0ieG1wLmlpZDo2N0M5MkQ3ODcxRUUxMUUyQjc5NzlGRUJDNjcwRkVDM'
. 'yIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpI'
. 'j4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDI4M'
. 'DExNzQwNzIwNjgxMThDMTRGNzRCQjNFMzY1OEMiIHN0UmVmOmRvY3VtZW50SUQ9Inhtc'
. 'C5kaWQ6MDI4MDExNzQwNzIwNjgxMThDMTRGNzRCQjNFMzY1OEMiLz4gPC9yZGY6RGVzY'
. '3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiP'
. 'z6uF8PaAAAL1UlEQVR42txae4wdVRn/zdzXPu/d1227W5aWPizQFrolXZAFS3hUpICAC'
. 'oLVNlU0NcVoiTEm/mEqGMVUVBqEolFEBUQbiokIAo1Q6COlrZTutrRLu9vd7pbtvu7uf'
. 'c7j+J2ZM/eemZ177xITX5N8e86dM3PO9/ve58wqjDH8r18q/g+uIP+jKIrVCOLAAlL/v'
. '+ligkwiQ7Q2CIfhvr6+9ubm5l+rqvqRk0MZzHvkPaA5RJAEvjxWC3nhzWMZbLq2AVtun'
. 'O27cm73biSuvNJ61OHCkLgxpX6pe9TqOvBKG/A5+pkh0oh0hfsEaYJLPKzr+ruBQGA+X'
. '/jp3YO4Z9txYG4Vve1hPN9XCvLpz+DAd5ag7fxqXyDDK1Ygt3+/1TeLkOHz2wua00eBh'
. 'dSMEyWJsqrkG2EHAL8GEprdsWamV3kAyIuImDeVgpg4IHruyX3DRe0gfPPNtu597MNpF'
. 'YkcJTPpGQcYXc1EMaIKbvoOCO4DEZcJGMyzimIDsfpMIjF7Uxg/PTSK4UndH0RHBxTPP'
. 'UViVJGW8j7j048T1fJpuQJUaTwkvxwJ0pAhMeoiRawoWv5MBclhMIsDvZP+EWTBAktSx'
. 'aTPPGNKEY8W96NCC5xnRTangPxSvCZUmN10TMrbd0ysAOaNbn8QTPInVWLSy7RXI8yjN'
. 'WGSEQHAmipYLGe0NtBzXBucUc6A4kifSStJrPDfoQD29qZ9QSiG4csYfJiWmPXVkldRw'
. 'WKOePF5FJXqyeRSNF2l4jFOaXlFmjekYChlWNaleOzBOHXKYkzxMQ+HaW+fTcPMSmbse'
. 'E0Yay6mAPBBpqAFeUbmMSnB0UjOgGZMddHczp0uiSvCfsPCQ+tEuIlKHhsoYV7T0gS/1'
. 'qyYgd++NGA7eF63ir9MFGY9F6EmGJg6l/6n7agRTDpXlmiU6GxdBNnKAGUtkikzEUgai'
. 'CayaJAY5EY6JhLDhwLx8aX1WHppPQ53Ul5pqRKGKlSiSGCcTE6md15tCKrXlh77Heq6O'
. 'q3s9GrHbHTOacTp+gqciVWgl+h4LIw0BUqdXlNJw1W6ifmJHFrGMoiPZzFrNI25p0dw0'
. 'e5+tJgfEgS/fnBLK1bvOQc0UPwPBwvJzXFsp88Nc1zDJ5fE3BOcHMSrP1qD7fctx74FM'
. '7B/NumjKmRrl5uiRlxlDSkHARM019nGSvKxeppXeEpaw8V3TGB53wTw8G6PEdhlRxV3A'
. '+qf8gOy6dmTePhX3cAiMoaQ6o5MHATPmafTqG6pxMADS1FbYdtTwsjiU4/eiVf0E/TuU'
. 'mCSqoCUBlVjNm8M7ujmm86Z7Xb8heqQLYA7nttAA51EPURDLk0kciai4am+/uO7LsBkU'
. 'scTf+gljVDojYftFMMBpEhDAxmoTRV4ZePCPICzmQRu3b4B+3KHgRmXINifcE/qmIV3P'
. '8PcAKzoQ43Knx+jtci09FLR6YatnTh5LuNrVtvWL8TW+y9C+zwq8IZyVPCRq51Joy6i4'
. 't5PtODoQ5fiirl28bd36Dgu+81nsK9nFwKxxQiks+SvrECG0zfd9wx53PQ8S0SAmY/KX'
. 'OaEL+w5VRnK4edrFmDtNc1F/eTt7gm8N5hGtCKIyxdG0RQtKPT1gSNY+dQdloQDDQvI9'
. 'nWfbMWKx01W3ryMr79W3JwwrxLp99JYt+Vd/OL1Qdy6rBHXUoRqm1NDVVYh4lw2v9Yi7'
. '9U91oeVz6yzTCVQTwWxrk0FUM58GMqaV+noxCNFjByH6qZd/xjBrt0UlRojmN9ajZnRM'
. 'KojASR6Urhr9Wx8Y9VUTf3k4LMkly4EWjtormxp6X4o7bDimW4KCB72dPFkIxWJTSR9i'
. 'iTdJyfRnaP7WQKZMbH5ywt9J+ubHCIvq6Q6SaN1zfIMytKd7jgrV3aYcmUqQPGtKSUwr'
. 'hHe1rU1YNXimC+I2+ZdTfE8AT0zKUoSmoQXfoYp+qa7bxpSXyImxpjhuSfasiDy9ZBPb'
. 'USA0mM5DDm7Ps+1dvFqbLjp+7Sv6IKRHBYllmmTKbVW35D60n1OhhgzzEIr+haQaWvCt'
. 'V8QoMgnsqeS+GvXeNHI9eh19+OhOx+ltHsO5nCPvcQU6Rtu6bs0ZfhozITCyRozyoEwB'
. 'cNe9TqaoX6Fggef7ytZqnyz/fN44YvPobKiAeaZLnpdEXMZPkybBbMrAk7J91mhGC2tC'
. 'anU5k4+odvleD/Vjz1EGR3HXujFmm0nUOrs8Jb5V+HI117G4jntYKffscwh72cOOUzJD'
. 'JqF+4ppk1crZQpAxk8IKLVnrSjE9821rTWYNyOG1sYwmusqMCteiUUNlQhWRnw3KPJ1Q'
. 'W0c79z3F1zzxD144+CfgeZF9I7iCZnFQ6vriLVEdHKDOEqSDmloa2vEygvrcfmFdVh1S'
. 'RwN1f/aOenr9/4eVzz+Wew99CIUAmJHnhKhtRjTjJUHse6uZtoIxXEdZWn3NQ7t9IvIj'
. 'XVBT/XDTA+CZSn6ZMl5NR3h+ApEr3ycduz1RcHs+cozaHnwagz0H4PS1GqbBjy73FJ5o'
. 'YQmSpbiyRNPItn9NLSh/dASw9bJlXVmELAPe1QhApYiBTbFUfexbQjPvq0okH2Dx3D5A'
. 'yupnK4jwBF/AL7acJuVueVdV+3ku8c2Uu9jYEcHzuxYh0TnS9AmhxGoiiAQrUKglqimC'
. 'kpVFViY2gj9bqyBNjaEoRduR/Lgt4qCaJ+1CDd13A2cOyOqAx42Tau1I5Xk9DrLPzPlX'
. 'rmDAm3sEE4+tQxjXW8hGA0hGCMlhSrJjFUiZhHTbYITNanmUmuqoVBiH975EMZ2FtfG2'
. 'ktWgz/IqLpVGCtkYeaTmS3fMadm7XIgep+/AZnhCYSbiHEWsBkXqYI5KcNJJwKUST8ML'
. 'rFgFYL1YSTe3oGRl6/3BdHGHTsap/IkXZCyLktehFR5zPCMlQKR6NqKyd5zCBMjBkmXM'
. '2eKlG84zFKri9YUcZ0Zjlb4xiVIZXgE4wdexcTb901ZcG6sGfXVFACyual1Uz4fMN+8k'
. 'R8rBWKi5wmQ8Eniql17SfWXxaCoyeBolhVM2QLsEE0bqFUwtncrjMQx14KhQBBV3Kn5l'
. 'wZTOpg2i7RyteDcKwVCT49YJ5x5E5KrBMEoj2b5AtMoVAt5wGKbiXAFcrStTh5/zLVgM'
. 'pfG2GTCDnGmT/b2trIGipQdQXfpZFhhVKcHVee00rTPBJh0/Ep6sk8r7PwLptpnyvlwq'
. 'YiTFi6YRK9rwSND7yM5SvuOQMjNEJvGZmk6myIlGIOWGYAaEeehrHC4p0qHf873FQVWj'
. 'rFOIphaOMFRrWeYrSXTvfKbPQeprCGNNzXbkvVjshSAcpui6pabkEvZNYtj305flwtay'
. 'Sf4KYTpmJgYswOKAoN8N1A907XgL3f90VpWtfg3i/uCWWKsFIjGZd9FOEYbnxG+vVQLF'
. 'bJpM+vYP4RPyM7NgciOb6ZylCBJMIu+lJ9/+9G/48ibf6N9fKOwc8/HOkPaUZp+YwJIK'
. 'RBqqBYLP70VGoXwNFWy3DMcodjRT4RZ4V+Oc+cjmXVGRC2pLfmBiWjbagrXl+Wr/PU/2'
. '2DVKgo5jGtH52oN/90gK5xRlUt2rO7Cr2Lx+i3Wl51Ef5b2y1req/NadkKpZHa8b5A/5'
. 'NJZJAc01C1ZhFnX78hPvOx7N2L8/RNQGhqFBLzfd6UtcdGx8powR0dHD/NOw5JNWL5pD'
. '+KXtlGkUjAxqCF1TkNuQoOW1SgRGiRtg8aIcjr5kYYsmWBqIEfaUBDvWIUL1h6l2e0jz'
. 'at+eDsO73oNSnwW1TWGzYju2RQVC7WGO7xS4h3xfC3OV7H8I17Dxo0b2zdv3vxIfX39e'
. 'Q6y1NABDB/cionT+6CND0JPUlme0vNfg0IUKcOxKMK0Aao9fzma2r+NcEOb9e6u3oO4e'
. '8t69HUeAWbOpNSgFiRZpEItta8gsxrBofFncCL5Ft05zk+JiEYcEGHx/YOHkjlEHEQjD'
. '1jeD5L/wYtLP8W/6/NP/0R823CWV0tBaXed45UHr8/FPf4ZtGo63zD+zSD4UcsHgld+z'
. 'GjIILJiAOLhIfGp9b/ln1QcQacEnxPit/lPAQYAZxcBzaHB/oIAAAAASUVORK5CYII=';
}
/**
* {@inheritdoc}
*/
public static function create(UrlGenerator $generator, SessionInterface $session, array $options)
{
$client = new \Google_Client();
$client->setApplicationName('Phraseanet');
$client->setClientId($options['client-id']);
$client->setClientSecret($options['client-secret']);
return new GooglePlus($generator, $session, $client, new Guzzle());
}
}

View File

@@ -0,0 +1,259 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\Provider\Token\Token;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
use Alchemy\Phrasea\Authentication\Exception\NotAuthenticatedException;
use Alchemy\Phrasea\Exception\RuntimeException;
use Guzzle\Http\Client as Guzzle;
use Guzzle\Http\ClientInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class Linkedin extends AbstractProvider
{
private $client;
private $key;
private $secret;
public function __construct(UrlGenerator $generator, SessionInterface $session, ClientInterface $client, $key, $secret)
{
$this->generator = $generator;
$this->session = $session;
$this->client = $client;
$this->key = $key;
$this->secret = $secret;
}
/**
* @param ClientInterface $client
*
* @return Github
*/
public function setGuzzleClient(ClientInterface $client)
{
$this->client = $client;
return $this;
}
/**
* @return ClientInterface
*/
public function getGuzzleClient()
{
return $this->client;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return 'linkedin';
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'LinkedIN';
}
/**
* {@inheritdoc}
*/
public function authenticate()
{
$state = $this->createState();
$this->session->set('linkedin.provider.state', $state);
return new RedirectResponse('https://www.linkedin.com/uas/oauth2/authorization?' . http_build_query(array(
'response_type' => 'code',
'client_id' => $this->key,
'scope' => 'r_basicprofile r_emailaddress',
'state' => $state,
'redirect_uri' => $this->generator->generate(
'login_authentication_provider_callback',
array('providerId' => $this->getId()),
UrlGenerator::ABSOLUTE_URL
),
), '', '&'));
}
/**
* {@inheritdoc}
*/
public function onCallback(Request $request)
{
if (!$this->session->has('linkedin.provider.state')) {
throw new RuntimeException('Invalid state value ; CSRF try ?');
}
if ($request->query->get('state') !== $this->session->remove('linkedin.provider.state')) {
throw new RuntimeException('Invalid state value ; CSRF try ?');
}
$guzzleRequest = $this->client->post('https://www.linkedin.com/uas/oauth2/accessToken?' . http_build_query(array(
'grant_type' => 'authorization_code',
'code' => $request->query->get('code'),
'redirect_uri' => $this->generator->generate(
'login_authentication_provider_callback',
array('providerId' => $this->getId()),
UrlGenerator::ABSOLUTE_URL
),
'client_id' => $this->key,
'client_secret' => $this->secret,
), '', '&'));
$response = $guzzleRequest->send();
if (200 !== $response->getStatusCode()) {
throw new RuntimeException('Error while getting access_token');
}
$data = json_decode($response->getBody(true), true);
$this->session->remove('linkedin.provider.state');
$this->session->set('linkedin.provider.access_token', $data['access_token']);
$request = $this->client->get('https://api.linkedin.com/v1/people/~:(id,first-name,last-name,positions,industry,picture-url,email-address)');
$request->getQuery()
->add('oauth2_access_token', $data['access_token'])
->add('format', 'json');
$response = $request->send();
if (200 !== $response->getStatusCode()) {
throw new RuntimeException('Error while retrieving user info');
}
$data = json_decode($response->getBody(true), true);
$this->session->set('linkedin.provider.id', $data['id']);
}
/**
* {@inheritdoc}
*/
public function getToken()
{
if ('' === trim($this->session->get('linkedin.provider.id'))) {
throw new RuntimeException('Linkedin has not authenticated');
}
return new Token($this, $this->session->get('linkedin.provider.id'));
}
/**
* {@inheritdoc}
*/
public function getIdentity()
{
$identity = new Identity();
$request = $this->client->get('https://api.linkedin.com/v1/people/~:(id,first-name,last-name,positions,industry,picture-url;secure=true,email-address)');
$request->getQuery()
->add('oauth2_access_token', $this->session->get('linkedin.provider.access_token'))
->add('format', 'json');
$response = $request->send();
if (200 !== $response->getStatusCode()) {
throw new RuntimeException('Error while retrieving user info');
}
$data = json_decode($response->getBody(true), true);
if (0 < $data['positions']['_total']) {
$position = array_pop($data['positions']['values']);
$identity->set(Identity::PROPERTY_COMPANY, $position['company']['name']);
}
$identity->set(Identity::PROPERTY_EMAIL, $data['emailAddress']);
$identity->set(Identity::PROPERTY_FIRSTNAME, $data['firstName']);
$identity->set(Identity::PROPERTY_ID, $data['id']);
$identity->set(Identity::PROPERTY_IMAGEURL, $data['pictureUrl']);
$identity->set(Identity::PROPERTY_LASTNAME, $data['lastName']);
return $identity;
}
/**
* {@inheritdoc}
*/
public function getIconURI()
{
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADEAAAAwCAYAAAC4w'
. 'JK5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MO'
. 'mNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ'
. '2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6b'
. 'WV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgM'
. 'jAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJod'
. 'HRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZ'
. 'XNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZ'
. 'S5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL'
. '3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZ'
. 'G9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZ'
. 'DowMjgwMTE3NDA3MjA2ODExOEMxNEY3NEJCM0UzNjU4QyIgeG1wTU06RG9jdW1lbnRJR'
. 'D0ieG1wLmRpZDo2N0M5MkQ3RDcxRUUxMUUyQjc5NzlGRUJDNjcwRkVDMyIgeG1wTU06S'
. 'W5zdGFuY2VJRD0ieG1wLmlpZDo2N0M5MkQ3QzcxRUUxMUUyQjc5NzlGRUJDNjcwRkVDM'
. 'yIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpI'
. 'j4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDI4M'
. 'DExNzQwNzIwNjgxMThDMTRGNzRCQjNFMzY1OEMiIHN0UmVmOmRvY3VtZW50SUQ9Inhtc'
. 'C5kaWQ6MDI4MDExNzQwNzIwNjgxMThDMTRGNzRCQjNFMzY1OEMiLz4gPC9yZGY6RGVzY'
. '3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiP'
. 'z4zg+u0AAAD+0lEQVR42uxaSWgTURj+kkwWS7VirdZaS6Xa2tqCW3FBq4IHl0MPFT14c'
. 'wE9K+hBD3oTRD1UPAgeBOsCgoinFulBQRGt2orSWqS2VatNapqmS5Z543uTyTKTTDIJI'
. '32RPvjJvLxl3jffv7z/zVgkSUK+Fyv+gyJELyy0JACzKb8WRXgqTHVIoghJoPqHHiEk7'
. 'kUgZAdTNd60TSIe/Bm7jj3bWmktQCVsidoEJYI9fSc+DUxieBTw+GizyJ/uML1w2MM4t'
. 'LuGXtFFYjKRCQbChUAQcHuB32OUKE6NXrCxdZcqtbCgMXKHvHDGgMix1wrJGlLCWNAyw'
. 'YiK2wHh3vUWUZnHeNGCiLhcGQj3IFzyQ9eAQBwAiQj/Mc6iihMqL8yAkPyJ5ELKWBIFk'
. 'rcgokad/0wYswmHzYpj66pRt7gIXSNjuNPdTz2zxAMTkmGbaGtpQkttZaxeX7IQp9tfc'
. '7CLJVF1ImnFSoE211Sohh5YVZ5xnGmSFoQoxW0ijRCR4O0Pt2po10+3obGmSEZ1IsZso'
. 'uVeB87vWo/VxUV4/9ODS51dsxJfkkGEjXun714/Tj1+zqF3iqlT5id6ZN1qlBcVxurvq'
. 'Hq1fxmSr8/uXK/qy/7vc4/j6KY12FtdgUXznBj2+fHk0wDufehHmBCTQYjEEIiTm9die'
. '+WyWP3mq49o7/0mX1/c0winYItvdKg7bmmoQkNpccIMS3GwvgrHG2ux//ZTTAZDJnknE'
. 'caNS9IPlDOahOrc7o0aAPHStLIMl/dtzdmwU3sno0C0KCRJ155clJWBMR9aX3SjrasvS'
. 'X1ObK7DfLvdJO8kSil9sW7KnsqzpWj8MupF49X7GJ8JRNzx0AZcad6REP1t2FKxBB29g'
. '2aok5RFwJFSg5BdtLrp1ssejE9Nx9rvvvmcdOuy+QU5BTsdw5ZyZAK64/yMgYS2Ee9EU'
. 'p8Cu83YfTN6pxADIUYkWxQs0OmNI1LmOY30MQSCZJGepuqiN87InDmmxSkiNolITkyke'
. 'ZLEwJyEmMSECFO8k81qSeO50jBhik2EszBsPe9ES6HTMYsgRGJSnMBsghANgzh84wFc9'
. 'vgUvum4G606c03V1z0xlTSnkT65g4gCyVB+eMZ1276OeDKON9LnnzPB72lHFkzwDSLvm'
. 'ZA3b1KenwDOqdOcOpmtTvnOROwlC9dMqBJ8ISmbCM4MAZYVXIOY8g0q+215z23VpEMiP'
. 'jy7wHYUEBzqRIUXmfGPoqfzIYNCJQjNy3gn/Vkgn2gBlVSWU2EHRQWIvOPmobBDVvbal'
. '226vlNhJ3W/BE06xM5TWAb/O5rfcwiCMeClMqqsNSho1CmoNEDpzAA4wM/XOKKyxilln'
. 'eyziMBfAQYA4zp6M1LIbMYAAAAASUVORK5CYII=';
}
/**
* {@inheritdoc}
*/
public static function create(UrlGenerator $generator, SessionInterface $session, array $options)
{
if (!isset($options['client-id'])) {
throw new InvalidArgumentException('Missing LinkedIn client-id parameter');
}
if (!isset($options['client-secret'])) {
throw new InvalidArgumentException('Missing LinkedIn client-secret parameter');
}
return new Linkedin($generator, $session, new Guzzle(), $options['client-id'], $options['client-secret']);
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Alchemy\Phrasea\Authentication\Provider\Token\Token;
interface ProviderInterface
{
/**
* Returns a unique identifier for the provider.
*
* Allowed characters are a-z and - (minus).
* Examples : twitter => Twitter
* google-plus => GooglePlus
*
* @return string
*/
public function getId();
/**
* Returns an UTF-8 name for the provider.
*
* @return string
*/
public function getName();
/**
* Redirects to the actual authentication provider
*
* @return RedirectResponse
*/
public function authenticate();
/**
* This method is called on provider callback, whenever the auth was
* successful or failure.
*
* @param Application $app
* @param Request $request
*/
public function onCallback(Request $request);
/**
* Returns the identity
*
* @return Identity
*/
public function getIdentity();
/**
* Returns a Token
*
* @return Token
*/
public function getToken();
/**
* Get an URI representing the provider
*
* @return string
*/
public function getIconURI();
/**
* Creates a provider
*
* @param UrlGenerator $generator
* @param SessionInterface $session
* @param array $options
*/
public static function create(UrlGenerator $generator, SessionInterface $session, array $options);
}

View File

@@ -0,0 +1,244 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\Provider\Token\Token;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
use Alchemy\Phrasea\Authentication\Exception\NotAuthenticatedException;
use Alchemy\Phrasea\Exception\RuntimeException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class Twitter extends AbstractProvider
{
private $twitter;
public function __construct(UrlGenerator $generator, SessionInterface $session, \tmhOAuth $twitter)
{
$this->generator = $generator;
$this->twitter = $twitter;
$this->session = $session;
}
/**
* @param \tmhOAuth $twitter
*
* @return Twitter
*/
public function setTwitterClient(\tmhOAuth $twitter)
{
$this->twitter = $twitter;
return $this;
}
/**
* @return \tmhOAuth
*/
public function getTwitterClient()
{
return $this->twitter;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return 'twitter';
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'Twitter';
}
/**
* {@inheritdoc}
*/
public function authenticate()
{
$code = $this->twitter->request(
'POST',
$this->twitter->url('oauth/request_token', ''),
array('oauth_callback' => $this->generator->generate(
'login_authentication_provider_callback',
array('providerId' => $this->getId()),
UrlGenerator::ABSOLUTE_URL
))
);
if ($code != 200) {
throw new RuntimeException('Unable to request twitter token');
}
$oauth = $this->twitter->extract_params($this->twitter->response['response']);
$this->session->set('twitter.provider.oauth', $oauth);
return new RedirectResponse(sprintf(
'%s?%s',
$this->twitter->url("oauth/authenticate", ''),
http_build_query(array('oauth_token' => $oauth['oauth_token']), '', '&')
));
}
/**
* {@inheritdoc}
*/
public function onCallback(Request $request)
{
$oauth = $this->session->get('twitter.provider.oauth');
$this->twitter->config['user_token'] = $oauth['oauth_token'];
$this->twitter->config['user_secret'] = $oauth['oauth_token_secret'];
$code = $this->twitter->request(
'POST',
$this->twitter->url('oauth/access_token', ''),
array('oauth_verifier' => $request->query->get('oauth_verifier'))
);
if ($code != 200) {
throw new RuntimeException('Unable to get twitter access token');
}
$access_token = $this->twitter->extract_params($this->twitter->response['response']);
$this->session->set('twitter.provider.access_token', $access_token);
$this->session->remove('twitter.provider.oauth');
$this->twitter->config['user_token'] = $access_token['oauth_token'];
$this->twitter->config['user_secret'] = $access_token['oauth_token_secret'];
$code = $this->twitter->request(
'GET', $this->twitter->url('1/account/verify_credentials')
);
if ($code != 200) {
throw new RuntimeException('Unable to get twitter credentials');
}
$resp = json_decode($this->twitter->response['response'], true);
$this->session->set('twitter.provider.id', $resp['id']);
}
/**
* {@inheritdoc}
*/
public function getToken()
{
if (0 >= $this->session->get('twitter.provider.id')) {
throw new RuntimeException('Provider has not authenticated');
}
return new Token($this, $this->session->get('twitter.provider.id'));
}
/**
* {@inheritdoc}
*/
public function getIdentity()
{
$access_token = $this->session->get('twitter.provider.access_token');
$this->twitter->config['user_token'] = $access_token['oauth_token'];
$this->twitter->config['user_secret'] = $access_token['oauth_token_secret'];
$code = $this->twitter->request(
'GET', $this->twitter->url('1/account/verify_credentials')
);
if ($code != 200) {
throw new RuntimeException('Unable to retrieve twitter identity');
}
$resp = json_decode($this->twitter->response['response'], true);
$identity = new Identity();
$identity->set(Identity::PROPERTY_USERNAME, $resp['screen_name']);
$identity->set(Identity::PROPERTY_IMAGEURL, $resp['profile_image_url_https']);
$identity->set(Identity::PROPERTY_ID, $resp['id']);
return $identity;
}
/**
* {@inheritdoc}
*/
public function getIconURI()
{
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADEAAAAwCAYAAAC4w'
. 'JK5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MO'
. 'mNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ'
. '2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6b'
. 'WV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgM'
. 'jAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJod'
. 'HRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZ'
. 'XNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZ'
. 'S5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL'
. '3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZ'
. 'G9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZ'
. 'DowMjgwMTE3NDA3MjA2ODExOEMxNEY3NEJCM0UzNjU4QyIgeG1wTU06RG9jdW1lbnRJR'
. 'D0ieG1wLmRpZDpFRjg3OUMwRTcxRUMxMUUyQjc5NzlGRUJDNjcwRkVDMyIgeG1wTU06S'
. 'W5zdGFuY2VJRD0ieG1wLmlpZDpFRjg3OUMwRDcxRUMxMUUyQjc5NzlGRUJDNjcwRkVDM'
. 'yIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpI'
. 'j4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDI4M'
. 'DExNzQwNzIwNjgxMThDMTRGNzRCQjNFMzY1OEMiIHN0UmVmOmRvY3VtZW50SUQ9Inhtc'
. 'C5kaWQ6MDI4MDExNzQwNzIwNjgxMThDMTRGNzRCQjNFMzY1OEMiLz4gPC9yZGY6RGVzY'
. '3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiP'
. 'z67Nf6GAAAEqklEQVR42txaS2zcRBj+vXa8u26UV9NQQtsESFW1OVCVnuASDkXqpRISC'
. 'HGgl9IeUNRDxRFx44B64UaLVFU8hYRUcUC9IFW8GsRDREWKFEAJJTyySTbpZr3rzfoxw'
. 'z/j8darpM44D8nDSP/uOrY8/zf/983/z0w0Simo3nLwP2gG+9A0jX8JY8D02O8sNYLGq'
. 'BPEflND3OQOvzY5c7JZ2PtBw4XDWRxx4nszpZ+/u3Dz/Jmf8NJF89if4yA6Cv0DH95b9'
. 'keqdT+rzHm8MHLiGn6PodloDgNjxLRhUl0foeBDlqWeM/PD+PWQuGSj7UcgmAbyBL0Pk'
. 'GmUZF7LAyIKzJptdGIg2IxLsj/rdqNZzGfme5xOeih5iiAyj6LI6C8YlDPidCM8ClSFS'
. 'Bhi4HOtPHF/EqYQcEqplcXbQARCD4FilUgbCD8CQkD1SDBNEIVBCCBK04kIUVOisLA9B'
. 'OCh/74kBl2Tj9qTvSY8N2TBwT0GVD0C35TW4MZfDu/roGVAB75rdos12zo6MVHLBuL0Y'
. 'BH68zq8N1tLfO6FQxZcHO3mZUHUnujLwzMPF8HHzgYRxNlvl7acn9pnJ8ryBJrk2473m'
. 'XD6gAWFHIUrv9c2dOJAUYfxo11tAKI2itFhkfx8rgZn8D3vbzIYknQSQCRBGMKzlw93w'
. 'TEc2cu/rMJszWt7Zmx/AfSclkjJYz15eGe6LN1v4vKUxOgkY3dW3Puc35uHj8YG4M0Tv'
. 'fDUPhOd0/gzvfnkxWETQ3Hu9hKUm0S6Xwk6ERwRuTxx488avPhYJxdrNKqnHrG4udjbH'
. '1UPrI5kEBUUue0GO5vsfLGmkGmXRnvhru21QMSbiRQ60mNu+o7ltUC6P0lhCzpJvvTfu'
. 'gfPP9qzLQemK+62y5y2WHutjE2l7OOZGiytbY8KtzFfyPYXWbKwWd2EKKikVRoBnL21A'
. 'FMxgadpLOnd+tuR7i+y5LKDz0w4S6SIb2XNh3nHh6EuAzqNdNtU13+tQn2bot5YE0Fos'
. 'm2pHqA4SWoA/2CJcX1qNVVfUiAoCaNAU04Xb0wswkrTh1eOdkNHQmJraQ+n3/GvF8Bu7'
. 'Mz+VtvwUfHBK9kU5iHoyz+U4dnP5mC26iV2yKI9/tUi3Ck1UvcT2aalOHmAeNblAcxsO'
. 'czKnVh+jmC2PjXUCS9h+VE0HhyJElLo1S9LMDnf2MVFEQnrJiJBJ8PQ4dzxPrgw2sOBJ'
. 'LU6Dv+7UxW4NrkMdjOAnW7rMjZbEMlEwsYZ6e2JBbjyYxmeHt4DJ/dbWASasM/S+f0yT'
. 'r/TOPV+P+/AxFwdGrvg/MbCxumVizvFlo3j+vDFb6vcMrM8ZSmCBgovTymvm6jaIAKxS'
. 'UBV3rLhZ0eqR4JTiWtC4X0nEQq16cQ3z9Q4KUrKE1RQSmVNRAlPYToR6jTmUBeHIMMYS'
. 'H1lGcJTCCKsVYqzi6D66dXXSb06v9USebctsMv3nJtvfYK+sjLYjXZeNXZT07Q8XnRBe'
. 'D48jDaI1g/hCaWekSAwh9mRb5lttKDdRVtgS3Uj9kATwlP6xaiChvCU0sgICF9EYFX4a'
. 'AufAyNGJ1fcAIGYRcGE7PyTSuSjI/y0xTX5T4ABAP5AumSxg/sSAAAAAElFTkSuQmCC';
}
/**
* {@inheritdoc}
*/
public static function create(UrlGenerator $generator, SessionInterface $session, array $options)
{
$twitter = new \tmhOAuth(array(
'consumer_key' => $options['consumer-key'],
'consumer_secret' => $options['consumer-secret'],
));
return new Twitter($generator, $session, $twitter);
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace Alchemy\Tests\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Authentication\Provider\Facebook;
use Alchemy\Phrasea\Authentication\Provider\ProviderInterface;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
class FacebookTest extends ProviderTestCase
{
public function testGetSetSession()
{
$this->markTestSkipped('testGetSetSession disabled for facebook');
}
public function testGetSetGuzzleClient()
{
$this->markTestSkipped('testGetSetGuzzleClient disabled for facebook');
}
public function testIsBuiltWithFactory()
{
$this->markTestSkipped('Skipping because \Facebook runs session_start');
}
public function testCreate()
{
$this->markTestSkipped('Skipping because \Facebook runs session_start');
}
public function provideDataForFailingCallback()
{
$provider = $this->getProvider();
$provider->getFacebook()->expects($this->any())
->method('getUser')
->will($this->returnValue(null));
return array(
array($provider, $this->getRequestMock())
);
}
public function provideDataForSuccessCallback()
{
$provider = $this->getProvider();
$provider->getFacebook()->expects($this->any())
->method('getUser')
->will($this->returnValue('123456'));
return array(
array($provider, $this->getRequestMock())
);
}
protected function getProvider()
{
return new Facebook($this->getFacebookMock(), $this->getUrlGeneratorMock());
}
protected function authenticate(ProviderInterface $provider)
{
$provider->getFacebook()->expects($this->any())
->method('getUser')
->will($this->returnValue('123456'));
}
protected function getProviderForSuccessIdentity()
{
$provider = $this->getProvider();
$this->authenticate($provider);
$facebook = $this->getMockBuilder('Facebook')
->disableOriginalConstructor()
->setMethods(array('getLoginUrl', 'api', 'getUser'))
->getMock();
$facebook->expects($this->any())
->method('getLoginUrl')
->will($this->returnValue('http://www.facebook.com/'));
$facebook->expects($this->once())
->method('api')
->will($this->returnValue(array(
'id' => self::ID,
'username' => self::FIRSTNAME,
'first_name' => self::FIRSTNAME,
'last_name' => self::LASTNAME,
'email' => self::EMAIL,
)));
$provider->setFacebook($facebook);
return $provider;
}
protected function getProviderForFailingIdentity()
{
return $this->getProvider();
}
protected function getAvailableFieldsForIdentity()
{
return array(
Identity::PROPERTY_ID => self::ID,
Identity::PROPERTY_USERNAME => self::FIRSTNAME,
Identity::PROPERTY_FIRSTNAME => self::FIRSTNAME,
Identity::PROPERTY_LASTNAME => self::LASTNAME,
Identity::PROPERTY_EMAIL => self::EMAIL,
);
}
protected function getTestOptions()
{
return array(
'app-id' => 'zizi',
'secret' => 's3cr3t',
);
}
protected function getProviderForAuthentication()
{
return $this->getProvider();
}
private function getFacebookMock()
{
$facebook = $this->getMockBuilder('Facebook')
->disableOriginalConstructor()
->setMethods(array('getLoginUrl', 'api', 'getUser'))
->getMock();
$facebook->expects($this->any())
->method('getLoginUrl')
->will($this->returnValue('http://www.facebook.com/'));
$facebook->expects($this->any())
->method('api')
->will($this->returnCallback(function () use ($facebook) {
if (!$facebook->getUser()) {
throw new \FacebookApiException('Not authenticated');
}
}));
return $facebook;
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace Alchemy\Tests\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Authentication\Provider\Github;
use Alchemy\Phrasea\Authentication\Provider\ProviderInterface;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
class GithubTest extends ProviderTestCase
{
public function provideDataForFailingCallback()
{
$state = md5(mt_rand());
$provider = $this->provideFailingProvider();
$provider->getSession()->set('github.provider.state', $state . mt_rand());
$request = $this->getRequestMock();
$this->addQueryParameter($request, array('state' => $state));
return array(
array($this->getProvider(), $this->getRequestMock()),
array($provider, $request),
);
}
public function provideDataForSuccessCallback()
{
$provider = $this->getProvider();
$guzzle = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$requestPost = $this->getMock('Guzzle\Http\Message\EntityEnclosingRequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue(200));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$requestPost->expects($this->any())
->method('send')
->will($this->returnValue($response));
$guzzle->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$guzzle->expects($this->any())
->method('post')
->will($this->returnValue($requestPost));
$provider->setGuzzleClient($guzzle);
$state = md5(mt_rand());
$provider->getSession()->set('github.provider.state', $state);
$request = $this->getRequestMock();
$this->addQueryParameter($request, array('state' => $state));
return array(
array($provider, $request),
);
}
public function getTestOptions()
{
return array(
'client-id' => 'github-client-id',
'client-secret' => 'github-client-secret',
);
}
protected function getProviderForAuthentication()
{
return $this->getProvider();
}
protected function getProviderForSuccessIdentity()
{
$provider = $this->getProvider();
$guzzle = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue(200));
$response->expects($this->any())
->method('getBody')
->with($this->equalTo(true))
->will($this->returnValue(json_encode(array(
'id' => self::ID,
'name' => self::FIRSTNAME . ' ' . self::LASTNAME,
'email' => self::EMAIL,
'avatar_url' => self::IMAGEURL,
))));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$guzzle->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$provider->setGuzzleClient($guzzle);
$provider->getSession()->set('github.provider.id', 'github-id');
return $provider;
}
protected function getProviderForFailingIdentity()
{
return $this->provideFailingProvider();
}
protected function provideFailingProvider()
{
$provider = $this->getProvider();
$guzzle = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue(401));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$guzzle->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$provider->setGuzzleClient($guzzle);
return $provider;
}
protected function getAvailableFieldsForIdentity()
{
return array(
Identity::PROPERTY_ID => self::ID,
Identity::PROPERTY_FIRSTNAME => self::FIRSTNAME,
Identity::PROPERTY_LASTNAME => self::LASTNAME,
Identity::PROPERTY_EMAIL => self::EMAIL,
Identity::PROPERTY_IMAGEURL => self::IMAGEURL,
);
}
protected function authenticate(ProviderInterface $provider)
{
$provider->getSession()->set('github.provider.id', 'github-id');
}
protected function getProvider()
{
return new Github($this->getUrlGeneratorMock(), $this->getMockSession(), $this->getGuzzleMock(), 'key', 'secret');
}
}

View File

@@ -0,0 +1,258 @@
<?php
namespace Alchemy\Tests\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Authentication\Provider\GooglePlus;
use Alchemy\Phrasea\Authentication\Provider\ProviderInterface;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
class GooglePlusTest extends ProviderTestCase
{
public function testGetSetGoogleClient()
{
$provider = $this->getProvider();
$this->assertInstanceOf('Google_client', $provider->getGoogleClient());
$google = $this->getMockBuilder('Google_Client')
->disableOriginalConstructor()
->getMock();
$provider->setGoogleClient($google);
$this->assertEquals($google, $provider->getGoogleClient());
}
public function provideDataForFailingCallback()
{
$provider = $this->getProvider();
$state = md5(mt_rand());
$provider->getSession()->set('google-plus.provider.state', $state . mt_rand());
$request = $this->getRequestMock();
$this->addQueryParameter($request, array('state' => $state));
return array(
array($this->provideFailingProvider(), $this->getRequestMock()),
array($provider, $request),
);
}
public function provideDataForSuccessCallback()
{
$provider = $this->getProvider();
$guzzle = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$requestPost = $this->getMock('Guzzle\Http\Message\EntityEnclosingRequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue(200));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$requestPost->expects($this->any())
->method('send')
->will($this->returnValue($response));
$guzzle->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$guzzle->expects($this->any())
->method('post')
->will($this->returnValue($requestPost));
$provider->setGuzzleClient($guzzle);
$state = md5(mt_rand());
$provider->getSession()->set('google-plus.provider.state', $state);
$request = $this->getRequestMock();
$this->addQueryParameter($request, array(
'state' => $state,
));
$ticket = $this->getMockBuilder('Google_LoginTicket')
->disableOriginalConstructor()
->getMock();
$provider->getGoogleClient()->expects($this->once())
->method('verifyIdToken')
->will($this->returnValue($ticket));
return array(
array($provider, $request),
);
}
public function getTestOptions()
{
return array(
'client-id' => 'google-plus-client-id',
'client-secret' => 'google-plus-client-secret',
);
}
protected function getProviderForSuccessIdentity()
{
$provider = $this->getProvider();
$guzzle = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue(200));
$response->expects($this->any())
->method('getBody')
->with($this->equalTo(true))
->will($this->returnValue(json_encode(array(
'email' => self::EMAIL
))));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$guzzle->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$provider->setGuzzleClient($guzzle);
$provider->getSession()->set('google-plus.provider.id', '12345678');
$people = $this->getMockBuilder('Google_PeopleServiceResource')
->disableOriginalConstructor()
->getMock();
$people->expects($this->once())
->method('get')
->will($this->returnValue(array(
'name' => array(
'givenName' => self::FIRSTNAME,
'familyName' => self::LASTNAME,
),
'id' => self::ID,
'image' => array(
'url' => self::IMAGEURL
)
)));
$provider->getGooglePlusService()->people = $people;
return $provider;
}
protected function getAvailableFieldsForIdentity()
{
return array(
Identity::PROPERTY_ID => self::ID,
Identity::PROPERTY_FIRSTNAME => self::FIRSTNAME,
Identity::PROPERTY_LASTNAME => self::LASTNAME,
Identity::PROPERTY_EMAIL => self::EMAIL,
Identity::PROPERTY_IMAGEURL => self::IMAGEURL,
);
}
protected function getProviderForFailingIdentity()
{
return $this->provideFailingProvider();
}
protected function provideFailingProvider()
{
$provider = $this->getProvider();
$guzzle = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue(401));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$guzzle->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$provider->setGuzzleClient($guzzle);
return $provider;
}
protected function authenticate(ProviderInterface $provider)
{
$provider->getSession()->set('google-plus.provider.id', '12345678');
}
protected function getProvider()
{
$googleMock = $this->getMockBuilder('Google_Client')
->disableOriginalConstructor()
->getMock();
$googleMock->expects($this->any())
->method('createAuthUrl')
->will($this->returnValue('https://www.google.com/auth'));
$plus = $this->getMockBuilder('Google_PlusService')
->disableOriginalConstructor()
->getMock();
$google = new GooglePlus($this->getUrlGeneratorMock(), $this->getMockSession(), $googleMock, $this->getGuzzleMock());
$google->setGooglePlusService($plus);
return $google;
}
protected function getProviderForAuthentication()
{
return $this->getProvider();
}
}

View File

@@ -0,0 +1,280 @@
<?php
namespace Alchemy\Tests\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Authentication\Provider\Linkedin;
use Alchemy\Phrasea\Authentication\Provider\ProviderInterface;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
class LinkedinTest extends ProviderTestCase
{
public function provideDataForFailingCallback()
{
$state = md5(mt_rand());
$request = $this->getRequestMock();
$this->addQueryParameter($request, array('state' => $state));
$provider1 = $this->getProvider();
$provider1->setGuzzleClient($this->getGuzzleMock(401));
$provider1->getSession()->set('linkedin.provider.state', $state);
$mock = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$requestPost = $this->getMock('Guzzle\Http\Message\RequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$queryString->expects($this->any())
->method('add')
->will($this->returnSelf());
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->at(0))
->method('getStatusCode')
->will($this->returnValue(200));
$response->expects($this->at(1))
->method('getStatusCode')
->will($this->returnValue(401));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$requestPost->expects($this->any())
->method('send')
->will($this->returnValue($response));
$mock->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$mock->expects($this->any())
->method('post')
->will($this->returnValue($requestPost));
$provider2 = $this->getProvider();
$provider2->setGuzzleClient($mock);
$provider2->getSession()->set('linkedin.provider.state', $state);
return array(
array($this->getProvider(), $this->getRequestMock()),
array($provider1, $request),
array($provider2, $request),
);
}
public function provideDataForSuccessCallback()
{
$provider = $this->getProvider();
$guzzle = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$requestPost = $this->getMock('Guzzle\Http\Message\EntityEnclosingRequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$queryString->expects($this->any())
->method('add')
->will($this->returnSelf());
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue(200));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$requestPost->expects($this->any())
->method('send')
->will($this->returnValue($response));
$guzzle->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$guzzle->expects($this->any())
->method('post')
->will($this->returnValue($requestPost));
$provider->setGuzzleClient($guzzle);
$state = md5(mt_rand());
$provider->getSession()->set('linkedin.provider.state', $state);
$request = $this->getRequestMock();
$this->addQueryParameter($request, array('state' => $state));
return array(
array($provider, $request),
);
}
public function getTestOptions()
{
return array(
'client-id' => 'linkedin-client-id',
'client-secret' => 'linkedin-client-secret',
);
}
protected function getProviderForSuccessIdentity()
{
$provider = $this->getProvider();
$guzzle = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$queryString->expects($this->any())
->method('add')
->will($this->returnSelf());
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue(200));
$response->expects($this->any())
->method('getBody')
->with($this->equalTo(true))
->will($this->returnValue(json_encode(array(
'positions' => array(
'_total' => 1,
'values' => array(
array(
'company' => array(
'name' => self::COMPANY
)
)
)
),
'emailAddress' => self::EMAIL,
'firstName' => self::FIRSTNAME,
'id' => self::ID,
'pictureUrl' => self::IMAGEURL,
'lastName' => self::LASTNAME,
))));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$guzzle->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$provider->setGuzzleClient($guzzle);
$provider->getSession()->set('linkedin.provider.id', 'linkedin-id');
return $provider;
}
protected function getAvailableFieldsForIdentity()
{
return array(
Identity::PROPERTY_ID => self::ID,
Identity::PROPERTY_FIRSTNAME => self::FIRSTNAME,
Identity::PROPERTY_LASTNAME => self::LASTNAME,
Identity::PROPERTY_EMAIL => self::EMAIL,
Identity::PROPERTY_IMAGEURL => self::IMAGEURL,
Identity::PROPERTY_COMPANY => self::COMPANY,
);
}
protected function getProviderForFailingIdentity()
{
return $this->provideFailingProvider();
}
protected function provideFailingProvider()
{
$provider = $this->getProvider();
$guzzle = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$queryString->expects($this->any())
->method('add')
->will($this->returnSelf());
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue(401));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$guzzle->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$provider->setGuzzleClient($guzzle);
return $provider;
}
protected function getProviderForAuthentication()
{
return $this->getProvider();
}
protected function authenticate(ProviderInterface $provider)
{
$provider->getSession()->set('linkedin.provider.id', 'linkedin-id');
}
protected function getProvider()
{
return new Linkedin($this->getUrlGeneratorMock(), $this->getMockSession(), $this->getGuzzleMock(), 'key', 'secret');
}
}

View File

@@ -0,0 +1,262 @@
<?php
namespace Alchemy\Tests\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Authentication\Provider\ProviderInterface;
use DataURI\Parser;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
use Alchemy\Phrasea\Authentication\Provider\Factory as ProviderFactory;
use Guzzle\Http\Message\RequestInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;
use Symfony\Component\HttpFoundation\Session\Session;
abstract class ProviderTestCase extends \PHPUnit_Framework_TestCase
{
protected $session;
const COMPANY = 'Company test';
const EMAIL = 'email@test.com';
const FIRSTNAME = 'first-name';
const USERNAME = 'user-name';
const LASTNAME = 'last-name';
const ID = '1234567890';
const IMAGEURL = 'https://www.home.org/image.png';
public function testGetId()
{
$this->assertInternalType('string', $this->getProvider()->getId());
}
public function testGetSetUrlGenerator()
{
$provider = $this->getProvider();
$this->assertInstanceOf('Symfony\Component\Routing\Generator\UrlGenerator', $provider->getUrlGenerator());
$generator = $this->getUrlGeneratorMock();
$provider->setUrlGenerator($generator);
$this->assertEquals($generator, $provider->getUrlGenerator());
}
public function testGetSetSession()
{
$provider = $this->getProvider();
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\SessionInterface', $provider->getSession());
$session = $this->getMockSession();
$provider->setSession($session);
$this->assertEquals($session, $provider->getSession());
}
public function testGetSetGuzzleClient()
{
$provider = $this->getProvider();
$this->assertInstanceOf('Guzzle\Http\CLientInterface', $provider->getGuzzleClient());
$guzzle = $this->getGuzzleMock();
$provider->setGuzzleClient($guzzle);
$this->assertEquals($guzzle, $provider->getGuzzleClient());
}
public function testGetName()
{
$this->assertInternalType('string', $this->getProvider()->getName());
}
public function testIsBuiltWithFactory()
{
$provider = $this->getProvider();
$built = $this->getProviderFactory()->build($provider->getId(), $this->getTestOptions());
$this->assertInstanceOf(get_class($provider), $built);
}
public function testAuthenticate()
{
$provider = $this->getProviderForAuthentication();
$redirect = $provider->authenticate();
$this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $redirect);
}
/**
* @dataProvider provideDataForSuccessCallback
*/
public function testOnCallbackWithSuccess(ProviderInterface $provider, $request)
{
$provider->onCallback($request);
}
/**
* @dataProvider provideDataForFailingCallback
* @expectedException Alchemy\Phrasea\Exception\RuntimeException
*/
public function testOnCallbackWithFailure($provider, $request)
{
$provider->onCallback($request);
}
public function testGetToken()
{
$provider = $this->getProvider();
$this->authenticate($provider);
$token = $provider->getToken();
$this->assertInstanceOf('Alchemy\Phrasea\Authentication\Provider\Token\Token', $token);
$this->assertEquals($provider, $token->getProvider());
}
/**
* @expectedException Alchemy\Phrasea\Exception\RuntimeException
*/
public function testGetTokenWhenNotAuthenticated()
{
$this->getProvider()->getToken();
}
public function testGetIdentity()
{
$provider = $this->getProviderForSuccessIdentity();
$identity = $provider->getIdentity();
$this->assertInstanceOf('Alchemy\Phrasea\Authentication\Provider\Token\Identity', $identity);
foreach ($this->getAvailableFieldsForIdentity() as $name=>$value) {
$this->assertEquals($value, $identity->get($name), "testing $name");
}
}
/**
* @expectedException Alchemy\Phrasea\Exception\RuntimeException
*/
public function testGetIdentityWhenNotAuthenticated()
{
$provider = $this->getProviderForFailingIdentity();
$provider->getIdentity();
}
public function testGetIconURI()
{
Parser::parse($this->getProvider()->getIconURI());
}
public function testCreate()
{
$name = get_class($this->getProvider());
$provider = $name::create($this->getUrlGeneratorMock(), $this->getMockSession(), $this->getTestOptions());
$this->assertInstanceOf($name, $provider);
}
abstract public function provideDataForFailingCallback();
abstract public function provideDataForSuccessCallback();
protected function addQueryParameter(Request $request, array $parameters)
{
$query = $this->getMockBuilder('Symfony\Component\HttpFoundation\ParameterBag')
->disableOriginalConstructor()
->getMock();
$query->expects($this->any())
->method('get')
->will($this->returnCallback(function ($param) use ($parameters) {
if (isset($parameters[$param])) {
return $parameters[$param];
}
}));
$request->query = $query;
}
protected function getMockSession()
{
return new Session(new MockFileSessionStorage());
}
abstract protected function authenticate(ProviderInterface $provider);
abstract protected function getProviderForAuthentication();
abstract protected function getProviderForSuccessIdentity();
abstract protected function getProviderForFailingIdentity();
abstract protected function getAvailableFieldsForIdentity();
/**
* @return ProviderInterface
*/
abstract protected function getProvider();
/**
* @return array
*/
abstract protected function getTestOptions();
protected function getUrlGeneratorMock()
{
return $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGenerator')
->disableOriginalConstructor()
->getMock();
}
protected function getRequestMock()
{
return $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')
->disableOriginalConstructor()
->getMock();
}
protected function getGuzzleMock($statusCode = 200)
{
$mock = $this->getMock('Guzzle\Http\ClientInterface');
$requestGet = $this->getMock('Guzzle\Http\Message\RequestInterface');
$requestPost = $this->getMock('Guzzle\Http\Message\RequestInterface');
$queryString = $this->getMockBuilder('Guzzle\Http\QueryString')
->disableOriginalConstructor()
->getMock();
$requestGet->expects($this->any())
->method('getQuery')
->will($this->returnValue($queryString));
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->disableOriginalConstructor()
->getMock();
$response->expects($this->any())
->method('getStatusCode')
->will($this->returnValue($statusCode));
$requestGet->expects($this->any())
->method('send')
->will($this->returnValue($response));
$requestPost->expects($this->any())
->method('send')
->will($this->returnValue($response));
$mock->expects($this->any())
->method('get')
->will($this->returnValue($requestGet));
$mock->expects($this->any())
->method('post')
->will($this->returnValue($requestPost));
return $mock;
}
/**
* @return ProviderFactory
*/
private function getProviderFactory()
{
return new ProviderFactory($this->getUrlGeneratorMock(), $this->getMockSession());
}
}

View File

@@ -0,0 +1,214 @@
<?php
namespace Alchemy\Tests\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Authentication\Provider\Twitter;
use Alchemy\Phrasea\Authentication\Provider\ProviderInterface;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
class TwitterTest extends ProviderTestCase
{
public function testGetSetGuzzleClient()
{
$this->markTestSkipped('testGetSetGuzzleClient disabled for facebook');
}
public function testGetSetTwitterClient()
{
$provider = $this->getProvider();
$this->assertInstanceOf('tmhOAuth', $provider->getTwitterClient());
$client = $this->getMockBuilder('tmhOAuth')
->disableOriginalConstructor()
->getMock();
$provider->setTwitterClient($client);
$this->assertEquals($client, $provider->getTwitterClient());
}
/**
* @expectedException Alchemy\Phrasea\Exception\RuntimeException
*/
public function testAuthenticateWithFailure()
{
$provider = $this->getProvider();
$provider->getTwitterClient()->expects($this->once())
->method('request')
->will($this->returnValue(401));
$provider->authenticate();
}
public function getProviderForAuthentication()
{
$provider = $this->getProvider();
$provider->getTwitterClient()->expects($this->once())
->method('request')
->will($this->returncallback(function () use ($provider) {
$provider->getTwitterClient()->response = array(
'response' => array(
'oauth_token' => 'twitter-oauth-token',
)
);
return 200;
}));
return $provider;
}
public function provideDataForFailingCallback()
{
$request = $this->getRequestMock();
$this->addQueryParameter($request, array());
$provider1 = $this->getProvider();
$provider1->getTwitterClient()->expects($this->once())
->method('request')
->will($this->returnValue(401));
$first = true;
$provider2 = $this->getProvider();
$provider2->getTwitterClient()->expects($this->exactly(2))
->method('request')
->will($this->returnCallback(function () use (&$first, $provider2) {
if (!$first) {
return 401;
} else {
$provider2->getTwitterClient()->response = array(
'response' => array(
'oauth_token' => 'twitter-oauth-token',
)
);
$first = false;
return 200;
}
}));
return array(
array($provider1, $request),
array($provider2, $request),
);
}
public function provideDataForSuccessCallback()
{
$provider = $this->getProvider();
$state = md5(mt_rand());
$provider->getSession()->set('twitter.provider.state', $state);
$request = $this->getRequestMock();
$this->addQueryParameter($request, array('state' => $state));
$provider->getTwitterClient()->expects($this->any())
->method('request')
->will($this->returnCallback(function ($method) use ($provider) {
switch($method) {
case 'POST':
$provider->getTwitterClient()->response = array(
'response' => array(
'oauth_token' => 'twitter-oauth-token',
)
);
break;
case 'GET':
$provider->getTwitterClient()->response = array(
'response' => json_encode(array(
'id' => self::ID,
))
);
break;
default:
throw new \InvalidArgumentException(sprintf('Invalid method %s', $method));
break;
}
return 200;
}));
$provider->getTwitterClient()->expects($this->once())
->method('extract_params')
->will($this->returnValue(array(
'oauth_token_secret' => 'token secret',
'oauth_token' => 'token',
)));
return array(
array($provider, $request),
);
}
public function getTestOptions()
{
return array(
'consumer-key' => 'twitter-consumer-key',
'consumer-secret' => 'twitter-consumer-secret',
);
}
protected function getProviderForSuccessIdentity()
{
$provider = $this->getProvider();
$provider->getSession()->set('twitter.provider.access_token', array(
'oauth_token' => 'twitter token',
'oauth_token_secret' => 'token secret',
));
$provider->getTwitterClient()->expects($this->once())
->method('request')
->will($this->returncallback(function () use ($provider) {
$provider->getTwitterClient()->response = array(
'response' => json_encode(array(
'screen_name' => self::USERNAME,
'profile_image_url_https' => self::IMAGEURL,
'id' => self::ID,
))
);
return 200;
}));
return $provider;
}
protected function getAvailableFieldsForIdentity()
{
return array(
Identity::PROPERTY_ID => self::ID,
Identity::PROPERTY_USERNAME => self::USERNAME,
Identity::PROPERTY_IMAGEURL => self::IMAGEURL,
);
}
protected function getProviderForFailingIdentity()
{
return $this->provideFailingProvider();
}
protected function provideFailingProvider()
{
$provider = $this->getProvider();
return $provider;
}
protected function authenticate(ProviderInterface $provider)
{
$provider->getSession()->set('twitter.provider.id', '12345');
}
protected function getProvider()
{
$twitter = $this->getMockBuilder('tmhOAuth')
->disableOriginalConstructor()
->getMock();
$twitter->config = $this->getTestOptions();
return new Twitter($this->getUrlGeneratorMock(), $this->getMockSession(), $twitter);
}
}