From 4897f1a0bc68dc546339bb56554cf2052cdc919b Mon Sep 17 00:00:00 2001 From: Erin Millard Date: Tue, 24 Sep 2013 12:50:05 +1000 Subject: [PATCH] Refactored to use driver classes. --- .../AbstractCounterBasedOtpConfiguration.php | 76 ++++++ .../AbstractOtpConfiguration.php} | 37 +-- .../AbstractTimeBasedOtpConfiguration.php | 93 +++++++ .../CounterBasedOtpConfigurationInterface.php | 34 +++ .../MfaConfigurationInterface.php | 2 +- .../OtpConfigurationInterface.php | 32 +++ .../TimeBasedOtpConfigurationInterface.php | 40 +++ .../Credentials/MfaCredentialsInterface.php | 2 +- .../Credentials/OtpCredentialsInterface.php | 2 +- .../Otis/Driver/AbstractMfaDriver.php | 73 +++++ src/Eloquent/Otis/Driver/MfaDriverFactory.php | 51 ++++ .../Otis/Driver/MfaDriverFactoryInterface.php | 31 +++ .../Otis/Driver/MfaDriverInterface.php | 43 +++ .../InvalidPasswordLengthException.php | 5 +- .../UnsupportedArgumentsException.php | 34 --- .../UnsupportedConfigurationException.php | 55 ++++ .../AbstractGoogleAuthenticatorUriFactory.php | 86 ++++++ ... => GoogleAuthenticatorHotpUriFactory.php} | 55 ++-- ...leAuthenticatorHotpUriFactoryInterface.php | 45 ++++ .../GoogleAuthenticatorTotpUriFactory.php | 90 +++++++ ...leAuthenticatorTotpUriFactoryInterface.php | 45 ++++ .../GoogleAuthenticatorUriFactory.php | 243 ----------------- .../HotpBasedConfigurationInterface.php | 20 +- .../Hotp/Configuration/HotpConfiguration.php | 37 +-- .../HotpConfigurationInterface.php | 21 +- src/Eloquent/Otis/Hotp/HotpDriver.php | 55 ++++ src/Eloquent/Otis/Hotp/HotpHashAlgorithm.php | 2 +- .../Otis/Hotp/Validator/HotpValidator.php | 72 +---- .../Motp/Configuration/MotpConfiguration.php | 31 +-- .../MotpConfigurationInterface.php | 21 +- .../Otis/Motp/Generator/MotpGenerator.php | 4 +- src/Eloquent/Otis/Motp/MotpDriver.php | 43 +++ .../MotpSharedParametersGenerator.php | 79 ++++++ ...MotpSharedParametersGeneratorInterface.php | 30 +++ .../MotpSharedParametersInterface.php | 2 +- .../Otis/Motp/Validator/MotpValidator.php | 28 +- ...unterBasedOtpSharedParametersInterface.php | 2 +- ...unterBasedOtpSharedParametersGenerator.php | 79 ++++++ ...dOtpSharedParametersGeneratorInterface.php | 33 +++ .../MfaSharedParametersGeneratorInterface.php | 31 +++ .../TimeBasedOtpSharedParametersGenerator.php | 80 ++++++ ...dOtpSharedParametersGeneratorInterface.php | 33 +++ .../MfaSharedParametersInterface.php | 2 +- .../OtpSharedParametersInterface.php | 2 +- .../TimeBasedOtpSharedParametersInterface.php | 2 +- .../Totp/Configuration/TotpConfiguration.php | 59 ++--- .../TotpConfigurationInterface.php | 27 +- src/Eloquent/Otis/Totp/TotpDriver.php | 55 ++++ .../Otis/Totp/Validator/TotpValidator.php | 28 +- .../InitializationUriFactory.php | 109 -------- .../InitializationUriFactoryInterface.php | 18 +- .../MfaSequenceValidatorInterface.php | 18 -- src/Eloquent/Otis/Validator/MfaValidator.php | 174 ------------ .../Otis/Validator/MfaValidatorInterface.php | 18 -- .../Otis/Driver/MfaDriverFactoryTest.php | 57 ++++ .../UnsupportedArgumentsExceptionTest.php | 13 +- .../GoogleAuthenticatorHotpUriFactoryTest.php | 95 +++++++ .../GoogleAuthenticatorTotpUriFactoryTest.php | 97 +++++++ .../GoogleAuthenticatorUriFactoryTest.php | 145 ---------- .../Configuration/HotpConfigurationTest.php | 3 +- .../Eloquent/Otis/Hotp/HotpDriverTest.php | 53 ++++ .../Otis/Hotp/Validator/HotpValidatorTest.php | 84 +++--- .../Configuration/MotpConfigurationTest.php | 5 + .../Eloquent/Otis/Motp/MotpDriverTest.php | 48 ++++ .../MotpSharedParametersGeneratorTest.php | 41 +++ .../Parameters/MotpSharedParametersTest.php | 4 - .../Otis/Motp/Validator/MotpValidatorTest.php | 45 ++-- ...rBasedOtpSharedParametersGeneratorTest.php | 39 +++ ...eBasedOtpSharedParametersGeneratorTest.php | 40 +++ .../Configuration/TotpConfigurationTest.php | 3 +- .../Eloquent/Otis/Totp/TotpDriverTest.php | 53 ++++ .../Otis/Totp/Validator/TotpValidatorTest.php | 43 ++- .../InitializationUriFactoryTest.php | 130 --------- .../Otis/Validator/MfaValidatorTest.php | 250 ------------------ 74 files changed, 2053 insertions(+), 1584 deletions(-) create mode 100644 src/Eloquent/Otis/Configuration/AbstractCounterBasedOtpConfiguration.php rename src/Eloquent/Otis/{Hotp/Configuration/AbstractHotpBasedOtpConfiguration.php => Configuration/AbstractOtpConfiguration.php} (56%) create mode 100644 src/Eloquent/Otis/Configuration/AbstractTimeBasedOtpConfiguration.php create mode 100644 src/Eloquent/Otis/Configuration/CounterBasedOtpConfigurationInterface.php create mode 100644 src/Eloquent/Otis/Configuration/OtpConfigurationInterface.php create mode 100644 src/Eloquent/Otis/Configuration/TimeBasedOtpConfigurationInterface.php create mode 100644 src/Eloquent/Otis/Driver/AbstractMfaDriver.php create mode 100644 src/Eloquent/Otis/Driver/MfaDriverFactory.php create mode 100644 src/Eloquent/Otis/Driver/MfaDriverFactoryInterface.php create mode 100644 src/Eloquent/Otis/Driver/MfaDriverInterface.php delete mode 100644 src/Eloquent/Otis/Exception/UnsupportedArgumentsException.php create mode 100644 src/Eloquent/Otis/Exception/UnsupportedConfigurationException.php create mode 100644 src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/AbstractGoogleAuthenticatorUriFactory.php rename src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/{GoogleAuthenticatorUriFactoryInterface.php => GoogleAuthenticatorHotpUriFactory.php} (57%) create mode 100644 src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactoryInterface.php create mode 100644 src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorTotpUriFactory.php create mode 100644 src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorTotpUriFactoryInterface.php delete mode 100644 src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorUriFactory.php create mode 100644 src/Eloquent/Otis/Hotp/HotpDriver.php create mode 100644 src/Eloquent/Otis/Motp/MotpDriver.php create mode 100644 src/Eloquent/Otis/Motp/Parameters/Generator/MotpSharedParametersGenerator.php create mode 100644 src/Eloquent/Otis/Motp/Parameters/Generator/MotpSharedParametersGeneratorInterface.php create mode 100644 src/Eloquent/Otis/Parameters/Generator/CounterBasedOtpSharedParametersGenerator.php create mode 100644 src/Eloquent/Otis/Parameters/Generator/CounterBasedOtpSharedParametersGeneratorInterface.php create mode 100644 src/Eloquent/Otis/Parameters/Generator/MfaSharedParametersGeneratorInterface.php create mode 100644 src/Eloquent/Otis/Parameters/Generator/TimeBasedOtpSharedParametersGenerator.php create mode 100644 src/Eloquent/Otis/Parameters/Generator/TimeBasedOtpSharedParametersGeneratorInterface.php create mode 100644 src/Eloquent/Otis/Totp/TotpDriver.php delete mode 100644 src/Eloquent/Otis/Uri/Initialization/InitializationUriFactory.php delete mode 100644 src/Eloquent/Otis/Validator/MfaValidator.php create mode 100644 test/suite/Eloquent/Otis/Driver/MfaDriverFactoryTest.php create mode 100644 test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactoryTest.php create mode 100644 test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorTotpUriFactoryTest.php delete mode 100644 test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorUriFactoryTest.php create mode 100644 test/suite/Eloquent/Otis/Hotp/HotpDriverTest.php create mode 100644 test/suite/Eloquent/Otis/Motp/MotpDriverTest.php create mode 100644 test/suite/Eloquent/Otis/Motp/Parameters/Generator/MotpSharedParametersGeneratorTest.php create mode 100644 test/suite/Eloquent/Otis/Parameters/Generator/CounterBasedOtpSharedParametersGeneratorTest.php create mode 100644 test/suite/Eloquent/Otis/Parameters/Generator/TimeBasedOtpSharedParametersGeneratorTest.php create mode 100644 test/suite/Eloquent/Otis/Totp/TotpDriverTest.php delete mode 100644 test/suite/Eloquent/Otis/Uri/Initialization/InitializationUriFactoryTest.php delete mode 100644 test/suite/Eloquent/Otis/Validator/MfaValidatorTest.php diff --git a/src/Eloquent/Otis/Configuration/AbstractCounterBasedOtpConfiguration.php b/src/Eloquent/Otis/Configuration/AbstractCounterBasedOtpConfiguration.php new file mode 100644 index 0000000..fa200be --- /dev/null +++ b/src/Eloquent/Otis/Configuration/AbstractCounterBasedOtpConfiguration.php @@ -0,0 +1,76 @@ +window = $window; + $this->initialCounter = $initialCounter; + } + + /** + * Get the amount of counter increments to search through for a match. + * + * @return integer The amount of counter increments to search through for a match. + */ + public function window() + { + return $this->window; + } + + /** + * Get the initial counter value. + * + * @return integer The initial counter value. + */ + public function initialCounter() + { + return $this->initialCounter; + } + + private $window; + private $initialCounter; +} diff --git a/src/Eloquent/Otis/Hotp/Configuration/AbstractHotpBasedOtpConfiguration.php b/src/Eloquent/Otis/Configuration/AbstractOtpConfiguration.php similarity index 56% rename from src/Eloquent/Otis/Hotp/Configuration/AbstractHotpBasedOtpConfiguration.php rename to src/Eloquent/Otis/Configuration/AbstractOtpConfiguration.php index abbc902..0308c7c 100644 --- a/src/Eloquent/Otis/Hotp/Configuration/AbstractHotpBasedOtpConfiguration.php +++ b/src/Eloquent/Otis/Configuration/AbstractOtpConfiguration.php @@ -9,49 +9,39 @@ * file that was distributed with this source code. */ -namespace Eloquent\Otis\Hotp\Configuration; +namespace Eloquent\Otis\Configuration; use Eloquent\Otis\Exception\InvalidPasswordLengthException; -use Eloquent\Otis\Hotp\HotpHashAlgorithm; /** * An abstract base class for implementing one-time password authentication - * configurations based upon HOTP. + * configurations. */ -abstract class AbstractHotpBasedOtpConfiguration implements - HotpBasedConfigurationInterface +abstract class AbstractOtpConfiguration implements OtpConfigurationInterface { /** * Construct a new one-time password authentication configuration. * - * @param integer|null $digits The number of password digits. - * @param integer|null $secretLength The length of the shared secret. - * @param HotpHashAlgorithm|null $algorithm The underlying hash algorithm to use. + * @param integer|null $digits The number of password digits. + * @param integer|null $secretLength The length of the shared secret. * * @throws InvalidPasswordLengthException If the number of digits is invalid. */ - public function __construct( - $digits = null, - $secretLength = null, - HotpHashAlgorithm $algorithm = null - ) { + public function __construct($digits = null, $secretLength = null) + { if (null === $digits) { $digits = 6; } if (null === $secretLength) { $secretLength = 10; } - if (null === $algorithm) { - $algorithm = HotpHashAlgorithm::SHA1(); - } - if ($digits < 6 || $digits > 10) { + if ($digits < 6) { throw new InvalidPasswordLengthException($digits); } $this->digits = $digits; $this->secretLength = $secretLength; - $this->algorithm = $algorithm; } /** @@ -74,17 +64,6 @@ public function secretLength() return $this->secretLength; } - /** - * Get the underlying algorithm name. - * - * @return HotpHashAlgorithm The algorithm name. - */ - public function algorithm() - { - return $this->algorithm; - } - private $digits; private $secretLength; - private $algorithm; } diff --git a/src/Eloquent/Otis/Configuration/AbstractTimeBasedOtpConfiguration.php b/src/Eloquent/Otis/Configuration/AbstractTimeBasedOtpConfiguration.php new file mode 100644 index 0000000..76fd266 --- /dev/null +++ b/src/Eloquent/Otis/Configuration/AbstractTimeBasedOtpConfiguration.php @@ -0,0 +1,93 @@ +window = $window; + $this->futureWindows = $futureWindows; + $this->pastWindows = $pastWindows; + } + + /** + * Get the number of seconds each token is valid for. + * + * @return integer The number of seconds each token is valid for. + */ + public function window() + { + return $this->window; + } + + /** + * Get the number of future windows to check. + * + * @return integer The number of future windows to check. + */ + public function futureWindows() + { + return $this->futureWindows; + } + + /** + * Get the number of past windows to check. + * + * @return integer The number of past windows to check. + */ + public function pastWindows() + { + return $this->pastWindows; + } + + private $window; + private $futureWindows; + private $pastWindows; +} diff --git a/src/Eloquent/Otis/Configuration/CounterBasedOtpConfigurationInterface.php b/src/Eloquent/Otis/Configuration/CounterBasedOtpConfigurationInterface.php new file mode 100644 index 0000000..da03e7b --- /dev/null +++ b/src/Eloquent/Otis/Configuration/CounterBasedOtpConfigurationInterface.php @@ -0,0 +1,34 @@ +validator = $validator; + $this->sharedParametersGenerator = $sharedParametersGenerator; + $this->initializationUriFactory = $initializationUriFactory; + } + + /** + * Get the validator. + * + * @return MfaValidatorInterface The validator. + */ + public function validator() + { + return $this->validator; + } + + /** + * Get the shared parameters generator. + * + * @return MfaSharedParametersGeneratorInterface The shared parameters generator. + */ + public function sharedParametersGenerator() + { + return $this->sharedParametersGenerator; + } + + /** + * Get the initialization URI factory. + * + * @return InitializationUriFactoryInterface|null The initialization URI factory, or null if not supported. + */ + public function initializationUriFactory() + { + return $this->initializationUriFactory; + } + + private $validator; + private $sharedParametersGenerator; + private $initializationUriFactory; +} diff --git a/src/Eloquent/Otis/Driver/MfaDriverFactory.php b/src/Eloquent/Otis/Driver/MfaDriverFactory.php new file mode 100644 index 0000000..a786412 --- /dev/null +++ b/src/Eloquent/Otis/Driver/MfaDriverFactory.php @@ -0,0 +1,51 @@ +digits = $digits; parent::__construct( - sprintf( - 'Invalid password length (%s).', - var_export($digits, true) - ), + sprintf('Invalid password length (%s).', var_export($digits, true)), 0, $previous ); diff --git a/src/Eloquent/Otis/Exception/UnsupportedArgumentsException.php b/src/Eloquent/Otis/Exception/UnsupportedArgumentsException.php deleted file mode 100644 index 2c3e6a3..0000000 --- a/src/Eloquent/Otis/Exception/UnsupportedArgumentsException.php +++ /dev/null @@ -1,34 +0,0 @@ -configuration = $configuration; + + parent::__construct( + sprintf( + 'Unsupported configuration of type %s supplied.', + var_export(get_class($configuration), true) + ), + 0, + $previous + ); + } + + /** + * Get the configuration. + * + * @return MfaConfigurationInterface The configuration. + */ + public function configuration() + { + return $this->configuration; + } + + private $configuration; +} diff --git a/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/AbstractGoogleAuthenticatorUriFactory.php b/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/AbstractGoogleAuthenticatorUriFactory.php new file mode 100644 index 0000000..bd071a4 --- /dev/null +++ b/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/AbstractGoogleAuthenticatorUriFactory.php @@ -0,0 +1,86 @@ +digits()) { + $parameters .= '&digits=' . rawurlencode($configuration->digits()); + } + + if (HotpHashAlgorithm::SHA1() !== $configuration->algorithm()) { + $parameters .= '&algorithm=' . rawurlencode( + $configuration->algorithm()->value() + ); + } + + $legacyIssuer = ''; + if (null !== $issuer) { + if ($issuerInLabel) { + $legacyIssuer = rawurlencode($issuer) . ':'; + } + + $parameters .= '&issuer=' . rawurlencode($issuer); + } + + return sprintf( + 'otpauth://%s/%s%s?secret=%s%s', + rawurlencode($type), + $legacyIssuer, + rawurlencode($label), + rawurlencode(Base32::encode($shared->secret())), + $parameters + ); + } +} diff --git a/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorUriFactoryInterface.php b/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactory.php similarity index 57% rename from src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorUriFactoryInterface.php rename to src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactory.php index 139664c..51fa575 100644 --- a/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorUriFactoryInterface.php +++ b/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactory.php @@ -11,39 +11,42 @@ namespace Eloquent\Otis\GoogleAuthenticator\Uri\Initialization; +use Eloquent\Otis\Configuration\MfaConfigurationInterface; use Eloquent\Otis\Hotp\Configuration\HotpConfigurationInterface; use Eloquent\Otis\Parameters\CounterBasedOtpSharedParametersInterface; -use Eloquent\Otis\Parameters\TimeBasedOtpSharedParametersInterface; -use Eloquent\Otis\Totp\Configuration\TotpConfigurationInterface; +use Eloquent\Otis\Parameters\MfaSharedParametersInterface; /** - * The interface implemented by Google Authenticator URI factories. + * Generates HOTP URIs for use with Google Authenticator, and other compatible + * OTP apps. */ -interface GoogleAuthenticatorUriFactoryInterface +class GoogleAuthenticatorHotpUriFactory + extends AbstractGoogleAuthenticatorUriFactory + implements GoogleAuthenticatorHotpUriFactoryInterface { /** - * Create a TOTP URI for use with Google Authenticator. + * Create an initialization URI. * * Note that this is not a URI for the QR code used by Google Authenticator. * The URI produced by this method is used as the actual content of the QR * code, and follows a special set of conventions understood by Google * Authenticator, and other OTP apps. * - * @param TotpConfigurationInterface $configuration The TOTP configuration. - * @param TimeBasedOtpSharedParametersInterface $shared The shared parameters. - * @param string $label The label for the account. - * @param string|null $issuer The issuer name. - * @param boolean|null $issuerInLabel True if legacy issuer support should be enabled by prefixing the label with the issuer name. + * @param MfaConfigurationInterface $configuration The multi-factor authentication configuration. + * @param MfaSharedParametersInterface $shared The shared parameters. + * @param string $label The label for the account. + * @param string|null $issuer The issuer name. * - * @return string The TOTP URI. + * @return string The initialization URI. */ - public function createTotp( - TotpConfigurationInterface $configuration, - TimeBasedOtpSharedParametersInterface $shared, + public function create( + MfaConfigurationInterface $configuration, + MfaSharedParametersInterface $shared, $label, - $issuer = null, - $issuerInLabel = null - ); + $issuer = null + ) { + return $this->createHotp($configuration, $shared, $label, $issuer); + } /** * Create a HOTP URI for use with Google Authenticator. @@ -67,5 +70,21 @@ public function createHotp( $label, $issuer = null, $issuerInLabel = null - ); + ) { + if (1 === $shared->counter()) { + $parameters = ''; + } else { + $parameters = '&counter=' . rawurlencode($shared->counter()); + } + + return $this->buildUri( + 'hotp', + $parameters, + $configuration, + $shared, + $label, + $issuer, + $issuerInLabel + ); + } } diff --git a/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactoryInterface.php b/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactoryInterface.php new file mode 100644 index 0000000..9c92a27 --- /dev/null +++ b/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactoryInterface.php @@ -0,0 +1,45 @@ +createTotp($configuration, $shared, $label, $issuer); + } + + /** + * Create a TOTP URI for use with Google Authenticator. + * + * Note that this is not a URI for the QR code used by Google Authenticator. + * The URI produced by this method is used as the actual content of the QR + * code, and follows a special set of conventions understood by Google + * Authenticator, and other OTP apps. + * + * @param TotpConfigurationInterface $configuration The TOTP configuration. + * @param TimeBasedOtpSharedParametersInterface $shared The shared parameters. + * @param string $label The label for the account. + * @param string|null $issuer The issuer name. + * @param boolean|null $issuerInLabel True if legacy issuer support should be enabled by prefixing the label with the issuer name. + * + * @return string The TOTP URI. + */ + public function createTotp( + TotpConfigurationInterface $configuration, + TimeBasedOtpSharedParametersInterface $shared, + $label, + $issuer = null, + $issuerInLabel = null + ) { + if (30 === $configuration->window()) { + $parameters = ''; + } else { + $parameters = '&period=' . rawurlencode($configuration->window()); + } + + return $this->buildUri( + 'totp', + $parameters, + $configuration, + $shared, + $label, + $issuer, + $issuerInLabel + ); + } +} diff --git a/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorTotpUriFactoryInterface.php b/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorTotpUriFactoryInterface.php new file mode 100644 index 0000000..e078d69 --- /dev/null +++ b/src/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorTotpUriFactoryInterface.php @@ -0,0 +1,45 @@ +createTotp($configuration, $shared, $label, $issuer); - } - - if ( - $configuration instanceof HotpConfigurationInterface && - $shared instanceof CounterBasedOtpSharedParametersInterface - ) { - return $this->createHotp($configuration, $shared, $label, $issuer); - } - - throw new UnsupportedArgumentsException; - } - - /** - * Create a TOTP URI for use with Google Authenticator. - * - * Note that this is not a URI for the QR code used by Google Authenticator. - * The URI produced by this method is used as the actual content of the QR - * code, and follows a special set of conventions understood by Google - * Authenticator, and other OTP apps. - * - * @param TotpConfigurationInterface $configuration The TOTP configuration. - * @param TimeBasedOtpSharedParametersInterface $shared The shared parameters. - * @param string $label The label for the account. - * @param string|null $issuer The issuer name. - * @param boolean|null $issuerInLabel True if legacy issuer support should be enabled by prefixing the label with the issuer name. - * - * @return string The TOTP URI. - */ - public function createTotp( - TotpConfigurationInterface $configuration, - TimeBasedOtpSharedParametersInterface $shared, - $label, - $issuer = null, - $issuerInLabel = null - ) { - if (30 === $configuration->window()) { - $parameters = ''; - } else { - $parameters = '&period=' . rawurlencode($configuration->window()); - } - - return $this->buildUri( - 'totp', - $parameters, - $configuration, - $shared, - $label, - $issuer, - $issuerInLabel - ); - } - - /** - * Create a HOTP URI for use with Google Authenticator. - * - * Note that this is not a URI for the QR code used by Google Authenticator. - * The URI produced by this method is used as the actual content of the QR - * code, and follows a special set of conventions understood by Google - * Authenticator, and other OTP apps. - * - * @param HotpConfigurationInterface $configuration The HOTP configuration. - * @param CounterBasedOtpSharedParametersInterface $shared The shared parameters. - * @param string $label The label for the account. - * @param string|null $issuer The issuer name. - * @param boolean|null $issuerInLabel True if legacy issuer support should be enabled by prefixing the label with the issuer name. - * - * @return string The HOTP URI. - */ - public function createHotp( - HotpConfigurationInterface $configuration, - CounterBasedOtpSharedParametersInterface $shared, - $label, - $issuer = null, - $issuerInLabel = null - ) { - if (1 === $shared->counter()) { - $parameters = ''; - } else { - $parameters = '&counter=' . rawurlencode($shared->counter()); - } - - return $this->buildUri( - 'hotp', - $parameters, - $configuration, - $shared, - $label, - $issuer, - $issuerInLabel - ); - } - - /** - * Create an OTP URI for use with Google Authenticator. - * - * Note that this is not a URI for the QR code used by Google Authenticator. - * The URI produced by this method is used as the actual content of the QR - * code, and follows a special set of conventions understood by Google - * Authenticator, and other OTP apps. - * - * @param string $type The otp type identifier. - * @param string $parameters Additional URI parameters. - * @param HotpBasedConfigurationInterface $configuration The OTP configuration. - * @param OtpSharedParametersInterface $shared The shared parameters. - * @param string $label The label for the account. - * @param string|null $issuer The issuer name. - * @param boolean|null $issuerInLabel True if legacy issuer support should be enabled by prefixing the label with the issuer name. - * - * @return string The OTP URI. - */ - protected function buildUri( - $type, - $parameters, - HotpBasedConfigurationInterface $configuration, - OtpSharedParametersInterface $shared, - $label, - $issuer = null, - $issuerInLabel = null - ) { - if (null === $issuerInLabel) { - $issuerInLabel = false; - } - - if (6 !== $configuration->digits()) { - $parameters .= '&digits=' . rawurlencode($configuration->digits()); - } - - if (HotpHashAlgorithm::SHA1() !== $configuration->algorithm()) { - $parameters .= '&algorithm=' . rawurlencode( - $configuration->algorithm()->value() - ); - } - - $legacyIssuer = ''; - if (null !== $issuer) { - if ($issuerInLabel) { - $legacyIssuer = rawurlencode($issuer) . ':'; - } - - $parameters .= '&issuer=' . rawurlencode($issuer); - } - - return sprintf( - 'otpauth://%s/%s%s?secret=%s%s', - rawurlencode($type), - $legacyIssuer, - rawurlencode($label), - rawurlencode(Base32::encode($shared->secret())), - $parameters - ); - } -} diff --git a/src/Eloquent/Otis/Hotp/Configuration/HotpBasedConfigurationInterface.php b/src/Eloquent/Otis/Hotp/Configuration/HotpBasedConfigurationInterface.php index 1073254..d188996 100644 --- a/src/Eloquent/Otis/Hotp/Configuration/HotpBasedConfigurationInterface.php +++ b/src/Eloquent/Otis/Hotp/Configuration/HotpBasedConfigurationInterface.php @@ -1,4 +1,4 @@ - 10) { + throw new InvalidPasswordLengthException($digits); } - if (null === $initialCounter) { - $initialCounter = 1; + if (null === $algorithm) { + $algorithm = HotpHashAlgorithm::SHA1(); } - parent::__construct($digits, $secretLength, $algorithm); + parent::__construct($digits, $window, $initialCounter, $secretLength); - $this->window = $window; - $this->initialCounter = $initialCounter; + $this->algorithm = $algorithm; } /** - * Get the amount of counter increments to search through for a match. + * Get the underlying algorithm name. * - * @return integer The amount of counter increments to search through for a match. + * @return HotpHashAlgorithm The algorithm name. */ - public function window() + public function algorithm() { - return $this->window; + return $this->algorithm; } - /** - * Get the initial counter value. - * - * @return integer The initial counter value. - */ - public function initialCounter() - { - return $this->initialCounter; - } - - private $window; - private $initialCounter; + private $algorithm; } diff --git a/src/Eloquent/Otis/Hotp/Configuration/HotpConfigurationInterface.php b/src/Eloquent/Otis/Hotp/Configuration/HotpConfigurationInterface.php index f4f787b..54b650c 100644 --- a/src/Eloquent/Otis/Hotp/Configuration/HotpConfigurationInterface.php +++ b/src/Eloquent/Otis/Hotp/Configuration/HotpConfigurationInterface.php @@ -1,4 +1,4 @@ -generator; } - /** - * Returns true if this validator supports the supplied combination of - * configuration, shared parameters, and credentials. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param MfaCredentialsInterface $credentials The credentials to validate. - * - * @return boolean True if this validator supports the supplied combination. - */ - public function supports( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - MfaCredentialsInterface $credentials - ) { - return $configuration instanceof HotpConfigurationInterface && - $shared instanceof CounterBasedOtpSharedParametersInterface && - $credentials instanceof OtpCredentialsInterface; - } - /** * Validate a set of multi-factor authentication parameters. * @@ -85,52 +64,16 @@ public function supports( * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. * @param MfaCredentialsInterface $credentials The credentials to validate. * - * @return MfaValidationResultInterface The validation result. - * @throws UnsupportedArgumentsException If the combination of configuration, shared parameters, and credentials is not supported. + * @return MfaValidationResultInterface The validation result. */ public function validate( MfaConfigurationInterface $configuration, MfaSharedParametersInterface $shared, MfaCredentialsInterface $credentials ) { - if (!$this->supports($configuration, $shared, $credentials)) { - throw new UnsupportedArgumentsException; - } - return $this->validateHotp($configuration, $shared, $credentials); } - /** - * Returns true if this validator supports the supplied combination of - * configuration, shared parameters, and credential sequence. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param array $credentialSequence The sequence of credentials to validate. - * - * @return boolean True if this validator supports the supplied combination. - */ - public function supportsSequence( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - array $credentialSequence - ) { - if ( - !$configuration instanceof HotpConfigurationInterface || - !$shared instanceof CounterBasedOtpSharedParametersInterface - ) { - return false; - } - - foreach ($credentialSequence as $credentials) { - if (!$credentials instanceof OtpCredentialsInterface) { - return false; - } - } - - return true; - } - /** * Validate a sequence of multi-factor authentication parameters. * @@ -138,24 +81,13 @@ public function supportsSequence( * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. * @param array $credentialSequence The sequence of credentials to validate. * - * @return MfaValidationResultInterface The validation result. - * @throws UnsupportedArgumentsException If the combination of configuration, shared parameters, and credentials is not supported. + * @return MfaValidationResultInterface The validation result. */ public function validateSequence( MfaConfigurationInterface $configuration, MfaSharedParametersInterface $shared, array $credentialSequence ) { - if ( - !$this->supportsSequence( - $configuration, - $shared, - $credentialSequence - ) - ) { - throw new UnsupportedArgumentsException; - } - return $this->validateHotpSequence( $configuration, $shared, diff --git a/src/Eloquent/Otis/Motp/Configuration/MotpConfiguration.php b/src/Eloquent/Otis/Motp/Configuration/MotpConfiguration.php index 4fe2473..386fb5c 100644 --- a/src/Eloquent/Otis/Motp/Configuration/MotpConfiguration.php +++ b/src/Eloquent/Otis/Motp/Configuration/MotpConfiguration.php @@ -11,10 +11,13 @@ namespace Eloquent\Otis\Motp\Configuration; +use Eloquent\Otis\Configuration\AbstractTimeBasedOtpConfiguration; + /** * Represents a complete set of mOTP configuration settings. */ -class MotpConfiguration implements MotpConfigurationInterface +class MotpConfiguration extends AbstractTimeBasedOtpConfiguration implements + MotpConfigurationInterface { /** * Construct a new mOTP configuration. @@ -31,30 +34,6 @@ public function __construct($futureWindows = null, $pastWindows = null) $pastWindows = 3; } - $this->futureWindows = $futureWindows; - $this->pastWindows = $pastWindows; - } - - /** - * Get the number of future windows to check. - * - * @return integer The number of future windows to check. - */ - public function futureWindows() - { - return $this->futureWindows; - } - - /** - * Get the number of past windows to check. - * - * @return integer The number of past windows to check. - */ - public function pastWindows() - { - return $this->pastWindows; + parent::__construct(6, 10, $futureWindows, $pastWindows, 8); } - - private $futureWindows; - private $pastWindows; } diff --git a/src/Eloquent/Otis/Motp/Configuration/MotpConfigurationInterface.php b/src/Eloquent/Otis/Motp/Configuration/MotpConfigurationInterface.php index d63efa9..96e2283 100644 --- a/src/Eloquent/Otis/Motp/Configuration/MotpConfigurationInterface.php +++ b/src/Eloquent/Otis/Motp/Configuration/MotpConfigurationInterface.php @@ -1,4 +1,4 @@ -time() / 10)) . + strval(intval($shared->time() / $configuration->window())) . bin2hex($shared->secret()) . $shared->pin() ), 0, - 6 + $configuration->digits() ); } } diff --git a/src/Eloquent/Otis/Motp/MotpDriver.php b/src/Eloquent/Otis/Motp/MotpDriver.php new file mode 100644 index 0000000..b304094 --- /dev/null +++ b/src/Eloquent/Otis/Motp/MotpDriver.php @@ -0,0 +1,43 @@ +isolator = Isolator::get($isolator); + } + + /** + * Generate a set of multi-factor authentication shared parameters. + * + * @param MfaConfigurationInterface $configuration The configuration to generate shared parameters for. + * + * @return MfaSharedParametersInterface The generated shared parameters. + */ + public function generate(MfaConfigurationInterface $configuration) + { + return $this->generateMotp($configuration); + } + + /** + * Generate a set of mOTP shared parameters. + * + * @param MotpConfigurationInterface $configuration The configuration to generate shared parameters for. + * + * @return MotpSharedParametersInterface The generated shared parameters. + */ + public function generateMotp(MotpConfigurationInterface $configuration) + { + return new MotpSharedParameters( + $this->isolator()->mcrypt_create_iv($configuration->secretLength()), + str_pad($this->isolator()->mt_rand(0, 9999), 4, '0', STR_PAD_LEFT), + $this->isolator()->time(), + $this->isolator() + ); + } + + /** + * Get the isolator. + * + * @return Isolator The isolator. + */ + protected function isolator() + { + return $this->isolator; + } + + private $isolator; +} diff --git a/src/Eloquent/Otis/Motp/Parameters/Generator/MotpSharedParametersGeneratorInterface.php b/src/Eloquent/Otis/Motp/Parameters/Generator/MotpSharedParametersGeneratorInterface.php new file mode 100644 index 0000000..102e87a --- /dev/null +++ b/src/Eloquent/Otis/Motp/Parameters/Generator/MotpSharedParametersGeneratorInterface.php @@ -0,0 +1,30 @@ +generator; } - /** - * Returns true if this validator supports the supplied combination of - * configuration, shared parameters, and credentials. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param MfaCredentialsInterface $credentials The credentials to validate. - * - * @return boolean True if this validator supports the supplied combination. - */ - public function supports( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - MfaCredentialsInterface $credentials - ) { - return $configuration instanceof MotpConfigurationInterface && - $shared instanceof MotpSharedParametersInterface && - $credentials instanceof OtpCredentialsInterface; - } - /** * Validate a set of multi-factor authentication parameters. * @@ -81,18 +60,13 @@ public function supports( * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. * @param MfaCredentialsInterface $credentials The credentials to validate. * - * @return MfaValidationResultInterface The validation result. - * @throws UnsupportedArgumentsException If the combination of configuration, shared parameters, and credentials is not supported. + * @return MfaValidationResultInterface The validation result. */ public function validate( MfaConfigurationInterface $configuration, MfaSharedParametersInterface $shared, MfaCredentialsInterface $credentials ) { - if (!$this->supports($configuration, $shared, $credentials)) { - throw new UnsupportedArgumentsException; - } - return $this->validateMotp($configuration, $shared, $credentials); } diff --git a/src/Eloquent/Otis/Parameters/CounterBasedOtpSharedParametersInterface.php b/src/Eloquent/Otis/Parameters/CounterBasedOtpSharedParametersInterface.php index 2af0233..1154f63 100644 --- a/src/Eloquent/Otis/Parameters/CounterBasedOtpSharedParametersInterface.php +++ b/src/Eloquent/Otis/Parameters/CounterBasedOtpSharedParametersInterface.php @@ -1,4 +1,4 @@ -isolator = Isolator::get($isolator); + } + + /** + * Generate a set of multi-factor authentication shared parameters. + * + * @param MfaConfigurationInterface $configuration The configuration to generate shared parameters for. + * + * @return MfaSharedParametersInterface The generated shared parameters. + */ + public function generate(MfaConfigurationInterface $configuration) + { + return $this->generateCounterBased($configuration); + } + + /** + * Generate a set of counter-based one-time password shared parameters. + * + * @param CounterBasedOtpConfigurationInterface $configuration The configuration to generate shared parameters for. + * + * @return CounterBasedOtpSharedParametersInterface The generated shared parameters. + */ + public function generateCounterBased( + CounterBasedOtpConfigurationInterface $configuration + ) { + return new CounterBasedOtpSharedParameters( + $this->isolator()->mcrypt_create_iv($configuration->secretLength()), + $configuration->initialCounter() + ); + } + + /** + * Get the isolator. + * + * @return Isolator The isolator. + */ + protected function isolator() + { + return $this->isolator; + } + + private $isolator; +} diff --git a/src/Eloquent/Otis/Parameters/Generator/CounterBasedOtpSharedParametersGeneratorInterface.php b/src/Eloquent/Otis/Parameters/Generator/CounterBasedOtpSharedParametersGeneratorInterface.php new file mode 100644 index 0000000..de13f0e --- /dev/null +++ b/src/Eloquent/Otis/Parameters/Generator/CounterBasedOtpSharedParametersGeneratorInterface.php @@ -0,0 +1,33 @@ +isolator = Isolator::get($isolator); + } + + /** + * Generate a set of multi-factor authentication shared parameters. + * + * @param MfaConfigurationInterface $configuration The configuration to generate shared parameters for. + * + * @return MfaSharedParametersInterface The generated shared parameters. + */ + public function generate(MfaConfigurationInterface $configuration) + { + return $this->generateTimeBased($configuration); + } + + /** + * Generate a set of time-based one-time password shared parameters. + * + * @param TimeBasedOtpConfigurationInterface $configuration The configuration to generate shared parameters for. + * + * @return TimeBasedOtpSharedParametersInterface The generated shared parameters. + */ + public function generateTimeBased( + TimeBasedOtpConfigurationInterface $configuration + ) { + return new TimeBasedOtpSharedParameters( + $this->isolator()->mcrypt_create_iv($configuration->secretLength()), + $this->isolator()->time(), + $this->isolator() + ); + } + + /** + * Get the isolator. + * + * @return Isolator The isolator. + */ + protected function isolator() + { + return $this->isolator; + } + + private $isolator; +} diff --git a/src/Eloquent/Otis/Parameters/Generator/TimeBasedOtpSharedParametersGeneratorInterface.php b/src/Eloquent/Otis/Parameters/Generator/TimeBasedOtpSharedParametersGeneratorInterface.php new file mode 100644 index 0000000..585062d --- /dev/null +++ b/src/Eloquent/Otis/Parameters/Generator/TimeBasedOtpSharedParametersGeneratorInterface.php @@ -0,0 +1,33 @@ + 10) { + throw new InvalidPasswordLengthException($digits); } - if (null === $futureWindows) { - $futureWindows = 1; + if (null === $algorithm) { + $algorithm = HotpHashAlgorithm::SHA1(); } - if (null === $pastWindows) { - $pastWindows = 1; - } - - parent::__construct($digits, $secretLength, $algorithm); - $this->window = $window; - $this->futureWindows = $futureWindows; - $this->pastWindows = $pastWindows; - } + parent::__construct( + $digits, + $window, + $futureWindows, + $pastWindows, + $secretLength + ); - /** - * Get the number of seconds each token is valid for. - * - * @return integer The number of seconds each token is valid for. - */ - public function window() - { - return $this->window; - } - - /** - * Get the number of future windows to check. - * - * @return integer The number of future windows to check. - */ - public function futureWindows() - { - return $this->futureWindows; + $this->algorithm = $algorithm; } /** - * Get the number of past windows to check. + * Get the underlying algorithm name. * - * @return integer The number of past windows to check. + * @return HotpHashAlgorithm The algorithm name. */ - public function pastWindows() + public function algorithm() { - return $this->pastWindows; + return $this->algorithm; } - private $window; - private $futureWindows; - private $pastWindows; + private $algorithm; } diff --git a/src/Eloquent/Otis/Totp/Configuration/TotpConfigurationInterface.php b/src/Eloquent/Otis/Totp/Configuration/TotpConfigurationInterface.php index b102c81..c923926 100644 --- a/src/Eloquent/Otis/Totp/Configuration/TotpConfigurationInterface.php +++ b/src/Eloquent/Otis/Totp/Configuration/TotpConfigurationInterface.php @@ -1,4 +1,4 @@ -generator; } - /** - * Returns true if this validator supports the supplied combination of - * configuration, shared parameters, and credentials. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param MfaCredentialsInterface $credentials The credentials to validate. - * - * @return boolean True if this validator supports the supplied combination. - */ - public function supports( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - MfaCredentialsInterface $credentials - ) { - return $configuration instanceof TotpConfigurationInterface && - $shared instanceof TimeBasedOtpSharedParametersInterface && - $credentials instanceof OtpCredentialsInterface; - } - /** * Validate a set of multi-factor authentication parameters. * @@ -81,18 +60,13 @@ public function supports( * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. * @param MfaCredentialsInterface $credentials The credentials to validate. * - * @return MfaValidationResultInterface The validation result. - * @throws UnsupportedArgumentsException If the combination of configuration, shared parameters, and credentials is not supported. + * @return MfaValidationResultInterface The validation result. */ public function validate( MfaConfigurationInterface $configuration, MfaSharedParametersInterface $shared, MfaCredentialsInterface $credentials ) { - if (!$this->supports($configuration, $shared, $credentials)) { - throw new UnsupportedArgumentsException; - } - return $this->validateTotp($configuration, $shared, $credentials); } diff --git a/src/Eloquent/Otis/Uri/Initialization/InitializationUriFactory.php b/src/Eloquent/Otis/Uri/Initialization/InitializationUriFactory.php deleted file mode 100644 index a20dd8e..0000000 --- a/src/Eloquent/Otis/Uri/Initialization/InitializationUriFactory.php +++ /dev/null @@ -1,109 +0,0 @@ -|null $factories The factories to aggregate. - */ - public function __construct(array $factories = null) - { - if (null === $factories) { - $factories = array( - new GoogleAuthenticatorUriFactory, - ); - } - - $this->factories = $factories; - } - - /** - * Get the aggregated factories. - * - * @return array The aggregated factories. - */ - public function factories() - { - return $this->factories; - } - - /** - * Returns true if this initialization URI factory supports the supplied - * combination of configuration and shared parameters. - * - * @param MfaConfigurationInterface $configuration The multi-factor authentication configuration. - * @param MfaSharedParametersInterface $shared The shared parameters. - * - * @return boolean True if the configuration and shared parameters are supported. - */ - public function supports( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared - ) { - foreach ($this->factories() as $factory) { - if ($factory->supports($configuration, $shared)) { - return true; - } - } - - return false; - } - - /** - * Create an initialization URI. - * - * Note that this is not a URI for the QR code used by Google Authenticator. - * The URI produced by this method is used as the actual content of the QR - * code, and follows a special set of conventions understood by Google - * Authenticator, and other OTP apps. - * - * @param MfaConfigurationInterface $configuration The multi-factor authentication configuration. - * @param MfaSharedParametersInterface $shared The shared parameters. - * @param string $label The label for the account. - * @param string|null $issuer The issuer name. - * - * @return string The initialization URI. - * @throws UnsupportedArgumentsException If the combination of configuration and shared parameters is not supported. - */ - public function create( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - $label, - $issuer = null - ) { - foreach ($this->factories() as $factory) { - if ($factory->supports($configuration, $shared)) { - return $factory->create( - $configuration, - $shared, - $label, - $issuer - ); - } - } - - throw new UnsupportedArgumentsException; - } - - private $factories; -} diff --git a/src/Eloquent/Otis/Uri/Initialization/InitializationUriFactoryInterface.php b/src/Eloquent/Otis/Uri/Initialization/InitializationUriFactoryInterface.php index 47db9c9..182df5b 100644 --- a/src/Eloquent/Otis/Uri/Initialization/InitializationUriFactoryInterface.php +++ b/src/Eloquent/Otis/Uri/Initialization/InitializationUriFactoryInterface.php @@ -13,27 +13,12 @@ use Eloquent\Otis\Configuration\MfaConfigurationInterface; use Eloquent\Otis\Parameters\MfaSharedParametersInterface; -use Eloquent\Otis\Validator\Exception\UnsupportedArgumentsException; /** * The interface implemented by initialization URI factories. */ interface InitializationUriFactoryInterface { - /** - * Returns true if this initialization URI factory supports the supplied - * combination of configuration and shared parameters. - * - * @param MfaConfigurationInterface $configuration The multi-factor authentication configuration. - * @param MfaSharedParametersInterface $shared The shared parameters. - * - * @return boolean True if the configuration and shared parameters are supported. - */ - public function supports( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared - ); - /** * Create an initialization URI. * @@ -47,8 +32,7 @@ public function supports( * @param string $label The label for the account. * @param string|null $issuer The issuer name. * - * @return string The initialization URI. - * @throws UnsupportedArgumentsException If the combination of configuration and shared parameters is not supported. + * @return string The initialization URI. */ public function create( MfaConfigurationInterface $configuration, diff --git a/src/Eloquent/Otis/Validator/MfaSequenceValidatorInterface.php b/src/Eloquent/Otis/Validator/MfaSequenceValidatorInterface.php index 05458ea..651d187 100644 --- a/src/Eloquent/Otis/Validator/MfaSequenceValidatorInterface.php +++ b/src/Eloquent/Otis/Validator/MfaSequenceValidatorInterface.php @@ -14,7 +14,6 @@ use Eloquent\Otis\Configuration\MfaConfigurationInterface; use Eloquent\Otis\Credentials\MfaCredentialsInterface; use Eloquent\Otis\Parameters\MfaSharedParametersInterface; -use Eloquent\Otis\Validator\Exception\UnsupportedArgumentsException; /** * The interface implemented by generic multi-factor authentication validators @@ -22,22 +21,6 @@ */ interface MfaSequenceValidatorInterface extends MfaValidatorInterface { - /** - * Returns true if this validator supports the supplied combination of - * configuration, shared parameters, and credential sequence. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param array $credentialSequence The sequence of credentials to validate. - * - * @return boolean True if this validator supports the supplied combination. - */ - public function supportsSequence( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - array $credentialSequence - ); - /** * Validate a sequence of multi-factor authentication parameters. * @@ -46,7 +29,6 @@ public function supportsSequence( * @param array $credentialSequence The sequence of credentials to validate. * * @return Result\MfaValidationResultInterface The validation result. - * @throws UnsupportedArgumentsException If the combination of configuration, shared parameters, and credentials is not supported. */ public function validateSequence( MfaConfigurationInterface $configuration, diff --git a/src/Eloquent/Otis/Validator/MfaValidator.php b/src/Eloquent/Otis/Validator/MfaValidator.php deleted file mode 100644 index a5fcedb..0000000 --- a/src/Eloquent/Otis/Validator/MfaValidator.php +++ /dev/null @@ -1,174 +0,0 @@ -|null $validators The validators to aggregate. - */ - public function __construct(array $validators = null) - { - if (null === $validators) { - $validators = array( - new TotpValidator, - new HotpValidator, - new MotpValidator, - ); - } - - $this->validators = $validators; - } - - /** - * Get the aggregated validators. - * - * @return array The aggregated validators. - */ - public function validators() - { - return $this->validators; - } - - /** - * Returns true if this validator supports the supplied combination of - * configuration, shared parameters, and credentials. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param MfaCredentialsInterface $credentials The credentials to validate. - * - * @return boolean True if this validator supports the supplied combination. - */ - public function supports( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - MfaCredentialsInterface $credentials - ) { - foreach ($this->validators() as $validator) { - if ($validator->supports($configuration, $shared, $credentials)) { - return true; - } - } - - return false; - } - - /** - * Validate a set of multi-factor authentication parameters. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param MfaCredentialsInterface $credentials The credentials to validate. - * - * @return Result\MfaValidationResultInterface The validation result. - * @throws UnsupportedArgumentsException If the combination of configuration, shared parameters, and credentials is not supported. - */ - public function validate( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - MfaCredentialsInterface $credentials - ) { - foreach ($this->validators() as $validator) { - if ($validator->supports($configuration, $shared, $credentials)) { - return $validator->validate( - $configuration, - $shared, - $credentials - ); - } - } - - throw new UnsupportedArgumentsException; - } - - /** - * Returns true if this validator supports the supplied combination of - * configuration, shared parameters, and credential sequence. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param array $credentialSequence The sequence of credentials to validate. - * - * @return boolean True if this validator supports the supplied combination. - */ - public function supportsSequence( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - array $credentialSequence - ) { - foreach ($this->validators() as $validator) { - if ( - $validator instanceof MfaSequenceValidatorInterface && - $validator->supportsSequence( - $configuration, - $shared, - $credentialSequence - ) - ) { - return true; - } - } - - return false; - } - - /** - * Validate a sequence of multi-factor authentication parameters. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param array $credentialSequence The sequence of credentials to validate. - * - * @return Result\MfaValidationResultInterface The validation result. - * @throws UnsupportedArgumentsException If the combination of configuration, shared parameters, and credentials is not supported. - */ - public function validateSequence( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - array $credentialSequence - ) { - foreach ($this->validators() as $validator) { - if ( - $validator instanceof MfaSequenceValidatorInterface && - $validator->supportsSequence( - $configuration, - $shared, - $credentialSequence - ) - ) { - return $validator->validateSequence( - $configuration, - $shared, - $credentialSequence - ); - } - } - - throw new UnsupportedArgumentsException; - } - - private $validators; -} diff --git a/src/Eloquent/Otis/Validator/MfaValidatorInterface.php b/src/Eloquent/Otis/Validator/MfaValidatorInterface.php index f1718b0..31e504d 100644 --- a/src/Eloquent/Otis/Validator/MfaValidatorInterface.php +++ b/src/Eloquent/Otis/Validator/MfaValidatorInterface.php @@ -14,29 +14,12 @@ use Eloquent\Otis\Configuration\MfaConfigurationInterface; use Eloquent\Otis\Credentials\MfaCredentialsInterface; use Eloquent\Otis\Parameters\MfaSharedParametersInterface; -use Eloquent\Otis\Validator\Exception\UnsupportedArgumentsException; /** * The interface implemented by generic multi-factor authentication validators. */ interface MfaValidatorInterface { - /** - * Returns true if this validator supports the supplied combination of - * configuration, shared parameters, and credentials. - * - * @param MfaConfigurationInterface $configuration The configuration to use for validation. - * @param MfaSharedParametersInterface $shared The shared parameters to use for validation. - * @param MfaCredentialsInterface $credentials The credentials to validate. - * - * @return boolean True if this validator supports the supplied combination. - */ - public function supports( - MfaConfigurationInterface $configuration, - MfaSharedParametersInterface $shared, - MfaCredentialsInterface $credentials - ); - /** * Validate a set of multi-factor authentication parameters. * @@ -45,7 +28,6 @@ public function supports( * @param MfaCredentialsInterface $credentials The credentials to validate. * * @return Result\MfaValidationResultInterface The validation result. - * @throws UnsupportedArgumentsException If the combination of configuration, shared parameters, and credentials is not supported. */ public function validate( MfaConfigurationInterface $configuration, diff --git a/test/suite/Eloquent/Otis/Driver/MfaDriverFactoryTest.php b/test/suite/Eloquent/Otis/Driver/MfaDriverFactoryTest.php new file mode 100644 index 0000000..bf2c534 --- /dev/null +++ b/test/suite/Eloquent/Otis/Driver/MfaDriverFactoryTest.php @@ -0,0 +1,57 @@ +factory = new MfaDriverFactory; + } + + public function testCreateTotp() + { + $configuration = new TotpConfiguration; + + $this->assertInstanceOf('Eloquent\Otis\Totp\TotpDriver', $this->factory->create($configuration)); + } + + public function testCreateHotp() + { + $configuration = new HotpConfiguration; + + $this->assertInstanceOf('Eloquent\Otis\Hotp\HotpDriver', $this->factory->create($configuration)); + } + + public function testCreateMotp() + { + $configuration = new MotpConfiguration; + + $this->assertInstanceOf('Eloquent\Otis\Motp\MotpDriver', $this->factory->create($configuration)); + } + + public function testCreateUnsupported() + { + $configuration = Phake::mock('Eloquent\Otis\Configuration\MfaConfigurationInterface'); + + $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedConfigurationException'); + $this->factory->create($configuration); + } +} diff --git a/test/suite/Eloquent/Otis/Exception/UnsupportedArgumentsExceptionTest.php b/test/suite/Eloquent/Otis/Exception/UnsupportedArgumentsExceptionTest.php index 443d551..7dfd088 100644 --- a/test/suite/Eloquent/Otis/Exception/UnsupportedArgumentsExceptionTest.php +++ b/test/suite/Eloquent/Otis/Exception/UnsupportedArgumentsExceptionTest.php @@ -11,17 +11,24 @@ namespace Eloquent\Otis\Exception; +use Eloquent\Otis\Totp\Configuration\TotpConfiguration; use Exception; use PHPUnit_Framework_TestCase; -class UnsupportedArgumentsExceptionTest extends PHPUnit_Framework_TestCase +class UnsupportedConfigurationExceptionTest extends PHPUnit_Framework_TestCase { public function testException() { $previous = new Exception; - $exception = new UnsupportedArgumentsException($previous); + $configuration = new TotpConfiguration; + $exception = new UnsupportedConfigurationException($configuration, $previous); - $this->assertSame('The supplied arguments are not supported by this method.', $exception->getMessage()); + $this->assertSame($configuration, $exception->configuration()); + $this->assertSame( + "Unsupported configuration of type " . + "'Eloquent\\\\Otis\\\\Totp\\\\Configuration\\\\TotpConfiguration' supplied.", + $exception->getMessage() + ); $this->assertSame(0, $exception->getCode()); $this->assertSame($previous, $exception->getPrevious()); } diff --git a/test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactoryTest.php b/test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactoryTest.php new file mode 100644 index 0000000..158dbd1 --- /dev/null +++ b/test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorHotpUriFactoryTest.php @@ -0,0 +1,95 @@ +factory = new GoogleAuthenticatorHotpUriFactory; + } + + public function createData() + { + // secret label issuer counter digits algorithm expected + return array( + 'All defaults' => array('12345678901234567890', 'test.ease@example.org', null, null, null, null, 'otpauth://hotp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'), + 'All options' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', 'otpauth://hotp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&counter=111&digits=10&algorithm=SHA256&issuer=Skynet'), + ); + } + + /** + * @dataProvider createData + */ + public function testCreate($secret, $label, $issuer, $counter, $digits, $algorithm, $expected) + { + $configuration = new HotpConfiguration( + $digits, + null, + null, + null, + HotpHashAlgorithm::memberByValueWithDefault($algorithm) + ); + $shared = new CounterBasedOtpSharedParameters($secret, $counter); + + $this->assertSame($expected, $this->factory->create($configuration, $shared, $label, $issuer)); + } + + public function createHotpData() + { + // secret label issuer counter digits algorithm issuerInLabel expected + return array( + 'All defaults' => array('12345678901234567890', 'test.ease@example.org', null, null, null, null, null, 'otpauth://hotp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'), + 'All options' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', true, 'otpauth://hotp/Skynet:test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&counter=111&digits=10&algorithm=SHA256&issuer=Skynet'), + 'No legacy issuer' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', false, 'otpauth://hotp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&counter=111&digits=10&algorithm=SHA256&issuer=Skynet'), + ); + } + + /** + * @dataProvider createHotpData + */ + public function testCreateHotp( + $secret, + $label, + $issuer, + $counter, + $digits, + $algorithm, + $issuerInLabel, + $expected + ) { + $configuration = new HotpConfiguration( + $digits, + null, + null, + null, + HotpHashAlgorithm::memberByValueWithDefault($algorithm) + ); + $shared = new CounterBasedOtpSharedParameters($secret, $counter); + + $this->assertSame( + $expected, + $this->factory->createHotp($configuration, $shared, $label, $issuer, $issuerInLabel) + ); + } +} diff --git a/test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorTotpUriFactoryTest.php b/test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorTotpUriFactoryTest.php new file mode 100644 index 0000000..49001d1 --- /dev/null +++ b/test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorTotpUriFactoryTest.php @@ -0,0 +1,97 @@ +factory = new GoogleAuthenticatorTotpUriFactory; + } + + public function createData() + { + // secret label issuer window digits algorithm expected + return array( + 'All defaults' => array('12345678901234567890', 'test.ease@example.org', null, null, null, null, 'otpauth://totp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'), + 'All options' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', 'otpauth://totp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&period=111&digits=10&algorithm=SHA256&issuer=Skynet'), + ); + } + + /** + * @dataProvider createData + */ + public function testCreate($secret, $label, $issuer, $window, $digits, $algorithm, $expected) + { + $configuration = new TotpConfiguration( + $digits, + $window, + null, + null, + null, + HotpHashAlgorithm::memberByValueWithDefault($algorithm) + ); + $shared = new TimeBasedOtpSharedParameters($secret, 111); + + $this->assertSame($expected, $this->factory->create($configuration, $shared, $label, $issuer)); + } + + public function createTotpData() + { + // secret label issuer window digits algorithm issuerInLabel expected + return array( + 'All defaults' => array('12345678901234567890', 'test.ease@example.org', null, null, null, null, null, 'otpauth://totp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'), + 'All options' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', true, 'otpauth://totp/Skynet:test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&period=111&digits=10&algorithm=SHA256&issuer=Skynet'), + 'No legacy issuer' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', false, 'otpauth://totp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&period=111&digits=10&algorithm=SHA256&issuer=Skynet'), + ); + } + + /** + * @dataProvider createTotpData + */ + public function testCreateTotp( + $secret, + $label, + $issuer, + $window, + $digits, + $algorithm, + $issuerInLabel, + $expected + ) { + $configuration = new TotpConfiguration( + $digits, + $window, + null, + null, + null, + HotpHashAlgorithm::memberByValueWithDefault($algorithm) + ); + $shared = new TimeBasedOtpSharedParameters($secret, 111); + + $this->assertSame( + $expected, + $this->factory->createTotp($configuration, $shared, $label, $issuer, $issuerInLabel) + ); + } +} diff --git a/test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorUriFactoryTest.php b/test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorUriFactoryTest.php deleted file mode 100644 index 3d42852..0000000 --- a/test/suite/Eloquent/Otis/GoogleAuthenticator/Uri/Initialization/GoogleAuthenticatorUriFactoryTest.php +++ /dev/null @@ -1,145 +0,0 @@ -factory = new GoogleAuthenticatorUriFactory; - } - - public function supportsData() - { - // configuration shared expected - return array( - 'HOTP' => array(new HotpConfiguration, new CounterBasedOtpSharedParameters('secret', 111), true), - 'TOTP' => array(new TotpConfiguration, new TimeBasedOtpSharedParameters('secret', 111), true), - 'Unsupported configuration' => array(new MotpConfiguration, new TimeBasedOtpSharedParameters('secret', 111), false), - 'Unsupported shared parameters' => array(new HotpConfiguration, new MotpSharedParameters('secret', 111), false), - ); - } - - /** - * @dataProvider supportsData - */ - public function testSupports($configuration, $shared, $expected) - { - $this->assertSame($expected, $this->factory->supports($configuration, $shared)); - } - - public function testCreateFailureUnsupportedConfiguration() - { - $configuration = new MotpConfiguration; - $shared = new CounterBasedOtpSharedParameters('secret', 111); - - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->factory->create($configuration, $shared, 'label'); - } - - public function testCreateFailureUnsupportedSharedParameters() - { - $configuration = new HotpConfiguration; - $shared = new MotpSharedParameters('secret', 111); - - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->factory->create($configuration, $shared, 'label'); - } - - public function createHotpData() - { - // secret label issuer counter digits algorithm issuerInLabel expected - return array( - 'All defaults' => array('12345678901234567890', 'test.ease@example.org', null, null, null, null, null, 'otpauth://hotp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'), - 'All options' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', true, 'otpauth://hotp/Skynet:test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&counter=111&digits=10&algorithm=SHA256&issuer=Skynet'), - 'No legacy issuer' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', false, 'otpauth://hotp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&counter=111&digits=10&algorithm=SHA256&issuer=Skynet'), - ); - } - - /** - * @dataProvider createHotpData - */ - public function testCreateHotp( - $secret, - $label, - $issuer, - $counter, - $digits, - $algorithm, - $issuerInLabel, - $expected - ) { - $configuration = new HotpConfiguration( - $digits, - null, - null, - null, - HotpHashAlgorithm::memberByValueWithDefault($algorithm) - ); - $shared = new CounterBasedOtpSharedParameters($secret, $counter); - - $this->assertSame( - $expected, - $this->factory->createHotp($configuration, $shared, $label, $issuer, $issuerInLabel) - ); - } - - public function createTotpData() - { - // secret label issuer window digits algorithm issuerInLabel expected - return array( - 'All defaults' => array('12345678901234567890', 'test.ease@example.org', null, null, null, null, null, 'otpauth://totp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'), - 'All options' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', true, 'otpauth://totp/Skynet:test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&period=111&digits=10&algorithm=SHA256&issuer=Skynet'), - 'No legacy issuer' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', false, 'otpauth://totp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&period=111&digits=10&algorithm=SHA256&issuer=Skynet'), - ); - } - - /** - * @dataProvider createTotpData - */ - public function testCreateTotp( - $secret, - $label, - $issuer, - $window, - $digits, - $algorithm, - $issuerInLabel, - $expected - ) { - $configuration = new TotpConfiguration( - $digits, - $window, - null, - null, - null, - HotpHashAlgorithm::memberByValueWithDefault($algorithm) - ); - $shared = new TimeBasedOtpSharedParameters($secret, 111); - - $this->assertSame( - $expected, - $this->factory->createTotp($configuration, $shared, $label, $issuer, $issuerInLabel) - ); - } -} diff --git a/test/suite/Eloquent/Otis/Hotp/Configuration/HotpConfigurationTest.php b/test/suite/Eloquent/Otis/Hotp/Configuration/HotpConfigurationTest.php index b70c081..66dc23f 100644 --- a/test/suite/Eloquent/Otis/Hotp/Configuration/HotpConfigurationTest.php +++ b/test/suite/Eloquent/Otis/Hotp/Configuration/HotpConfigurationTest.php @@ -16,7 +16,8 @@ /** * @covers \Eloquent\Otis\Hotp\Configuration\HotpConfiguration - * @covers \Eloquent\Otis\Hotp\Configuration\AbstractHotpBasedOtpConfiguration + * @covers \Eloquent\Otis\Configuration\AbstractCounterBasedOtpConfiguration + * @covers \Eloquent\Otis\Configuration\AbstractOtpConfiguration */ class HotpConfigurationTest extends PHPUnit_Framework_TestCase { diff --git a/test/suite/Eloquent/Otis/Hotp/HotpDriverTest.php b/test/suite/Eloquent/Otis/Hotp/HotpDriverTest.php new file mode 100644 index 0000000..c9c7b5a --- /dev/null +++ b/test/suite/Eloquent/Otis/Hotp/HotpDriverTest.php @@ -0,0 +1,53 @@ +validator = new Validator\HotpValidator; + $this->sharedParametersGenerator = new CounterBasedOtpSharedParametersGenerator; + $this->initializationUriFactory = new GoogleAuthenticatorHotpUriFactory; + $this->driver = new HotpDriver( + $this->validator, + $this->sharedParametersGenerator, + $this->initializationUriFactory + ); + } + + public function testConstructor() + { + $this->assertSame($this->validator, $this->driver->validator()); + $this->assertSame($this->sharedParametersGenerator, $this->driver->sharedParametersGenerator()); + $this->assertSame($this->initializationUriFactory, $this->driver->initializationUriFactory()); + } + + public function testConstructorDefaults() + { + $this->driver = new HotpDriver; + + $this->assertEquals($this->validator, $this->driver->validator()); + $this->assertEquals($this->sharedParametersGenerator, $this->driver->sharedParametersGenerator()); + $this->assertEquals($this->initializationUriFactory, $this->driver->initializationUriFactory()); + } +} diff --git a/test/suite/Eloquent/Otis/Hotp/Validator/HotpValidatorTest.php b/test/suite/Eloquent/Otis/Hotp/Validator/HotpValidatorTest.php index 201aedb..26012da 100644 --- a/test/suite/Eloquent/Otis/Hotp/Validator/HotpValidatorTest.php +++ b/test/suite/Eloquent/Otis/Hotp/Validator/HotpValidatorTest.php @@ -15,10 +15,7 @@ use Eloquent\Otis\Hotp\Configuration\HotpConfiguration; use Eloquent\Otis\Hotp\Generator\HotpGenerator; use Eloquent\Otis\Parameters\CounterBasedOtpSharedParameters; -use Eloquent\Otis\Parameters\TimeBasedOtpSharedParameters; -use Eloquent\Otis\Totp\Configuration\TotpConfiguration; use PHPUnit_Framework_TestCase; -use Phake; class HotpValidatorTest extends PHPUnit_Framework_TestCase { @@ -42,60 +39,63 @@ public function testConstructorDefaults() $this->assertEquals($this->generator, $this->validator->generator()); } - public function supportsData() + public function validateHotpData() { - $mockCredentials = Phake::mock('Eloquent\Otis\Credentials\MfaCredentialsInterface'); - $mockSharedParameters = Phake::mock('Eloquent\Otis\Parameters\MfaSharedParametersInterface'); - - // configuration shared credentials expected + // password secret currentCounter digits window result newCounter return array( - 'Valid combination' => array(new HotpConfiguration, new CounterBasedOtpSharedParameters('secret', 123), new OtpCredentials('password'), true), - 'Unsupported credentials' => array(new HotpConfiguration, new CounterBasedOtpSharedParameters('secret', 123), $mockCredentials, false), - 'Unsupported shared parameters' => array(new HotpConfiguration, $mockSharedParameters, new OtpCredentials('password'), false), - 'Unsupported configuration' => array(new TotpConfiguration, new CounterBasedOtpSharedParameters('secret', 123), new OtpCredentials('password'), false), + 'No window, valid' => array('969429', '12345678901234567890', 3, null, null, 'valid', 4), + 'With window, valid' => array('520489', '12345678901234567890', 0, null, 9, 'valid', 10), + + 'No window, invalid' => array('338314', '12345678901234567890', 3, null, 0, 'invalid-credentials', null), + 'With window, invalid' => array('520489', '12345678901234567890', 0, null, 8, 'invalid-credentials', null), + 'Length mismatch' => array('969429', '12345678901234567890', 3, 8, null, 'credential-length-mismatch', null), ); } /** - * @dataProvider supportsData + * @dataProvider validateHotpData */ - public function testSupports($configuration, $shared, $credentials, $expected) + public function testValidateHotp($password, $secret, $currentCounter, $digits, $window, $result, $newCounter) { - $this->assertSame($expected, $this->validator->supports($configuration, $shared, $credentials)); + $configuration = new HotpConfiguration($digits, $window); + $shared = new CounterBasedOtpSharedParameters($secret, $currentCounter); + $credentials = new OtpCredentials($password); + $actual = $this->validator->validate($configuration, $shared, $credentials); + + $this->assertInstanceOf('Eloquent\Otis\Validator\Result\CounterBasedOtpValidationResult', $actual); + $this->assertSame($result, $actual->type()); + $this->assertSame($newCounter, $actual->counter()); } - public function testValidateFailureUnsupported() + public function validateHotpSequenceData() { - $configuration = new TotpConfiguration; - $shared = new TimeBasedOtpSharedParameters('secret', 123); - $credentials = new OtpCredentials('password'); - - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->validator->validate($configuration, $shared, $credentials); - } + // passwords secret currentCounter digits window result newCounter + return array( + 'No window, valid' => array(array('969429', '338314'), '12345678901234567890', 3, null, null, 'valid', 5), + 'With window, valid' => array(array('399871', '520489'), '12345678901234567890', 0, null, 8, 'valid', 10), - public function testValidateSequenceFailureUnsupportedConfig() - { - $configuration = new TotpConfiguration; - $shared = new TimeBasedOtpSharedParameters('secret', 123); - $credentialSequence = array( - new OtpCredentials('password'), + 'No window, invalid' => array(array('359152', '969429'), '12345678901234567890', 3, null, 0, 'invalid-credentials', null), + 'With window, invalid' => array(array('755224', '359152'), '12345678901234567890', 0, null, 100, 'invalid-credentials', null), + 'Length mismatch' => array(array('969429', '338314'), '12345678901234567890', 3, 8, null, 'credential-length-mismatch', null), + 'No credentials' => array(array(), '12345678901234567890', 0, null, 100, 'empty-credential-sequence', null), ); - - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->validator->validateSequence($configuration, $shared, $credentialSequence); } - public function testValidateSequenceFailureUnsupportedCredential() + /** + * @dataProvider validateHotpSequenceData + */ + public function testValidateHotpSequence($passwords, $secret, $currentCounter, $digits, $window, $result, $newCounter) { - $configuration = new HotpConfiguration; - $shared = new CounterBasedOtpSharedParameters('secret', 123); - $credentialSequence = array( - new OtpCredentials('password'), - Phake::mock('Eloquent\Otis\Credentials\MfaCredentialsInterface'), - ); - - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->validator->validateSequence($configuration, $shared, $credentialSequence); + $configuration = new HotpConfiguration($digits, $window); + $shared = new CounterBasedOtpSharedParameters($secret, $currentCounter); + $credentialSequence = array(); + foreach ($passwords as $password) { + $credentialSequence[] = new OtpCredentials($password); + } + $actual = $this->validator->validateSequence($configuration, $shared, $credentialSequence); + + $this->assertInstanceOf('Eloquent\Otis\Validator\Result\CounterBasedOtpValidationResult', $actual); + $this->assertSame($result, $actual->type()); + $this->assertSame($newCounter, $actual->counter()); } } diff --git a/test/suite/Eloquent/Otis/Motp/Configuration/MotpConfigurationTest.php b/test/suite/Eloquent/Otis/Motp/Configuration/MotpConfigurationTest.php index 449a2b1..229d6d1 100644 --- a/test/suite/Eloquent/Otis/Motp/Configuration/MotpConfigurationTest.php +++ b/test/suite/Eloquent/Otis/Motp/Configuration/MotpConfigurationTest.php @@ -13,6 +13,11 @@ use PHPUnit_Framework_TestCase; +/** + * @covers \Eloquent\Otis\Motp\Configuration\MotpConfiguration + * @covers \Eloquent\Otis\Configuration\AbstractTimeBasedOtpConfiguration + * @covers \Eloquent\Otis\Configuration\AbstractOtpConfiguration + */ class MotpConfigurationTest extends PHPUnit_Framework_TestCase { protected function setUp() diff --git a/test/suite/Eloquent/Otis/Motp/MotpDriverTest.php b/test/suite/Eloquent/Otis/Motp/MotpDriverTest.php new file mode 100644 index 0000000..ec48965 --- /dev/null +++ b/test/suite/Eloquent/Otis/Motp/MotpDriverTest.php @@ -0,0 +1,48 @@ +validator = new Validator\MotpValidator; + $this->sharedParametersGenerator = new Parameters\Generator\MotpSharedParametersGenerator; + $this->driver = new MotpDriver( + $this->validator, + $this->sharedParametersGenerator + ); + } + + public function testConstructor() + { + $this->assertSame($this->validator, $this->driver->validator()); + $this->assertSame($this->sharedParametersGenerator, $this->driver->sharedParametersGenerator()); + $this->assertNull($this->driver->initializationUriFactory()); + } + + public function testConstructorDefaults() + { + $this->driver = new MotpDriver; + + $this->assertEquals($this->validator, $this->driver->validator()); + $this->assertEquals($this->sharedParametersGenerator, $this->driver->sharedParametersGenerator()); + } +} diff --git a/test/suite/Eloquent/Otis/Motp/Parameters/Generator/MotpSharedParametersGeneratorTest.php b/test/suite/Eloquent/Otis/Motp/Parameters/Generator/MotpSharedParametersGeneratorTest.php new file mode 100644 index 0000000..48e4e4d --- /dev/null +++ b/test/suite/Eloquent/Otis/Motp/Parameters/Generator/MotpSharedParametersGeneratorTest.php @@ -0,0 +1,41 @@ +isolator = Phake::mock(Isolator::className()); + $this->generator = new MotpSharedParametersGenerator($this->isolator); + } + + public function testGenerateMotp() + { + $configuration = new MotpConfiguration; + Phake::when($this->isolator)->mcrypt_create_iv(8)->thenReturn('1234567890'); + Phake::when($this->isolator)->time()->thenReturn(111); + $actual = $this->generator->generate($configuration); + + $this->assertInstanceOf('Eloquent\Otis\Motp\Parameters\MotpSharedParameters', $actual); + $this->assertSame('1234567890', $actual->secret()); + $this->assertSame(111, $actual->time()); + } +} diff --git a/test/suite/Eloquent/Otis/Motp/Parameters/MotpSharedParametersTest.php b/test/suite/Eloquent/Otis/Motp/Parameters/MotpSharedParametersTest.php index d26a026..71a7e8e 100644 --- a/test/suite/Eloquent/Otis/Motp/Parameters/MotpSharedParametersTest.php +++ b/test/suite/Eloquent/Otis/Motp/Parameters/MotpSharedParametersTest.php @@ -15,10 +15,6 @@ use Phake; use PHPUnit_Framework_TestCase; -/** - * @covers \Eloquent\Otis\Motp\Parameters\MotpSharedParameters - * @covers \Eloquent\Otis\Parameters\AbstractOtpSharedParameters - */ class MotpSharedParametersTest extends PHPUnit_Framework_TestCase { protected function setUp() diff --git a/test/suite/Eloquent/Otis/Motp/Validator/MotpValidatorTest.php b/test/suite/Eloquent/Otis/Motp/Validator/MotpValidatorTest.php index f738415..003cb1f 100644 --- a/test/suite/Eloquent/Otis/Motp/Validator/MotpValidatorTest.php +++ b/test/suite/Eloquent/Otis/Motp/Validator/MotpValidatorTest.php @@ -12,14 +12,10 @@ namespace Eloquent\Otis\Motp\Validator; use Eloquent\Otis\Credentials\OtpCredentials; -use Eloquent\Otis\Hotp\Configuration\HotpConfiguration; use Eloquent\Otis\Motp\Configuration\MotpConfiguration; use Eloquent\Otis\Motp\Generator\MotpGenerator; use Eloquent\Otis\Motp\Parameters\MotpSharedParameters; -use Eloquent\Otis\Parameters\TimeBasedOtpSharedParameters; -use Eloquent\Otis\Totp\Configuration\TotpConfiguration; use PHPUnit_Framework_TestCase; -use Phake; class MotpValidatorTest extends PHPUnit_Framework_TestCase { @@ -43,35 +39,34 @@ public function testConstructorDefaults() $this->assertEquals($this->generator, $this->validator->generator()); } - public function supportsData() + public function validateMotpData() { - $mockCredentials = Phake::mock('Eloquent\Otis\Credentials\MfaCredentialsInterface'); - $mockSharedParameters = Phake::mock('Eloquent\Otis\Parameters\MfaSharedParametersInterface'); - - // configuration shared credentials expected + // password secret pin time pastWindows futureWindows result drift return array( - 'Valid combination' => array(new MotpConfiguration, new MotpSharedParameters('secret', 123), new OtpCredentials('password'), true), - 'Unsupported credentials' => array(new MotpConfiguration, new MotpSharedParameters('secret', 123), $mockCredentials, false), - 'Unsupported shared parameters' => array(new MotpConfiguration, $mockSharedParameters, new OtpCredentials('password'), false), - 'Unsupported configuration' => array(new HotpConfiguration, new MotpSharedParameters('secret', 123), new OtpCredentials('password'), false), + 'Valid, no drift' => array('3fadec', '12345678', 1234, 1111111111, null, null, 'valid', 0), + 'Valid, 3 past drift' => array('81d313', '12345678', 1234, 1111111111, null, null, 'valid', -3), + 'Valid, 3 future drift' => array('f5521c', '12345678', 1234, 1111111111, null, null, 'valid', 3), + 'Valid, 10 past drift' => array('1ea954', '12345678', 1234, 1111111111, 100, 100, 'valid', -10), + 'Valid, 10 future drift' => array('69bfeb', '12345678', 1234, 1111111111, 100, 100, 'valid', 10), + + 'Invalid, too far past' => array('1ea954', '12345678', 1234, 1111111111, 9, null, 'invalid-credentials', null), + 'Invalid, too far future' => array('69bfeb', '12345678', 1234, 1111111111, null, 9, 'invalid-credentials', null), + 'Length mismatch' => array('1234567', '12345678', 1234, 1111111111, null, null, 'credential-length-mismatch', null), ); } /** - * @dataProvider supportsData + * @dataProvider validateMotpData */ - public function testSupports($configuration, $shared, $credentials, $expected) - { - $this->assertSame($expected, $this->validator->supports($configuration, $shared, $credentials)); - } - - public function testValidateFailureUnsupported() + public function testValidateMotp($password, $secret, $pin, $time, $pastWindows, $futureWindows, $result, $drift) { - $configuration = new TotpConfiguration; - $shared = new TimeBasedOtpSharedParameters('secret', 123); - $credentials = new OtpCredentials('password'); + $configuration = new MotpConfiguration($futureWindows, $pastWindows); + $shared = new MotpSharedParameters($secret, $pin, $time); + $credentials = new OtpCredentials($password); + $actual = $this->validator->validate($configuration, $shared, $credentials); - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->validator->validate($configuration, $shared, $credentials); + $this->assertInstanceOf('Eloquent\Otis\Validator\Result\TimeBasedOtpValidationResult', $actual); + $this->assertSame($result, $actual->type()); + $this->assertSame($drift, $actual->drift()); } } diff --git a/test/suite/Eloquent/Otis/Parameters/Generator/CounterBasedOtpSharedParametersGeneratorTest.php b/test/suite/Eloquent/Otis/Parameters/Generator/CounterBasedOtpSharedParametersGeneratorTest.php new file mode 100644 index 0000000..ac49dd3 --- /dev/null +++ b/test/suite/Eloquent/Otis/Parameters/Generator/CounterBasedOtpSharedParametersGeneratorTest.php @@ -0,0 +1,39 @@ +isolator = Phake::mock(Isolator::className()); + $this->generator = new CounterBasedOtpSharedParametersGenerator($this->isolator); + } + + public function testGenerateCounterBased() + { + $configuration = new HotpConfiguration(null, null, 111, 222); + Phake::when($this->isolator)->mcrypt_create_iv(222)->thenReturn('1234567890'); + $actual = $this->generator->generate($configuration); + + $this->assertInstanceOf('Eloquent\Otis\Parameters\CounterBasedOtpSharedParameters', $actual); + $this->assertSame('1234567890', $actual->secret()); + $this->assertSame(111, $actual->counter()); + } +} diff --git a/test/suite/Eloquent/Otis/Parameters/Generator/TimeBasedOtpSharedParametersGeneratorTest.php b/test/suite/Eloquent/Otis/Parameters/Generator/TimeBasedOtpSharedParametersGeneratorTest.php new file mode 100644 index 0000000..12ec9fb --- /dev/null +++ b/test/suite/Eloquent/Otis/Parameters/Generator/TimeBasedOtpSharedParametersGeneratorTest.php @@ -0,0 +1,40 @@ +isolator = Phake::mock(Isolator::className()); + $this->generator = new TimeBasedOtpSharedParametersGenerator($this->isolator); + } + + public function testGenerateTimeBased() + { + $configuration = new TotpConfiguration(null, null, null, null, 111); + Phake::when($this->isolator)->mcrypt_create_iv(111)->thenReturn('1234567890'); + Phake::when($this->isolator)->time()->thenReturn(222); + $actual = $this->generator->generate($configuration); + + $this->assertInstanceOf('Eloquent\Otis\Parameters\TimeBasedOtpSharedParameters', $actual); + $this->assertSame('1234567890', $actual->secret()); + $this->assertSame(222, $actual->time()); + } +} diff --git a/test/suite/Eloquent/Otis/Totp/Configuration/TotpConfigurationTest.php b/test/suite/Eloquent/Otis/Totp/Configuration/TotpConfigurationTest.php index 67cbd7b..c4f0f2b 100644 --- a/test/suite/Eloquent/Otis/Totp/Configuration/TotpConfigurationTest.php +++ b/test/suite/Eloquent/Otis/Totp/Configuration/TotpConfigurationTest.php @@ -16,7 +16,8 @@ /** * @covers \Eloquent\Otis\Totp\Configuration\TotpConfiguration - * @covers \Eloquent\Otis\Hotp\Configuration\AbstractHotpBasedOtpConfiguration + * @covers \Eloquent\Otis\Configuration\AbstractTimeBasedOtpConfiguration + * @covers \Eloquent\Otis\Configuration\AbstractOtpConfiguration */ class TotpConfigurationTest extends PHPUnit_Framework_TestCase { diff --git a/test/suite/Eloquent/Otis/Totp/TotpDriverTest.php b/test/suite/Eloquent/Otis/Totp/TotpDriverTest.php new file mode 100644 index 0000000..87f5e86 --- /dev/null +++ b/test/suite/Eloquent/Otis/Totp/TotpDriverTest.php @@ -0,0 +1,53 @@ +validator = new Validator\TotpValidator; + $this->sharedParametersGenerator = new TimeBasedOtpSharedParametersGenerator; + $this->initializationUriFactory = new GoogleAuthenticatorTotpUriFactory; + $this->driver = new TotpDriver( + $this->validator, + $this->sharedParametersGenerator, + $this->initializationUriFactory + ); + } + + public function testConstructor() + { + $this->assertSame($this->validator, $this->driver->validator()); + $this->assertSame($this->sharedParametersGenerator, $this->driver->sharedParametersGenerator()); + $this->assertSame($this->initializationUriFactory, $this->driver->initializationUriFactory()); + } + + public function testConstructorDefaults() + { + $this->driver = new TotpDriver; + + $this->assertEquals($this->validator, $this->driver->validator()); + $this->assertEquals($this->sharedParametersGenerator, $this->driver->sharedParametersGenerator()); + $this->assertEquals($this->initializationUriFactory, $this->driver->initializationUriFactory()); + } +} diff --git a/test/suite/Eloquent/Otis/Totp/Validator/TotpValidatorTest.php b/test/suite/Eloquent/Otis/Totp/Validator/TotpValidatorTest.php index 85aaa4f..25208fb 100644 --- a/test/suite/Eloquent/Otis/Totp/Validator/TotpValidatorTest.php +++ b/test/suite/Eloquent/Otis/Totp/Validator/TotpValidatorTest.php @@ -12,12 +12,10 @@ namespace Eloquent\Otis\Totp\Validator; use Eloquent\Otis\Credentials\OtpCredentials; -use Eloquent\Otis\Hotp\Configuration\HotpConfiguration; use Eloquent\Otis\Parameters\TimeBasedOtpSharedParameters; use Eloquent\Otis\Totp\Configuration\TotpConfiguration; use Eloquent\Otis\Totp\Generator\TotpGenerator; use PHPUnit_Framework_TestCase; -use Phake; class TotpValidatorTest extends PHPUnit_Framework_TestCase { @@ -41,35 +39,34 @@ public function testConstructorDefaults() $this->assertEquals($this->generator, $this->validator->generator()); } - public function supportsData() + public function validateTotpData() { - $mockCredentials = Phake::mock('Eloquent\Otis\Credentials\MfaCredentialsInterface'); - $mockSharedParameters = Phake::mock('Eloquent\Otis\Parameters\MfaSharedParametersInterface'); - - // configuration shared credentials expected + // password secret digits window time pastWindows futureWindows result drift return array( - 'Valid combination' => array(new TotpConfiguration, new TimeBasedOtpSharedParameters('secret', 123), new OtpCredentials('password'), true), - 'Unsupported credentials' => array(new TotpConfiguration, new TimeBasedOtpSharedParameters('secret', 123), $mockCredentials, false), - 'Unsupported shared parameters' => array(new TotpConfiguration, $mockSharedParameters, new OtpCredentials('password'), false), - 'Unsupported configuration' => array(new HotpConfiguration, new TimeBasedOtpSharedParameters('secret', 123), new OtpCredentials('password'), false), + 'Valid, no drift' => array('14050471', '12345678901234567890', 8, null, 1111111111, null, null, 'valid', 0), + 'Valid, 1 past drift' => array('07081804', '12345678901234567890', 8, null, 1111111111, null, null, 'valid', -1), + 'Valid, 1 future drift' => array('44266759', '12345678901234567890', 8, null, 1111111111, null, null, 'valid', 1), + 'Valid, 10 past drift' => array('13755423', '12345678901234567890', 8, null, 1111111111, 100, 100, 'valid', -10), + 'Valid, 10 future drift' => array('78536305', '12345678901234567890', 8, null, 1111111111, 100, 100, 'valid', 10), + + 'Invalid, too far past' => array('13755423', '12345678901234567890', 8, null, 1111111111, 9, null, 'invalid-credentials', null), + 'Invalid, too far future' => array('78536305', '12345678901234567890', 8, null, 1111111111, null, 9, 'invalid-credentials', null), + 'Length mismatch' => array('14050471', '12345678901234567890', null, null, 1111111111, null, null, 'credential-length-mismatch', null), ); } /** - * @dataProvider supportsData + * @dataProvider validateTotpData */ - public function testSupports($configuration, $shared, $credentials, $expected) - { - $this->assertSame($expected, $this->validator->supports($configuration, $shared, $credentials)); - } - - public function testValidateFailureUnsupported() + public function testValidateTotp($password, $secret, $digits, $window, $time, $pastWindows, $futureWindows, $result, $drift) { - $configuration = new HotpConfiguration; - $shared = Phake::mock('Eloquent\Otis\Parameters\MfaSharedParametersInterface'); - $credentials = new OtpCredentials('password'); + $configuration = new TotpConfiguration($digits, $window, $futureWindows, $pastWindows); + $shared = new TimeBasedOtpSharedParameters($secret, $time); + $credentials = new OtpCredentials($password); + $actual = $this->validator->validate($configuration, $shared, $credentials); - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->validator->validate($configuration, $shared, $credentials); + $this->assertInstanceOf('Eloquent\Otis\Validator\Result\TimeBasedOtpValidationResult', $actual); + $this->assertSame($result, $actual->type()); + $this->assertSame($drift, $actual->drift()); } } diff --git a/test/suite/Eloquent/Otis/Uri/Initialization/InitializationUriFactoryTest.php b/test/suite/Eloquent/Otis/Uri/Initialization/InitializationUriFactoryTest.php deleted file mode 100644 index e99b7b2..0000000 --- a/test/suite/Eloquent/Otis/Uri/Initialization/InitializationUriFactoryTest.php +++ /dev/null @@ -1,130 +0,0 @@ -googleAuthenticatorFactory = new GoogleAuthenticatorUriFactory; - $this->factories = array( - $this->googleAuthenticatorFactory, - ); - $this->factory = new InitializationUriFactory($this->factories); - } - - public function testConstructor() - { - $this->assertSame($this->factories, $this->factory->factories()); - } - - public function testConstructorDefaults() - { - $this->factory = new InitializationUriFactory; - - $this->assertEquals($this->factories, $this->factory->factories()); - } - - public function supportsData() - { - // configuration shared expected - return array( - 'Supported' => array(new HotpConfiguration, new CounterBasedOtpSharedParameters('secret', 111), true), - 'Unsupported' => array(new MotpConfiguration, new TimeBasedOtpSharedParameters('secret', 111), false), - ); - } - - /** - * @dataProvider supportsData - */ - public function testSupports($configuration, $shared, $expected) - { - $this->assertSame($expected, $this->factory->supports($configuration, $shared)); - } - - public function createHotpData() - { - // secret label issuer counter digits algorithm expected - return array( - 'All defaults' => array('12345678901234567890', 'test.ease@example.org', null, null, null, null, 'otpauth://hotp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'), - 'All options' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', 'otpauth://hotp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&counter=111&digits=10&algorithm=SHA256&issuer=Skynet'), - ); - } - - /** - * @dataProvider createHotpData - */ - public function testCreateHotp($secret, $label, $issuer, $counter, $digits, $algorithm, $expected) - { - $configuration = new HotpConfiguration( - $digits, - null, - null, - null, - HotpHashAlgorithm::memberByValueWithDefault($algorithm) - ); - $shared = new CounterBasedOtpSharedParameters($secret, $counter); - - $this->assertSame($expected, $this->factory->create($configuration, $shared, $label, $issuer)); - } - - public function createTotpData() - { - // secret label issuer window digits algorithm expected - return array( - 'All defaults' => array('12345678901234567890', 'test.ease@example.org', null, null, null, null, 'otpauth://totp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'), - 'All options' => array('12345678901234567890', 'test.ease@example.org', 'Skynet', 111, 10, 'SHA256', 'otpauth://totp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&period=111&digits=10&algorithm=SHA256&issuer=Skynet'), - ); - } - - /** - * @dataProvider createTotpData - */ - public function testCreateTotp($secret, $label, $issuer, $window, $digits, $algorithm, $expected) - { - $configuration = new TotpConfiguration( - $digits, - $window, - null, - null, - null, - HotpHashAlgorithm::memberByValueWithDefault($algorithm) - ); - $shared = new TimeBasedOtpSharedParameters($secret, 111); - - $this->assertSame($expected, $this->factory->create($configuration, $shared, $label, $issuer)); - } - - public function testCreateFailureUnsupported() - { - $configuration = new HotpConfiguration; - $shared = new TimeBasedOtpSharedParameters('secret', 111); - - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->factory->create($configuration, $shared, 'label'); - } -} diff --git a/test/suite/Eloquent/Otis/Validator/MfaValidatorTest.php b/test/suite/Eloquent/Otis/Validator/MfaValidatorTest.php deleted file mode 100644 index 09f39bb..0000000 --- a/test/suite/Eloquent/Otis/Validator/MfaValidatorTest.php +++ /dev/null @@ -1,250 +0,0 @@ -isolator = Phake::mock(Isolator::className()); - $this->totpValidator = new TotpValidator; - $this->hotpValidator = new HotpValidator; - $this->motpValidator = new MotpValidator(null, $this->isolator); - $this->validators = array($this->totpValidator, $this->hotpValidator, $this->motpValidator); - $this->validator = new MfaValidator($this->validators); - } - - public function testConstructor() - { - $this->assertSame($this->validators, $this->validator->validators()); - } - - public function testConstructorDefaults() - { - $this->validator = new MfaValidator; - $validators = $this->validator->validators(); - - $this->assertArrayHasKey(0, $validators); - $this->assertEquals(new TotpValidator, $validators[0]); - $this->assertArrayHasKey(1, $validators); - $this->assertEquals(new HotpValidator, $validators[1]); - $this->assertArrayHasKey(2, $validators); - $this->assertEquals(new MotpValidator, $validators[2]); - } - - public function supportsData() - { - // configuration shared credentials expected - return array( - 'TOTP' => array(new TotpConfiguration, new TimeBasedOtpSharedParameters('secret', 123), new OtpCredentials('password'), true), - 'HOTP' => array(new HotpConfiguration, new CounterBasedOtpSharedParameters('secret', 111), new OtpCredentials('password'), true), - 'mOTP' => array(new MotpConfiguration, new MotpSharedParameters('secret', 1234), new OtpCredentials('password'), true), - 'Unsupported combination' => array(new HotpConfiguration, new TimeBasedOtpSharedParameters('secret', 123), new OtpCredentials('password'), false), - ); - } - - /** - * @dataProvider supportsData - */ - public function testSupports($configuration, $shared, $credentials, $expected) - { - $this->assertSame($expected, $this->validator->supports($configuration, $shared, $credentials)); - } - - public function validateTotpData() - { - // password secret digits window time pastWindows futureWindows result drift - return array( - 'Valid, no drift' => array('14050471', '12345678901234567890', 8, null, 1111111111, null, null, 'valid', 0), - 'Valid, 1 past drift' => array('07081804', '12345678901234567890', 8, null, 1111111111, null, null, 'valid', -1), - 'Valid, 1 future drift' => array('44266759', '12345678901234567890', 8, null, 1111111111, null, null, 'valid', 1), - 'Valid, 10 past drift' => array('13755423', '12345678901234567890', 8, null, 1111111111, 100, 100, 'valid', -10), - 'Valid, 10 future drift' => array('78536305', '12345678901234567890', 8, null, 1111111111, 100, 100, 'valid', 10), - - 'Invalid, too far past' => array('13755423', '12345678901234567890', 8, null, 1111111111, 9, null, 'invalid-credentials', null), - 'Invalid, too far future' => array('78536305', '12345678901234567890', 8, null, 1111111111, null, 9, 'invalid-credentials', null), - 'Length mismatch' => array('14050471', '12345678901234567890', null, null, 1111111111, null, null, 'credential-length-mismatch', null), - ); - } - - /** - * @dataProvider validateTotpData - */ - public function testValidateTotp($password, $secret, $digits, $window, $time, $pastWindows, $futureWindows, $result, $drift) - { - Phake::when($this->isolator)->time()->thenReturn($time); - $configuration = new TotpConfiguration($digits, $window, $futureWindows, $pastWindows); - $shared = new TimeBasedOtpSharedParameters($secret, $time, $this->isolator); - $credentials = new OtpCredentials($password); - $actual = $this->validator->validate($configuration, $shared, $credentials); - - $this->assertInstanceOf('Eloquent\Otis\Validator\Result\TimeBasedOtpValidationResult', $actual); - $this->assertSame($result, $actual->type()); - $this->assertSame($drift, $actual->drift()); - } - - public function validateHotpData() - { - // password secret currentCounter digits window result newCounter - return array( - 'No window, valid' => array('969429', '12345678901234567890', 3, null, null, 'valid', 4), - 'With window, valid' => array('520489', '12345678901234567890', 0, null, 9, 'valid', 10), - - 'No window, invalid' => array('338314', '12345678901234567890', 3, null, 0, 'invalid-credentials', null), - 'With window, invalid' => array('520489', '12345678901234567890', 0, null, 8, 'invalid-credentials', null), - 'Length mismatch' => array('969429', '12345678901234567890', 3, 8, null, 'credential-length-mismatch', null), - ); - } - - /** - * @dataProvider validateHotpData - */ - public function testValidateHotp($password, $secret, $currentCounter, $digits, $window, $result, $newCounter) - { - $configuration = new HotpConfiguration($digits, $window); - $shared = new CounterBasedOtpSharedParameters($secret, $currentCounter); - $credentials = new OtpCredentials($password); - $actual = $this->validator->validate($configuration, $shared, $credentials); - - $this->assertInstanceOf('Eloquent\Otis\Validator\Result\CounterBasedOtpValidationResult', $actual); - $this->assertSame($result, $actual->type()); - $this->assertSame($newCounter, $actual->counter()); - } - - public function supportsSequenceData() - { - $mockCredentials = Phake::mock('Eloquent\Otis\Credentials\MfaCredentialsInterface'); - - // configuration shared credentialSequence expected - return array( - 'HOTP' => array(new HotpConfiguration, new CounterBasedOtpSharedParameters('secret', 111), array(new OtpCredentials('password'), new OtpCredentials('password')), true), - 'Unsupported combination' => array(new TotpConfiguration, new TimeBasedOtpSharedParameters('secret', 111), array(new OtpCredentials('password'), $mockCredentials), false), - ); - } - - /** - * @dataProvider supportsSequenceData - */ - public function testSupportsSequence($configuration, $shared, $credentialSequence, $expected) - { - $this->assertSame($expected, $this->validator->supportsSequence($configuration, $shared, $credentialSequence)); - } - - public function validateHotpSequenceData() - { - // passwords secret currentCounter digits window result newCounter - return array( - 'No window, valid' => array(array('969429', '338314'), '12345678901234567890', 3, null, null, 'valid', 5), - 'With window, valid' => array(array('399871', '520489'), '12345678901234567890', 0, null, 8, 'valid', 10), - - 'No window, invalid' => array(array('359152', '969429'), '12345678901234567890', 3, null, 0, 'invalid-credentials', null), - 'With window, invalid' => array(array('755224', '359152'), '12345678901234567890', 0, null, 100, 'invalid-credentials', null), - 'Length mismatch' => array(array('969429', '338314'), '12345678901234567890', 3, 8, null, 'credential-length-mismatch', null), - 'No credentials' => array(array(), '12345678901234567890', 0, null, 100, 'empty-credential-sequence', null), - ); - } - - /** - * @dataProvider validateHotpSequenceData - */ - public function testValidateHotpSequence($passwords, $secret, $currentCounter, $digits, $window, $result, $newCounter) - { - $configuration = new HotpConfiguration($digits, $window); - $shared = new CounterBasedOtpSharedParameters($secret, $currentCounter); - $credentialSequence = array(); - foreach ($passwords as $password) { - $credentialSequence[] = new OtpCredentials($password); - } - $actual = $this->validator->validateSequence($configuration, $shared, $credentialSequence); - - $this->assertInstanceOf('Eloquent\Otis\Validator\Result\CounterBasedOtpValidationResult', $actual); - $this->assertSame($result, $actual->type()); - $this->assertSame($newCounter, $actual->counter()); - } - - public function validateMotpData() - { - // password secret pin time pastWindows futureWindows result drift - return array( - 'Valid, no drift' => array('3fadec', '12345678', 1234, 1111111111, null, null, 'valid', 0), - 'Valid, 3 past drift' => array('81d313', '12345678', 1234, 1111111111, null, null, 'valid', -3), - 'Valid, 3 future drift' => array('f5521c', '12345678', 1234, 1111111111, null, null, 'valid', 3), - 'Valid, 10 past drift' => array('1ea954', '12345678', 1234, 1111111111, 100, 100, 'valid', -10), - 'Valid, 10 future drift' => array('69bfeb', '12345678', 1234, 1111111111, 100, 100, 'valid', 10), - - 'Invalid, too far past' => array('1ea954', '12345678', 1234, 1111111111, 9, null, 'invalid-credentials', null), - 'Invalid, too far future' => array('69bfeb', '12345678', 1234, 1111111111, null, 9, 'invalid-credentials', null), - 'Length mismatch' => array('1234567', '12345678', 1234, 1111111111, null, null, 'credential-length-mismatch', null), - ); - } - - /** - * @dataProvider validateMotpData - */ - public function testValidateMotp($password, $secret, $pin, $time, $pastWindows, $futureWindows, $result, $drift) - { - Phake::when($this->isolator)->time()->thenReturn($time); - $configuration = new MotpConfiguration($futureWindows, $pastWindows); - $shared = new MotpSharedParameters($secret, $pin, $time, $this->isolator); - $credentials = new OtpCredentials($password); - $actual = $this->validator->validate($configuration, $shared, $credentials); - - $this->assertInstanceOf('Eloquent\Otis\Validator\Result\TimeBasedOtpValidationResult', $actual); - $this->assertSame($result, $actual->type()); - $this->assertSame($drift, $actual->drift()); - } - - public function testValidateFailureUnsupportedCombination() - { - $configuration = new HotpConfiguration; - $shared = new TimeBasedOtpSharedParameters('secret', 123); - $credentials = new OtpCredentials('password'); - - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->validator->validate($configuration, $shared, $credentials); - } - - public function testValidateFailureUnsupportedCombinationSequence() - { - $configuration = new TotpConfiguration; - $shared = new TimeBasedOtpSharedParameters('secret', 123); - $credentialSequence = array( - new OtpCredentials('password'), - Phake::mock('Eloquent\Otis\Credentials\MfaCredentialsInterface'), - ); - - $this->setExpectedException('Eloquent\Otis\Exception\UnsupportedArgumentsException'); - $this->validator->validateSequence($configuration, $shared, $credentialSequence); - } -}