migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author J0WI <J0WI@users.noreply.github.com>
* @author Joas Schilling <coding@schilljs.com>
*
* @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 OCA\TwoFactorBackupCodes\Activity;
use InvalidArgumentException;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
use OCP\IURLGenerator;
use OCP\L10N\IFactory as L10nFactory;
class Provider implements IProvider {
/** @var L10nFactory */
private $l10n;
/** @var IURLGenerator */
private $urlGenerator;
/** @var IManager */
private $activityManager;
/**
* @param L10nFactory $l10n
* @param IURLGenerator $urlGenerator
* @param IManager $activityManager
*/
public function __construct(L10nFactory $l10n, IURLGenerator $urlGenerator, IManager $activityManager) {
$this->urlGenerator = $urlGenerator;
$this->activityManager = $activityManager;
$this->l10n = $l10n;
}
public function parse($language, IEvent $event, IEvent $previousEvent = null): IEvent {
if ($event->getApp() !== 'twofactor_backupcodes') {
throw new InvalidArgumentException();
}
$l = $this->l10n->get('twofactor_backupcodes', $language);
switch ($event->getSubject()) {
case 'codes_generated':
$event->setParsedSubject($l->t('You created two-factor backup codes for your account'));
if ($this->activityManager->getRequirePNG()) {
$event->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'actions/password.png')));
} else {
$event->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'actions/password.svg')));
}
break;
default:
throw new InvalidArgumentException();
}
return $event;
}
}
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\AppInfo;
use OCA\TwoFactorBackupCodes\Event\CodesGenerated;
use OCA\TwoFactorBackupCodes\Listener\ActivityPublisher;
use OCA\TwoFactorBackupCodes\Listener\ClearNotifications;
use OCA\TwoFactorBackupCodes\Listener\ProviderDisabled;
use OCA\TwoFactorBackupCodes\Listener\ProviderEnabled;
use OCA\TwoFactorBackupCodes\Listener\RegistryUpdater;
use OCA\TwoFactorBackupCodes\Listener\UserDeleted;
use OCA\TwoFactorBackupCodes\Notifications\Notifier;
use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Authentication\TwoFactorAuth\IRegistry;
use OCP\User\Events\UserDeletedEvent;
class Application extends App implements IBootstrap {
public const APP_ID = 'twofactor_backupcodes';
public function __construct() {
parent::__construct(self::APP_ID);
}
public function register(IRegistrationContext $context): void {
$context->registerNotifierService(Notifier::class);
$context->registerEventListener(CodesGenerated::class, ActivityPublisher::class);
$context->registerEventListener(CodesGenerated::class, RegistryUpdater::class);
$context->registerEventListener(CodesGenerated::class, ClearNotifications::class);
$context->registerEventListener(IRegistry::EVENT_PROVIDER_ENABLED, ProviderEnabled::class);
$context->registerEventListener(IRegistry::EVENT_PROVIDER_DISABLED, ProviderDisabled::class);
$context->registerEventListener(UserDeletedEvent::class, UserDeleted::class);
$context->registerTwoFactorProvider(BackupCodesProvider::class);
}
public function boot(IBootContext $context): void {
}
}
@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\BackgroundJob;
use OC\Authentication\TwoFactorAuth\Manager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Authentication\TwoFactorAuth\IRegistry;
use OCP\BackgroundJob\IJobList;
use OCP\BackgroundJob\QueuedJob;
use OCP\IUser;
use OCP\IUserManager;
class CheckBackupCodes extends QueuedJob {
/** @var IUserManager */
private $userManager;
/** @var IJobList */
private $jobList;
/** @var IRegistry */
private $registry;
/** @var Manager */
private $twofactorManager;
public function __construct(ITimeFactory $timeFactory, IUserManager $userManager, IJobList $jobList, Manager $twofactorManager, IRegistry $registry) {
parent::__construct($timeFactory);
$this->userManager = $userManager;
$this->jobList = $jobList;
$this->twofactorManager = $twofactorManager;
$this->registry = $registry;
}
protected function run($argument) {
$this->userManager->callForSeenUsers(function (IUser $user) {
if (!$user->isEnabled()) {
return;
}
$providers = $this->registry->getProviderStates($user);
$isTwoFactorAuthenticated = $this->twofactorManager->isTwoFactorAuthenticated($user);
if ($isTwoFactorAuthenticated && isset($providers['backup_codes']) && $providers['backup_codes'] === false) {
$this->jobList->add(RememberBackupCodesJob::class, ['uid' => $user->getUID()]);
}
});
}
}
@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\BackgroundJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Authentication\TwoFactorAuth\IRegistry;
use OCP\BackgroundJob\IJob;
use OCP\BackgroundJob\IJobList;
use OCP\BackgroundJob\TimedJob;
use OCP\IUserManager;
use OCP\Notification\IManager;
class RememberBackupCodesJob extends TimedJob {
/** @var IRegistry */
private $registry;
/** @var IUserManager */
private $userManager;
/** @var IManager */
private $notificationManager;
/** @var IJobList */
private $jobList;
public function __construct(IRegistry $registry,
IUserManager $userManager,
ITimeFactory $timeFactory,
IManager $notificationManager,
IJobList $jobList) {
parent::__construct($timeFactory);
$this->registry = $registry;
$this->userManager = $userManager;
$this->time = $timeFactory;
$this->notificationManager = $notificationManager;
$this->jobList = $jobList;
$this->setInterval(60 * 60 * 24 * 14);
$this->setTimeSensitivity(IJob::TIME_INSENSITIVE);
}
protected function run($argument) {
$uid = $argument['uid'];
$user = $this->userManager->get($uid);
if ($user === null) {
// We can't run with an invalid user
$this->jobList->remove(self::class, $argument);
return;
}
$providers = $this->registry->getProviderStates($user);
$state2fa = array_reduce($providers, function (bool $carry, bool $state) {
return $carry || $state;
}, false);
/*
* If no provider is active or if the backup codes are already generate
* we can remove the job
*/
if ($state2fa === false || (isset($providers['backup_codes']) && $providers['backup_codes'] === true)) {
// Backup codes already generated lets remove this job
$this->jobList->remove(self::class, $argument);
return;
}
$date = new \DateTime();
$date->setTimestamp($this->time->getTime());
$notification = $this->notificationManager->createNotification();
$notification->setApp('twofactor_backupcodes')
->setUser($user->getUID())
->setObject('create', 'codes')
->setSubject('create_backupcodes');
$this->notificationManager->markProcessed($notification);
if (!$user->isEnabled()) {
// Don't recreate a notification for a user that can not read it
$this->jobList->remove(self::class, $argument);
return;
}
$notification->setDateTime($date);
$this->notificationManager->notify($notification);
}
}
@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author J0WI <J0WI@users.noreply.github.com>
* @author Joas Schilling <coding@schilljs.com>
*
* @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 OCA\TwoFactorBackupCodes\Controller;
use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\IUserSession;
class SettingsController extends Controller {
/** @var BackupCodeStorage */
private $storage;
/** @var IUserSession */
private $userSession;
/**
* @param string $appName
* @param IRequest $request
* @param BackupCodeStorage $storage
* @param IUserSession $userSession
*/
public function __construct($appName, IRequest $request, BackupCodeStorage $storage, IUserSession $userSession) {
parent::__construct($appName, $request);
$this->userSession = $userSession;
$this->storage = $storage;
}
/**
* @NoAdminRequired
* @PasswordConfirmationRequired
*
* @return JSONResponse
*/
public function createCodes(): JSONResponse {
$user = $this->userSession->getUser();
$codes = $this->storage->createCodes($user);
return new JSONResponse([
'codes' => $codes,
'state' => $this->storage->getBackupCodesState($user),
]);
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 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 OCA\TwoFactorBackupCodes\Db;
use OCP\AppFramework\Db\Entity;
/**
* @method string getUserId()
* @method void setUserId(string $userId)
* @method string getCode()
* @method void setCode(string $code)
* @method int getUsed()
* @method void setUsed(int $code)
*/
class BackupCode extends Entity {
/** @var string */
protected $userId;
/** @var string */
protected $code;
/** @var int */
protected $used;
}
@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author J0WI <J0WI@users.noreply.github.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Db;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUser;
/**
* @template-extends QBMapper<BackupCode>
*/
class BackupCodeMapper extends QBMapper {
public function __construct(IDBConnection $db) {
parent::__construct($db, 'twofactor_backupcodes');
}
/**
* @param IUser $user
* @return BackupCode[]
*/
public function getBackupCodes(IUser $user): array {
/* @var IQueryBuilder $qb */
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'user_id', 'code', 'used')
->from('twofactor_backupcodes')
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID())));
return self::findEntities($qb);
}
/**
* @param IUser $user
*/
public function deleteCodes(IUser $user): void {
$this->deleteCodesByUserId($user->getUID());
}
/**
* @param string $uid
*/
public function deleteCodesByUserId(string $uid): void {
/* @var IQueryBuilder $qb */
$qb = $this->db->getQueryBuilder();
$qb->delete('twofactor_backupcodes')
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)));
$qb->execute();
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 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 OCA\TwoFactorBackupCodes\Event;
use OCP\EventDispatcher\Event;
use OCP\IUser;
class CodesGenerated extends Event {
/** @var IUser */
private $user;
public function __construct(IUser $user) {
parent::__construct();
$this->user = $user;
}
/**
* @return IUser
*/
public function getUser(): IUser {
return $this->user;
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 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 OCA\TwoFactorBackupCodes\Listener;
use BadMethodCallException;
use OCA\TwoFactorBackupCodes\Event\CodesGenerated;
use OCP\Activity\IManager;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use Psr\Log\LoggerInterface;
class ActivityPublisher implements IEventListener {
public function __construct(
private IManager $activityManager,
private LoggerInterface $logger,
) {
}
/**
* Push an event to the user's activity stream
*/
public function handle(Event $event): void {
if ($event instanceof CodesGenerated) {
$activity = $this->activityManager->generateEvent();
$activity->setApp('twofactor_backupcodes')
->setType('security')
->setAuthor($event->getUser()->getUID())
->setAffectedUser($event->getUser()->getUID())
->setSubject('codes_generated');
try {
$this->activityManager->publish($activity);
} catch (BadMethodCallException $e) {
$this->logger->error('Could not publish backup code creation activity', ['exception' => $e]);
}
}
}
}
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Listener;
use OCA\TwoFactorBackupCodes\Event\CodesGenerated;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Notification\IManager;
class ClearNotifications implements IEventListener {
/** @var IManager */
private $manager;
public function __construct(IManager $manager) {
$this->manager = $manager;
}
public function handle(Event $event): void {
if (!($event instanceof CodesGenerated)) {
return;
}
$notification = $this->manager->createNotification();
$notification->setApp('twofactor_backupcodes')
->setUser($event->getUser()->getUID())
->setObject('create', 'codes');
$this->manager->markProcessed($notification);
}
}
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Listener;
use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob;
use OCP\Authentication\TwoFactorAuth\IRegistry;
use OCP\Authentication\TwoFactorAuth\RegistryEvent;
use OCP\BackgroundJob\IJobList;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
class ProviderDisabled implements IEventListener {
/** @var IRegistry */
private $registry;
/** @var IJobList */
private $jobList;
public function __construct(IRegistry $registry,
IJobList $jobList) {
$this->registry = $registry;
$this->jobList = $jobList;
}
public function handle(Event $event): void {
if (!($event instanceof RegistryEvent)) {
return;
}
$providers = $this->registry->getProviderStates($event->getUser());
// Loop over all providers. If all are disabled we remove the job
$state = array_reduce($providers, function (bool $carry, bool $enabled) {
return $carry || $enabled;
}, false);
if ($state === false) {
$this->jobList->remove(RememberBackupCodesJob::class, ['uid' => $event->getUser()->getUID()]);
}
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Listener;
use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob;
use OCP\Authentication\TwoFactorAuth\IRegistry;
use OCP\Authentication\TwoFactorAuth\RegistryEvent;
use OCP\BackgroundJob\IJobList;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
class ProviderEnabled implements IEventListener {
/** @var IRegistry */
private $registry;
/** @var IJobList */
private $jobList;
public function __construct(IRegistry $registry,
IJobList $jobList) {
$this->registry = $registry;
$this->jobList = $jobList;
}
public function handle(Event $event): void {
if (!($event instanceof RegistryEvent)) {
return;
}
$providers = $this->registry->getProviderStates($event->getUser());
if (isset($providers['backup_codes']) && $providers['backup_codes'] === true) {
// Backup codes already generated nothing to do here
return;
}
$this->jobList->add(RememberBackupCodesJob::class, ['uid' => $event->getUser()->getUID()]);
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 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 OCA\TwoFactorBackupCodes\Listener;
use OCA\TwoFactorBackupCodes\Event\CodesGenerated;
use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider;
use OCP\Authentication\TwoFactorAuth\IRegistry;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
class RegistryUpdater implements IEventListener {
/** @var IRegistry */
private $registry;
/** @var BackupCodesProvider */
private $provider;
public function __construct(IRegistry $registry, BackupCodesProvider $provider) {
$this->registry = $registry;
$this->provider = $provider;
}
public function handle(Event $event): void {
if ($event instanceof CodesGenerated) {
$this->registry->enableProviderFor($this->provider, $event->getUser());
}
}
}
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Listener;
use OCA\TwoFactorBackupCodes\Db\BackupCodeMapper;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\UserDeletedEvent;
class UserDeleted implements IEventListener {
/** @var BackupCodeMapper */
private $backupCodeMapper;
public function __construct(BackupCodeMapper $backupCodeMapper) {
$this->backupCodeMapper = $backupCodeMapper;
}
public function handle(Event $event): void {
if (!($event instanceof UserDeletedEvent)) {
return;
}
$this->backupCodeMapper->deleteCodes($event->getUser());
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Migration;
use OCP\BackgroundJob\IJobList;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class CheckBackupCodes implements IRepairStep {
/** @var IJobList */
private $jobList;
public function __construct(IJobList $jobList) {
$this->jobList = $jobList;
}
public function getName(): string {
return 'Add background job to check for backup codes';
}
public function run(IOutput $output) {
$this->jobList->add(\OCA\TwoFactorBackupCodes\BackgroundJob\CheckBackupCodes::class);
}
}
@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
*
* @author Daniel Kesselberg <mail@danielkesselberg.de>
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Migration;
use Doctrine\DBAL\Types\Types;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version1002Date20170607104347 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
* @since 13.0.0
*/
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if (!$schema->hasTable('twofactor_backupcodes')) {
$table = $schema->createTable('twofactor_backupcodes');
$table->addColumn('id', Types::INTEGER, [
'autoincrement' => true,
'notnull' => true,
'length' => 20,
]);
$table->addColumn('user_id', Types::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('code', Types::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('used', Types::INTEGER, [
'notnull' => true,
'length' => 1,
'default' => 0,
]);
$table->setPrimaryKey(['id']);
$table->addIndex(['user_id'], 'twofactor_backupcodes_uid');
}
return $schema;
}
}
@@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Migration;
use OCP\DB\ISchemaWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version1002Date20170607113030 extends SimpleMigrationStep {
/** @var IDBConnection */
protected $connection;
/**
* @param IDBConnection $connection
*/
public function __construct(IDBConnection $connection) {
$this->connection = $connection;
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @since 13.0.0
*/
public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if (!$schema->hasTable('twofactor_backup_codes')) {
// Legacy table does not exist
return;
}
$insert = $this->connection->getQueryBuilder();
$insert->insert('twofactor_backupcodes')
->values([
// Inserting with id might fail: 'id' => $insert->createParameter('id'),
'user_id' => $insert->createParameter('user_id'),
'code' => $insert->createParameter('code'),
'used' => $insert->createParameter('used'),
]);
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('twofactor_backup_codes')
->orderBy('id', 'ASC');
$result = $query->execute();
$output->startProgress();
while ($row = $result->fetch()) {
$output->advance();
$insert
// Inserting with id might fail: ->setParameter('id', $row['id'], IQueryBuilder::PARAM_INT)
->setParameter('user_id', $row['user_id'], IQueryBuilder::PARAM_STR)
->setParameter('code', $row['code'], IQueryBuilder::PARAM_STR)
->setParameter('used', $row['used'], IQueryBuilder::PARAM_INT)
->execute();
}
$output->finishProgress();
}
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
* @since 13.0.0
*/
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if ($schema->hasTable('twofactor_backup_codes')) {
$schema->dropTable('twofactor_backup_codes');
return $schema;
}
return null;
}
}
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
*
* @author Daniel Kesselberg <mail@danielkesselberg.de>
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Migration;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version1002Date20170919123342 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
* @since 13.0.0
*/
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('twofactor_backupcodes');
$column = $table->getColumn('user_id');
$column->setDefault('');
$column = $table->getColumn('used');
if ($column->getType()->getName() !== Types::SMALLINT) {
$column->setType(Type::getType(Types::SMALLINT));
$column->setOptions(['length' => 6]);
}
return $schema;
}
}
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
*
* @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 OCA\TwoFactorBackupCodes\Migration;
use OCP\Migration\BigIntMigration;
/**
* Auto-generated migration step: Please modify to your needs!
*/
class Version1002Date20170926101419 extends BigIntMigration {
/**
* @return array Returns an array with the following structure
* ['table1' => ['column1', 'column2'], ...]
* @since 13.0.0
*/
protected function getColumnsByTable() {
return [
'twofactor_backupcodes' => ['id'],
];
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version1002Date20180821043638 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*
* @return ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('twofactor_backupcodes');
$table->getColumn('code')->setLength(128);
return $schema;
}
}
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Jan-Christoph Borchardt <hey@jancborchardt.net>
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Notifications;
use OCP\IURLGenerator;
use OCP\L10N\IFactory;
use OCP\Notification\INotification;
use OCP\Notification\INotifier;
class Notifier implements INotifier {
/** @var IFactory */
private $factory;
/** @var IURLGenerator */
private $url;
public function __construct(IFactory $factory, IURLGenerator $url) {
$this->factory = $factory;
$this->url = $url;
}
/**
* Identifier of the notifier, only use [a-z0-9_]
*
* @return string
* @since 17.0.0
*/
public function getID(): string {
return 'twofactor_backupcodes';
}
/**
* Human readable name describing the notifier
*
* @return string
* @since 17.0.0
*/
public function getName(): string {
return $this->factory->get('twofactor_backupcodes')->t('Second-factor backup codes');
}
public function prepare(INotification $notification, string $languageCode): INotification {
if ($notification->getApp() !== 'twofactor_backupcodes') {
// Not my app => throw
throw new \InvalidArgumentException();
}
// Read the language from the notification
$l = $this->factory->get('twofactor_backupcodes', $languageCode);
switch ($notification->getSubject()) {
case 'create_backupcodes':
$notification->setParsedSubject(
$l->t('Generate backup codes')
)->setParsedMessage(
$l->t('You enabled two-factor authentication but did not generate backup codes yet. They are needed to restore access to your account in case you lose your second factor.')
);
$notification->setLink($this->url->linkToRouteAbsolute('settings.PersonalSettings.index', ['section' => 'security']));
$notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/password.svg')));
return $notification;
default:
// Unknown subject => Unknown notification => throw
throw new \InvalidArgumentException();
}
}
}
@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Provider;
use OC\App\AppManager;
use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage;
use OCA\TwoFactorBackupCodes\Settings\Personal;
use OCP\Authentication\TwoFactorAuth\IDeactivatableByAdmin;
use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings;
use OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings;
use OCP\IInitialStateService;
use OCP\IL10N;
use OCP\IUser;
use OCP\Template;
class BackupCodesProvider implements IDeactivatableByAdmin, IProvidesPersonalSettings {
/** @var string */
private $appName;
/** @var BackupCodeStorage */
private $storage;
/** @var IL10N */
private $l10n;
/** @var AppManager */
private $appManager;
/** @var IInitialStateService */
private $initialStateService;
/**
* @param string $appName
* @param BackupCodeStorage $storage
* @param IL10N $l10n
* @param AppManager $appManager
*/
public function __construct(string $appName,
BackupCodeStorage $storage,
IL10N $l10n,
AppManager $appManager,
IInitialStateService $initialStateService) {
$this->appName = $appName;
$this->l10n = $l10n;
$this->storage = $storage;
$this->appManager = $appManager;
$this->initialStateService = $initialStateService;
}
/**
* Get unique identifier of this 2FA provider
*
* @return string
*/
public function getId(): string {
return 'backup_codes';
}
/**
* Get the display name for selecting the 2FA provider
*
* @return string
*/
public function getDisplayName(): string {
return $this->l10n->t('Backup code');
}
/**
* Get the description for selecting the 2FA provider
*
* @return string
*/
public function getDescription(): string {
return $this->l10n->t('Use backup code');
}
/**
* Get the template for rending the 2FA provider view
*
* @param IUser $user
* @return Template
*/
public function getTemplate(IUser $user): Template {
return new Template('twofactor_backupcodes', 'challenge');
}
/**
* Verify the given challenge
*
* @param IUser $user
* @param string $challenge
* @return bool
*/
public function verifyChallenge(IUser $user, string $challenge): bool {
return $this->storage->validateCode($user, $challenge);
}
/**
* Decides whether 2FA is enabled for the given user
*
* @param IUser $user
* @return boolean
*/
public function isTwoFactorAuthEnabledForUser(IUser $user): bool {
return $this->storage->hasBackupCodes($user);
}
/**
* Determine whether backup codes should be active or not
*
* Backup codes only make sense if at least one 2FA provider is active,
* hence this method checks all enabled apps on whether they provide 2FA
* functionality or not. If there's at least one app, backup codes are
* enabled on the personal settings page.
*
* @param IUser $user
* @return boolean
*/
public function isActive(IUser $user): bool {
$appIds = array_filter($this->appManager->getEnabledAppsForUser($user), function ($appId) {
return $appId !== $this->appName;
});
foreach ($appIds as $appId) {
$info = $this->appManager->getAppInfo($appId);
if (isset($info['two-factor-providers']) && count($info['two-factor-providers']) > 0) {
return true;
}
}
return false;
}
/**
* @param IUser $user
*
* @return IPersonalProviderSettings
*/
public function getPersonalSettings(IUser $user): IPersonalProviderSettings {
$state = $this->storage->getBackupCodesState($user);
$this->initialStateService->provideInitialState($this->appName, 'state', $state);
return new Personal();
}
public function disableFor(IUser $user) {
$this->storage->deleteCodes($user);
}
}
@@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author J0WI <J0WI@users.noreply.github.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Service;
use OCA\TwoFactorBackupCodes\Db\BackupCode;
use OCA\TwoFactorBackupCodes\Db\BackupCodeMapper;
use OCA\TwoFactorBackupCodes\Event\CodesGenerated;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IUser;
use OCP\Security\IHasher;
use OCP\Security\ISecureRandom;
class BackupCodeStorage {
private static $CODE_LENGTH = 16;
/** @var BackupCodeMapper */
private $mapper;
/** @var IHasher */
private $hasher;
/** @var ISecureRandom */
private $random;
/** @var IEventDispatcher */
private $eventDispatcher;
public function __construct(BackupCodeMapper $mapper,
ISecureRandom $random,
IHasher $hasher,
IEventDispatcher $eventDispatcher) {
$this->mapper = $mapper;
$this->hasher = $hasher;
$this->random = $random;
$this->eventDispatcher = $eventDispatcher;
}
/**
* @param IUser $user
* @param int $number
* @return string[]
*/
public function createCodes(IUser $user, int $number = 10): array {
$result = [];
// Delete existing ones
$this->mapper->deleteCodes($user);
$uid = $user->getUID();
foreach (range(1, min([$number, 20])) as $i) {
$code = $this->random->generate(self::$CODE_LENGTH, ISecureRandom::CHAR_HUMAN_READABLE);
$dbCode = new BackupCode();
$dbCode->setUserId($uid);
$dbCode->setCode($this->hasher->hash($code));
$dbCode->setUsed(0);
$this->mapper->insert($dbCode);
$result[] = $code;
}
$this->eventDispatcher->dispatchTyped(new CodesGenerated($user));
return $result;
}
/**
* @param IUser $user
* @return bool
*/
public function hasBackupCodes(IUser $user): bool {
$codes = $this->mapper->getBackupCodes($user);
return count($codes) > 0;
}
/**
* @param IUser $user
* @return array
*/
public function getBackupCodesState(IUser $user): array {
$codes = $this->mapper->getBackupCodes($user);
$total = count($codes);
$used = 0;
array_walk($codes, function (BackupCode $code) use (&$used) {
if (1 === (int)$code->getUsed()) {
$used++;
}
});
return [
'enabled' => $total > 0,
'total' => $total,
'used' => $used,
];
}
/**
* @param IUser $user
* @param string $code
* @return bool
*/
public function validateCode(IUser $user, string $code): bool {
$dbCodes = $this->mapper->getBackupCodes($user);
foreach ($dbCodes as $dbCode) {
if (0 === (int)$dbCode->getUsed() && $this->hasher->verify($code, $dbCode->getCode())) {
$dbCode->setUsed(1);
$this->mapper->update($dbCode);
return true;
}
}
return false;
}
public function deleteCodes(IUser $user): void {
$this->mapper->deleteCodes($user);
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @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 OCA\TwoFactorBackupCodes\Settings;
use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings;
use OCP\Template;
class Personal implements IPersonalProviderSettings {
public function getBody(): Template {
return new Template('twofactor_backupcodes', 'personal');
}
}