Skip to content

Commit

Permalink
Merge a58ef2d into 4349878
Browse files Browse the repository at this point in the history
  • Loading branch information
DASPRiD committed Jun 3, 2013
2 parents 4349878 + a58ef2d commit 49aa9ef
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 26 deletions.
2 changes: 2 additions & 0 deletions src/BaconAuthentication/AuthenticationEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class AuthenticationEvent extends Event
{
const EVENT_AUTHENTICATE_PRE = 'authenticate.pre';
const EVENT_AUTHENTICATE_POST = 'authenticate.post';
const EVENT_RESOLVE_PRE = 'resolve.pre';
const EVENT_RESOLVE_POST = 'resolve.post';

/**
* @var RequestInterface
Expand Down
56 changes: 56 additions & 0 deletions src/BaconAuthentication/AuthenticationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use BaconAuthentication\Plugin\EventAwarePluginInterface;
use BaconAuthentication\Plugin\ExtractionPluginInterface;
use BaconAuthentication\Plugin\ResetPluginInterface;
use BaconAuthentication\Plugin\ResolutionPluginInterface;
use BaconAuthentication\Result\Error;
use BaconAuthentication\Result\Result;
use BaconAuthentication\Result\ResultInterface;
use Zend\EventManager\EventManagerAwareInterface;
Expand Down Expand Up @@ -51,6 +53,11 @@ class AuthenticationService implements
*/
protected $resetPlugins;

/**
* @var PriorityQueue
*/
protected $resolutionPlugins;

/**
* @var EventManagerInterface
*/
Expand All @@ -65,6 +72,7 @@ public function __construct()
$this->challengePlugins = new PriorityQueue();
$this->extractionPlugins = new PriorityQueue();
$this->resetPlugins = new PriorityQueue();
$this->resolutionPlugins = new PriorityQueue();
}

/**
Expand Down Expand Up @@ -99,6 +107,11 @@ public function addPlugin($plugin, $priority = 1)
$isValid = true;
}

if ($plugin instanceof ResolutionPluginInterface) {
$this->resolutionPlugins->insert($plugin, $priority);
$isValid = true;
}

if ($plugin instanceof EventAwarePluginInterface) {
$plugin->attachToEvents($this->getEventManager());
$isValid = true;
Expand Down Expand Up @@ -156,12 +169,35 @@ public function authenticate(RequestInterface $request, ResponseInterface $respo

if ($eventResult->stopped()) {
$result = $eventResult->last();
$event->setResult($result);
}

if ($result === null) {
throw new Exception\RuntimeException('No plugin was able to generate a result');
}

if ($result->isSuccess() && is_scalar($result->getPayload())) {
$eventResult = $events->trigger(AuthenticationEvent::EVENT_RESOLVE_PRE, $event, $shortCircuit);

if ($eventResult->stopped()) {
return $eventResult->last();
}

$subject = $this->resolveSubject($result->getPayload());

if ($subject !== null) {
$result = new Result(Result::STATE_SUCCESS, $subject);
} else {
$result = new Result(Result::STATE_FAILURE, new Error(__CLASS__, 'Subject could not be resolved'));
}

$eventResult = $events->trigger(AuthenticationEvent::EVENT_RESOLVE_POST, $event, $shortCircuit);

if ($eventResult->stopped()) {
return $eventResult->last();
}
}

return $result;
}

Expand Down Expand Up @@ -233,6 +269,26 @@ protected function challenge(RequestInterface $request, ResponseInterface $respo
return false;
}

/**
* Tries to resolve a subject.
*
* @param int|float|string $identifier
* @return mixed|null
*/
protected function resolveSubject($identifier)
{
foreach ($this->resolutionPlugins as $resolutionPlugin) {
/* @var $resolutionPlugin ResolutionPluginInterface */
$subject = $resolutionPlugin->resolveSubject($identifier);

if ($subject !== null) {
return $subject;
}
}

return null;
}

/**
* Sets the event manager.
*
Expand Down
24 changes: 24 additions & 0 deletions src/BaconAuthentication/Plugin/ResolutionPluginInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* BaconAuthentication
*
* @link http://github.com/Bacon/BaconAuthentication For the canonical source repository
* @copyright 2013 Ben Scholzen 'DASPRiD'
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/

namespace BaconAuthentication\Plugin;

/**
* Plugin interface for subject resolution.
*/
interface ResolutionPluginInterface
{
/**
* Tries to resolve a subject from an identifier.
*
* @param int|float|string $identifier
* @return mixed|null
*/
public function resolveSubject($identifier);
}
18 changes: 9 additions & 9 deletions src/BaconAuthentication/Plugin/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function __construct($namespace = null, ManagerInterface $manager = null)
public function attachToEvents(EventManagerInterface $events)
{
$events->attach(AuthenticationEvent::EVENT_AUTHENTICATE_PRE, array($this, 'checkSession'));
$events->attach(AuthenticationEvent::EVENT_AUTHENTICATE_POST, array($this, 'storeIdentity'));
$events->attach(AuthenticationEvent::EVENT_AUTHENTICATE_POST, array($this, 'storeIdentifier'));
}

