Merge pull request #17076 from nextcloud/enh/settings/personal/security

Split personal security settings in code
This commit is contained in:
Roeland Jago Douma 2019-09-10 21:16:54 +02:00 committed by GitHub
commit 902bc3e473
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 289 additions and 155 deletions

View File

@ -1167,6 +1167,7 @@ return array(
'OC\\Settings\\Personal\\Additional' => $baseDir . '/settings/Settings/Personal/Additional.php',
'OC\\Settings\\Personal\\PersonalInfo' => $baseDir . '/settings/Settings/Personal/PersonalInfo.php',
'OC\\Settings\\Personal\\Security' => $baseDir . '/settings/Settings/Personal/Security.php',
'OC\\Settings\\Personal\\Security\\Authtokens' => $baseDir . '/settings/Settings/Personal/Security/Authtokens.php',
'OC\\Settings\\Personal\\ServerDevNotice' => $baseDir . '/settings/Settings/Personal/ServerDevNotice.php',
'OC\\Settings\\Section' => $baseDir . '/lib/private/Settings/Section.php',
'OC\\Setup' => $baseDir . '/lib/private/Setup.php',

View File

@ -1201,6 +1201,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Settings\\Personal\\Additional' => __DIR__ . '/../../..' . '/settings/Settings/Personal/Additional.php',
'OC\\Settings\\Personal\\PersonalInfo' => __DIR__ . '/../../..' . '/settings/Settings/Personal/PersonalInfo.php',
'OC\\Settings\\Personal\\Security' => __DIR__ . '/../../..' . '/settings/Settings/Personal/Security.php',
'OC\\Settings\\Personal\\Security\\Authtokens' => __DIR__ . '/../../..' . '/settings/Settings/Personal/Security/Authtokens.php',
'OC\\Settings\\Personal\\ServerDevNotice' => __DIR__ . '/../../..' . '/settings/Settings/Personal/ServerDevNotice.php',
'OC\\Settings\\Section' => __DIR__ . '/../../..' . '/lib/private/Settings/Section.php',
'OC\\Setup' => __DIR__ . '/../../..' . '/lib/private/Setup.php',

View File

@ -298,6 +298,10 @@ class Manager implements IManager {
/** @var ISettings $form */
$form = $this->container->query(Personal\Security::class);
$forms[$form->getPriority()] = [$form];
/** @var ISettings $form */
$form = $this->container->query(Personal\Security\Authtokens::class);
$forms[$form->getPriority()] = [$form];
}
if ($section === 'additional') {
/** @var ISettings $form */

View File

@ -49,69 +49,37 @@ class Security implements ISettings {
/** @var IUserManager */
private $userManager;
/** @var TwoFactorManager */
private $twoFactorManager;
/** @var IAuthTokenProvider */
private $tokenProvider;
/** @var ProviderLoader */
private $providerLoader;
/** @var IUserSession */
private $userSession;
/** @var ISession */
private $session;
/** @var IInitialStateService */
private $initialStateService;
/**
* @var string|null
*/
/** @var string|null */
private $uid;
/**
*@var IConfig
*/
/** @var IConfig */
private $config;
public function __construct(IUserManager $userManager,
TwoFactorManager $providerManager,
IAuthTokenProvider $tokenProvider,
ProviderLoader $providerLoader,
IUserSession $userSession,
ISession $session,
IConfig $config,
IInitialStateService $initialStateService,
?string $UserId) {
$this->userManager = $userManager;
$this->twoFactorManager = $providerManager;
$this->tokenProvider = $tokenProvider;
$this->providerLoader = $providerLoader;
$this->userSession = $userSession;
$this->session = $session;
$this->initialStateService = $initialStateService;
$this->uid = $UserId;
$this->config = $config;
}
/**
* @return TemplateResponse returns the instance with all parameters set, ready to be rendered
* @since 9.1
*/
public function getForm() {
public function getForm(): TemplateResponse {
$user = $this->userManager->get($this->uid);
$passwordChangeSupported = false;
if ($user !== null) {
$passwordChangeSupported = $user->canChangePassword();
}
$this->initialStateService->provideInitialState(
'settings',
'app_tokens',
$this->getAppTokens()
);
return new TemplateResponse('settings', 'settings/personal/security', [
'passwordChangeSupported' => $passwordChangeSupported,
'twoFactorProviderData' => $this->getTwoFactorProviderData(),
@ -119,23 +87,11 @@ class Security implements ISettings {
]);
}
/**
* @return string the section ID, e.g. 'sharing'
* @since 9.1
*/
public function getSection() {
public function getSection(): string {
return 'security';
}
/**
* @return int whether the form should be rather on the top or bottom of
* the admin section. The forms are arranged in ascending order of the
* priority values. It is required to return a value between 0 and 100.
*
* E.g.: 70
* @since 9.1
*/
public function getPriority() {
public function getPriority(): int {
return 10;
}
@ -157,32 +113,4 @@ class Security implements ISettings {
}))
];
}
private function getAppTokens(): array {
$tokens = $this->tokenProvider->getTokenByUser($this->uid);
try {
$sessionId = $this->session->getId();
} catch (SessionNotAvailableException $ex) {
return [];
}
try {
$sessionToken = $this->tokenProvider->getToken($sessionId);
} catch (InvalidTokenException $ex) {
return [];
}
return array_map(function (IToken $token) use ($sessionToken) {
$data = $token->jsonSerialize();
$data['canDelete'] = true;
$data['canRename'] = $token instanceof INamedToken;
if ($sessionToken->getId() === $token->getId()) {
$data['canDelete'] = false;
$data['canRename'] = false;
$data['current'] = true;
}
return $data;
}, $tokens);
}
}

View File

@ -0,0 +1,107 @@
<?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\Settings\Personal\Security;
use function array_map;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Token\INamedToken;
use OC\Authentication\Token\IProvider as IAuthTokenProvider;
use OC\Authentication\Token\IToken;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IInitialStateService;
use OCP\ISession;
use OCP\Session\Exceptions\SessionNotAvailableException;
use OCP\Settings\ISettings;
class Authtokens implements ISettings {
/** @var IAuthTokenProvider */
private $tokenProvider;
/** @var ISession */
private $session;
/** @var IInitialStateService */
private $initialStateService;
/** @var string|null */
private $uid;
public function __construct(IAuthTokenProvider $tokenProvider,
ISession $session,
IInitialStateService $initialStateService,
?string $UserId) {
$this->tokenProvider = $tokenProvider;
$this->session = $session;
$this->initialStateService = $initialStateService;
$this->uid = $UserId;
}
public function getForm(): TemplateResponse {
$this->initialStateService->provideInitialState(
'settings',
'app_tokens',
$this->getAppTokens()
);
return new TemplateResponse('settings', 'settings/personal/security/authtokens');
}
public function getSection(): string {
return 'security';
}
public function getPriority(): int {
return 100;
}
private function getAppTokens(): array {
$tokens = $this->tokenProvider->getTokenByUser($this->uid);
try {
$sessionId = $this->session->getId();
} catch (SessionNotAvailableException $ex) {
return [];
}
try {
$sessionToken = $this->tokenProvider->getToken($sessionId);
} catch (InvalidTokenException $ex) {
return [];
}
return array_map(function (IToken $token) use ($sessionToken) {
$data = $token->jsonSerialize();
$data['canDelete'] = true;
$data['canRename'] = $token instanceof INamedToken;
if ($sessionToken->getId() === $token->getId()) {
$data['canDelete'] = false;
$data['canRename'] = false;
$data['current'] = true;
}
return $data;
}, $tokens);
}
}

View File

@ -904,5 +904,5 @@ function y(){throw new Error("Dynamic requires are not currently supported by ro
* 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/>.
*/
i.nc=btoa(OC.requestToken),r.default.use(a.a),r.default.use(s.a,{defaultHtml:!1}),r.default.prototype.t=t,new(r.default.extend(N))({propsData:{tokens:OCP.InitialState.loadState("settings","app_tokens")}}).$mount("#security")}]);
//# sourceMappingURL=vue-settings-personal-security.js.map?v=bedaaf1d6e26dcd2fca0
i.nc=btoa(OC.requestToken),r.default.use(a.a),r.default.use(s.a,{defaultHtml:!1}),r.default.prototype.t=t,new(r.default.extend(N))({propsData:{tokens:OCP.InitialState.loadState("settings","app_tokens")}}).$mount("#security-authtokens")}]);
//# sourceMappingURL=vue-settings-personal-security.js.map?v=a2158eef5a0e4bafeee7

