This adds persistence to the Nextcloud server 2FA logic so that the server knows which 2FA providers are enabled for a specific user at any time, even when the provider is not available. The `IStatefulProvider` interface was added as tagging interface for providers that are compatible with this new API. Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>pull/9632/head
parent
cad8824a8e
commit
13d93f5b25
@ -0,0 +1,110 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Core\Command\TwoFactorAuth; |
||||
|
||||
use OC\Core\Command\Base; |
||||
use OCP\Authentication\TwoFactorAuth\IRegistry; |
||||
use OCP\IUserManager; |
||||
use Symfony\Component\Console\Input\InputArgument; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
|
||||
class State extends Base { |
||||
|
||||
/** @var IRegistry */ |
||||
private $registry; |
||||
|
||||
/** @var IUserManager */ |
||||
private $userManager; |
||||
|
||||
public function __construct(IRegistry $registry, IUserManager $userManager) { |
||||
parent::__construct('twofactorauth:state'); |
||||
|
||||
$this->registry = $registry; |
||||
$this->userManager = $userManager; |
||||
} |
||||
|
||||
protected function configure() { |
||||
parent::configure(); |
||||
|
||||
$this->setName('twofactorauth:state'); |
||||
$this->setDescription('Get the two-factor authentication (2FA) state of a user'); |
||||
$this->addArgument('uid', InputArgument::REQUIRED); |
||||
} |
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) { |
||||
$uid = $input->getArgument('uid'); |
||||
$user = $this->userManager->get($uid); |
||||
if (is_null($user)) { |
||||
$output->writeln("<error>Invalid UID</error>"); |
||||
return; |
||||
} |
||||
|
||||
$providerStates = $this->registry->getProviderStates($user); |
||||
$filtered = $this->filterEnabledDisabledUnknownProviders($providerStates); |
||||
list ($enabled, $disabled) = $filtered; |
||||
|
||||
if (!empty($enabled)) { |
||||
$output->writeln("Two-factor authentication is enabled for user $uid"); |
||||
} else { |
||||
$output->writeln("Two-factor authentication is not enabled for user $uid"); |
||||
} |
||||
|
||||
$output->writeln(""); |
||||
$this->printProviders("Enabled providers", $enabled, $output); |
||||
$this->printProviders("Disabled providers", $disabled, $output); |
||||
} |
||||
|
||||
private function filterEnabledDisabledUnknownProviders(array $providerStates): array { |
||||
$enabled = []; |
||||
$disabled = []; |
||||
|
||||
foreach ($providerStates as $providerId => $isEnabled) { |
||||
if ($isEnabled) { |
||||
$enabled[] = $providerId; |
||||
} else { |
||||
$disabled[] = $providerId; |
||||
} |
||||
} |
||||
|
||||
return [$enabled, $disabled]; |
||||
} |
||||
|
||||
private function printProviders(string $title, array $providers, |
||||
OutputInterface $output) { |
||||
if (empty($providers)) { |
||||
// Ignore and don't print anything |
||||
return; |
||||
} |
||||
|
||||
$output->writeln($title . ":"); |
||||
foreach ($providers as $provider) { |
||||
$output->writeln("- " . $provider); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,62 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Core\Migrations; |
||||
|
||||
use Closure; |
||||
use OCP\DB\ISchemaWrapper; |
||||
use OCP\Migration\IOutput; |
||||
use OCP\Migration\SimpleMigrationStep; |
||||
|
||||
class Version14000Date20180522074438 extends SimpleMigrationStep { |
||||
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, |
||||
array $options): ISchemaWrapper { |
||||
|
||||
$schema = $schemaClosure(); |
||||
|
||||
if (!$schema->hasTable('twofactor_providers')) { |
||||
$table = $schema->createTable('twofactor_providers'); |
||||
$table->addColumn('provider_id', 'string', |
||||
[ |
||||
'notnull' => true, |
||||
'length' => 32, |
||||
]); |
||||
$table->addColumn('uid', 'string', |
||||
[ |
||||
'notnull' => true, |
||||
'length' => 64, |
||||
]); |
||||
$table->addColumn('enabled', 'smallint', |
||||
[ |
||||
'notnull' => true, |
||||
'length' => 1, |
||||
]); |
||||
$table->setPrimaryKey(['provider_id', 'uid']); |
||||
} |
||||
|
||||
return $schema; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,86 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Authentication\TwoFactorAuth\Db; |
||||
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder; |
||||
use OCP\IDBConnection; |
||||
|
||||
/** |
||||
* Data access object to query and assign (provider_id, uid, enabled) tuples of |
||||
* 2FA providers |
||||
*/ |
||||
class ProviderUserAssignmentDao { |
||||
|
||||
const TABLE_NAME = 'twofactor_providers'; |
||||
|
||||
/** @var IDBConnection */ |
||||
private $conn; |
||||
|
||||
public function __construct(IDBConnection $dbConn) { |
||||
$this->conn = $dbConn; |
||||
} |
||||
|
||||
/** |
||||
* Get all assigned provider IDs for the given user ID |
||||
* |
||||
* @return string[] where the array key is the provider ID (string) and the |
||||
* value is the enabled state (bool) |
||||
*/ |
||||
public function getState(string $uid): array { |
||||
$qb = $this->conn->getQueryBuilder(); |
||||
|
||||
$query = $qb->select('provider_id', 'enabled') |
||||
->from(self::TABLE_NAME) |
||||
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))); |
||||
$result = $query->execute(); |
||||
$providers = []; |
||||
foreach ($result->fetchAll() as $row) { |
||||
$providers[$row['provider_id']] = 1 === (int) $row['enabled']; |
||||
} |
||||
$result->closeCursor(); |
||||
|
||||
return $providers; |
||||
} |
||||
|
||||
/** |
||||
* Persist a new/updated (provider_id, uid, enabled) tuple |
||||
*/ |
||||
public function persist(string $providerId, string $uid, int $enabled) { |
||||
$qb = $this->conn->getQueryBuilder(); |
||||
|
||||
// TODO: concurrency? What if (providerId, uid) private key is inserted |
||||
// twice at the same time? |
||||
$query = $qb->insert(self::TABLE_NAME)->values([ |
||||
'provider_id' => $qb->createNamedParameter($providerId), |
||||
'uid' => $qb->createNamedParameter($uid), |
||||
'enabled' => $qb->createNamedParameter($enabled, IQueryBuilder::PARAM_INT), |
||||
]); |
||||
|
||||
$query->execute(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,88 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Authentication\TwoFactorAuth; |
||||
|
||||
use Exception; |
||||
use OC; |
||||
use OC_App; |
||||
use OCP\App\IAppManager; |
||||
use OCP\AppFramework\QueryException; |
||||
use OCP\Authentication\TwoFactorAuth\IProvider; |
||||
use OCP\IUser; |
||||
|
||||
class ProviderLoader { |
||||
|
||||
const BACKUP_CODES_APP_ID = 'twofactor_backupcodes'; |
||||
|
||||
/** @var IAppManager */ |
||||
private $appManager; |
||||
|
||||
public function __construct(IAppManager $appManager) { |
||||
$this->appManager = $appManager; |
||||
} |
||||
|
||||
/** |
||||
* Get the list of 2FA providers for the given user |
||||
* |
||||
* @return IProvider[] |
||||
* @throws Exception |
||||
*/ |
||||
public function getProviders(IUser $user): array { |
||||
$allApps = $this->appManager->getEnabledAppsForUser($user); |
||||
$providers = []; |
||||
|
||||
foreach ($allApps as $appId) { |
||||
$info = $this->appManager->getAppInfo($appId); |
||||
if (isset($info['two-factor-providers'])) { |
||||
/** @var string[] $providerClasses */ |
||||
$providerClasses = $info['two-factor-providers']; |
||||
foreach ($providerClasses as $class) { |
||||
try { |
||||
$this->loadTwoFactorApp($appId); |
||||
$provider = OC::$server->query($class); |
||||
$providers[$provider->getId()] = $provider; |
||||
} catch (QueryException $exc) { |
||||
// Provider class can not be resolved |
||||
throw new Exception("Could not load two-factor auth provider $class"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return $providers; |
||||
} |
||||
|
||||
/** |
||||
* Load an app by ID if it has not been loaded yet |
||||
* |
||||
* @param string $appId |
||||
*/ |
||||
protected function loadTwoFactorApp(string $appId) { |
||||
if (!OC_App::isAppLoaded($appId)) { |
||||
OC_App::loadApp($appId); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,71 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Authentication\TwoFactorAuth; |
||||
|
||||
use OCP\Authentication\TwoFactorAuth\IProvider; |
||||
|
||||
/** |
||||
* Contains all two-factor provider information for the two-factor login challenge |
||||
*/ |
||||
class ProviderSet { |
||||
|
||||
/** @var IProvider */ |
||||
private $providers; |
||||
|
||||
/** @var bool */ |
||||
private $providerMissing; |
||||
|
||||
/** |
||||
* @param IProvider[] $providers |
||||
* @param bool $providerMissing |
||||
*/ |
||||
public function __construct(array $providers, bool $providerMissing) { |
||||
$this->providers = []; |
||||
foreach ($providers as $provider) { |
||||
$this->providers[$provider->getId()] = $provider; |
||||
} |
||||
$this->providerMissing = $providerMissing; |
||||
} |
||||
|
||||
/** |
||||
* @param string $providerId |
||||
* @return IProvider|null |
||||
*/ |
||||
public function getProvider(string $providerId) { |
||||
return $this->providers[$providerId] ?? null; |
||||
} |
||||
|
||||
/** |
||||
* @return IProvider[] |
||||
*/ |
||||
public function getProviders(): array { |
||||
return $this->providers; |
||||
} |
||||
|
||||
public function isProviderMissing(): bool { |
||||
return $this->providerMissing; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,55 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Authentication\TwoFactorAuth; |
||||
|
||||
use OC\Authentication\TwoFactorAuth\Db\ProviderUserAssignmentDao; |
||||
use OCP\Authentication\TwoFactorAuth\IProvider; |
||||
use OCP\Authentication\TwoFactorAuth\IRegistry; |
||||
use OCP\IUser; |
||||
|
||||
class Registry implements IRegistry { |
||||
|
||||
/** @var ProviderUserAssignmentDao */ |
||||
private $assignmentDao; |
||||
|
||||
public function __construct(ProviderUserAssignmentDao $assignmentDao) { |
||||
$this->assignmentDao = $assignmentDao; |
||||
} |
||||
|
||||
public function getProviderStates(IUser $user): array { |
||||
return $this->assignmentDao->getState($user->getUID()); |
||||
} |
||||
|
||||
public function enableProviderFor(IProvider $provider, IUser $user) { |
||||
$this->assignmentDao->persist($provider->getId(), $user->getUID(), 1); |
||||
} |
||||
|
||||
public function disableProviderFor(IProvider $provider, IUser $user) { |
||||
$this->assignmentDao->persist($provider->getId(), $user->getUID(), 0); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,65 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\Authentication\TwoFactorAuth; |
||||
|
||||
use OCP\IUser; |
||||
|
||||
/** |
||||
* Nextcloud 2FA provider registry for stateful 2FA providers |
||||
* |
||||
* This service keeps track of which providers are currently active for a specific |
||||
* user. Stateful 2FA providers (IStatefulProvider) must use this service to save |
||||
* their enabled/disabled state. |
||||
* |
||||
* @since 14.0.0 |
||||
*/ |
||||
interface IRegistry { |
||||
|
||||
/** |
||||
* Get a key-value map of providers and their enabled/disabled state for |
||||
* the given user. |
||||
* |
||||
* @since 14.0.0 |
||||
* @return string[] where the array key is the provider ID (string) and the |
||||
* value is the enabled state (bool) |
||||
*/ |
||||
public function getProviderStates(IUser $user): array; |
||||
|
||||
/** |
||||
* Enable the given 2FA provider for the given user |
||||
* |
||||
* @since 14.0.0 |
||||
*/ |
||||
public function enableProviderFor(IProvider $provider, IUser $user); |
||||
|
||||
/** |
||||
* Disable the given 2FA provider for the given user |
||||
* |
||||
* @since 14.0.0 |
||||
*/ |
||||
public function disableProviderFor(IProvider $provider, IUser $user); |
||||
} |
||||
@ -0,0 +1,95 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace Test\Authentication\TwoFactorAuth\Db; |
||||
|
||||
use OC; |
||||
use OC\Authentication\TwoFactorAuth\Db\ProviderUserAssignmentDao; |
||||
use OCP\IDBConnection; |
||||
use Test\TestCase; |
||||
|
||||
/** |
||||
* @group DB |
||||
*/ |
||||
class ProviderUserAssignmentDaoTest extends TestCase { |
||||
|
||||
/** @var IDBConnection */ |
||||
private $dbConn; |
||||
|
||||
/** @var ProviderUserAssignmentDao */ |
||||
private $dao; |
||||
|
||||
protected function setUp() { |
||||
parent::setUp(); |
||||
|
||||
$this->dbConn = OC::$server->getDatabaseConnection(); |
||||
$qb = $this->dbConn->getQueryBuilder(); |
||||
$q = $qb->delete(ProviderUserAssignmentDao::TABLE_NAME); |
||||
$q->execute(); |
||||
|
||||
$this->dao = new ProviderUserAssignmentDao($this->dbConn); |
||||
} |
||||
|
||||
public function testGetState() { |
||||
$qb = $this->dbConn->getQueryBuilder(); |
||||
$q1 = $qb->insert(ProviderUserAssignmentDao::TABLE_NAME)->values([ |
||||
'provider_id' => $qb->createNamedParameter('twofactor_u2f'), |
||||
'uid' => $qb->createNamedParameter('user123'), |
||||
'enabled' => $qb->createNamedParameter(1), |
||||
]); |
||||
$q1->execute(); |
||||
$q2 = $qb->insert(ProviderUserAssignmentDao::TABLE_NAME)->values([ |
||||
'provider_id' => $qb->createNamedParameter('twofactor_totp'), |
||||
'uid' => $qb->createNamedParameter('user123'), |
||||
'enabled' => $qb->createNamedParameter(0), |
||||
]); |
||||
$q2->execute(); |
||||
$expected = [ |
||||
'twofactor_u2f' => true, |
||||
'twofactor_totp' => false, |
||||
]; |
||||
|
||||
$state = $this->dao->getState('user123'); |
||||
|
||||
$this->assertEquals($expected, $state); |
||||
} |
||||
|
||||
public function testPersist() { |
||||
$qb = $this->dbConn->getQueryBuilder(); |
||||
|
||||
$this->dao->persist('twofactor_totp', 'user123', 0); |
||||
|
||||
$q = $qb |
||||
->select('*') |
||||
->from(ProviderUserAssignmentDao::TABLE_NAME) |
||||
->where($qb->expr()->eq('provider_id', $qb->createNamedParameter('twofactor_totp'))) |
||||
->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter('user123'))) |
||||
->andWhere($qb->expr()->eq('enabled', $qb->createNamedParameter(0))); |
||||
$res = $q->execute(); |
||||
$data = $res->fetchAll(); |
||||
$res->closeCursor(); |
||||
$this->assertCount(1, $data); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,86 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Created by PhpStorm. |
||||
* User: christoph |
||||
* Date: 04.06.18 |
||||
* Time: 13:29 |
||||
*/ |
||||
|
||||
namespace lib\Authentication\TwoFactorAuth; |
||||
|
||||
use Exception; |
||||
use OC\Authentication\TwoFactorAuth\ProviderLoader; |
||||
use OCP\App\IAppManager; |
||||
use OCP\Authentication\TwoFactorAuth\IProvider; |
||||
use PHPUnit_Framework_MockObject_MockObject; |
||||
use Test\TestCase; |
||||
|
||||
class ProviderLoaderTest extends TestCase { |
||||
|
||||
/** @var IAppManager|PHPUnit_Framework_MockObject_MockObject */ |
||||
private $appManager; |
||||
|
||||
/** @var IUser|PHPUnit_Framework_MockObject_MockObject */ |
||||
private $user; |
||||
|
||||
/** @var ProviderLoader */ |
||||
private $loader; |
||||
|
||||
protected function setUp() { |
||||
parent::setUp(); |
||||
|
||||
$this->appManager = $this->createMock(IAppManager::class); |
||||
$this->user = $this->createMock(\OCP\IUser::class); |
||||
|
||||
$this->loader = new ProviderLoader($this->appManager); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException Exception |
||||
* @expectedExceptionMessage Could not load two-factor auth provider \OCA\MyFaulty2faApp\DoesNotExist |
||||
*/ |
||||
public function testFailHardIfProviderCanNotBeLoaded() { |
||||
$this->appManager->expects($this->once()) |
||||
->method('getEnabledAppsForUser') |
||||
->with($this->user) |
||||
->willReturn(['mail', 'twofactor_totp']); |
||||
$this->appManager |
||||
->method('getAppInfo') |
||||
->will($this->returnValueMap([ |
||||
['mail', false, null, []], |
||||
['twofactor_totp', false, null, [ |
||||
'two-factor-providers' => [ |
||||
'\\OCA\\MyFaulty2faApp\\DoesNotExist', |
||||
], |
||||
]], |
||||
])); |
||||
|
||||
$this->loader->getProviders($this->user); |
||||
} |
||||
|
||||
public function testGetProviders() { |
||||
$provider = $this->createMock(IProvider::class); |
||||
$provider->method('getId')->willReturn('test'); |
||||
\OC::$server->registerService('\\OCA\\TwoFactorTest\\Provider', function () use ($provider) { |
||||
return $provider; |
||||
}); |
||||
$this->appManager->expects($this->once()) |
||||
->method('getEnabledAppsForUser') |
||||
->with($this->user) |
||||
->willReturn(['twofactor_test']); |
||||
$this->appManager |
||||
->method('getAppInfo') |
||||
->with('twofactor_test') |
||||
->willReturn(['two-factor-providers' => [ |
||||
'\\OCA\\TwoFactorTest\\Provider', |
||||
]]); |
||||
|
||||
$providers = $this->loader->getProviders($this->user); |
||||
|
||||
$this->assertCount(1, $providers); |
||||
$this->assertArrayHasKey('test', $providers); |
||||
$this->assertSame($provider, $providers['test']); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,74 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace Test\Authentication\TwoFactorAuth; |
||||
|
||||
use OC\Authentication\TwoFactorAuth\ProviderSet; |
||||
use OCP\Authentication\TwoFactorAuth\IProvider; |
||||
use Test\TestCase; |
||||
|
||||
class ProviderSetTest extends TestCase { |
||||
|
||||
/** @var ProviderSet */ |
||||
private $providerSet; |
||||
|
||||
public function testIndexesProviders() { |
||||
$p1 = $this->createMock(IProvider::class); |
||||
$p1->method('getId')->willReturn('p1'); |
||||
$p2 = $this->createMock(IProvider::class); |
||||
$p2->method('getId')->willReturn('p2'); |
||||
$expected = [ |
||||
'p1' => $p1, |
||||
'p2' => $p2, |
||||
]; |
||||
|
||||
$set = new ProviderSet([$p2, $p1], false); |
||||
|
||||
$this->assertEquals($expected, $set->getProviders()); |
||||
} |
||||
|
||||
public function testGetProvider() { |
||||
$p1 = $this->createMock(IProvider::class); |
||||
$p1->method('getId')->willReturn('p1'); |
||||
|
||||
$set = new ProviderSet([$p1], false); |
||||
$provider = $set->getProvider('p1'); |
||||
|
||||
$this->assertEquals($p1, $provider); |
||||
} |
||||
|
||||
public function testGetProviderNotFound() { |
||||
$set = new ProviderSet([], false); |
||||
$provider = $set->getProvider('p1'); |
||||
|
||||
$this->assertNull($provider); |
||||
} |
||||
|
||||
public function testIsProviderMissing() { |
||||
$set = new ProviderSet([], true); |
||||
|
||||
$this->assertTrue($set->isProviderMissing()); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,85 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace Test\Authentication\TwoFactorAuth; |
||||
|
||||
use OC\Authentication\TwoFactorAuth\Db\ProviderUserAssignmentDao; |
||||
use OC\Authentication\TwoFactorAuth\Registry; |
||||
use OCP\Authentication\TwoFactorAuth\IProvider; |
||||
use OCP\IUser; |
||||
use PHPUnit_Framework_MockObject_MockObject; |
||||
use Test\TestCase; |
||||
|
||||
class RegistryTest extends TestCase { |
||||
|
||||
/** @var ProviderUserAssignmentDao|PHPUnit_Framework_MockObject_MockObject */ |
||||
private $dao; |
||||
|
||||
/** @var Registry */ |
||||
private $registry; |
||||
|
||||
protected function setUp() { |
||||
parent::setUp(); |
||||
|
||||
$this->dao = $this->createMock(ProviderUserAssignmentDao::class); |
||||
|
||||
$this->registry = new Registry($this->dao); |
||||
} |
||||
|
||||
public function testGetProviderStates() { |
||||
$user = $this->createMock(IUser::class); |
||||
$user->expects($this->once())->method('getUID')->willReturn('user123'); |
||||
$state = [ |
||||
'twofactor_totp' => true, |
||||
]; |
||||
$this->dao->expects($this->once())->method('getState')->willReturn($state); |
||||
|
||||
$actual = $this->registry->getProviderStates($user); |
||||
|
||||
$this->assertEquals($state, $actual); |
||||
} |
||||
|
||||
public function testEnableProvider() { |
||||
$user = $this->createMock(IUser::class); |
||||
$provider = $this->createMock(IProvider::class); |
||||
$user->expects($this->once())->method('getUID')->willReturn('user123'); |
||||
$provider->expects($this->once())->method('getId')->willReturn('p1'); |
||||
$this->dao->expects($this->once())->method('persist')->with('p1', 'user123', |
||||
true); |
||||
|
||||
$this->registry->enableProviderFor($provider, $user); |
||||
} |
||||
|
||||
public function testDisableProvider() { |
||||
$user = $this->createMock(IUser::class); |
||||
$provider = $this->createMock(IProvider::class); |
||||
$user->expects($this->once())->method('getUID')->willReturn('user123'); |
||||
$provider->expects($this->once())->method('getId')->willReturn('p1'); |
||||
$this->dao->expects($this->once())->method('persist')->with('p1', 'user123', |
||||
false); |
||||
|
||||
$this->registry->disableProviderFor($provider, $user); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue