diff --git a/src/Adapter/Callback.php b/src/Adapter/Callback.php new file mode 100644 index 0000000..dd39956 --- /dev/null +++ b/src/Adapter/Callback.php @@ -0,0 +1,90 @@ +setCallback($callback); + } + } + + /** + * Authenticate using the provided callback + * + * @return Result The authentication result + * @throws RuntimeException + */ + public function authenticate() + { + $callback = $this->getCallback(); + if (! $callback) { + throw new RuntimeException('No callback provided'); + } + + try { + $identity = call_user_func($callback, $this->getIdentity(), $this->getCredential()); + } catch (Exception $e) { + return new Result(Result::FAILURE_UNCATEGORIZED, null, array($e->getMessage())); + } + + if (! $identity) { + return new Result(Result::FAILURE, null, array('Authentication failure')); + } + + return new Result(Result::SUCCESS, $identity, array('Authentication success')); + } + + /** + * Gets the value of callback. + * + * @return null|callable + */ + public function getCallback() + { + return $this->callback; + } + + /** + * Sets the value of callback. + * + * @param callable $callback the callback + * @throws InvalidArgumentException + */ + public function setCallback($callback) + { + if (! is_callable($callback)) { + throw new InvalidArgumentException('Invalid callback provided'); + } + + $this->callback = $callback; + } +} diff --git a/test/Adapter/CallbackTest.php b/test/Adapter/CallbackTest.php new file mode 100644 index 0000000..a205dc0 --- /dev/null +++ b/test/Adapter/CallbackTest.php @@ -0,0 +1,160 @@ +setupAuthAdapter(); + } + + public function tearDown() + { + $this->adapter = null; + } + + protected function setupAuthAdapter() + { + $this->adapter = new Callback(); + } + + /** + * 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 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(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(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(Result::SUCCESS, $result->getCode()); + $this->assertEquals('identity', $result->getIdentity()); + $this->assertEquals(array('Authentication success'), $result->getMessages()); + } +}