File diff suppressed because one or more lines are too long

View File

@ -36,4 +36,4 @@ new View({
propsData: {
tokens: OCP.InitialState.loadState('settings', 'app_tokens'),
}
}).$mount('#security');
}).$mount('#security-authtokens');

View File

@ -104,4 +104,3 @@ if($_['passwordChangeSupported']) {
</ul>
</div>
<div id="security" class="section"></div>

View File

@ -0,0 +1,31 @@
<?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/>.
*
*/
script('settings', [
'vue-settings-personal-security',
]);
?>
<div id="security-authtokens" class="section"></div>

View File

@ -0,0 +1,124 @@
<?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 Test\Settings\Personal\Security;
use OC\Authentication\Token\DefaultToken;
use OC\Authentication\Token\IProvider as IAuthTokenProvider;
use OC\Settings\Personal\Security;
use OC\Settings\Personal\Security\Authtokens;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IInitialStateService;
use OCP\ISession;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class AuthtokensTest extends TestCase {
/** @var IAuthTokenProvider|MockObject */
private $authTokenProvider;
/** @var ISession|MockObject */
private $session;
/** @var IInitialStateService|MockObject */
private $initialStateService;
/** @var string */
private $uid;
/** @var Security\Authtokens */
private $section;
public function setUp() {
parent::setUp();
$this->authTokenProvider = $this->createMock(IAuthTokenProvider::class);
$this->session = $this->createMock(ISession::class);
$this->initialStateService = $this->createMock(IInitialStateService::class);
$this->uid = 'test123';
$this->section = new Authtokens(
$this->authTokenProvider,
$this->session,
$this->initialStateService,
$this->uid
);
}
public function testGetForm() {
$token1 = new DefaultToken();
$token1->setId(100);
$token2 = new DefaultToken();
$token2->setId(200);
$tokens = [
$token1,
$token2,
];
$sessionToken = new DefaultToken();
$sessionToken->setId(100);
$this->authTokenProvider->expects($this->once())
->method('getTokenByUser')
->with($this->uid)
->willReturn($tokens);
$this->session->expects($this->once())
->method('getId')
->willReturn('session123');
$this->authTokenProvider->expects($this->once())
->method('getToken')
->with('session123')
->willReturn($sessionToken);
$this->initialStateService->expects($this->once())
->method('provideInitialState')
->with('settings', 'app_tokens', [
[
'id' => 100,
'name' => null,
'lastActivity' => 0,
'type' => 0,
'canDelete' => false,
'current' => true,
'scope' => ['filesystem' => true],
'canRename' => false,
],
[
'id' => 200,
'name' => null,
'lastActivity' => 0,
'type' => 0,
'canDelete' => true,
'scope' => ['filesystem' => true],
'canRename' => true,
],
]);
$form = $this->section->getForm();
$expected = new TemplateResponse('settings', 'settings/personal/security/authtokens');
$this->assertEquals($expected, $form);
}
}

View File

@ -244,15 +244,24 @@ class ManagerTest extends TestCase {
$section->expects($this->once())
->method('getPriority')
->willReturn(16);
$this->container->expects($this->once())
$section2 = $this->createMock(Security\Authtokens::class);
$section2->expects($this->once())
->method('getPriority')
->willReturn(100);
$this->container->expects($this->at(0))
->method('query')
->with(Security::class)
->willReturn($section);
$this->container->expects($this->at(1))
->method('query')
->with(Security\Authtokens::class)
->willReturn($section2);
$settings = $this->manager->getPersonalSettings('security');
$this->assertEquals([
16 => [$section]
16 => [$section],
100 => [$section2],
], $settings);
}

View File

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
/**
@ -25,15 +24,10 @@ declare(strict_types=1);
namespace Test\Settings\Personal;
use OC\Authentication\Token\DefaultToken;
use OC\Authentication\Token\IProvider as IAuthTokenProvider;
use OC\Authentication\TwoFactorAuth\Manager as TwoFactorManager;
use OC\Authentication\TwoFactorAuth\ProviderLoader;
use OC\Settings\Personal\Security;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IInitialStateService;
use OCP\ISession;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
@ -45,27 +39,15 @@ class SecurityTest extends TestCase {
/** @var IUserManager|MockObject */
private $userManager;
/** @var TwoFactorManager|MockObject */
private $twoFactorManager;
/** @var IAuthTokenProvider|MockObject */
private $authTokenProvider;
/** @var ProviderLoader|MockObject */
private $providerLoader;
/** @var IUserSession|MockObject */
private $userSession;
/** @var ISession|MockObject */
private $session;
/** @var IConfig|MockObject */
private $config;
/** @var IInitialStateService|MockObject */
private $initialStateService;
/** @var string */
private $uid;
@ -76,39 +58,21 @@ class SecurityTest extends TestCase {
parent::setUp();
$this->userManager = $this->createMock(IUserManager::class);
$this->twoFactorManager = $this->createMock(TwoFactorManager::class);
$this->authTokenProvider = $this->createMock(IAuthTokenProvider::class);
$this->providerLoader = $this->createMock(ProviderLoader::class);
$this->userSession = $this->createMock(IUserSession::class);
$this->config = $this->createMock(IConfig::class);
$this->session = $this->createMock(ISession::class);
$this->initialStateService = $this->createMock(IInitialStateService::class);
$this->uid = 'test123';
$this->section = new Security(
$this->userManager,
$this->twoFactorManager,
$this->authTokenProvider,
$this->providerLoader,
$this->userSession,
$this->session,
$this->config,
$this->initialStateService,
$this->uid
);
}
public function testGetForm() {
$token1 = new DefaultToken();
$token1->setId(100);
$token2 = new DefaultToken();
$token2->setId(200);
$tokens = [
$token1,
$token2,
];
$sessionToken = new DefaultToken();
$sessionToken->setId(100);
$user = $this->createMock(IUser::class);
$this->userManager->expects($this->once())
->method('get')
@ -117,40 +81,6 @@ class SecurityTest extends TestCase {
$user->expects($this->once())
->method('canChangePassword')
->willReturn(true);
$this->authTokenProvider->expects($this->once())
->method('getTokenByUser')
->with($this->uid)
->willReturn($tokens);
$this->session->expects($this->once())
->method('getId')
->willReturn('session123');
$this->authTokenProvider->expects($this->once())
->method('getToken')
->with('session123')
->willReturn($sessionToken);
$this->initialStateService->expects($this->once())
->method('provideInitialState')
->with('settings', 'app_tokens', [
[
'id' => 100,
'name' => null,
'lastActivity' => 0,
'type' => 0,
'canDelete' => false,
'current' => true,
'scope' => ['filesystem' => true],
'canRename' => false,
],
[
'id' => 200,
'name' => null,
'lastActivity' => 0,
'type' => 0,
'canDelete' => true,
'scope' => ['filesystem' => true],
'canRename' => true,
],
]);
$this->userSession->expects($this->once())
->method('getUser')
->willReturn($user);