Коммит
81b8ba0765
|
@ -5,3 +5,5 @@ vendor/
|
|||
|
||||
# Ignore NetBeans project
|
||||
/nbproject/
|
||||
|
||||
tests/clover.xml
|
||||
|
|
24
.travis.yml
24
.travis.yml
|
@ -11,12 +11,17 @@ addons:
|
|||
- mysql-server-5.6
|
||||
- mysql-client-core-5.6
|
||||
- mysql-client-5.6
|
||||
sauce_connect:
|
||||
username: "nextcloud-totp"
|
||||
jwt:
|
||||
secure: dUvqHso+T2Gkb4Nlg4feP3s1wnFy9XWwYakckpcl24HLd7yN/zMU3/qWfAZ0NzKOvQvILdBtjjj9Bdo4jJXzfnbRPwcWE7i2E9TvHMJYYOCEymUG0IZyKNZSyxlt4FlgTXYLUTyPLeEQwb4F01Uy3kbERowfT6LtPPeReLpKcE4R3Sbz4AI4p3ugWoI9E/+zHzUHAdWJTrlwLiIVx2/9eQ+fy5BJXeFPzNeNuDhzzC0ceXkv6bKAn5Fuh+QzPCf8AURezZQ04cWoSsIymSwAlzRDBZsUqib/aOyHgkwCQFsUuRuCaZwz2DhG+PwXe0FZD/EuRYE+huVL9jV6Q7mkaclOU3UyOlsIKogLLY9HhekMStOpH2rXP0+xYmogWT1eRqsvXcODD1/3ujPWsQOR78j5KlrIEHeqfKJ9TC3gBNLPTTHIjqmadTRBsuxuWkcoZAMthaQF/j2lBk0al5rk5h9sb53MQVUy3mzDL0ou9lFRJv4hEYQoyIS9tfe6CSyHbTtQAPkeMqSZ3fKI6Jl4AP4MtN1ITOyHsY2psmsdOzDj7qlC6/+A7ApyPsshRQ0vpr9YTZhMmNZFLS6Vr7y+mdGFlkH90LK4RPEUWkn1ROaaS2BeBPGoYqNJq16wT3I2ijN3uvA8/G+4Y2F9aezYIdgUW0WG3vvtP7SW57m80Do=
|
||||
services:
|
||||
- postgresql
|
||||
env:
|
||||
global:
|
||||
- CORE_BRANCH=master
|
||||
- PHP_COVERAGE=FALSE
|
||||
- SAUCE=FALSE
|
||||
matrix:
|
||||
- DB=sqlite
|
||||
branches:
|
||||
|
@ -51,17 +56,28 @@ before_script:
|
|||
# Set up core
|
||||
- php -f core/occ maintenance:install --database-name nc_autotest --database-user nc_autotest --admin-user admin --admin-pass admin --database $DB --database-pass=''
|
||||
|
||||
# Enable debug mode to get more info in case a test fails
|
||||
- php -f core/occ config:system:set debug --value=true --type boolean
|
||||
|
||||
# Set up app
|
||||
- php -f core/occ app:enable twofactor_totp
|
||||
- cd core/apps/twofactor_totp
|
||||
|
||||
# Start php server for acceptance tests
|
||||
- php -S 0.0.0.0:8080 -t ../.. > webserver.log 2>&1 &
|
||||
|
||||
script:
|
||||
- find . -name \*.php -not -path './vendor/*' -exec php -l "{}" \;
|
||||
- if [[ "$SAUCE" = "FALSE" ]]; then find . -name \*.php -not -path './vendor/*' -exec php -l "{}" \;; fi
|
||||
- cd tests
|
||||
- phpunit --configuration phpunit.xml
|
||||
- if [[ "$SAUCE" = "FALSE" ]]; then phpunit --group default --configuration phpunit.xml; fi
|
||||
- if [[ "$SAUCE" = "TRUE" ]]; then phpunit --group Acceptance --no-coverage --configuration phpunit.xml; fi
|
||||
- if [[ "$PHP_COVERAGE" = "TRUE" ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi
|
||||
- if [[ "$PHP_COVERAGE" = "TRUE" ]]; then php ocular.phar code-coverage:upload --format=php-clover clover.xml; fi
|
||||
|
||||
after_script:
|
||||
- cd ..
|
||||
- cat webserver.log
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 7
|
||||
|
@ -70,5 +86,9 @@ matrix:
|
|||
env: "DB=pgsql PHP_COVERAGE=TRUE"
|
||||
- php: 7
|
||||
env: "DB=mysql"
|
||||
- php: 7.1
|
||||
env: "DB=mysql SAUCE=TRUE SELENIUM_BROWSER=firefox"
|
||||
- php: 7.1
|
||||
env: "DB=mysql SAUCE=TRUE SELENIUM_BROWSER=chrome"
|
||||
|
||||
fast_finish: true
|
||||
|
|
|
@ -5,5 +5,8 @@
|
|||
},
|
||||
"platform": {
|
||||
"php": ">=5.6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"facebook/webdriver": "^1.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "56e515ddb1d2e85e3b723819b9bab28e",
|
||||
"content-hash": "a5cf309909d0bb78424ce70f0fb83e8d",
|
||||
"content-hash": "00ae934f22af3ba602fd75a3d4cbbc75",
|
||||
"packages": [
|
||||
{
|
||||
"name": "christian-riesen/base32",
|
||||
|
@ -59,7 +58,7 @@
|
|||
"encode",
|
||||
"rfc4648"
|
||||
],
|
||||
"time": "2016-05-05 11:49:03"
|
||||
"time": "2016-05-05T11:49:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "christian-riesen/otp",
|
||||
|
@ -110,7 +109,7 @@
|
|||
"rfc6238",
|
||||
"totp"
|
||||
],
|
||||
"time": "2015-10-08 08:17:59"
|
||||
"time": "2015-10-08T08:17:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "endroid/qrcode",
|
||||
|
@ -161,7 +160,7 @@
|
|||
"qr",
|
||||
"qrcode"
|
||||
],
|
||||
"time": "2016-07-26 09:39:07"
|
||||
"time": "2016-07-26T09:39:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
|
@ -215,10 +214,109 @@
|
|||
"configuration",
|
||||
"options"
|
||||
],
|
||||
"time": "2016-05-12 15:59:27"
|
||||
"time": "2016-05-12T15:59:27+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "facebook/webdriver",
|
||||
"version": "1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/php-webdriver.git",
|
||||
"reference": "77300c4ab2025d4316635f592ec849ca7323bd8c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/facebook/php-webdriver/zipball/77300c4ab2025d4316635f592ec849ca7323bd8c",
|
||||
"reference": "77300c4ab2025d4316635f592ec849ca7323bd8c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"php": "^5.5 || ~7.0",
|
||||
"symfony/process": "^2.8 || ^3.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^1.11",
|
||||
"php-mock/php-mock-phpunit": "^1.1",
|
||||
"phpunit/phpunit": "4.6.* || ~5.0",
|
||||
"satooshi/php-coveralls": "^1.0",
|
||||
"squizlabs/php_codesniffer": "^2.6"
|
||||
},
|
||||
"suggest": {
|
||||
"phpdocumentor/phpdocumentor": "2.*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Facebook\\WebDriver\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"description": "A PHP client for WebDriver",
|
||||
"homepage": "https://github.com/facebook/php-webdriver",
|
||||
"keywords": [
|
||||
"facebook",
|
||||
"php",
|
||||
"selenium",
|
||||
"webdriver"
|
||||
],
|
||||
"time": "2017-01-13T15:48:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "350e810019fc52dd06ae844b6a6d382f8a0e8893"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/350e810019fc52dd06ae844b6a6d382f8a0e8893",
|
||||
"reference": "350e810019fc52dd06ae844b6a6d382f8a0e8893",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.9"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Process\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-01-02T20:32:22+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
|
|
|
@ -26,12 +26,12 @@ use Base32\Base32;
|
|||
use OCA\TwoFactorTOTP\Db\TotpSecret;
|
||||
use OCA\TwoFactorTOTP\Db\TotpSecretMapper;
|
||||
use OCA\TwoFactorTOTP\Exception\NoTotpSecretFoundException;
|
||||
use OCP\Activity\IManager as ActivityManager;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\IUser;
|
||||
use OCP\Security\ICrypto;
|
||||
use Otp\GoogleAuthenticator;
|
||||
use Otp\Otp;
|
||||
use OCP\Activity\IManager as ActivityManager;
|
||||
|
||||
class Totp implements ITotp {
|
||||
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @copyright Copyright (c) 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* Two-factor TOTP
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\TwoFactorTOTP\Tests\Acceptance;
|
||||
|
||||
use Facebook\WebDriver\Exception\ElementNotSelectableException;
|
||||
use Facebook\WebDriver\Remote\RemoteWebDriver;
|
||||
use Facebook\WebDriver\Remote\WebDriverBrowserType;
|
||||
use Facebook\WebDriver\Remote\WebDriverCapabilityType;
|
||||
use Facebook\WebDriver\WebDriver;
|
||||
use Facebook\WebDriver\WebDriverBy;
|
||||
use Facebook\WebDriver\WebDriverExpectedCondition;
|
||||
use OC;
|
||||
use OCA\TwoFactorTOTP\Db\TotpSecret;
|
||||
use OCA\TwoFactorTOTP\Db\TotpSecretMapper;
|
||||
use OCP\IUser;
|
||||
use Otp\GoogleAuthenticator;
|
||||
use PHPUnit_Framework_TestCase;
|
||||
|
||||
/**
|
||||
* @group Acceptance
|
||||
*/
|
||||
class TOTPAcceptenceTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
/** @var IUser */
|
||||
private static $user;
|
||||
|
||||
/** @var TotpSecretMapper */
|
||||
private static $secretMapper;
|
||||
|
||||
/** @var RemoteWebDriver */
|
||||
protected $webDriver;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
self::$user = OC::$server->getUserManager()->get('admin');
|
||||
self::$secretMapper = new TotpSecretMapper(OC::$server->getDatabaseConnection());
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
$capabilities = [
|
||||
WebDriverCapabilityType::BROWSER_NAME => $this->getBrowser(),
|
||||
];
|
||||
|
||||
if ($this->isRunningOnCI()) {
|
||||
$capabilities['tunnel-identifier'] = getenv('TRAVIS_JOB_NUMBER');
|
||||
$capabilities['build'] = getenv('TRAVIS_BUILD_NUMBER');
|
||||
$capabilities['name'] = 'PR' . getenv('TRAVIS_PULL_REQUEST') . ', Build ' . getenv('TRAVIS_BUILD_NUMBER');
|
||||
$user = 'nextcloud-totp';
|
||||
$accessKey = getenv('SAUCE_ACCESS_KEY');
|
||||
$this->webDriver = RemoteWebDriver::create("http://$user:$accessKey@ondemand.saucelabs.com/wd/hub", $capabilities);
|
||||
} else {
|
||||
$this->webDriver = RemoteWebDriver::create("http://localhost:4444/wd/hub", $capabilities);
|
||||
}
|
||||
}
|
||||
|
||||
private function getBrowser() {
|
||||
$env = getenv('SELENIUM_BROWSER');
|
||||
if ($env !== false) {
|
||||
return $env;
|
||||
}
|
||||
return WebDriverBrowserType::FIREFOX;
|
||||
}
|
||||
|
||||
private function isRunningOnCI() {
|
||||
return getenv('TRAVIS') !== false;
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
// Always delete secret again
|
||||
$secret = self::$secretMapper->getSecret(self::$user);
|
||||
if (!is_null($secret)) {
|
||||
self::$secretMapper->delete($secret);
|
||||
}
|
||||
|
||||
$this->webDriver->quit();
|
||||
}
|
||||
|
||||
public function testEnableTOTP() {
|
||||
$this->webDriver->get('http://localhost:8080/index.php/login');
|
||||
$this->assertContains('Nextcloud', $this->webDriver->getTitle());
|
||||
|
||||
// Log in
|
||||
$this->webDriver->findElement(WebDriverBy::id('user'))->sendKeys('admin');
|
||||
$this->webDriver->findElement(WebDriverBy::id('password'))->sendKeys('admin');
|
||||
$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->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->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) {
|
||||
try {
|
||||
return count($driver->findElements(WebDriverBy::id('totp-enabled'))) > 0;
|
||||
} catch (ElementNotSelectableException $ex) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
$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:'));
|
||||
}
|
||||
|
||||
private function createSecret() {
|
||||
$secret = GoogleAuthenticator::generateRandom();
|
||||
$dbsecret = new TotpSecret();
|
||||
$dbsecret->setSecret(OC::$server->getCrypto()->encrypt($secret));
|
||||
$dbsecret->setUserId(self::$user->getUID());
|
||||
self::$secretMapper->insert($dbsecret);
|
||||
return $secret;
|
||||
}
|
||||
|
||||
public function testLoginShouldFailWithWrongOTP() {
|
||||
$this->createSecret();
|
||||
|
||||
$this->webDriver->get('http://localhost:8080/index.php/login');
|
||||
$this->assertContains('Nextcloud', $this->webDriver->getTitle());
|
||||
|
||||
// Log in
|
||||
$this->webDriver->findElement(WebDriverBy::id('user'))->sendKeys('admin');
|
||||
$this->webDriver->findElement(WebDriverBy::id('password'))->sendKeys('admin');
|
||||
$this->webDriver->findElement(WebDriverBy::cssSelector('form[name=login] input[type=submit]'))->click();
|
||||
|
||||
// Enter a wrong OTP
|
||||
$this->webDriver->findElement(WebDriverBy::name('challenge'))->sendKeys('000');
|
||||
$this->webDriver->findElement(WebDriverBy::cssSelector('input.confirm-inline.icon-confirm'))->click();
|
||||
|
||||
$this->assertEquals('http://localhost:8080/index.php/login/challenge/totp', $this->webDriver->getCurrentURL());
|
||||
}
|
||||
|
||||
}
|
|
@ -19,4 +19,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../tests/bootstrap.php';
|
||||
|
||||
require_once __DIR__ . '/../../../tests/bootstrap.php';
|
||||
|
||||
\OC_App::loadApp('twofactor_totp');
|
Загрузка…
Ссылка в новой задаче