From c912a618db79b37aebcda69eebd91a58cf98c2ea Mon Sep 17 00:00:00 2001 From: Carnage Date: Mon, 25 Jan 2016 07:33:06 +0000 Subject: [PATCH 1/7] Store full result in session - keep it around for 2fa later --- src/AuthenticationService.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/AuthenticationService.php b/src/AuthenticationService.php index 06e82e2..6a1665f 100644 --- a/src/AuthenticationService.php +++ b/src/AuthenticationService.php @@ -117,9 +117,7 @@ public function authenticate(Adapter\AdapterInterface $adapter = null) $this->clearIdentity(); } - if ($result->isValid()) { - $this->getStorage()->write($result->getIdentity()); - } + $this->getStorage()->write($result); return $result; } @@ -131,7 +129,7 @@ public function authenticate(Adapter\AdapterInterface $adapter = null) */ public function hasIdentity() { - return !$this->getStorage()->isEmpty(); + return !$this->getStorage()->isEmpty() && $this->getStorage()->read()->isValid(); } /** @@ -144,10 +142,14 @@ public function getIdentity() $storage = $this->getStorage(); if ($storage->isEmpty()) { - return; + return null; } - return $storage->read(); + $result = $storage->read(); + + if ($result->isValid()) { + return $result->getIdentity(); + } } /** From 28bc8c9abcb729155e593c4d9a1a8fbbbdaea82a Mon Sep 17 00:00:00 2001 From: Carnage Date: Mon, 25 Jan 2016 07:48:24 +0000 Subject: [PATCH 2/7] Refactor auth service to be event based --- composer.json | 3 +- src/AuthenticationService.php | 58 +++++++++----------------- src/Event/Authenticate.php | 28 +++++++++++++ src/Listener/LegacyAdapterListener.php | 45 ++++++++++++++++++++ 4 files changed, 94 insertions(+), 40 deletions(-) create mode 100644 src/Event/Authenticate.php create mode 100644 src/Listener/LegacyAdapterListener.php diff --git a/composer.json b/composer.json index ff59374..6d7f0a2 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ }, "require": { "php": ">=5.5", - "zendframework/zend-stdlib": "~2.5" + "zendframework/zend-stdlib": "~2.5", + "zendframework/zend-eventmanager": "^3.0.0" }, "require-dev": { "zendframework/zend-db": "~2.5", diff --git a/src/AuthenticationService.php b/src/AuthenticationService.php index 6a1665f..578c1d6 100644 --- a/src/AuthenticationService.php +++ b/src/AuthenticationService.php @@ -9,6 +9,9 @@ namespace Zend\Authentication; +use Zend\Authentication\Event\Authenticate; +use Zend\EventManager\EventManagerInterface; + class AuthenticationService implements AuthenticationServiceInterface { /** @@ -21,49 +24,25 @@ class AuthenticationService implements AuthenticationServiceInterface /** * Authentication adapter * - * @var Adapter\AdapterInterface + * @var EventManagerInterface */ - protected $adapter = null; + protected $events; /** * Constructor * - * @param Storage\StorageInterface $storage - * @param Adapter\AdapterInterface $adapter + * @param EventManagerInterface $eventManager + * @param Storage\StorageInterface $storage */ - public function __construct(Storage\StorageInterface $storage = null, Adapter\AdapterInterface $adapter = null) + public function __construct(EventManagerInterface $eventManager, Storage\StorageInterface $storage = null) { if (null !== $storage) { $this->setStorage($storage); } - if (null !== $adapter) { - $this->setAdapter($adapter); - } - } - /** - * Returns the authentication adapter - * - * The adapter does not have a default if the storage adapter has not been set. - * - * @return Adapter\AdapterInterface|null - */ - public function getAdapter() - { - return $this->adapter; + $this->events = $eventManager; } - /** - * Sets the authentication adapter - * - * @param Adapter\AdapterInterface $adapter - * @return AuthenticationService Provides a fluent interface - */ - public function setAdapter(Adapter\AdapterInterface $adapter) - { - $this->adapter = $adapter; - return $this; - } /** * Returns the persistent storage handler @@ -96,18 +75,19 @@ public function setStorage(Storage\StorageInterface $storage) /** * Authenticates against the supplied adapter * - * @param Adapter\AdapterInterface $adapter + * @TODO Authentication context needs working out -> should be a DTO or something + * @param array $authenticationContext * @return Result - * @throws Exception\RuntimeException */ - public function authenticate(Adapter\AdapterInterface $adapter = null) + public function authenticate($authenticationContext = []) { - if (!$adapter) { - if (!$adapter = $this->getAdapter()) { - throw new Exception\RuntimeException('An adapter must be set or passed prior to calling authenticate()'); - } - } - $result = $adapter->authenticate(); + $event = new Authenticate(); + $event->setTarget($this); + $event->setParams($authenticationContext); + + $this->events->triggerEvent($event); + + $result = $event->getResult(); /** * ZF-7546 - prevent multiple successive calls from storing inconsistent results diff --git a/src/Event/Authenticate.php b/src/Event/Authenticate.php new file mode 100644 index 0000000..efd19d2 --- /dev/null +++ b/src/Event/Authenticate.php @@ -0,0 +1,28 @@ +result; + } + + /** + * @param mixed $result + */ + public function setResult($result) + { + $this->result = $result; + } +} \ No newline at end of file diff --git a/src/Listener/LegacyAdapterListener.php b/src/Listener/LegacyAdapterListener.php new file mode 100644 index 0000000..be7d839 --- /dev/null +++ b/src/Listener/LegacyAdapterListener.php @@ -0,0 +1,45 @@ +adapter = $adapter; + } + + public function onAuthenticate(Authenticate $event) + { + $result = $event->getResult(); + if ($result instanceof Result && $result->isValid()) { + //If a previous adapter has already returned a valid result don't change that + return null; + } + + if ($this->adapter instanceof ValidatableAdapterInterface) { + $this->adapter->setIdentity($event->getParam('identity')); + $this->adapter->setCredential($event->getParam('credential')); + } + + $result = $this->adapter->authenticate(); + + $event->setResult($result); + + return $result; + } +} \ No newline at end of file From 75f8c0a52905adb375e36c893baa453fc18e24b4 Mon Sep 17 00:00:00 2001 From: Carnage Date: Mon, 25 Jan 2016 08:35:10 +0000 Subject: [PATCH 3/7] Added events for failed/successful authentication --- src/AuthenticationService.php | 14 ++++++++++++++ src/Event/AuthenticationFailed.php | 28 +++++++++++++++++++++++++++ src/Event/AuthenticationSucceeded.php | 28 +++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/Event/AuthenticationFailed.php create mode 100644 src/Event/AuthenticationSucceeded.php diff --git a/src/AuthenticationService.php b/src/AuthenticationService.php index 578c1d6..473ad49 100644 --- a/src/AuthenticationService.php +++ b/src/AuthenticationService.php @@ -10,6 +10,8 @@ namespace Zend\Authentication; use Zend\Authentication\Event\Authenticate; +use Zend\Authentication\Event\AuthenticationFailed; +use Zend\Authentication\Event\AuthenticationSucceeded; use Zend\EventManager\EventManagerInterface; class AuthenticationService implements AuthenticationServiceInterface @@ -89,6 +91,18 @@ public function authenticate($authenticationContext = []) $result = $event->getResult(); + if ($result->isValid()) { + $event = new AuthenticationSucceeded(); + } else { + $event = new AuthenticationFailed(); + } + + $event->setTarget($this); + $event->setResult($result); + $event->setParams($authenticationContext); + + $this->events->trigger($event); + /** * ZF-7546 - prevent multiple successive calls from storing inconsistent results * Ensure storage has clean state diff --git a/src/Event/AuthenticationFailed.php b/src/Event/AuthenticationFailed.php new file mode 100644 index 0000000..f2ab250 --- /dev/null +++ b/src/Event/AuthenticationFailed.php @@ -0,0 +1,28 @@ +result; + } + + /** + * @param mixed $result + */ + public function setResult($result) + { + $this->result = $result; + } +} \ No newline at end of file diff --git a/src/Event/AuthenticationSucceeded.php b/src/Event/AuthenticationSucceeded.php new file mode 100644 index 0000000..2095cdb --- /dev/null +++ b/src/Event/AuthenticationSucceeded.php @@ -0,0 +1,28 @@ +result; + } + + /** + * @param mixed $result + */ + public function setResult($result) + { + $this->result = $result; + } +} \ No newline at end of file From 8df2bf79d46c658e3f5924564b4c25e9abcc140e Mon Sep 17 00:00:00 2001 From: Carnage Date: Mon, 25 Jan 2016 08:41:52 +0000 Subject: [PATCH 4/7] Added cookbook showing common use cases --- cookbook/AuditLog.php | 48 ++++++++++++++++++ cookbook/BruteForceProtection.php | 83 +++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 cookbook/AuditLog.php create mode 100644 cookbook/BruteForceProtection.php diff --git a/cookbook/AuditLog.php b/cookbook/AuditLog.php new file mode 100644 index 0000000..4f7cdf1 --- /dev/null +++ b/cookbook/AuditLog.php @@ -0,0 +1,48 @@ +log = $log; + } + + public function onAuthenticationFailed(\Zend\Authentication\Event\Authenticate $event) + { + $this->log->warn( + sprintf('Authenication Failure for (%s) from (%s)', $event->getParam('identity'), $event->getParam('ip')) + ); + } +} + +$auditLog = new AuditLog(new stdClass()); + +$callback = function ($identity, $credential) { + if ($identity === $credential) { + return new \Zend\Stdlib\ArrayObject(['identity' => $identity, 'credential' => $credential]); + } + + throw new \Exception('Authentication failed'); +}; + +$adapter = new \Zend\Authentication\Adapter\Callback(); +$listener = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter); + +$events = new EventManager(); +$events->attach('Authenticate', [$firewall, 'onAuthenticate'] , -1); +$events->attach('Authenticate', [$listener, 'onAuthenticate'], 10); +$events->attach('AuthenticationFailed', [$auditLog, 'onAuthenticationFailed'] , -1); + +$authService = new AuthenticationService($events); + +$authService->authenticate(['ip' => '127.0.0.1', 'identity' => 'test', 'credential' => 'failed']); diff --git a/cookbook/BruteForceProtection.php b/cookbook/BruteForceProtection.php new file mode 100644 index 0000000..5efd4b8 --- /dev/null +++ b/cookbook/BruteForceProtection.php @@ -0,0 +1,83 @@ +authFails[$identifier])) { + return $this->authFails[$identifier]; + } + + return 0; + } + + public function onAuthenticate(\Zend\Authentication\Event\Authenticate $event) + { + $identity = $event->getParam('identity'); + if ($identity !== null) { + if ($this->getFailurecount($identity) > 2) { + $result = new \Zend\Authentication\Result(-4, $identity, 'Too many failed attempts'); + $event->setResult($result); + $event->stopPropagation(); + + return $result; + } + } + + $ip = $event->getParam('ip'); + if ($ip !== null) { + if ($this->getFailurecount($ip) > 2) { + $result = new \Zend\Authentication\Result(-4, $ip, 'Too many failed attempts'); + $event->setResult($result); + $event->stopPropagation(); + + return $result; + } + } + } + + public function onAuthenticationFailed(\Zend\Authentication\Event\Authenticate $event) + { + $identity = $event->getParam('identity'); + if ($identity !== null) { + $this->authFails[$identity]++; + } + + $ip = $event->getParam('ip'); + if ($ip !== null) { + $this->authFails[$ip]++; + } + } +} + +$firewall = new Firewall(); + +$callback = function ($identity, $credential) { + if ($identity === $credential) { + return new \Zend\Stdlib\ArrayObject(['identity' => $identity, 'credential' => $credential]); + } + + throw new \Exception('Authentication failed'); +}; + +$adapter = new \Zend\Authentication\Adapter\Callback(); +$listener = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter); + +$events = new EventManager(); +$events->attach('Authenticate', [$firewall, 'onAuthenticate'] , -1); +$events->attach('Authenticate', [$listener, 'onAuthenticate'], 10); +$events->attach('AuthenticationFailed', [$firewall, 'onAuthenticationFailed'] , -1); + +$authService = new AuthenticationService($events); + +$authService->authenticate(['ip' => '127.0.0.1', 'identity' => 'test', 'credential' => 'failed']); +$authService->authenticate(['ip' => '127.0.0.1', 'identity' => 'test', 'credential' => 'failed']); +$authService->authenticate(['ip' => '127.0.0.1', 'identity' => 'test2', 'credential' => 'failed']); + +// result = failure to many attempts +$authService->authenticate(['ip' => '127.0.0.1', 'identity' => 'test2', 'credential' => 'failed']); \ No newline at end of file From abeee3819fab616a3eaa4fc0ad920d31a68acd9c Mon Sep 17 00:00:00 2001 From: Carnage Date: Tue, 26 Jan 2016 07:44:30 +0000 Subject: [PATCH 5/7] Added more cookbook examples --- cookbook/2fa.php | 71 ++++++++++++++++++++++++++++++ cookbook/AuditLog.php | 2 +- cookbook/BruteForceProtection.php | 2 +- cookbook/ChainedAuthentication.php | 39 ++++++++++++++++ 4 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 cookbook/2fa.php create mode 100644 cookbook/ChainedAuthentication.php diff --git a/cookbook/2fa.php b/cookbook/2fa.php new file mode 100644 index 0000000..16e4469 --- /dev/null +++ b/cookbook/2fa.php @@ -0,0 +1,71 @@ +getResult(); + if ($result->isValid()) { + $prevResult = $event->getPreviousResult(); + $identity = $result->getIdentity(); + + if ($prevResult !== null) { + $identity = $prevResult->getIdentity(); + } + + if (isset($identity['do2fa']) && $identity['do2fa']) { + $twoFactorResponse = $event->getParam('twoFactorResponse'); + + if (isset($twoFactorResponse)) { + if ( + $prevResult !== null && + isset($prevResult->twoFactorToken) && + $twoFactorResponse === $prevResult->twoFactorToken + ) { + $result = new \Zend\Authentication\Result(\Zend\Authentication\Result::SUCCESS, $identity); + $event->setResult($result); + + return $result; + } + } + + $result = new \Zend\Authentication\Result(-4, $identity, 'Requires 2 factor Auth'); + $result->twoFactorToken = 'efg456'; //generate randomly + $event->setResult($result); + $event->stopPropagation(); + + return $result; + + } + } + + return $result; + } +} + +$twoFaListener = new TwoFAListener(); + +$callback = function ($identity, $credential) { + if ($identity === $credential) { + return new \Zend\Stdlib\ArrayObject(['identity' => $identity, 'credential' => $credential, 'do2fa' => true]); + } + + throw new \Exception('Authentication failed'); +}; + +$adapter = new \Zend\Authentication\Adapter\Callback($callback); +$listener = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter); + +$events = new EventManager(); +$events->attach('Authenticate', [$listener, 'onAuthenticate'], 10); +$events->attach('Authenticate', [$TwoFAlistener, 'onAuthenticate'], 20); + +$authService = new AuthenticationService($events); + +$authService->authenticate(['identity' => 'test', 'credential' => 'test']); + +//result success with test identity +$authService->authenticate(['twoFactorResponse' => 'efg456']); \ No newline at end of file diff --git a/cookbook/AuditLog.php b/cookbook/AuditLog.php index 4f7cdf1..1566b06 100644 --- a/cookbook/AuditLog.php +++ b/cookbook/AuditLog.php @@ -35,7 +35,7 @@ public function onAuthenticationFailed(\Zend\Authentication\Event\Authenticate $ throw new \Exception('Authentication failed'); }; -$adapter = new \Zend\Authentication\Adapter\Callback(); +$adapter = new \Zend\Authentication\Adapter\Callback($callback); $listener = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter); $events = new EventManager(); diff --git a/cookbook/BruteForceProtection.php b/cookbook/BruteForceProtection.php index 5efd4b8..b5beb91 100644 --- a/cookbook/BruteForceProtection.php +++ b/cookbook/BruteForceProtection.php @@ -65,7 +65,7 @@ public function onAuthenticationFailed(\Zend\Authentication\Event\Authenticate $ throw new \Exception('Authentication failed'); }; -$adapter = new \Zend\Authentication\Adapter\Callback(); +$adapter = new \Zend\Authentication\Adapter\Callback($callback); $listener = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter); $events = new EventManager(); diff --git a/cookbook/ChainedAuthentication.php b/cookbook/ChainedAuthentication.php new file mode 100644 index 0000000..b4fb870 --- /dev/null +++ b/cookbook/ChainedAuthentication.php @@ -0,0 +1,39 @@ + $identity, 'credential' => $credential]); + } + + throw new \Exception('Authentication failed'); +}; + +$adapter = new \Zend\Authentication\Adapter\Callback($callback); +$listener = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter); + +$callback2 = function ($identity, $credential) { + if ($identity === 'test' && $credential === 'tester') { + return new \Zend\Stdlib\ArrayObject(['identity' => $identity, 'credential' => $credential]); + } + + throw new \Exception('Authentication failed'); +}; + +$adapter2 = new \Zend\Authentication\Adapter\Callback($callback2); +$listener2 = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter2); + +$events = new EventManager(); +$events->attach('Authenticate', [$listener, 'onAuthenticate'], 10); +$events->attach('Authenticate', [$listener2, 'onAuthenticate'], 20); + +$authService = new AuthenticationService($events); + +//auths against adapter 1 +$authService->authenticate(['identity' => 'test', 'credential' => 'test']); + +//auths against adapter 2 +$authService->authenticate(['identity' => 'test', 'credential' => 'tester']); + From ddc71f72d63c2e70a67e2029bc1cfe56b1543dd8 Mon Sep 17 00:00:00 2001 From: Carnage Date: Tue, 26 Jan 2016 07:56:23 +0000 Subject: [PATCH 6/7] Updated auth service to preserve and provide previous result to authentication event --- src/AuthenticationService.php | 28 ++++++++++++++++++++-------- src/Event/Authenticate.php | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/AuthenticationService.php b/src/AuthenticationService.php index 473ad49..0d0895c 100644 --- a/src/AuthenticationService.php +++ b/src/AuthenticationService.php @@ -86,6 +86,7 @@ public function authenticate($authenticationContext = []) $event = new Authenticate(); $event->setTarget($this); $event->setParams($authenticationContext); + $event->setPreviousResult($this->getResult()); $this->events->triggerEvent($event); @@ -133,17 +134,13 @@ public function hasIdentity() */ public function getIdentity() { - $storage = $this->getStorage(); - - if ($storage->isEmpty()) { - return null; - } + $result = $this->getResult(); - $result = $storage->read(); - - if ($result->isValid()) { + if ($result !== null && $result->isValid()) { return $result->getIdentity(); } + + return null; } /** @@ -155,4 +152,19 @@ public function clearIdentity() { $this->getStorage()->clear(); } + + /** + * @return Result|null + */ + private function getResult() + { + $storage = $this->getStorage(); + + if ($storage->isEmpty()) { + return null; + } + + $result = $storage->read(); + return $result; + } } diff --git a/src/Event/Authenticate.php b/src/Event/Authenticate.php index efd19d2..1eb8d9b 100644 --- a/src/Event/Authenticate.php +++ b/src/Event/Authenticate.php @@ -10,6 +10,8 @@ class Authenticate extends Event protected $result; + protected $previousResult; + /** * @return mixed */ @@ -25,4 +27,20 @@ public function setResult($result) { $this->result = $result; } + + /** + * @return mixed + */ + public function getPreviousResult() + { + return $this->previousResult; + } + + /** + * @param mixed $previousResult + */ + public function setPreviousResult($previousResult) + { + $this->previousResult = $previousResult; + } } \ No newline at end of file From 5b2c2b3234f8c722446741700530187ffda4a80b Mon Sep 17 00:00:00 2001 From: Carnage Date: Sun, 21 Feb 2016 19:47:03 +0000 Subject: [PATCH 7/7] Backward compatability changes --- cookbook/2fa.php | 14 ++++---- cookbook/AuditLog.php | 9 ++--- cookbook/BruteForceProtection.php | 10 ++---- cookbook/ChainedAuthentication.php | 9 ++--- src/AuthenticationService.php | 34 +++++++++++++++++-- ....php => AuthenticationAdapterListener.php} | 4 +-- 6 files changed, 48 insertions(+), 32 deletions(-) rename src/Listener/{LegacyAdapterListener.php => AuthenticationAdapterListener.php} (92%) diff --git a/cookbook/2fa.php b/cookbook/2fa.php index 16e4469..33dfeba 100644 --- a/cookbook/2fa.php +++ b/cookbook/2fa.php @@ -1,5 +1,11 @@ attach('Authenticate', [$listener, 'onAuthenticate'], 10); -$events->attach('Authenticate', [$TwoFAlistener, 'onAuthenticate'], 20); -$authService = new AuthenticationService($events); +$authService = new AuthenticationService(null, $adapter); +$authService->addListener('Authenticate', [$TwoFAlistener, 'onAuthenticate'], 20); $authService->authenticate(['identity' => 'test', 'credential' => 'test']); diff --git a/cookbook/AuditLog.php b/cookbook/AuditLog.php index 1566b06..da27ea9 100644 --- a/cookbook/AuditLog.php +++ b/cookbook/AuditLog.php @@ -36,13 +36,8 @@ public function onAuthenticationFailed(\Zend\Authentication\Event\Authenticate $ }; $adapter = new \Zend\Authentication\Adapter\Callback($callback); -$listener = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter); -$events = new EventManager(); -$events->attach('Authenticate', [$firewall, 'onAuthenticate'] , -1); -$events->attach('Authenticate', [$listener, 'onAuthenticate'], 10); -$events->attach('AuthenticationFailed', [$auditLog, 'onAuthenticationFailed'] , -1); - -$authService = new AuthenticationService($events); +$authService = new AuthenticationService(null, $adapter); +$authService->addListener('AuthenticationFailed', [$auditLog, 'onAuthenticationFailed'] , -1); $authService->authenticate(['ip' => '127.0.0.1', 'identity' => 'test', 'credential' => 'failed']); diff --git a/cookbook/BruteForceProtection.php b/cookbook/BruteForceProtection.php index b5beb91..b5da61c 100644 --- a/cookbook/BruteForceProtection.php +++ b/cookbook/BruteForceProtection.php @@ -66,14 +66,10 @@ public function onAuthenticationFailed(\Zend\Authentication\Event\Authenticate $ }; $adapter = new \Zend\Authentication\Adapter\Callback($callback); -$listener = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter); -$events = new EventManager(); -$events->attach('Authenticate', [$firewall, 'onAuthenticate'] , -1); -$events->attach('Authenticate', [$listener, 'onAuthenticate'], 10); -$events->attach('AuthenticationFailed', [$firewall, 'onAuthenticationFailed'] , -1); - -$authService = new AuthenticationService($events); +$authService = new AuthenticationService(null, $adapter, 10); +$authService->addListener('Authenticate', [$firewall, 'onAuthenticate'] , -1); +$authService->addListener('AuthenticationFailed', [$firewall, 'onAuthenticationFailed'] , -1); $authService->authenticate(['ip' => '127.0.0.1', 'identity' => 'test', 'credential' => 'failed']); $authService->authenticate(['ip' => '127.0.0.1', 'identity' => 'test', 'credential' => 'failed']); diff --git a/cookbook/ChainedAuthentication.php b/cookbook/ChainedAuthentication.php index b4fb870..4252b27 100644 --- a/cookbook/ChainedAuthentication.php +++ b/cookbook/ChainedAuthentication.php @@ -12,7 +12,6 @@ }; $adapter = new \Zend\Authentication\Adapter\Callback($callback); -$listener = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter); $callback2 = function ($identity, $credential) { if ($identity === 'test' && $credential === 'tester') { @@ -23,13 +22,9 @@ }; $adapter2 = new \Zend\Authentication\Adapter\Callback($callback2); -$listener2 = new \Zend\Authentication\Listener\LegacyAdapterListener($adapter2); -$events = new EventManager(); -$events->attach('Authenticate', [$listener, 'onAuthenticate'], 10); -$events->attach('Authenticate', [$listener2, 'onAuthenticate'], 20); - -$authService = new AuthenticationService($events); +$authService = new AuthenticationService(null, $adapter, 10); +$authService->addAdapter($adapter2, 20); //auths against adapter 1 $authService->authenticate(['identity' => 'test', 'credential' => 'test']); diff --git a/src/AuthenticationService.php b/src/AuthenticationService.php index 0d0895c..acb8171 100644 --- a/src/AuthenticationService.php +++ b/src/AuthenticationService.php @@ -12,6 +12,8 @@ use Zend\Authentication\Event\Authenticate; use Zend\Authentication\Event\AuthenticationFailed; use Zend\Authentication\Event\AuthenticationSucceeded; +use Zend\Authentication\Listener\AuthenticationAdapterListener; +use Zend\EventManager\EventManager; use Zend\EventManager\EventManagerInterface; class AuthenticationService implements AuthenticationServiceInterface @@ -36,13 +38,16 @@ class AuthenticationService implements AuthenticationServiceInterface * @param EventManagerInterface $eventManager * @param Storage\StorageInterface $storage */ - public function __construct(EventManagerInterface $eventManager, Storage\StorageInterface $storage = null) + public function __construct(Storage\StorageInterface $storage = null, Adapter\AdapterInterface $adapter = null, $priority = 10) { if (null !== $storage) { $this->setStorage($storage); } - $this->events = $eventManager; + $this->events = new EventManager(); + if ($adapter !== null) { + $this->addAdapter($adapter, $priority); + } } @@ -77,7 +82,8 @@ public function setStorage(Storage\StorageInterface $storage) /** * Authenticates against the supplied adapter * - * @TODO Authentication context needs working out -> should be a DTO or something + * @? This is currently a BC break the original takes an auth adapter as a parameter. This still conforms to the interface though. + * * @param array $authenticationContext * @return Result */ @@ -153,6 +159,28 @@ public function clearIdentity() $this->getStorage()->clear(); } + /** + * @param Adapter\AdapterInterface $adapter + * @param int $priority + */ + public function addAdapter(Adapter\AdapterInterface $adapter, $priority = 10) + { + $listener = new AuthenticationAdapterListener($adapter); + $this->events->attach('Authenticate', [$listener, 'onAuthenticate'], $priority); + } + + /** + * @? Should there be a separate method for each event type instead of passing the event as a string? + * + * @param $event + * @param callable $listener + * @param $priority + */ + public function addListener($event, callable $listener, $priority) + { + $this->events->attach($event, $listener, $priority); + } + /** * @return Result|null */ diff --git a/src/Listener/LegacyAdapterListener.php b/src/Listener/AuthenticationAdapterListener.php similarity index 92% rename from src/Listener/LegacyAdapterListener.php rename to src/Listener/AuthenticationAdapterListener.php index be7d839..cc70df4 100644 --- a/src/Listener/LegacyAdapterListener.php +++ b/src/Listener/AuthenticationAdapterListener.php @@ -7,7 +7,7 @@ use Zend\Authentication\Event\Authenticate; use Zend\Authentication\Result; -class LegacyAdapterListener +class AuthenticationAdapterListener { /** * @var AdapterInterface @@ -15,7 +15,7 @@ class LegacyAdapterListener private $adapter; /** - * LegacyAdapterListener constructor. + * AuthenticationAdapterListener constructor. * @param AdapterInterface $adapter */ public function __construct(AdapterInterface $adapter)