/**
Expand All @@ -68,36 +68,36 @@ public function attachToEvents(EventManagerInterface $events)
*/
public function resetCredentials(RequestInterface $request)
{
unset($this->session->identity);
unset($this->session->identifier);
}

/**
* Checks the session for an already stored identity.
* Checks the session for an already stored identifier.
*
* @param AuthenticationEvent $event
* @return Result|null
*/
public function checkSession(AuthenticationEvent $event)
{
if (isset($this->session->identity)) {
return new Result(Result::STATE_SUCCESS, $this->session->identity);
if (isset($this->session->identifier)) {
return new Result(Result::STATE_SUCCESS, $this->session->identifier);
}

return null;
}

/**
* Stores the identity if the authentication succeeded.
* Stores the identifier if the authentication succeeded.
*
* @param AuthenticationEvent $event
* @return Result
*/
public function storeIdentity(AuthenticationEvent $event)
public function storeIdentifier(AuthenticationEvent $event)
{
$result = $event->getResult();

if ($result->isSuccess()) {
$this->session->identity = $result->getPayload();
if ($result->isSuccess() && is_scalar($result->getPayload())) {
$this->session->identifier = $result->getPayload();
}
}
}
Expand Down
143 changes: 143 additions & 0 deletions tests/BaconAuthenticationTest/AuthenticationServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,149 @@ public function testExtractedCredentialsArePassedToAuthenticationPlugin()
);
}

public function testAuthenticationFailsWithoutResolution()
{
$result = $this->getMock('BaconAuthentication\Result\ResultInterface');
$result->expects($this->any())
->method('isSuccess')
->will($this->returnValue(true));
$result->expects($this->any())
->method('getPayload')
->will($this->returnValue(1));

$service = new AuthenticationService();
$service->getEventManager()->attach(
'authenticate.post',
function () use ($result) {
return $result;
}
);

$result = $service->authenticate(
$this->getMock('Zend\Stdlib\RequestInterface'),
$this->getMock('Zend\Stdlib\ResponseInterface')
);

$this->assertTrue($result->isFailure());
$this->assertEquals(
'BaconAuthentication\AuthenticationService',
$result->getPayload()->getScope()
);
$this->assertEquals(
'Subject could not be resolved',
$result->getPayload()->getMessage()
);
}

public function testSubjectResolution()
{
$result = $this->getMock('BaconAuthentication\Result\ResultInterface');
$result->expects($this->any())
->method('isSuccess')
->will($this->returnValue(true));
$result->expects($this->any())
->method('getPayload')
->will($this->returnValue(1));

$service = new AuthenticationService();
$service->getEventManager()->attach(
'authenticate.post',
function () use ($result) {
return $result;
}
);

$plugin = $this->getMock('BaconAuthentication\Plugin\ResolutionPluginInterface');
$plugin->expects($this->once())
->method('resolveSubject')
->with($this->equalTo(1))
->will($this->returnValue(array('name' => 'foo')));
$service->addPlugin($plugin);

$result = $service->authenticate(
$this->getMock('Zend\Stdlib\RequestInterface'),
$this->getMock('Zend\Stdlib\ResponseInterface')
);

$this->assertTrue($result->isSuccess());
$this->assertEquals(
array('name' => 'foo'),
$result->getPayload()
);
}

public function testResolutionPreShortCircuit()
{
$result = $this->getMock('BaconAuthentication\Result\ResultInterface');
$result->expects($this->any())
->method('isSuccess')
->will($this->returnValue(true));
$result->expects($this->any())
->method('getPayload')
->will($this->returnValue(1));

$service = new AuthenticationService();
$service->getEventManager()->attach(
'authenticate.post',
function () use ($result) {
return $result;
}
);

$expectedResult = $this->getMock('BaconAuthentication\Result\ResultInterface');

$service->getEventManager()->attach(
'resolve.pre',
function () use ($expectedResult) {
return $expectedResult;
}
);

$this->assertSame(
$expectedResult,
$service->authenticate(
$this->getMock('Zend\Stdlib\RequestInterface'),
$this->getMock('Zend\Stdlib\ResponseInterface')
)
);
}

public function testResolutionPostShortCircuit()
{
$result = $this->getMock('BaconAuthentication\Result\ResultInterface');
$result->expects($this->any())
->method('isSuccess')
->will($this->returnValue(true));
$result->expects($this->any())
->method('getPayload')
->will($this->returnValue(1));

$service = new AuthenticationService();
$service->getEventManager()->attach(
'authenticate.post',
function () use ($result) {
return $result;
}
);

$expectedResult = $this->getMock('BaconAuthentication\Result\ResultInterface');

$service->getEventManager()->attach(
'resolve.post',
function () use ($expectedResult) {
return $expectedResult;
}
);

$this->assertSame(
$expectedResult,
$service->authenticate(
$this->getMock('Zend\Stdlib\RequestInterface'),
$this->getMock('Zend\Stdlib\ResponseInterface')
)
);
}

public function testResetCredentials()
{
$request = $this->getMock('Zend\Stdlib\RequestInterface');
Expand Down

0 comments on commit 49aa9ef

Please sign in to comment.