Commit 776a9250 by Paul Klimov

"yii\authclient" structure refactored.

parent 1ef606da
......@@ -159,7 +159,7 @@ class AuthAction extends Action
'url' => $url,
'enforceRedirect' => $enforceRedirect,
];
$viewFile = __DIR__ . DIRECTORY_SEPARATOR . 'provider' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'redirect.php';
$viewFile = __DIR__ . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'redirect.php';
$response = Yii::$app->getResponse();
$response->content = Yii::$app->getView()->renderFile($viewFile, $viewData);
......@@ -208,7 +208,7 @@ class AuthAction extends Action
'id' => $provider->identity
);
$rawAttributes = $provider->getAttributes();
foreach ($provider->getRequiredAttributes() as $openIdAttributeName) {
foreach ($provider->requiredAttributes as $openIdAttributeName) {
if (isset($rawAttributes[$openIdAttributeName])) {
$attributes[$openIdAttributeName] = $rawAttributes[$openIdAttributeName];
} else {
......@@ -216,7 +216,6 @@ class AuthAction extends Action
}
}
$provider->setAttributes($attributes);
$provider->isAuthenticated = true;
return $this->authenticateSuccess($provider);
} else {
throw new Exception('Unable to complete the authentication because the required data was not received.');
......@@ -231,10 +230,6 @@ class AuthAction extends Action
}
} else {
$provider->identity = $provider->authUrl; // Setting identifier
$provider->required = []; // Try to get info from openid provider
foreach ($provider->getRequiredAttributes() as $openIdAttributeName) {
$this->required[] = $openIdAttributeName;
}
$request = Yii::$app->getRequest();
$provider->realm = $request->getHostInfo();
$provider->returnUrl = $provider->realm . $request->getUrl(); // getting return URL
......@@ -270,7 +265,6 @@ class AuthAction extends Action
} else {
// Upgrade to access token.
$accessToken = $provider->fetchAccessToken();
$provider->isAuthenticated = true;
return $this->authenticateSuccess($provider);
}
}
......@@ -304,7 +298,6 @@ class AuthAction extends Action
$code = $_GET['code'];
$token = $provider->fetchAccessToken($code);
if (!empty($token)) {
$provider->isAuthenticated = true;
return $this->authenticateSuccess($provider);
} else {
return $this->redirectCancel();
......
......@@ -5,7 +5,7 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\provider;
namespace yii\authclient;
/**
* Class ProviderInterface
......@@ -13,7 +13,7 @@ namespace yii\authclient\provider;
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
interface ProviderInterface
interface ClientInterface
{
/**
* @param string $id service id.
......@@ -46,28 +46,7 @@ interface ProviderInterface
public function setTitle($title);
/**
* @param string $url successful URL.
* @return array list of user attributes
*/
public function setSuccessUrl($url);
/**
* @return string successful URL.
*/
public function getSuccessUrl();
/**
* @param string $url cancel URL.
*/
public function setCancelUrl($url);
/**
* @return string cancel URL.
*/
public function getCancelUrl();
/**
* Authenticate the user.
* @return \yii\web\Response|boolean response instance or whether user was successfully authenticated.
*/
public function authenticate();
public function getUserAttributes();
}
\ No newline at end of file
......@@ -5,20 +5,21 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\provider;
namespace yii\authclient;
use Yii;
use yii\base\NotSupportedException;
use yii\helpers\StringHelper;
/**
* Class ProviderTrait
*
* @see ProviderInterface
* @see ClientInterface
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
trait ProviderTrait
trait ClientTrait
{
/**
* @var string service id.
......@@ -35,13 +36,9 @@ trait ProviderTrait
*/
private $_title;
/**
* @var string the redirect url after successful authorization.
* @var array authenticated user attributes.
*/
private $_successUrl = '';
/**
* @var string the redirect url after unsuccessful authorization (e.g. user canceled).
*/
private $_cancelUrl = '';
private $_userAttributes;
/**
* @param string $id service id.
......@@ -101,41 +98,22 @@ trait ProviderTrait
}
/**
* @param string $url successful URL.
* @return array list of user attributes
*/
public function setSuccessUrl($url)
public function getUserAttributes()
{
$this->_successUrl = $url;
if ($this->_userAttributes === null) {
$this->_userAttributes = $this->initUserAttributes();
}
/**
* @return string successful URL.
*/
public function getSuccessUrl()
{
if (empty($this->_successUrl)) {
$this->_successUrl = $this->defaultSuccessUrl();
}
return $this->_successUrl;
}
/**
* @param string $url cancel URL.
*/
public function setCancelUrl($url)
{
$this->_cancelUrl = $url;
return $this->_userAttributes;
}
/**
* @return string cancel URL.
* @param array $userAttributes list of user attributes
*/
public function getCancelUrl()
public function setUserAttributes(array $userAttributes)
{
if (empty($this->_cancelUrl)) {
$this->_cancelUrl = $this->defaultCancelUrl();
}
return $this->_cancelUrl;
$this->_userAttributes = $userAttributes;
}
/**
......@@ -157,65 +135,11 @@ trait ProviderTrait
}
/**
* Creates default {@link successUrl} value.
* @return string success URL value.
*/
protected function defaultSuccessUrl()
{
return Yii::$app->getUser()->getReturnUrl();
}
/**
* Creates default {@link cancelUrl} value.
* @return string cancel URL value.
* Initializes authenticated user attributes.
* @return array auth user attributes.
*/
protected function defaultCancelUrl()
protected function initUserAttributes()
{
return Yii::$app->getRequest()->getAbsoluteUrl();
}
/**
* Redirect to the given URL or simply close the popup window.
* @param mixed $url URL to redirect, could be a string or array config to generate a valid URL.
* @param boolean $enforceRedirect indicates if redirect should be performed even in case of popup window.
* @return \yii\web\Response response instance.
*/
public function redirect($url, $enforceRedirect = true)
{
$viewData = [
'url' => $url,
'enforceRedirect' => $enforceRedirect,
];
$viewFile = __DIR__ . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'redirect.php';
$response = Yii::$app->getResponse();
$response->content = Yii::$app->getView()->renderFile($viewFile, $viewData);
return $response;
}
/**
* Redirect to the URL. If URL is null, {@link successUrl} will be used.
* @param string $url URL to redirect.
* @return \yii\web\Response response instance.
*/
public function redirectSuccess($url = null)
{
if ($url === null) {
$url = $this->getSuccessUrl();
}
return $this->redirect($url);
}
/**
* Redirect to the {@link cancelUrl} or simply close the popup window.
* @param string $url URL to redirect.
* @return \yii\web\Response response instance.
*/
public function redirectCancel($url = null)
{
if ($url === null) {
$url = $this->getCancelUrl();
}
return $this->redirect($url, false);
throw new NotSupportedException('Method "' . get_class($this) . '::' . __FUNCTION__ . '" not implemented.');
}
}
\ No newline at end of file
......@@ -5,27 +5,27 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\provider;
namespace yii\authclient;
use yii\base\Component;
use yii\base\InvalidParamException;
use Yii;
/**
* Collection is a storage for all auth providers in the application.
* Collection is a storage for all auth clients in the application.
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'auth' => [
* 'class' => 'yii\authclient\provider\Collection',
* 'providers' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'google' => [
* 'class' => 'yii\authclient\provider\GoogleOpenId'
* 'class' => 'yii\authclient\clients\GoogleOpenId'
* ],
* 'facebook' => [
* 'class' => 'yii\authclient\provider\Facebook',
* 'class' => 'yii\authclient\clients\Facebook',
* 'clientId' => 'facebook_client_id',
* 'clientSecret' => 'facebook_client_secret',
* ],
......@@ -35,69 +35,71 @@ use Yii;
* ]
* ~~~
*
* @property array $clients list of Auth clients with their configuration in format: 'clientId' => [...]
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class Collection extends Component
{
/**
* @var array list of Auth providers with their configuration in format: 'providerId' => [...]
* @var array list of Auth clients with their configuration in format: 'clientId' => [...]
*/
private $_providers = [];
private $_clients = [];
/**
* @param array $providers list of auth providers
* @param array $clients list of auth clients
*/
public function setProviders(array $providers)
public function setClients(array $clients)
{
$this->_providers = $providers;
$this->_clients = $clients;
}
/**
* @return ProviderInterface[] list of auth providers.
* @return ClientInterface[] list of auth clients.
*/
public function getProviders()
public function getClients()
{
$providers = [];
foreach ($this->_providers as $id => $provider) {
$providers[$id] = $this->getProvider($id);
$clients = [];
foreach ($this->_clients as $id => $client) {
$clients[$id] = $this->getClient($id);
}
return $providers;
return $clients;
}
/**
* @param string $id service id.
* @return ProviderInterface auth service instance.
* @throws InvalidParamException on non existing provider request.
* @return ClientInterface auth client instance.
* @throws InvalidParamException on non existing client request.
*/
public function getProvider($id)
public function getClient($id)
{
if (!array_key_exists($id, $this->_providers)) {
throw new InvalidParamException("Unknown auth provider '{$id}'.");
if (!array_key_exists($id, $this->_clients)) {
throw new InvalidParamException("Unknown auth client '{$id}'.");
}
if (!is_object($this->_providers[$id])) {
$this->_providers[$id] = $this->createProvider($id, $this->_providers[$id]);
if (!is_object($this->_clients[$id])) {
$this->_clients[$id] = $this->createClient($id, $this->_clients[$id]);
}
return $this->_providers[$id];
return $this->_clients[$id];
}
/**
* Checks if provider exists in the hub.
* @param string $id provider id.
* @return boolean whether provider exist.
* Checks if client exists in the hub.
* @param string $id client id.
* @return boolean whether client exist.
*/
public function hasProvider($id)
public function hasClient($id)
{
return array_key_exists($id, $this->_providers);
return array_key_exists($id, $this->_clients);
}
/**
* Creates auth provider instance from its array configuration.
* @param string $id auth provider id.
* @param array $config auth provider instance configuration.
* @return ProviderInterface auth provider instance.
* Creates auth client instance from its array configuration.
* @param string $id auth client id.
* @param array $config auth client instance configuration.
* @return ClientInterface auth client instance.
*/
protected function createProvider($id, $config)
protected function createClient($id, $config)
{
$config['id'] = $id;
return Yii::createObject($config);
......
......@@ -27,7 +27,10 @@ use yii\base\NotSupportedException;
*/
class OpenId extends Component
{
public $required = [];
/**
* @var array list of attributes, which should be requested from server.
*/
public $requiredAttributes = [];
public $optional = [];
public $verify_peer;
public $capath;
......@@ -501,9 +504,9 @@ class OpenId extends Component
# That's because it's fully backwards compatibile with 1.0, and some providers
# advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com
$params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1';
if ($this->required) {
if ($this->requiredAttributes) {
$params['openid.sreg.required'] = [];
foreach ($this->required as $required) {
foreach ($this->requiredAttributes as $required) {
if (!isset(self::$axToSregMap[$required])) {
continue;
}
......@@ -528,7 +531,7 @@ class OpenId extends Component
protected function axParams()
{
$params = [];
if ($this->required || $this->optional) {
if ($this->requiredAttributes || $this->optional) {
$params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0';
$params['openid.ax.mode'] = 'fetch_request';
$this->aliases = [];
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\provider;
use Yii;
/**
* Class OAuth1
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class OAuth1 extends \yii\authclient\OAuth1 implements ProviderInterface
{
use ProviderTrait;
/**
* @inheritdoc
*/
public function authenticate()
{
// user denied error
if (isset($_GET['denied'])) {
return $this->redirectCancel();
}
if (isset($_REQUEST['oauth_token'])) {
$oauthToken = $_REQUEST['oauth_token'];
}
if (!isset($oauthToken)) {
// Get request token.
$requestToken = $this->fetchRequestToken();
// Get authorization URL.
$url = $this->buildAuthUrl($requestToken);
// Redirect to authorization URL.
return Yii::$app->getResponse()->redirect($url);
} else {
// Upgrade to access token.
$accessToken = $this->fetchAccessToken();
$this->isAuthenticated = true;
}
return $this->isAuthenticated;
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\provider;
use Yii;
use yii\base\Exception;
/**
* Class OAuth2
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class OAuth2 extends \yii\authclient\OAuth2 implements ProviderInterface
{
use ProviderTrait;
/**
* @inheritdoc
*/
public function authenticate()
{
if (isset($_GET['error'])) {
if ($_GET['error'] == 'access_denied') {
// user denied error
return $this->redirectCancel();
} else {
// request error
if (isset($_GET['error_description'])) {
$errorMessage = $_GET['error_description'];
} elseif (isset($_GET['error_message'])) {
$errorMessage = $_GET['error_message'];
} else {
$errorMessage = http_build_query($_GET);
}
throw new Exception('Auth error: ' . $errorMessage);
}
}
// Get the access_token and save them to the session.
if (isset($_GET['code'])) {
$code = $_GET['code'];
$token = $this->fetchAccessToken($code);
if (!empty($token)) {
$this->isAuthenticated = true;
}
} else {
$url = $this->buildAuthUrl();
return Yii::$app->getResponse()->redirect($url);
}
return $this->isAuthenticated;
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\provider;
use Yii;
use yii\base\Exception;
use yii\web\HttpException;
/**
* Class OpenId
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class OpenId extends \yii\authclient\OpenId implements ProviderInterface
{
use ProviderTrait;
/**
* @inheritdoc
*/
public function authenticate()
{
if (!empty($_REQUEST['openid_mode'])) {
switch ($_REQUEST['openid_mode']) {
case 'id_res':
if ($this->validate()) {
$attributes = array(
'id' => $this->identity
);
$rawAttributes = $this->getAttributes();
foreach ($this->getRequiredAttributes() as $openIdAttributeName) {
if (isset($rawAttributes[$openIdAttributeName])) {
$attributes[$openIdAttributeName] = $rawAttributes[$openIdAttributeName];
} else {
throw new Exception('Unable to complete the authentication because the required data was not received.');
}
}
$this->setAttributes($attributes);
$this->isAuthenticated = true;
return true;
} else {
throw new Exception('Unable to complete the authentication because the required data was not received.');
}
break;
case 'cancel':
$this->redirectCancel();
break;
default:
throw new HttpException(400);
break;
}
} else {
$this->identity = $this->authUrl; // Setting identifier
$this->required = []; // Try to get info from openid provider
foreach ($this->getRequiredAttributes() as $openIdAttributeName) {
$this->required[] = $openIdAttributeName;
}
$request = Yii::$app->getRequest();
$this->realm = $request->getHostInfo();
$this->returnUrl = $this->realm . $request->getUrl(); // getting return URL
$url = $this->authUrl();
return Yii::$app->getResponse()->redirect($url);
}
return false;
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient\provider;
namespace yiiunit\extensions\authclient;
use yii\authclient\provider\ProviderInterface;
use yii\authclient\provider\ProviderTrait;
use yii\authclient\ClientInterface;
use yii\authclient\ClientTrait;
use yii\base\Object;
use yiiunit\extensions\authclient\TestCase;
class ProviderTraitTest extends TestCase
class ClientTraitTest extends TestCase
{
protected function setUp()
{
......@@ -28,63 +26,33 @@ class ProviderTraitTest extends TestCase
public function testSetGet()
{
$provider = new Provider();
$provider = new Client();
$id = 'test_service_id';
$id = 'test_id';
$provider->setId($id);
$this->assertEquals($id, $provider->getId(), 'Unable to setup id!');
$successUrl = 'http://test.success.url';
$provider->setSuccessUrl($successUrl);
$this->assertEquals($successUrl, $provider->getSuccessUrl(), 'Unable to setup success URL!');
$name = 'test_name';
$provider->setName($name);
$this->assertEquals($name, $provider->getName(), 'Unable to setup name!');
$cancelUrl = 'http://test.cancel.url';
$provider->setCancelUrl($cancelUrl);
$this->assertEquals($cancelUrl, $provider->getCancelUrl(), 'Unable to setup cancel URL!');
$title = 'test_title';
$provider->setTitle($title);
$this->assertEquals($title, $provider->getTitle(), 'Unable to setup title!');
}
public function testGetDescriptiveData()
{
$provider = new Provider();
$provider = new Client();
$this->assertNotEmpty($provider->getName(), 'Unable to get name!');
$this->assertNotEmpty($provider->getTitle(), 'Unable to get title!');
}
/**
* @depends testSetGet
*/
public function testGetDefaultSuccessUrl()
{
$provider = new Provider();
$this->assertNotEmpty($provider->getSuccessUrl(), 'Unable to get default success URL!');
}
/**
* @depends testSetGet
*/
public function testGetDefaultCancelUrl()
{
$provider = new Provider();
$this->assertNotEmpty($provider->getSuccessUrl(), 'Unable to get default cancel URL!');
}
public function testRedirect()
{
$provider = new Provider();
$url = 'http://test.url';
$response = $provider->redirect($url, true);
$this->assertContains($url, $response->content);
}
}
class Provider extends Object implements ProviderInterface
class Client extends Object implements ClientInterface
{
use ProviderTrait;
use ClientTrait;
public function authenticate() {}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient\provider;
namespace yiiunit\extensions\authclient;
use yii\authclient\provider\Collection;
use yii\authclient\provider\ProviderInterface;
use yii\authclient\provider\ProviderTrait;
use yii\authclient\Collection;
use yii\authclient\ClientInterface;
use yii\authclient\ClientTrait;
use yii\base\Object;
use yiiunit\extensions\authclient\TestCase;
......@@ -16,12 +16,12 @@ class CollectionTest extends TestCase
{
$collection = new Collection();
$providers = [
'testProvider1' => new TestProvider(),
'testProvider2' => new TestProvider(),
$clients = [
'testClient1' => new TestClient(),
'testClient2' => new TestClient(),
];
$collection->setProviders($providers);
$this->assertEquals($providers, $collection->getProviders(), 'Unable to setup providers!');
$collection->setClients($clients);
$this->assertEquals($clients, $collection->getClients(), 'Unable to setup clients!');
}
/**
......@@ -31,14 +31,14 @@ class CollectionTest extends TestCase
{
$collection = new Collection();
$providerId = 'testProviderId';
$provider = new TestProvider();
$providers = [
$providerId => $provider
$clientId = 'testClientId';
$client = new TestClient();
$clients = [
$clientId => $client
];
$collection->setProviders($providers);
$collection->setClients($clients);
$this->assertEquals($provider, $collection->getProvider($providerId), 'Unable to get provider by id!');
$this->assertEquals($client, $collection->getClient($clientId), 'Unable to get client by id!');
}
/**
......@@ -48,18 +48,18 @@ class CollectionTest extends TestCase
{
$collection = new Collection();
$providerId = 'testProviderId';
$providerClassName = TestProvider::className();
$providers = [
$providerId => [
'class' => $providerClassName
$clientId = 'testClientId';
$clientClassName = TestClient::className();
$clients = [
$clientId => [
'class' => $clientClassName
]
];
$collection->setProviders($providers);
$collection->setClients($clients);
$provider = $collection->getProvider($providerId);
$this->assertTrue(is_object($provider), 'Unable to create provider by config!');
$this->assertTrue(is_a($provider, $providerClassName), 'Provider has wrong class name!');
$provider = $collection->getClient($clientId);
$this->assertTrue(is_object($provider), 'Unable to create client by config!');
$this->assertTrue(is_a($provider, $clientClassName), 'Client has wrong class name!');
}
/**
......@@ -69,22 +69,22 @@ class CollectionTest extends TestCase
{
$collection = new Collection();
$providerName = 'testProviderName';
$providers = [
$providerName => [
'class' => 'TestProvider1'
$clientName = 'testClientName';
$clients = [
$clientName => [
'class' => 'TestClient1'
],
];
$collection->setProviders($providers);
$collection->setClients($clients);
$this->assertTrue($collection->hasProvider($providerName), 'Existing provider check fails!');
$this->assertFalse($collection->hasProvider('unExistingProviderName'), 'Not existing provider check fails!');
$this->assertTrue($collection->hasClient($clientName), 'Existing client check fails!');
$this->assertFalse($collection->hasClient('unExistingClientName'), 'Not existing client check fails!');
}
}
class TestProvider extends Object implements ProviderInterface
class TestClient extends Object implements ClientInterface
{
use ProviderTrait;
use ClientTrait;
public function authenticate() {}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment