Merge pull request #15679 from nextcloud/feature/sub-admin-settings
Make it possible to show admin settings for sub admins
This commit is contained in:
commit
5b0e70ff77
|
@ -379,6 +379,7 @@ return array(
|
||||||
'OCP\\Settings\\IManager' => $baseDir . '/lib/public/Settings/IManager.php',
|
'OCP\\Settings\\IManager' => $baseDir . '/lib/public/Settings/IManager.php',
|
||||||
'OCP\\Settings\\ISection' => $baseDir . '/lib/public/Settings/ISection.php',
|
'OCP\\Settings\\ISection' => $baseDir . '/lib/public/Settings/ISection.php',
|
||||||
'OCP\\Settings\\ISettings' => $baseDir . '/lib/public/Settings/ISettings.php',
|
'OCP\\Settings\\ISettings' => $baseDir . '/lib/public/Settings/ISettings.php',
|
||||||
|
'OCP\\Settings\\ISubAdminSettings' => $baseDir . '/lib/public/Settings/ISubAdminSettings.php',
|
||||||
'OCP\\Share' => $baseDir . '/lib/public/Share.php',
|
'OCP\\Share' => $baseDir . '/lib/public/Share.php',
|
||||||
'OCP\\Share\\Exceptions\\GenericShareException' => $baseDir . '/lib/public/Share/Exceptions/GenericShareException.php',
|
'OCP\\Share\\Exceptions\\GenericShareException' => $baseDir . '/lib/public/Share/Exceptions/GenericShareException.php',
|
||||||
'OCP\\Share\\Exceptions\\IllegalIDChangeException' => $baseDir . '/lib/public/Share/Exceptions/IllegalIDChangeException.php',
|
'OCP\\Share\\Exceptions\\IllegalIDChangeException' => $baseDir . '/lib/public/Share/Exceptions/IllegalIDChangeException.php',
|
||||||
|
|
|
@ -409,6 +409,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
||||||
'OCP\\Settings\\IManager' => __DIR__ . '/../../..' . '/lib/public/Settings/IManager.php',
|
'OCP\\Settings\\IManager' => __DIR__ . '/../../..' . '/lib/public/Settings/IManager.php',
|
||||||
'OCP\\Settings\\ISection' => __DIR__ . '/../../..' . '/lib/public/Settings/ISection.php',
|
'OCP\\Settings\\ISection' => __DIR__ . '/../../..' . '/lib/public/Settings/ISection.php',
|
||||||
'OCP\\Settings\\ISettings' => __DIR__ . '/../../..' . '/lib/public/Settings/ISettings.php',
|
'OCP\\Settings\\ISettings' => __DIR__ . '/../../..' . '/lib/public/Settings/ISettings.php',
|
||||||
|
'OCP\\Settings\\ISubAdminSettings' => __DIR__ . '/../../..' . '/lib/public/Settings/ISubAdminSettings.php',
|
||||||
'OCP\\Share' => __DIR__ . '/../../..' . '/lib/public/Share.php',
|
'OCP\\Share' => __DIR__ . '/../../..' . '/lib/public/Share.php',
|
||||||
'OCP\\Share\\Exceptions\\GenericShareException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/GenericShareException.php',
|
'OCP\\Share\\Exceptions\\GenericShareException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/GenericShareException.php',
|
||||||
'OCP\\Share\\Exceptions\\IllegalIDChangeException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/IllegalIDChangeException.php',
|
'OCP\\Share\\Exceptions\\IllegalIDChangeException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/IllegalIDChangeException.php',
|
||||||
|
|
|
@ -56,6 +56,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
use OCP\Files\Folder;
|
use OCP\Files\Folder;
|
||||||
use OCP\Files\IAppData;
|
use OCP\Files\IAppData;
|
||||||
use OCP\GlobalScale\IConfig;
|
use OCP\GlobalScale\IConfig;
|
||||||
|
use OCP\Group\ISubAdmin;
|
||||||
use OCP\IL10N;
|
use OCP\IL10N;
|
||||||
use OCP\ILogger;
|
use OCP\ILogger;
|
||||||
use OCP\INavigationManager;
|
use OCP\INavigationManager;
|
||||||
|
@ -218,6 +219,7 @@ class DIContainer extends SimpleContainer implements IAppContainer {
|
||||||
$c['AppName'],
|
$c['AppName'],
|
||||||
$server->getUserSession()->isLoggedIn(),
|
$server->getUserSession()->isLoggedIn(),
|
||||||
$server->getGroupManager()->isAdmin($this->getUserId()),
|
$server->getGroupManager()->isAdmin($this->getUserId()),
|
||||||
|
$server->getUserSession()->getUser() !== null && $server->query(ISubAdmin::class)->isSubAdmin($server->getUserSession()->getUser()),
|
||||||
$server->getContentSecurityPolicyManager(),
|
$server->getContentSecurityPolicyManager(),
|
||||||
$server->getCsrfTokenManager(),
|
$server->getCsrfTokenManager(),
|
||||||
$server->getContentSecurityPolicyNonceManager(),
|
$server->getContentSecurityPolicyNonceManager(),
|
||||||
|
|
|
@ -82,6 +82,8 @@ class SecurityMiddleware extends Middleware {
|
||||||
private $isLoggedIn;
|
private $isLoggedIn;
|
||||||
/** @var bool */
|
/** @var bool */
|
||||||
private $isAdminUser;
|
private $isAdminUser;
|
||||||
|
/** @var bool */
|
||||||
|
private $isSubAdmin;
|
||||||
/** @var ContentSecurityPolicyManager */
|
/** @var ContentSecurityPolicyManager */
|
||||||
private $contentSecurityPolicyManager;
|
private $contentSecurityPolicyManager;
|
||||||
/** @var CsrfTokenManager */
|
/** @var CsrfTokenManager */
|
||||||
|
@ -101,6 +103,7 @@ class SecurityMiddleware extends Middleware {
|
||||||
string $appName,
|
string $appName,
|
||||||
bool $isLoggedIn,
|
bool $isLoggedIn,
|
||||||
bool $isAdminUser,
|
bool $isAdminUser,
|
||||||
|
bool $isSubAdmin,
|
||||||
ContentSecurityPolicyManager $contentSecurityPolicyManager,
|
ContentSecurityPolicyManager $contentSecurityPolicyManager,
|
||||||
CsrfTokenManager $csrfTokenManager,
|
CsrfTokenManager $csrfTokenManager,
|
||||||
ContentSecurityPolicyNonceManager $cspNonceManager,
|
ContentSecurityPolicyNonceManager $cspNonceManager,
|
||||||
|
@ -115,6 +118,7 @@ class SecurityMiddleware extends Middleware {
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
$this->isLoggedIn = $isLoggedIn;
|
$this->isLoggedIn = $isLoggedIn;
|
||||||
$this->isAdminUser = $isAdminUser;
|
$this->isAdminUser = $isAdminUser;
|
||||||
|
$this->isSubAdmin = $isSubAdmin;
|
||||||
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
|
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
|
||||||
$this->csrfTokenManager = $csrfTokenManager;
|
$this->csrfTokenManager = $csrfTokenManager;
|
||||||
$this->cspNonceManager = $cspNonceManager;
|
$this->cspNonceManager = $cspNonceManager;
|
||||||
|
@ -143,7 +147,14 @@ class SecurityMiddleware extends Middleware {
|
||||||
throw new NotLoggedInException();
|
throw new NotLoggedInException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$this->reflector->hasAnnotation('NoAdminRequired') && !$this->isAdminUser) {
|
if($this->reflector->hasAnnotation('SubAdminRequired')
|
||||||
|
&& !$this->isSubAdmin
|
||||||
|
&& !$this->isAdminUser) {
|
||||||
|
throw new NotAdminException($this->l10n->t('Logged in user must be an admin or sub admin'));
|
||||||
|
}
|
||||||
|
if(!$this->reflector->hasAnnotation('SubAdminRequired')
|
||||||
|
&& !$this->reflector->hasAnnotation('NoAdminRequired')
|
||||||
|
&& !$this->isAdminUser) {
|
||||||
throw new NotAdminException($this->l10n->t('Logged in user must be an admin'));
|
throw new NotAdminException($this->l10n->t('Logged in user must be an admin'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
namespace OC\Settings;
|
namespace OC\Settings;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
use OCP\AppFramework\QueryException;
|
use OCP\AppFramework\QueryException;
|
||||||
use OCP\IL10N;
|
use OCP\IL10N;
|
||||||
use OCP\ILogger;
|
use OCP\ILogger;
|
||||||
|
@ -38,6 +39,7 @@ use OCP\L10N\IFactory;
|
||||||
use OCP\Settings\ISettings;
|
use OCP\Settings\ISettings;
|
||||||
use OCP\Settings\IManager;
|
use OCP\Settings\IManager;
|
||||||
use OCP\Settings\ISection;
|
use OCP\Settings\ISection;
|
||||||
|
use OCP\Settings\ISubAdminSettings;
|
||||||
|
|
||||||
class Manager implements IManager {
|
class Manager implements IManager {
|
||||||
|
|
||||||
|
@ -150,10 +152,11 @@ class Manager implements IManager {
|
||||||
/**
|
/**
|
||||||
* @param string $type 'admin' or 'personal'
|
* @param string $type 'admin' or 'personal'
|
||||||
* @param string $section
|
* @param string $section
|
||||||
|
* @param Closure $filter optional filter to apply on all loaded ISettings
|
||||||
*
|
*
|
||||||
* @return ISettings[]
|
* @return ISettings[]
|
||||||
*/
|
*/
|
||||||
protected function getSettings(string $type, string $section): array {
|
protected function getSettings(string $type, string $section, Closure $filter = null): array {
|
||||||
if (!isset($this->settings[$type])) {
|
if (!isset($this->settings[$type])) {
|
||||||
$this->settings[$type] = [];
|
$this->settings[$type] = [];
|
||||||
}
|
}
|
||||||
|
@ -162,6 +165,10 @@ class Manager implements IManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->settingClasses as $class => $settingsType) {
|
foreach ($this->settingClasses as $class => $settingsType) {
|
||||||
|
if ($type !== $settingsType) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/** @var ISettings $setting */
|
/** @var ISettings $setting */
|
||||||
$setting = \OC::$server->query($class);
|
$setting = \OC::$server->query($class);
|
||||||
|
@ -175,6 +182,9 @@ class Manager implements IManager {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($filter !== null && !$filter($setting)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if ($setting->getSection() === null) {
|
if ($setting->getSection() === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -226,33 +236,44 @@ class Manager implements IManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $section
|
* @param string $section
|
||||||
|
* @param Closure $filter
|
||||||
*
|
*
|
||||||
* @return ISection[]
|
* @return ISection[]
|
||||||
*/
|
*/
|
||||||
private function getBuiltInAdminSettings($section): array {
|
private function getBuiltInAdminSettings($section, Closure $filter = null): array {
|
||||||
$forms = [];
|
$forms = [];
|
||||||
|
|
||||||
if ($section === 'overview') {
|
if ($section === 'overview') {
|
||||||
/** @var ISettings $form */
|
/** @var ISettings $form */
|
||||||
$form = $this->container->query(Admin\Overview::class);
|
$form = $this->container->query(Admin\Overview::class);
|
||||||
$forms[$form->getPriority()] = [$form];
|
if ($filter === null || $filter($form)) {
|
||||||
|
$forms[$form->getPriority()] = [$form];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($section === 'server') {
|
if ($section === 'server') {
|
||||||
/** @var ISettings $form */
|
/** @var ISettings $form */
|
||||||
$form = $this->container->query(Admin\Server::class);
|
$form = $this->container->query(Admin\Server::class);
|
||||||
$forms[$form->getPriority()] = [$form];
|
if ($filter === null || $filter($form)) {
|
||||||
|
$forms[$form->getPriority()] = [$form];
|
||||||
|
}
|
||||||
$form = $this->container->query(Admin\Mail::class);
|
$form = $this->container->query(Admin\Mail::class);
|
||||||
$forms[$form->getPriority()] = [$form];
|
if ($filter === null || $filter($form)) {
|
||||||
|
$forms[$form->getPriority()] = [$form];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($section === 'security') {
|
if ($section === 'security') {
|
||||||
/** @var ISettings $form */
|
/** @var ISettings $form */
|
||||||
$form = $this->container->query(Admin\Security::class);
|
$form = $this->container->query(Admin\Security::class);
|
||||||
$forms[$form->getPriority()] = [$form];
|
if ($filter === null || $filter($form)) {
|
||||||
|
$forms[$form->getPriority()] = [$form];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($section === 'sharing') {
|
if ($section === 'sharing') {
|
||||||
/** @var ISettings $form */
|
/** @var ISettings $form */
|
||||||
$form = $this->container->query(Admin\Sharing::class);
|
$form = $this->container->query(Admin\Sharing::class);
|
||||||
$forms[$form->getPriority()] = [$form];
|
if ($filter === null || $filter($form)) {
|
||||||
|
$forms[$form->getPriority()] = [$form];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $forms;
|
return $forms;
|
||||||
|
@ -290,9 +311,17 @@ class Manager implements IManager {
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
public function getAdminSettings($section): array {
|
public function getAdminSettings($section, bool $subAdminOnly = false): array {
|
||||||
$settings = $this->getBuiltInAdminSettings($section);
|
if ($subAdminOnly) {
|
||||||
$appSettings = $this->getSettings('admin', $section);
|
$subAdminSettingsFilter = function(ISettings $settings) {
|
||||||
|
return $settings instanceof ISubAdminSettings;
|
||||||
|
};
|
||||||
|
$settings = $this->getBuiltInAdminSettings($section, $subAdminSettingsFilter);
|
||||||
|
$appSettings = $this->getSettings('admin', $section, $subAdminSettingsFilter);
|
||||||
|
} else {
|
||||||
|
$settings = $this->getBuiltInAdminSettings($section);
|
||||||
|
$appSettings = $this->getSettings('admin', $section);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($appSettings as $setting) {
|
foreach ($appSettings as $setting) {
|
||||||
if (!isset($settings[$setting->getPriority()])) {
|
if (!isset($settings[$setting->getPriority()])) {
|
||||||
|
|
|
@ -82,10 +82,11 @@ interface IManager {
|
||||||
* returns a list of the admin settings
|
* returns a list of the admin settings
|
||||||
*
|
*
|
||||||
* @param string $section the section id for which to load the settings
|
* @param string $section the section id for which to load the settings
|
||||||
|
* @param bool $subAdminOnly only return settings sub admins are supposed to see (since 17.0.0)
|
||||||
* @return array array of IAdmin[] where key is the priority
|
* @return array array of IAdmin[] where key is the priority
|
||||||
* @since 9.1.0
|
* @since 9.1.0
|
||||||
*/
|
*/
|
||||||
public function getAdminSettings($section): array;
|
public function getAdminSettings($section, bool $subAdminOnly = false): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a list of the personal settings
|
* returns a list of the personal settings
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?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 OCP\Settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tagging interface for settings that should be shown to sub admins
|
||||||
|
*
|
||||||
|
* @since 17.0.0
|
||||||
|
*/
|
||||||
|
interface ISubAdminSettings extends ISettings {
|
||||||
|
|
||||||
|
}
|
|
@ -27,8 +27,12 @@ namespace OC\Settings\Controller;
|
||||||
|
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
|
use OCP\Group\ISubAdmin;
|
||||||
|
use OCP\IGroupManager;
|
||||||
use OCP\INavigationManager;
|
use OCP\INavigationManager;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
|
use OCP\IUser;
|
||||||
|
use OCP\IUserSession;
|
||||||
use OCP\Settings\IManager as ISettingsManager;
|
use OCP\Settings\IManager as ISettingsManager;
|
||||||
use OCP\Template;
|
use OCP\Template;
|
||||||
|
|
||||||
|
@ -38,21 +42,21 @@ use OCP\Template;
|
||||||
class AdminSettingsController extends Controller {
|
class AdminSettingsController extends Controller {
|
||||||
use CommonSettingsTrait;
|
use CommonSettingsTrait;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $appName
|
|
||||||
* @param IRequest $request
|
|
||||||
* @param INavigationManager $navigationManager
|
|
||||||
* @param ISettingsManager $settingsManager
|
|
||||||
*/
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$appName,
|
$appName,
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
INavigationManager $navigationManager,
|
INavigationManager $navigationManager,
|
||||||
ISettingsManager $settingsManager
|
ISettingsManager $settingsManager,
|
||||||
|
IUserSession $userSession,
|
||||||
|
IGroupManager $groupManager,
|
||||||
|
ISubAdmin $subAdmin
|
||||||
) {
|
) {
|
||||||
parent::__construct($appName, $request);
|
parent::__construct($appName, $request);
|
||||||
$this->navigationManager = $navigationManager;
|
$this->navigationManager = $navigationManager;
|
||||||
$this->settingsManager = $settingsManager;
|
$this->settingsManager = $settingsManager;
|
||||||
|
$this->userSession = $userSession;
|
||||||
|
$this->groupManager = $groupManager;
|
||||||
|
$this->subAdmin = $subAdmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,6 +64,7 @@ class AdminSettingsController extends Controller {
|
||||||
* @return TemplateResponse
|
* @return TemplateResponse
|
||||||
*
|
*
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
|
* @SubAdminRequired
|
||||||
*/
|
*/
|
||||||
public function index($section) {
|
public function index($section) {
|
||||||
return $this->getIndexResponse('admin', $section);
|
return $this->getIndexResponse('admin', $section);
|
||||||
|
@ -70,9 +75,16 @@ class AdminSettingsController extends Controller {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function getSettings($section) {
|
protected function getSettings($section) {
|
||||||
$settings = $this->settingsManager->getAdminSettings($section);
|
/** @var IUser $user */
|
||||||
|
$user = $this->userSession->getUser();
|
||||||
|
$isSubAdmin = !$this->groupManager->isAdmin($user->getUID()) && $this->subAdmin->isSubAdmin($user);
|
||||||
|
$settings = $this->settingsManager->getAdminSettings(
|
||||||
|
$section,
|
||||||
|
$isSubAdmin
|
||||||
|
);
|
||||||
$formatted = $this->formatSettings($settings);
|
$formatted = $this->formatSettings($settings);
|
||||||
if($section === 'additional') {
|
// Do not show legacy forms for sub admins
|
||||||
|
if($section === 'additional' && !$isSubAdmin) {
|
||||||
$formatted['content'] .= $this->getLegacyForms();
|
$formatted['content'] .= $this->getLegacyForms();
|
||||||
}
|
}
|
||||||
return $formatted;
|
return $formatted;
|
||||||
|
|
|
@ -25,18 +25,32 @@
|
||||||
namespace OC\Settings\Controller;
|
namespace OC\Settings\Controller;
|
||||||
|
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
|
use OCP\Group\ISubAdmin;
|
||||||
|
use OCP\IGroupManager;
|
||||||
use OCP\INavigationManager;
|
use OCP\INavigationManager;
|
||||||
|
use OCP\IUser;
|
||||||
|
use OCP\IUserSession;
|
||||||
use OCP\Settings\IManager as ISettingsManager;
|
use OCP\Settings\IManager as ISettingsManager;
|
||||||
use OCP\Settings\IIconSection;
|
use OCP\Settings\IIconSection;
|
||||||
use OCP\Settings\ISettings;
|
use OCP\Settings\ISettings;
|
||||||
|
|
||||||
trait CommonSettingsTrait {
|
trait CommonSettingsTrait {
|
||||||
|
|
||||||
/** @var ISettingsManager */
|
/** @var ISettingsManager */
|
||||||
private $settingsManager;
|
private $settingsManager;
|
||||||
|
|
||||||
/** @var INavigationManager */
|
/** @var INavigationManager */
|
||||||
private $navigationManager;
|
private $navigationManager;
|
||||||
|
|
||||||
|
/** @var IUserSession */
|
||||||
|
private $userSession;
|
||||||
|
|
||||||
|
/** @var IGroupManager */
|
||||||
|
private $groupManager;
|
||||||
|
|
||||||
|
/** @var ISubAdmin */
|
||||||
|
private $subAdmin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $currentSection
|
* @param string $currentSection
|
||||||
* @return array
|
* @return array
|
||||||
|
@ -47,8 +61,16 @@ trait CommonSettingsTrait {
|
||||||
'admin' => []
|
'admin' => []
|
||||||
];
|
];
|
||||||
|
|
||||||
if(\OC_User::isAdminUser(\OC_User::getUser())) {
|
/** @var IUser $user */
|
||||||
$templateParameters['admin'] = $this->formatAdminSections($currentType, $currentSection);
|
$user = $this->userSession->getUser();
|
||||||
|
$isAdmin = $this->groupManager->isAdmin($user->getUID());
|
||||||
|
$isSubAdmin = $this->subAdmin->isSubAdmin($user);
|
||||||
|
if ($isAdmin || $isSubAdmin) {
|
||||||
|
$templateParameters['admin'] = $this->formatAdminSections(
|
||||||
|
$currentType,
|
||||||
|
$currentSection,
|
||||||
|
!$isAdmin && $isSubAdmin
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
@ -56,13 +78,13 @@ trait CommonSettingsTrait {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function formatSections($sections, $currentSection, $type, $currentType) {
|
protected function formatSections($sections, $currentSection, $type, $currentType, bool $subAdminOnly = false) {
|
||||||
$templateParameters = [];
|
$templateParameters = [];
|
||||||
/** @var \OCP\Settings\ISection[] $prioritizedSections */
|
/** @var \OCP\Settings\ISection[] $prioritizedSections */
|
||||||
foreach($sections as $prioritizedSections) {
|
foreach($sections as $prioritizedSections) {
|
||||||
foreach ($prioritizedSections as $section) {
|
foreach ($prioritizedSections as $section) {
|
||||||
if($type === 'admin') {
|
if($type === 'admin') {
|
||||||
$settings = $this->settingsManager->getAdminSettings($section->getID());
|
$settings = $this->settingsManager->getAdminSettings($section->getID(), $subAdminOnly);
|
||||||
} else if($type === 'personal') {
|
} else if($type === 'personal') {
|
||||||
$settings = $this->settingsManager->getPersonalSettings($section->getID());
|
$settings = $this->settingsManager->getPersonalSettings($section->getID());
|
||||||
}
|
}
|
||||||
|
@ -96,9 +118,9 @@ trait CommonSettingsTrait {
|
||||||
return $templateParameters;
|
return $templateParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function formatAdminSections($currentType, $currentSections) {
|
protected function formatAdminSections($currentType, $currentSections, bool $subAdminOnly) {
|
||||||
$sections = $this->settingsManager->getAdminSections();
|
$sections = $this->settingsManager->getAdminSections();
|
||||||
$templateParameters = $this->formatSections($sections, $currentSections, 'admin', $currentType);
|
$templateParameters = $this->formatSections($sections, $currentSections, 'admin', $currentType, $subAdminOnly);
|
||||||
|
|
||||||
return $templateParameters;
|
return $templateParameters;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,11 @@ namespace OC\Settings\Controller;
|
||||||
|
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
|
use OCP\Group\ISubAdmin;
|
||||||
|
use OCP\IGroupManager;
|
||||||
use OCP\INavigationManager;
|
use OCP\INavigationManager;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
|
use OCP\IUserSession;
|
||||||
use OCP\Settings\IManager as ISettingsManager;
|
use OCP\Settings\IManager as ISettingsManager;
|
||||||
use OCP\Template;
|
use OCP\Template;
|
||||||
|
|
||||||
|
@ -38,11 +41,17 @@ class PersonalSettingsController extends Controller {
|
||||||
$appName,
|
$appName,
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
INavigationManager $navigationManager,
|
INavigationManager $navigationManager,
|
||||||
ISettingsManager $settingsManager
|
ISettingsManager $settingsManager,
|
||||||
|
IUserSession $userSession,
|
||||||
|
IGroupManager $groupManager,
|
||||||
|
ISubAdmin $subAdmin
|
||||||
) {
|
) {
|
||||||
parent::__construct($appName, $request);
|
parent::__construct($appName, $request);
|
||||||
$this->navigationManager = $navigationManager;
|
$this->navigationManager = $navigationManager;
|
||||||
$this->settingsManager = $settingsManager;
|
$this->settingsManager = $settingsManager;
|
||||||
|
$this->userSession = $userSession;
|
||||||
|
$this->subAdmin = $subAdmin;
|
||||||
|
$this->groupManager = $groupManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,9 +25,14 @@ namespace Tests\Settings\Controller;
|
||||||
use OC\Settings\Personal\ServerDevNotice;
|
use OC\Settings\Personal\ServerDevNotice;
|
||||||
use OC\Settings\Controller\AdminSettingsController;
|
use OC\Settings\Controller\AdminSettingsController;
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
|
use OCP\Group\ISubAdmin;
|
||||||
|
use OCP\IGroupManager;
|
||||||
use OCP\INavigationManager;
|
use OCP\INavigationManager;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
|
use OCP\IUser;
|
||||||
|
use OCP\IUserSession;
|
||||||
use OCP\Settings\IManager;
|
use OCP\Settings\IManager;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use Test\TestCase;
|
use Test\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,29 +43,42 @@ use Test\TestCase;
|
||||||
* @package Tests\Settings\Controller
|
* @package Tests\Settings\Controller
|
||||||
*/
|
*/
|
||||||
class AdminSettingsControllerTest extends TestCase {
|
class AdminSettingsControllerTest extends TestCase {
|
||||||
|
|
||||||
/** @var AdminSettingsController */
|
/** @var AdminSettingsController */
|
||||||
private $adminSettingsController;
|
private $adminSettingsController;
|
||||||
/** @var IRequest */
|
/** @var IRequest|MockObject */
|
||||||
private $request;
|
private $request;
|
||||||
/** @var INavigationManager */
|
/** @var INavigationManager|MockObject */
|
||||||
private $navigationManager;
|
private $navigationManager;
|
||||||
/** @var IManager|\PHPUnit_Framework_MockObject_MockObject */
|
/** @var IManager|MockObject */
|
||||||
private $settingsManager;
|
private $settingsManager;
|
||||||
|
/** @var IUserSession|MockObject */
|
||||||
|
private $userSession;
|
||||||
|
/** @var IGroupManager|MockObject */
|
||||||
|
private $groupManager;
|
||||||
|
/** @var ISubAdmin|MockObject */
|
||||||
|
private $subAdmin;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $adminUid = 'lololo';
|
private $adminUid = 'lololo';
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->request = $this->getMockBuilder(IRequest::class)->getMock();
|
$this->request = $this->createMock(IRequest::class);
|
||||||
$this->navigationManager = $this->getMockBuilder(INavigationManager::class)->getMock();
|
$this->navigationManager = $this->createMock(INavigationManager::class);
|
||||||
$this->settingsManager = $this->getMockBuilder(IManager::class)->getMock();
|
$this->settingsManager = $this->createMock(IManager::class);
|
||||||
|
$this->userSession = $this->createMock(IUserSession::class);
|
||||||
|
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||||
|
$this->subAdmin = $this->createMock(ISubAdmin::class);
|
||||||
|
|
||||||
$this->adminSettingsController = new AdminSettingsController(
|
$this->adminSettingsController = new AdminSettingsController(
|
||||||
'settings',
|
'settings',
|
||||||
$this->request,
|
$this->request,
|
||||||
$this->navigationManager,
|
$this->navigationManager,
|
||||||
$this->settingsManager
|
$this->settingsManager,
|
||||||
|
$this->userSession,
|
||||||
|
$this->groupManager,
|
||||||
|
$this->subAdmin
|
||||||
);
|
);
|
||||||
|
|
||||||
$user = \OC::$server->getUserManager()->createUser($this->adminUid, 'olo');
|
$user = \OC::$server->getUserManager()->createUser($this->adminUid, 'olo');
|
||||||
|
@ -75,6 +93,19 @@ class AdminSettingsControllerTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIndex() {
|
public function testIndex() {
|
||||||
|
$user = $this->createMock(IUser::class);
|
||||||
|
$this->userSession
|
||||||
|
->method('getUser')
|
||||||
|
->willReturn($user);
|
||||||
|
$user->method('getUID')->willReturn('user123');
|
||||||
|
$this->groupManager
|
||||||
|
->method('isAdmin')
|
||||||
|
->with('user123')
|
||||||
|
->willReturn(true);
|
||||||
|
$this->subAdmin
|
||||||
|
->method('isSubAdmin')
|
||||||
|
->with($user)
|
||||||
|
->willReturn(false);
|
||||||
$this->settingsManager
|
$this->settingsManager
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getAdminSections')
|
->method('getAdminSections')
|
||||||
|
@ -89,7 +120,9 @@ class AdminSettingsControllerTest extends TestCase {
|
||||||
->with('test')
|
->with('test')
|
||||||
->willReturn([5 => new ServerDevNotice()]);
|
->willReturn([5 => new ServerDevNotice()]);
|
||||||
|
|
||||||
|
$idx = $this->adminSettingsController->index('test');
|
||||||
|
|
||||||
$expected = new TemplateResponse('settings', 'settings/frame', ['forms' => ['personal' => [], 'admin' => []], 'content' => '']);
|
$expected = new TemplateResponse('settings', 'settings/frame', ['forms' => ['personal' => [], 'admin' => []], 'content' => '']);
|
||||||
$this->assertEquals($expected, $this->adminSettingsController->index('test'));
|
$this->assertEquals($expected, $idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,12 +96,12 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
$this->csrfTokenManager = $this->createMock(CsrfTokenManager::class);
|
$this->csrfTokenManager = $this->createMock(CsrfTokenManager::class);
|
||||||
$this->cspNonceManager = $this->createMock(ContentSecurityPolicyNonceManager::class);
|
$this->cspNonceManager = $this->createMock(ContentSecurityPolicyNonceManager::class);
|
||||||
$this->l10n = $this->createMock(IL10N::class);
|
$this->l10n = $this->createMock(IL10N::class);
|
||||||
$this->middleware = $this->getMiddleware(true, true);
|
$this->middleware = $this->getMiddleware(true, true, false);
|
||||||
$this->secException = new SecurityException('hey', false);
|
$this->secException = new SecurityException('hey', false);
|
||||||
$this->secAjaxException = new SecurityException('hey', true);
|
$this->secAjaxException = new SecurityException('hey', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getMiddleware(bool $isLoggedIn, bool $isAdminUser, bool $isAppEnabledForUser = true): SecurityMiddleware {
|
private function getMiddleware(bool $isLoggedIn, bool $isAdminUser, bool $isSubAdmin, bool $isAppEnabledForUser = true): SecurityMiddleware {
|
||||||
|
|
||||||
$this->appManager = $this->createMock(IAppManager::class);
|
$this->appManager = $this->createMock(IAppManager::class);
|
||||||
$this->appManager->expects($this->any())
|
$this->appManager->expects($this->any())
|
||||||
|
@ -117,6 +117,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
'files',
|
'files',
|
||||||
$isLoggedIn,
|
$isLoggedIn,
|
||||||
$isAdminUser,
|
$isAdminUser,
|
||||||
|
$isSubAdmin,
|
||||||
$this->contentSecurityPolicyManager,
|
$this->contentSecurityPolicyManager,
|
||||||
$this->csrfTokenManager,
|
$this->csrfTokenManager,
|
||||||
$this->cspNonceManager,
|
$this->cspNonceManager,
|
||||||
|
@ -153,7 +154,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
$isLoggedIn = true;
|
$isLoggedIn = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sec = $this->getMiddleware($isLoggedIn, $isAdminUser);
|
$sec = $this->getMiddleware($isLoggedIn, $isAdminUser, false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->reader->reflect(__CLASS__, $method);
|
$this->reader->reflect(__CLASS__, $method);
|
||||||
|
@ -214,11 +215,6 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
'isAdminUser',
|
'isAdminUser',
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
$this->ajaxExceptionStatus(
|
|
||||||
__FUNCTION__,
|
|
||||||
'isSubAdminUser',
|
|
||||||
0
|
|
||||||
);
|
|
||||||
$this->ajaxExceptionStatus(
|
$this->ajaxExceptionStatus(
|
||||||
__FUNCTION__,
|
__FUNCTION__,
|
||||||
'passesCSRFCheck',
|
'passesCSRFCheck',
|
||||||
|
@ -236,7 +232,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
->method('passesCSRFCheck')
|
->method('passesCSRFCheck')
|
||||||
->will($this->returnValue(false));
|
->will($this->returnValue(false));
|
||||||
|
|
||||||
$sec = $this->getMiddleware(false, false);
|
$sec = $this->getMiddleware(false, false, false);
|
||||||
|
|
||||||
$this->reader->reflect(__CLASS__, __FUNCTION__);
|
$this->reader->reflect(__CLASS__, __FUNCTION__);
|
||||||
$sec->beforeController($this->controller, __FUNCTION__);
|
$sec->beforeController($this->controller, __FUNCTION__);
|
||||||
|
@ -257,7 +253,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
$isAdminUser = false;
|
$isAdminUser = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sec = $this->getMiddleware($isLoggedIn, $isAdminUser);
|
$sec = $this->getMiddleware($isLoggedIn, $isAdminUser, false);
|
||||||
|
|
||||||
if($shouldFail) {
|
if($shouldFail) {
|
||||||
$this->expectException(SecurityException::class);
|
$this->expectException(SecurityException::class);
|
||||||
|
@ -452,6 +448,41 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
$this->securityCheck(__FUNCTION__, 'isAdminUser');
|
$this->securityCheck(__FUNCTION__, 'isAdminUser');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoCSRFRequired
|
||||||
|
* @SubAdminRequired
|
||||||
|
*/
|
||||||
|
public function testIsNotSubAdminCheck(){
|
||||||
|
$this->reader->reflect(__CLASS__,__FUNCTION__);
|
||||||
|
$sec = $this->getMiddleware(true, false, false);
|
||||||
|
|
||||||
|
$this->expectException(SecurityException::class);
|
||||||
|
$sec->beforeController($this, __METHOD__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoCSRFRequired
|
||||||
|
* @SubAdminRequired
|
||||||
|
*/
|
||||||
|
public function testIsSubAdminCheck(){
|
||||||
|
$this->reader->reflect(__CLASS__,__FUNCTION__);
|
||||||
|
$sec = $this->getMiddleware(true, false, true);
|
||||||
|
|
||||||
|
$sec->beforeController($this, __METHOD__);
|
||||||
|
$this->addToAssertionCount(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoCSRFRequired
|
||||||
|
* @SubAdminRequired
|
||||||
|
*/
|
||||||
|
public function testIsSubAdminAndAdminCheck(){
|
||||||
|
$this->reader->reflect(__CLASS__,__FUNCTION__);
|
||||||
|
$sec = $this->getMiddleware(true, true, true);
|
||||||
|
|
||||||
|
$sec->beforeController($this, __METHOD__);
|
||||||
|
$this->addToAssertionCount(1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
|
@ -479,7 +510,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
$this->createMock(ISecureRandom::class),
|
$this->createMock(ISecureRandom::class),
|
||||||
$this->createMock(IConfig::class)
|
$this->createMock(IConfig::class)
|
||||||
);
|
);
|
||||||
$this->middleware = $this->getMiddleware(false, false);
|
$this->middleware = $this->getMiddleware(false, false, false);
|
||||||
$this->urlGenerator
|
$this->urlGenerator
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('linkToRoute')
|
->method('linkToRoute')
|
||||||
|
@ -514,7 +545,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
$this->createMock(IConfig::class)
|
$this->createMock(IConfig::class)
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->middleware = $this->getMiddleware(false, false);
|
$this->middleware = $this->getMiddleware(false, false, false);
|
||||||
$response = $this->middleware->afterException(
|
$response = $this->middleware->afterException(
|
||||||
$this->controller,
|
$this->controller,
|
||||||
'test',
|
'test',
|
||||||
|
@ -559,7 +590,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
$this->createMock(ISecureRandom::class),
|
$this->createMock(ISecureRandom::class),
|
||||||
$this->createMock(IConfig::class)
|
$this->createMock(IConfig::class)
|
||||||
);
|
);
|
||||||
$this->middleware = $this->getMiddleware(false, false);
|
$this->middleware = $this->getMiddleware(false, false, false);
|
||||||
$this->logger
|
$this->logger
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('logException');
|
->method('logException');
|
||||||
|
@ -684,7 +715,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
*/
|
*/
|
||||||
public function testRestrictedAppLoggedInPublicPage() {
|
public function testRestrictedAppLoggedInPublicPage() {
|
||||||
$middleware = $this->getMiddleware(true, false);
|
$middleware = $this->getMiddleware(true, false, false);
|
||||||
$this->reader->reflect(__CLASS__,__FUNCTION__);
|
$this->reader->reflect(__CLASS__,__FUNCTION__);
|
||||||
|
|
||||||
$this->appManager->method('getAppPath')
|
$this->appManager->method('getAppPath')
|
||||||
|
@ -705,7 +736,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
*/
|
*/
|
||||||
public function testRestrictedAppNotLoggedInPublicPage() {
|
public function testRestrictedAppNotLoggedInPublicPage() {
|
||||||
$middleware = $this->getMiddleware(false, false);
|
$middleware = $this->getMiddleware(false, false, false);
|
||||||
$this->reader->reflect(__CLASS__,__FUNCTION__);
|
$this->reader->reflect(__CLASS__,__FUNCTION__);
|
||||||
|
|
||||||
$this->appManager->method('getAppPath')
|
$this->appManager->method('getAppPath')
|
||||||
|
@ -725,7 +756,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
*/
|
*/
|
||||||
public function testRestrictedAppLoggedIn() {
|
public function testRestrictedAppLoggedIn() {
|
||||||
$middleware = $this->getMiddleware(true, false, false);
|
$middleware = $this->getMiddleware(true, false, false, false);
|
||||||
$this->reader->reflect(__CLASS__,__FUNCTION__);
|
$this->reader->reflect(__CLASS__,__FUNCTION__);
|
||||||
|
|
||||||
$this->appManager->method('getAppPath')
|
$this->appManager->method('getAppPath')
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
namespace Tests\Settings;
|
namespace Tests\Settings;
|
||||||
|
|
||||||
|
use function get_class;
|
||||||
use OC\Settings\Admin\Sharing;
|
use OC\Settings\Admin\Sharing;
|
||||||
use OC\Settings\Manager;
|
use OC\Settings\Manager;
|
||||||
use OC\Settings\Mapper;
|
use OC\Settings\Mapper;
|
||||||
|
@ -34,6 +35,8 @@ use OCP\ILogger;
|
||||||
use OCP\IServerContainer;
|
use OCP\IServerContainer;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
use OCP\L10N\IFactory;
|
use OCP\L10N\IFactory;
|
||||||
|
use OCP\Settings\ISettings;
|
||||||
|
use OCP\Settings\ISubAdminSettings;
|
||||||
use Test\TestCase;
|
use Test\TestCase;
|
||||||
|
|
||||||
class ManagerTest extends TestCase {
|
class ManagerTest extends TestCase {
|
||||||
|
@ -207,6 +210,35 @@ class ManagerTest extends TestCase {
|
||||||
], $settings);
|
], $settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetAdminSettingsAsSubAdmin() {
|
||||||
|
$section = $this->createMock(Sharing::class);
|
||||||
|
$this->container->expects($this->once())
|
||||||
|
->method('query')
|
||||||
|
->with(Sharing::class)
|
||||||
|
->willReturn($section);
|
||||||
|
|
||||||
|
$settings = $this->manager->getAdminSettings('sharing', true);
|
||||||
|
|
||||||
|
$this->assertEquals([], $settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubAdminSettingsAsSubAdmin() {
|
||||||
|
$section = $this->createMock(ISubAdminSettings::class);
|
||||||
|
$section->expects($this->once())
|
||||||
|
->method('getPriority')
|
||||||
|
->willReturn(13);
|
||||||
|
$this->container->expects($this->once())
|
||||||
|
->method('query')
|
||||||
|
->with(Sharing::class)
|
||||||
|
->willReturn($section);
|
||||||
|
|
||||||
|
$settings = $this->manager->getAdminSettings('sharing', true);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
13 => [$section]
|
||||||
|
], $settings);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetPersonalSettings() {
|
public function testGetPersonalSettings() {
|
||||||
$section = $this->createMock(Security::class);
|
$section = $this->createMock(Security::class);
|
||||||
$section->expects($this->once())
|
$section->expects($this->once())
|
||||||
|
|
Loading…
Reference in New Issue