mirror of https://github.com/nextcloud/server.git
Remote wipe support
This allows a user to mark a token for remote wipe. Clients that support this can then wipe the device properly. Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl> Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>pull/15104/head
parent
ae7f89fd9f
commit
f03eb7ec3c
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2019, 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 OC\Core\Controller;
|
||||
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
use OC\Authentication\Token\RemoteWipe;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\IRequest;
|
||||
|
||||
class WipeController extends Controller {
|
||||
|
||||
/** @var RemoteWipe */
|
||||
private $remoteWipe;
|
||||
|
||||
public function __construct(string $appName,
|
||||
IRequest $request,
|
||||
RemoteWipe $remoteWipe) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->remoteWipe = $remoteWipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
* @PublicPage
|
||||
*
|
||||
* @AnonRateThrottle(limit=10, period=300)
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function checkWipe(string $token): JSONResponse {
|
||||
try {
|
||||
if ($this->remoteWipe->start($token)) {
|
||||
return new JSONResponse([
|
||||
'wipe' => true
|
||||
]);
|
||||
}
|
||||
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
} catch (InvalidTokenException $e) {
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
* @PublicPage
|
||||
*
|
||||
* @AnonRateThrottle(limit=10, period=300)
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function wipeDone(string $token): JSONResponse {
|
||||
try {
|
||||
if ($this->remoteWipe->finish($token)) {
|
||||
return new JSONResponse([]);
|
||||
}
|
||||
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
} catch (InvalidTokenException $e) {
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2019, 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 OC\Authentication\Exceptions;
|
||||
|
||||
use OC\Authentication\Token\IToken;
|
||||
|
||||
class WipeTokenException extends InvalidTokenException {
|
||||
/** @var IToken */
|
||||
private $token;
|
||||
|
||||
public function __construct(IToken $token) {
|
||||
parent::__construct();
|
||||
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function getToken(): IToken {
|
||||
return $this->token;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2019 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\Notifications;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use OCP\L10N\IFactory as IL10nFactory;
|
||||
use OCP\Notification\INotification;
|
||||
use OCP\Notification\INotifier;
|
||||
|
||||
class Notifier implements INotifier {
|
||||
|
||||
/** @var IL10nFactory */
|
||||
private $factory;
|
||||
|
||||
public function __construct(IL10nFactory $l10nFactory) {
|
||||
$this->factory = $l10nFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function prepare(INotification $notification, $languageCode) {
|
||||
if ($notification->getApp() !== 'auth') {
|
||||
// Not my app => throw
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
|
||||
// Read the language from the notification
|
||||
$l = $this->factory->get('lib', $languageCode);
|
||||
|
||||
switch ($notification->getSubject()) {
|
||||
case 'remote_wipe_start':
|
||||
$notification->setParsedSubject(
|
||||
$l->t('Remote wipe started')
|
||||
)->setParsedMessage(
|
||||
$l->t('A remote wipe was started on device %s', $notification->getSubjectParameters())
|
||||
);
|
||||
|
||||
return $notification;
|
||||
case 'remote_wipe_finish':
|
||||
$notification->setParsedSubject(
|
||||
$l->t('Remote wipe finished')
|
||||
)->setParsedMessage(
|
||||
$l->t('The remote wipe on %s has finished', $notification->getSubjectParameters())
|
||||
);
|
||||
|
||||
return $notification;
|
||||
default:
|
||||
// Unknown subject => Unknown notification => throw
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2019, 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 OC\Authentication\Token;
|
||||
|
||||
interface IWipeableToken {
|
||||
public function wipe(): void;
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2019 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\Token;
|
||||
|
||||
use BadMethodCallException;
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
use OC\Authentication\Exceptions\WipeTokenException;
|
||||
use OCP\Activity\IManager as IActivityManager;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\ILogger;
|
||||
use OCP\Notification\IManager as INotificationManager;
|
||||
|
||||
class RemoteWipe {
|
||||
|
||||
/** @var IProvider */
|
||||
private $tokenProvider;
|
||||
|
||||
/** @var IActivityManager */
|
||||
private $activityManager;
|
||||
|
||||
/** @var INotificationManager */
|
||||
private $notificationManager;
|
||||
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
public function __construct(IProvider $tokenProvider,
|
||||
IActivityManager $activityManager,
|
||||
INotificationManager $notificationManager,
|
||||
ITimeFactory $timeFactory,
|
||||
ILogger $logger) {
|
||||
$this->tokenProvider = $tokenProvider;
|
||||
$this->activityManager = $activityManager;
|
||||
$this->notificationManager = $notificationManager;
|
||||
$this->timeFactory = $timeFactory;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
*
|
||||
* @return bool whether wiping was started
|
||||
* @throws InvalidTokenException
|
||||
*
|
||||
*/
|
||||
public function start(string $token): bool {
|
||||
try {
|
||||
$this->tokenProvider->getToken($token);
|
||||
|
||||
// We expect a WipedTokenException here. If we reach this point this
|
||||
// is an ordinary token
|
||||
return false;
|
||||
} catch (WipeTokenException $e) {
|
||||
// Expected -> continue below
|
||||
}
|
||||
|
||||
$dbToken = $e->getToken();
|
||||
|
||||
$this->logger->info("user " . $dbToken->getUID() . " started a remote wipe");
|
||||
$this->sendNotification('remote_wipe_start', $e->getToken());
|
||||
$this->publishActivity('remote_wipe_start', $e->getToken());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
*
|
||||
* @return bool whether wiping could be finished
|
||||
* @throws InvalidTokenException
|
||||
*/
|
||||
public function finish(string $token): bool {
|
||||
try {
|
||||
$this->tokenProvider->getToken($token);
|
||||
|
||||
// We expect a WipedTokenException here. If we reach this point this
|
||||
// is an ordinary token
|
||||
return false;
|
||||
} catch (WipeTokenException $e) {
|
||||
// Expected -> continue below
|
||||
}
|
||||
|
||||
$dbToken = $e->getToken();
|
||||
|
||||
$this->tokenProvider->invalidateToken($token);
|
||||
|
||||
$this->logger->info("user " . $dbToken->getUID() . " finished a remote wipe");
|
||||
$this->sendNotification('remote_wipe_finish', $e->getToken());
|
||||
$this->publishActivity('remote_wipe_finish', $e->getToken());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function publishActivity(string $event, IToken $token): void {
|
||||
$activity = $this->activityManager->generateEvent();
|
||||
$activity->setApp('core')
|
||||
->setType('security')
|
||||
->setAuthor($token->getUID())
|
||||
->setAffectedUser($token->getUID())
|
||||
->setSubject($event, [
|
||||
'name' => $token->getName(),
|
||||
]);
|
||||
try {
|
||||
$this->activityManager->publish($activity);
|
||||
} catch (BadMethodCallException $e) {
|
||||
$this->logger->warning('could not publish activity', ['app' => 'core']);
|
||||
$this->logger->logException($e, ['app' => 'core']);
|
||||
}
|
||||
}
|
||||
|
||||
private function sendNotification(string $event, IToken $token): void {
|
||||
$notification = $this->notificationManager->createNotification();
|
||||
$notification->setApp('auth')
|
||||
->setUser($token->getUID())
|
||||
->setDateTime($this->timeFactory->getDateTime())
|
||||
->setObject('token', $token->getId())
|
||||
->setSubject($event, [
|
||||
'name' => $token->getName(),
|
||||
]);
|
||||
$this->notificationManager->notify($notification);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2019, 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 Tests\Core\Controller;
|
||||
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
use OC\Authentication\Token\RemoteWipe;
|
||||
use OC\Core\Controller\WipeController;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\IRequest;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class WipeControllerTest extends TestCase {
|
||||
|
||||
/** @var RemoteWipe|MockObject */
|
||||
private $remoteWipe;
|
||||
|
||||
/** @var WipeController */
|
||||
private $controller;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->remoteWipe = $this->createMock(RemoteWipe::class);
|
||||
$this->controller = new WipeController(
|
||||
'core',
|
||||
$this->createMock(IRequest::class),
|
||||
$this->remoteWipe);
|
||||
}
|
||||
|
||||
public function dataTest() {
|
||||
return [
|
||||
// valid token, could perform operation, valid result
|
||||
[ true, true, true],
|
||||
[ true, false, false],
|
||||
[false, true, false],
|
||||
[false, false, false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $valid
|
||||
* @param bool $couldPerform
|
||||
* @param bool $result
|
||||
*
|
||||
* @dataProvider dataTest
|
||||
*/
|
||||
public function testCheckWipe(bool $valid, bool $couldPerform, bool $result) {
|
||||
if (!$valid) {
|
||||
$this->remoteWipe->method('start')
|
||||
->with('mytoken')
|
||||
->willThrowException(new InvalidTokenException());
|
||||
} else {
|
||||
$this->remoteWipe->method('start')
|
||||
->with('mytoken')
|
||||
->willReturn($couldPerform);
|
||||
}
|
||||
|
||||
$result = $this->controller->checkWipe('mytoken');
|
||||
|
||||
if (!$valid || !$couldPerform) {
|
||||
$this->assertSame(Http::STATUS_NOT_FOUND, $result->getStatus());
|
||||
$this->assertSame([], $result->getData());
|
||||
} else {
|
||||
$this->assertSame(Http::STATUS_OK, $result->getStatus());
|
||||
$this->assertSame(['wipe' => true], $result->getData());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $valid
|
||||
* @param bool $couldPerform
|
||||
* @param bool $result
|
||||
*
|
||||
* @dataProvider dataTest
|
||||
*/
|
||||
public function testWipeDone(bool $valid, bool $couldPerform, bool $result) {
|
||||
if (!$valid) {
|
||||
$this->remoteWipe->method('finish')
|
||||
->with('mytoken')
|
||||
->willThrowException(new InvalidTokenException());
|
||||
} else {
|
||||
$this->remoteWipe->method('finish')
|
||||
->with('mytoken')
|
||||
->willReturn($couldPerform);
|
||||
}
|
||||
|
||||
$result = $this->controller->wipeDone('mytoken');
|
||||
|
||||
if (!$valid || !$couldPerform) {
|
||||
$this->assertSame(Http::STATUS_NOT_FOUND, $result->getStatus());
|
||||
$this->assertSame([], $result->getData());
|
||||
} else {
|
||||
$this->assertSame(Http::STATUS_OK, $result->getStatus());
|
||||
$this->assertSame([], $result->getData());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue