Provide initial state

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
This commit is contained in:
Roeland Jago Douma 2019-01-17 12:30:47 +01:00
parent 139055c1dd
commit f30877ea7c
No known key found for this signature in database
GPG Key ID: F941078878347C0C
20 changed files with 223 additions and 21 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -29,6 +29,7 @@ use OCA\TwoFactorBackupCodes\Settings\Personal;
use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings; use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings;
use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IProvider;
use OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings; use OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings;
use OCP\IInitialStateService;
use OCP\IL10N; use OCP\IL10N;
use OCP\IUser; use OCP\IUser;
use OCP\Template; use OCP\Template;
@ -46,6 +47,8 @@ class BackupCodesProvider implements IProvider, IProvidesPersonalSettings {
/** @var AppManager */ /** @var AppManager */
private $appManager; private $appManager;
/** @var IInitialStateService */
private $initialStateService;
/** /**
* @param string $appName * @param string $appName
@ -53,11 +56,16 @@ class BackupCodesProvider implements IProvider, IProvidesPersonalSettings {
* @param IL10N $l10n * @param IL10N $l10n
* @param AppManager $appManager * @param AppManager $appManager
*/ */
public function __construct(string $appName, BackupCodeStorage $storage, IL10N $l10n, AppManager $appManager) { public function __construct(string $appName,
BackupCodeStorage $storage,
IL10N $l10n,
AppManager $appManager,
IInitialStateService $initialStateService) {
$this->appName = $appName; $this->appName = $appName;
$this->l10n = $l10n; $this->l10n = $l10n;
$this->storage = $storage; $this->storage = $storage;
$this->appManager = $appManager; $this->appManager = $appManager;
$this->initialStateService = $initialStateService;
} }
/** /**
@ -149,7 +157,8 @@ class BackupCodesProvider implements IProvider, IProvidesPersonalSettings {
*/ */
public function getPersonalSettings(IUser $user): IPersonalProviderSettings { public function getPersonalSettings(IUser $user): IPersonalProviderSettings {
$state = $this->storage->getBackupCodesState($user); $state = $this->storage->getBackupCodesState($user);
return new Personal(base64_encode(json_encode($state))); $this->initialStateService->provideInitialState($this->appName, $state);
return new Personal();
} }
} }

View File

@ -28,18 +28,8 @@ use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings;
use OCP\Template; use OCP\Template;
class Personal implements IPersonalProviderSettings { class Personal implements IPersonalProviderSettings {
/** @var string */
private $state;
public function __construct(string $state) {
$this->state = $state;
}
public function getBody(): Template { public function getBody(): Template {
$template = new Template('twofactor_backupcodes', 'personal'); return new Template('twofactor_backupcodes', 'personal');
$template->assign('state', $this->state);
return $template;
} }
} }

View File

@ -4,9 +4,9 @@ import store from './store';
Vue.prototype.t = t; Vue.prototype.t = t;
const initialStateElem = document.getElementById('twofactor-backupcodes-initial-state'); const initialState = OCP.InitialState.loadState('twofactor_backupcodes');
store.replaceState( store.replaceState(
JSON.parse(atob(initialStateElem.value)) initialState
) )
const View = Vue.extend(PersonalSettings) const View = Vue.extend(PersonalSettings)

View File

