diff --git a/tests/Acceptance/TOTPAcceptanceTest.php b/tests/Acceptance/TOTPAcceptanceTest.php index 9d10074b8..4305f29b0 100644 --- a/tests/Acceptance/TOTPAcceptanceTest.php +++ b/tests/Acceptance/TOTPAcceptanceTest.php @@ -22,6 +22,7 @@ namespace OCA\TwoFactorTOTP\Tests\Acceptance; +use Base32\Base32; use Facebook\WebDriver\Exception\ElementNotSelectableException; use Facebook\WebDriver\WebDriver; use Facebook\WebDriver\WebDriverBy; @@ -29,9 +30,12 @@ use OC; use OCA\TwoFactorTOTP\Db\TotpSecret; use OCA\TwoFactorTOTP\Db\TotpSecretMapper; +use OCA\TwoFactorTOTP\Service\ITotp; use OCP\AppFramework\Db\DoesNotExistException; use OCP\IUser; use Otp\GoogleAuthenticator; +use Otp\Otp; +use PHPUnit_Framework_AssertionFailedError; /** * @group Acceptance @@ -80,17 +84,17 @@ public function testEnableTOTP() { $this->webDriver->findElement(WebDriverBy::cssSelector('form[name=login] input[type=submit]'))->click(); // Go to personal settings - $this->webDriver->wait(20, 1000)->until(WebDriverExpectedCondition::elementToBeClickable(WebDriverBy::id('expandDisplayName'))); + $this->webDriver->wait(20, 200)->until(WebDriverExpectedCondition::elementToBeClickable(WebDriverBy::id('expandDisplayName'))); $this->webDriver->findElement(WebDriverBy::id('expandDisplayName'))->click(); $this->webDriver->findElement(WebDriverBy::linkText('Personal'))->click(); // Go to TOTP settings - $this->webDriver->wait(20, 1000)->until(WebDriverExpectedCondition::elementToBeClickable(WebDriverBy::linkText('TOTP second-factor auth'))); + $this->webDriver->wait(20, 200)->until(WebDriverExpectedCondition::elementToBeClickable(WebDriverBy::linkText('TOTP second-factor auth'))); $this->webDriver->findElement(WebDriverBy::linkText('TOTP second-factor auth'))->click(); // Enable TOTP - usleep(15 * 1000 * 1000); // Hard-coded sleep because the scripts need some time load the page - $this->webDriver->wait(20, 1000)->until(function(WebDriver $driver) { + // Wait for state being loaded from the server + $this->webDriver->wait(20, 200)->until(function(WebDriver $driver) { try { return count($driver->findElements(WebDriverBy::id('totp-enabled'))) > 0; } catch (ElementNotSelectableException $ex) { @@ -100,7 +104,47 @@ public function testEnableTOTP() { $this->webDriver->executeScript('arguments[0].click(); console.log(arguments[0]);', [ $this->webDriver->findElement(WebDriverBy::id('totp-enabled')), ]); - $this->webDriver->wait(20, 1000)->until(WebDriverExpectedCondition::elementTextContains(WebDriverBy::id('twofactor-totp-settings'), 'This is your new TOTP secret:')); + $this->webDriver->wait(15, 200)->until(WebDriverExpectedCondition::elementTextContains(WebDriverBy::id('twofactor-totp-settings'), 'This is your new TOTP secret:')); + $this->assertHasSecret(ITotp::STATE_CREATED); + + // Enter a wrong OTP + $this->webDriver->findElement(WebDriverBy::id('totp-confirmation'))->sendKeys('000000'); + $this->webDriver->findElement(WebDriverBy::id('totp-confirmation-submit'))->click(); + + // Wait for the notification + $this->webDriver->wait(15, 200)->until(WebDriverExpectedCondition::elementTextContains(WebDriverBy::id('notification'), 'Could not verify your key. Please try again')); + + // Enter a correct OTP + $this->webDriver->findElement(WebDriverBy::id('totp-confirmation'))->sendKeys($this->getValidTOTP()); + $this->webDriver->findElement(WebDriverBy::id('totp-confirmation-submit'))->click(); + + // Try to locate checked checkbox + $this->webDriver->wait(20, 200)->until(function(WebDriver $driver) { + try { + return $driver->findElement(WebDriverBy::id('totp-enabled'))->getAttribute('checked') === 'true'; + } catch (ElementNotSelectableException $ex) { + return false; + } + }); + $this->assertHasSecret(ITotp::STATE_ENABLED); + } + + private function assertHasSecret($state) { + try { + $secret = $this->secretMapper->getSecret($this->user); + if ($state !== (int) $secret->getState()) { + throw new PHPUnit_Framework_AssertionFailedError('TOTP secret has wrong state'); + } + } catch (DoesNotExistException $ex) { + throw new PHPUnit_Framework_AssertionFailedError('User does not have a totp secret'); + } + } + + private function getValidTOTP() { + $dbSecret = $this->secretMapper->getSecret($this->user); + $secret = OC::$server->getCrypto()->decrypt($dbSecret->getSecret()); + $otp = new Otp(); + return $otp->totp(Base32::decode($secret)); } private function createSecret() { @@ -123,7 +167,7 @@ public function testLoginShouldFailWithWrongOTP() { $this->webDriver->findElement(WebDriverBy::id('password'))->sendKeys('admin'); $this->webDriver->findElement(WebDriverBy::cssSelector('form[name=login] input[type=submit]'))->click(); - $this->webDriver->wait(20, 1000)->until(function(WebDriver $driver) { + $this->webDriver->wait(20, 200)->until(function(WebDriver $driver) { try { return $driver->findElements(WebDriverBy::className('totp-form')); } catch (ElementNotSelectableException $ex) { @@ -135,7 +179,7 @@ public function testLoginShouldFailWithWrongOTP() { $this->webDriver->findElement(WebDriverBy::name('challenge'))->sendKeys('000000'); $this->webDriver->findElement(WebDriverBy::cssSelector('button[type="submit"]'))->submit(); - $this->webDriver->wait(20, 1000)->until(WebDriverExpectedCondition::elementTextContains(WebDriverBy::className('warning'), 'Error while validating your second factor')); + $this->webDriver->wait(20, 200)->until(WebDriverExpectedCondition::elementTextContains(WebDriverBy::className('warning'), 'Error while validating your second factor')); $this->assertEquals('http://localhost:8080/index.php/login/challenge/totp', $this->webDriver->getCurrentURL()); }