Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge pull request zendframework/zendframework#7310 from kanellov/fea…
Browse files Browse the repository at this point in the history
…ture/auth_callback_adapter

Callback Authentication Adapter
  • Loading branch information
Show file tree
Hide file tree
Showing 2 changed files with 253 additions and 0 deletions.
91 changes: 91 additions & 0 deletions src/Adapter/Callback.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Authentication\Adapter;

use Zend\Authentication\Exception\InvalidArgumentException;
use Zend\Authentication\Exception\RuntimeException;
use Zend\Authentication\Result;

/**
* Authentication Adapter authenticates using callback function.
* The Callback function must return the identity on authentication success
* and false on authentication failure.
*/
class Callback extends AbstractAdapter
{
/**
* @var callable
*/
protected $callback;

/**
* Class constructor
*
* @param callable $callback the autentication callback
*/
public function __construct($callback = null)
{
if (!is_null($callback)) {
$this->setCallback($callback);
}
}

/**
* Authenticate
* @return \Zend\Authentication\Result the authentication result
* @throws RuntimeException
*/
public function authenticate()
{
$callback = $this->getCallback();
if (is_null($callback)) {
throw new RuntimeException('No callback provided');
}
try {
$identity = call_user_func($callback, $this->getIdentity(), $this->getCredential());
if (!$identity) {
return new Result(Result::FAILURE, null, array('Authentication failure'));
}
} catch (\Exception $e) {
return new Result(Result::FAILURE_UNCATEGORIZED, null, array($e->getMessage()));
}

return new Result(Result::SUCCESS, $identity, array('Authentication success'));
}

/**
* Gets the value of callback.
*
* @return mixed
*/
public function getCallback()
{
return $this->callback;
}

/**
* Sets the value of callback.
*
* @param mixed $callback the callback
* @throws InvalidArgumentException
*
* @return self
*/
public function setCallback($callback)
{
if (!is_callable($callback)) {
throw new InvalidArgumentException('Invalid callback provided');
}

$this->callback = $callback;

return $this;
}
}
162 changes: 162 additions & 0 deletions test/Adapter/CallbackTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_Authentication
*/

namespace ZendTest\Authentication\Adapter;

use Zend\Authentication;
use Zend\Authentication\Adapter;

/**
* @group Zend_Auth
*/
class CallbackTest extends \PHPUnit_Framework_TestCase
{
/**
* Callback authentication adapter
*
* @var \Zend\Authentication\Adapter\Callback
*/
protected $_adapter = null;

/**
* Set up test configuration
*/
public function setUp()
{
$this->_setupAuthAdapter();
}

public function tearDown()
{
$this->_adapter = null;
}

/**
* Ensures expected behavior for an invalid callback
*/
public function testSetCallbackThrowsException()
{
$this->setExpectedException(
'Zend\Authentication\Exception\InvalidArgumentException',
'Invalid callback provided'
);
$this->_adapter->setCallback('This is not a valid callback');
}

/**
* Ensures setter/getter behaviour for callback
*/
public function testCallbackSetGetMethods()
{
$callback = function () {
};
$this->_adapter->setCallback($callback);
$this->assertEquals($callback, $this->_adapter->getCallback());
}

/**
* Ensures constructor sets callback if provided
*/
public function testClassConstructorSetCallback()
{
$callback = function () {
};
$adapter = new Adapter\Callback($callback);
$this->assertEquals($callback, $adapter->getCallback());
}

/**
* Ensures authenticate throws Exception if no callback is defined
*/
public function testAuthenticateThrowsException()
{
$this->setExpectedException(
'Zend\Authentication\Exception\RuntimeException',
'No callback provided'
);
$this->_adapter->authenticate();
}

/**
* Ensures identity and credential are provided as arguments to callback
*/
public function testAuthenticateProvidesCallbackWithIdentityAndCredentials()
{
$adapter = $this->_adapter;
$adapter->setIdentity('testIdentity');
$adapter->setCredential('testCredential');
$that = $this;
$callback = function ($identity, $credential) use ($that, $adapter) {
$that->assertEquals($identity, $adapter->getIdentity());
$that->assertEquals($credential, $adapter->getCredential());
};
$this->_adapter->setCallback($callback);
$this->_adapter->authenticate();
}

/**
* Ensures authentication result is invalid when callback throws exception
*/
public function testAuthenticateResultIfCallbackThrows()
{
$adapter = $this->_adapter;
$exception = new \Exception('Callback Exception');
$callback = function () use ($exception) {
throw $exception;
};
$adapter->setCallback($callback);
$result = $adapter->authenticate();
$this->assertFalse($result->isValid());
$this->assertEquals(Authentication\Result::FAILURE_UNCATEGORIZED, $result->getCode());
$this->assertEquals(array($exception->getMessage()), $result->getMessages());
}

/**
* Ensures authentication result is invalid when callback returns falsy value
*/
public function testAuthenticateResultIfCallbackReturnsFalsy()
{
$that = $this;
$adapter = $this->_adapter;
$falsyValues = array(false, null, '', '0', array(), 0, 0.0);
array_map(function ($falsy) use ($that, $adapter) {
$callback = function () use ($falsy) {
return $falsy;
};
$adapter->setCallback($callback);
$result = $adapter->authenticate();
$that->assertFalse($result->isValid());
$that->assertEquals(Authentication\Result::FAILURE, $result->getCode());
$that->assertEquals(array('Authentication failure'), $result->getMessages());
}, $falsyValues);
}

/**
* Ensures authentication result is valid when callback returns truthy value
*/
public function testAuthenticateResultIfCallbackReturnsIdentity()
{
$adapter = $this->_adapter;
$callback = function () {
return 'identity';
};
$adapter->setCallback($callback);
$result = $adapter->authenticate();
$this->assertTrue($result->isValid());
$this->assertEquals(Authentication\Result::SUCCESS, $result->getCode());
$this->assertEquals('identity', $result->getIdentity());
$this->assertEquals(array('Authentication success'), $result->getMessages());
}

protected function _setupAuthAdapter()
{
$this->_adapter = new Adapter\Callback();
}
}

0 comments on commit 9ec836b

Please # to comment.