@ -25,6 +25,7 @@ namespace OCA\TwoFactorBackupCodes\Tests\Unit\Provider;
use OC\App\AppManager; use OC\App\AppManager;
use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider; use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider;
use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage; use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage;
use OCP\IInitialStateService;
use OCP\IL10N; use OCP\IL10N;
use OCP\IUser; use OCP\IUser;
use OCP\Template; use OCP\Template;
@ -45,6 +46,9 @@ class BackupCodesProviderTest extends TestCase {
/** @var AppManager|PHPUnit_Framework_MockObject_MockObject */ /** @var AppManager|PHPUnit_Framework_MockObject_MockObject */
private $appManager; private $appManager;
/** @var IInitialStateService|PHPUnit_Framework_MockObject_MockObject */
private $initialState;
/** @var BackupCodesProvider */ /** @var BackupCodesProvider */
private $provider; private $provider;
@ -55,8 +59,9 @@ class BackupCodesProviderTest extends TestCase {
$this->storage = $this->createMock(BackupCodeStorage::class); $this->storage = $this->createMock(BackupCodeStorage::class);
$this->l10n = $this->createMock(IL10N::class); $this->l10n = $this->createMock(IL10N::class);
$this->appManager = $this->createMock(AppManager::class); $this->appManager = $this->createMock(AppManager::class);
$this->initialState = $this->createMock(IInitialStateService::class);
$this->provider = new BackupCodesProvider($this->appName, $this->storage, $this->l10n, $this->appManager); $this->provider = new BackupCodesProvider($this->appName, $this->storage, $this->l10n, $this->appManager, $this->initialState);
} }
public function testGetId() { public function testGetId() {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,8 +2,10 @@
* *
*/ */
import loader from './loader' import loader from './loader'
import initialState from './initialstate'
/** @namespace OCP */ /** @namespace OCP */
export default { export default {
Loader: loader, Loader: loader,
InitialState: initialState,
}; };

View File

@ -0,0 +1,42 @@
/*
* @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 OCP
* @class InitialState
*/
export default {
loadState: function(app) {
const elem = document.querySelector('#initial-state-' + app);
if (elem === null) {
console.error('Could not find initial state of ' + app);
throw new Error('Could not find initial state of ' + app);
}
try {
return JSON.parse(atob(elem.value));
} catch (e) {
console.error('Could not parse initial state of ' + app);
throw new Error('Could not parse initial state of ' + app);
}
},
}

View File

@ -18,6 +18,9 @@
</head> </head>
<body id="body-public" class="layout-base"> <body id="body-public" class="layout-base">
<?php include 'layout.noscript.warning.php'; ?> <?php include 'layout.noscript.warning.php'; ?>
<?php foreach ($_['initialStates'] as $app => $initialState) { ?>
<input type="hidden" id="initial-state-<?php p($app); ?>" value="<?php p(base64_encode($initialState)); ?>">
<?php }?>
<div id="content" class="app-public" role="main"> <div id="content" class="app-public" role="main">
<?php print_unescaped($_['content']); ?> <?php print_unescaped($_['content']); ?>
</div> </div>

View File

@ -20,6 +20,9 @@
</head> </head>
<body id="<?php p($_['bodyid']);?>"> <body id="<?php p($_['bodyid']);?>">
<?php include 'layout.noscript.warning.php'; ?> <?php include 'layout.noscript.warning.php'; ?>
<?php foreach ($_['initialStates'] as $app => $initialState) { ?>
<input type="hidden" id="initial-state-<?php p($app); ?>" value="<?php p(base64_encode($initialState)); ?>">
<?php }?>
<div class="wrapper"> <div class="wrapper">
<div class="v-align"> <div class="v-align">
<?php if ($_['bodyid'] === 'body-login' ): ?> <?php if ($_['bodyid'] === 'body-login' ): ?>

View File

@ -27,6 +27,9 @@
</head> </head>
<body id="<?php p($_['bodyid']);?>"> <body id="<?php p($_['bodyid']);?>">
<?php include('layout.noscript.warning.php'); ?> <?php include('layout.noscript.warning.php'); ?>
<?php foreach ($_['initialStates'] as $app => $initialState) { ?>
<input type="hidden" id="initial-state-<?php p($app); ?>" value="<?php p(base64_encode($initialState)); ?>">
<?php }?>
<div id="notification-container"> <div id="notification-container">
<div id="notification"></div> <div id="notification"></div>
</div> </div>

View File

@ -28,6 +28,10 @@
<body id="<?php p($_['bodyid']);?>"> <body id="<?php p($_['bodyid']);?>">
<?php include 'layout.noscript.warning.php'; ?> <?php include 'layout.noscript.warning.php'; ?>
<?php foreach ($_['initialStates'] as $app => $initialState) { ?>
<input type="hidden" id="initial-state-<?php p($app); ?>" value="<?php p(base64_encode($initialState)); ?>">
<?php }?>
<a href="#app-content" class="button primary skip-navigation skip-content"><?php p($l->t('Skip to main content')); ?></a> <a href="#app-content" class="button primary skip-navigation skip-content"><?php p($l->t('Skip to main content')); ?></a>
<a href="#app-navigation" class="button primary skip-navigation"><?php p($l->t('Skip to navigation of app')); ?></a> <a href="#app-navigation" class="button primary skip-navigation"><?php p($l->t('Skip to navigation of app')); ?></a>

View File

@ -284,6 +284,7 @@ return array(
'OCP\\IGroup' => $baseDir . '/lib/public/IGroup.php', 'OCP\\IGroup' => $baseDir . '/lib/public/IGroup.php',
'OCP\\IGroupManager' => $baseDir . '/lib/public/IGroupManager.php', 'OCP\\IGroupManager' => $baseDir . '/lib/public/IGroupManager.php',
'OCP\\IImage' => $baseDir . '/lib/public/IImage.php', 'OCP\\IImage' => $baseDir . '/lib/public/IImage.php',
'OCP\\IInitialStateService' => $baseDir . '/lib/public/IInitialStateService.php',
'OCP\\IL10N' => $baseDir . '/lib/public/IL10N.php', 'OCP\\IL10N' => $baseDir . '/lib/public/IL10N.php',
'OCP\\ILogger' => $baseDir . '/lib/public/ILogger.php', 'OCP\\ILogger' => $baseDir . '/lib/public/ILogger.php',
'OCP\\IMemcache' => $baseDir . '/lib/public/IMemcache.php', 'OCP\\IMemcache' => $baseDir . '/lib/public/IMemcache.php',
@ -854,6 +855,7 @@ return array(
'OC\\Http\\Client\\ClientService' => $baseDir . '/lib/private/Http/Client/ClientService.php', 'OC\\Http\\Client\\ClientService' => $baseDir . '/lib/private/Http/Client/ClientService.php',
'OC\\Http\\Client\\Response' => $baseDir . '/lib/private/Http/Client/Response.php', 'OC\\Http\\Client\\Response' => $baseDir . '/lib/private/Http/Client/Response.php',
'OC\\Http\\CookieHelper' => $baseDir . '/lib/private/Http/CookieHelper.php', 'OC\\Http\\CookieHelper' => $baseDir . '/lib/private/Http/CookieHelper.php',
'OC\\InitialStateService' => $baseDir . '/lib/private/InitialStateService.php',
'OC\\Installer' => $baseDir . '/lib/private/Installer.php', 'OC\\Installer' => $baseDir . '/lib/private/Installer.php',
'OC\\IntegrityCheck\\Checker' => $baseDir . '/lib/private/IntegrityCheck/Checker.php', 'OC\\IntegrityCheck\\Checker' => $baseDir . '/lib/private/IntegrityCheck/Checker.php',
'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException' => $baseDir . '/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php', 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException' => $baseDir . '/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php',

View File

@ -314,6 +314,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\IGroup' => __DIR__ . '/../../..' . '/lib/public/IGroup.php', 'OCP\\IGroup' => __DIR__ . '/../../..' . '/lib/public/IGroup.php',
'OCP\\IGroupManager' => __DIR__ . '/../../..' . '/lib/public/IGroupManager.php', 'OCP\\IGroupManager' => __DIR__ . '/../../..' . '/lib/public/IGroupManager.php',
'OCP\\IImage' => __DIR__ . '/../../..' . '/lib/public/IImage.php', 'OCP\\IImage' => __DIR__ . '/../../..' . '/lib/public/IImage.php',
'OCP\\IInitialStateService' => __DIR__ . '/../../..' . '/lib/public/IInitialStateService.php',
'OCP\\IL10N' => __DIR__ . '/../../..' . '/lib/public/IL10N.php', 'OCP\\IL10N' => __DIR__ . '/../../..' . '/lib/public/IL10N.php',
'OCP\\ILogger' => __DIR__ . '/../../..' . '/lib/public/ILogger.php', 'OCP\\ILogger' => __DIR__ . '/../../..' . '/lib/public/ILogger.php',
'OCP\\IMemcache' => __DIR__ . '/../../..' . '/lib/public/IMemcache.php', 'OCP\\IMemcache' => __DIR__ . '/../../..' . '/lib/public/IMemcache.php',
@ -884,6 +885,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Http\\Client\\ClientService' => __DIR__ . '/../../..' . '/lib/private/Http/Client/ClientService.php', 'OC\\Http\\Client\\ClientService' => __DIR__ . '/../../..' . '/lib/private/Http/Client/ClientService.php',
'OC\\Http\\Client\\Response' => __DIR__ . '/../../..' . '/lib/private/Http/Client/Response.php', 'OC\\Http\\Client\\Response' => __DIR__ . '/../../..' . '/lib/private/Http/Client/Response.php',
'OC\\Http\\CookieHelper' => __DIR__ . '/../../..' . '/lib/private/Http/CookieHelper.php', 'OC\\Http\\CookieHelper' => __DIR__ . '/../../..' . '/lib/private/Http/CookieHelper.php',
'OC\\InitialStateService' => __DIR__ . '/../../..' . '/lib/private/InitialStateService.php',
'OC\\Installer' => __DIR__ . '/../../..' . '/lib/private/Installer.php', 'OC\\Installer' => __DIR__ . '/../../..' . '/lib/private/Installer.php',
'OC\\IntegrityCheck\\Checker' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Checker.php', 'OC\\IntegrityCheck\\Checker' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Checker.php',
'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php', 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php',

View File

@ -0,0 +1,74 @@
<?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;
use OCP\IInitialStateService;
use OCP\ILogger;
class InitialStateService implements IInitialStateService {
/** @var ILogger */
private $logger;
/** @var array */
private $states = [];
/** @var array */
private $lazyStates = [];
public function __construct(ILogger $logger) {
$this->logger = $logger;
}
public function provideInitialState(string $appName, $data) {
// Scalars and JsonSerializable are fine
if (is_scalar($data) || $data instanceof \JsonSerializable || is_array($data)) {
$this->states[$appName] = json_encode($data);
return;
}
$this->logger->warning('Invalid data provided to provideInitialState by ' . $appName);
}
public function provideLazyInitialState(string $appName, \Closure $closure) {
$this->lazyStates[$appName] = $closure;
}
public function getInitialStates(): array {
$states = $this->states;
foreach ($this->lazyStates as $app => $lazyState) {
$state = $lazyState();
if (!($lazyState instanceof \JsonSerializable)) {
$this->logger->warning($app . ' provided an invalid lazy state');
}
$states[$app] = json_encode($state);
}
return $states;
}
}

View File

@ -144,6 +144,7 @@ use OCP\GlobalScale\IConfig;
use OCP\Group\ISubAdmin; use OCP\Group\ISubAdmin;
use OCP\ICacheFactory; use OCP\ICacheFactory;
use OCP\IDBConnection; use OCP\IDBConnection;
use OCP\IInitialStateService;
use OCP\IL10N; use OCP\IL10N;
use OCP\IServerContainer; use OCP\IServerContainer;
use OCP\ITempManager; use OCP\ITempManager;
@ -1204,6 +1205,8 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(ISubAdmin::class, SubAdmin::class); $this->registerAlias(ISubAdmin::class, SubAdmin::class);
$this->registerAlias(IInitialStateService::class, InitialStateService::class);
$this->connectDispatcher(); $this->connectDispatcher();
} }

View File

@ -220,6 +220,10 @@ class TemplateLayout extends \OC_Template {
} }
} }
/** @var InitialStateService $initialState */
$initialState = \OC::$server->query(InitialStateService::class);
$this->assign('initialStates', $initialState->getInitialStates());
} }
/** /**

View File

@ -0,0 +1,56 @@
<?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 OCP;
/**
* @since 16.0.0
*/
interface IInitialStateService {
/**
* Allows an app to provide its initial state to the template system.
* Use this if you know your initial state sill be used for example if
* you are in the render function of you controller.
*
* @since 16.0.0
*
* @param string $appName
* @param bool|int|float|string|array|\JsonSerializable $data
*/
public function provideInitialState(string $appName, $data);
/**
* Allows an app to provide its initial state via a lazy method.
* This will call the closure when the template is being generated.
* Use this if your app is injected into pages. Since then the render method
* is not called explicitly. But we do not want to load the state on webdav
* requests for example.
*
* @since 16.0.0
*
* @param string $appName
* @param \Closure $closure Has to return an object that implements JsonSerializable
*/
public function provideLazyInitialState(string $appName, \Closure $closure);
}