Merge pull request #29240 from nextcloud/work/admin-delegation-implementation

Add support for Delegation Settings for more apps
pull/29271/head
Carl Schwan 3 years ago committed by GitHub
commit a67a12cb96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -88,6 +88,7 @@ class BirthdayCalendarController extends Controller {
/**
* @return Response
* @AuthorizedAdminSetting(settings=OCA\DAV\Settings\CalDAVSettings)
*/
public function enable() {
$this->config->setAppValue($this->appName, 'generateBirthdayCalendar', 'yes');
@ -104,6 +105,7 @@ class BirthdayCalendarController extends Controller {
/**
* @return Response
* @AuthorizedAdminSetting(settings=OCA\DAV\Settings\CalDAVSettings)
*/
public function disable() {
$this->config->setAppValue($this->appName, 'generateBirthdayCalendar', 'no');

@ -29,9 +29,9 @@ use OCA\DAV\AppInfo\Application;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\AppFramework\Services\IInitialState;
use OCP\Settings\ISettings;
use OCP\Settings\IDelegatedSettings;
class CalDAVSettings implements ISettings {
class CalDAVSettings implements IDelegatedSettings {
/** @var IConfig */
private $config;
@ -39,6 +39,13 @@ class CalDAVSettings implements ISettings {
/** @var IInitialState */
private $initialState;
private const defaults = [
'sendInvitations' => 'yes',
'generateBirthdayCalendar' => 'yes',
'sendEventReminders' => 'yes',
'sendEventRemindersPush' => 'no',
];
/**
* CalDAVSettings constructor.
*
@ -51,13 +58,7 @@ class CalDAVSettings implements ISettings {
}
public function getForm(): TemplateResponse {
$defaults = [
'sendInvitations' => 'yes',
'generateBirthdayCalendar' => 'yes',
'sendEventReminders' => 'yes',
'sendEventRemindersPush' => 'no',
];
foreach ($defaults as $key => $default) {
foreach (self::defaults as $key => $default) {
$value = $this->config->getAppValue(Application::APP_ID, $key, $default);
$this->initialState->provideInitialState($key, $value === 'yes');
}
@ -77,4 +78,14 @@ class CalDAVSettings implements ISettings {
public function getPriority() {
return 10;
}
public function getName(): ?string {
return null; // Only setting in this section
}
public function getAuthorizedAppConfig(): array {
return [
'dav' => ['/(' . implode('|', array_keys(self::defaults)) . ')/']
];
}
}

@ -27,9 +27,10 @@ namespace OCA\FederatedFileSharing\Settings;
use OCA\FederatedFileSharing\FederatedShareProvider;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\GlobalScale\IConfig;
use OCP\Settings\ISettings;
use OCP\IL10N;
use OCP\Settings\IDelegatedSettings;
class Admin implements ISettings {
class Admin implements IDelegatedSettings {
/** @var FederatedShareProvider */
private $fedShareProvider;
@ -37,15 +38,19 @@ class Admin implements ISettings {
/** @var IConfig */
private $gsConfig;
/** @var IL10N */
private $l;
/**
* Admin constructor.
*
* @param FederatedShareProvider $fedShareProvider
* @param IConfig $globalScaleConfig
*/
public function __construct(FederatedShareProvider $fedShareProvider, IConfig $globalScaleConfig) {
public function __construct(FederatedShareProvider $fedShareProvider, IConfig $globalScaleConfig, IL10N $l) {
$this->fedShareProvider = $fedShareProvider;
$this->gsConfig = $globalScaleConfig;
$this->l = $l;
}
/**
@ -83,4 +88,22 @@ class Admin implements ISettings {
public function getPriority() {
return 20;
}
public function getName(): ?string {
return $this->l->t('Federated Cloud Sharing');
}
public function getAuthorizedAppConfig(): array {
return [
'files_sharing' => [
'outgoing_server2server_share_enabled',
'incoming_server2server_share_enabled',
'federatedGroupSharingSupported',
'outgoingServer2serverGroupShareEnabled',
'incomingServer2serverGroupShareEnabled',
'lookupServerEnabled',
'lookupServerUploadEnabled',
],
];
}
}

@ -29,6 +29,7 @@ use OCA\FederatedFileSharing\FederatedShareProvider;
use OCA\FederatedFileSharing\Settings\Admin;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\GlobalScale\IConfig;
use OCP\IL10N;
use Test\TestCase;
class AdminTest extends TestCase {
@ -45,7 +46,8 @@ class AdminTest extends TestCase {
$this->gsConfig = $this->createMock(IConfig::class);
$this->admin = new Admin(
$this->federatedShareProvider,
$this->gsConfig
$this->gsConfig,
$this->createMock(IL10N::class)
);
}

@ -56,8 +56,9 @@ class SettingsController extends Controller {
/**
* add server to the list of trusted Nextclouds
* Add server to the list of trusted Nextclouds.
*
* @AuthorizedAdminSetting(settings=OCA\Federation\Settings\Admin)
* @param string $url
* @return DataResponse
* @throws HintException
@ -76,8 +77,9 @@ class SettingsController extends Controller {
}
/**
* add server to the list of trusted Nextclouds
* Add server to the list of trusted Nextclouds.
*
* @AuthorizedAdminSetting(settings=OCA\Federation\Settings\Admin)
* @param int $id
* @return DataResponse
*/
@ -87,8 +89,9 @@ class SettingsController extends Controller {
}
/**
* check if the server should be added to the list of trusted servers or not
* Check if the server should be added to the list of trusted servers or not.
*
* @AuthorizedAdminSetting(settings=OCA\Federation\Settings\Admin)
* @param string $url
* @return bool
* @throws HintException

@ -24,15 +24,26 @@ namespace OCA\Federation\Settings;
use OCA\Federation\TrustedServers;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Settings\ISettings;
use OCP\IL10N;
use OCP\Settings\IDelegatedSettings;
class Admin implements ISettings {
class Admin implements IDelegatedSettings {
/** @var TrustedServers */
private $trustedServers;
public function __construct(TrustedServers $trustedServers) {
/** @var IL10N */
private $l;
/**
* Admin constructor.
*
* @param TrustedServers $trustedServers
* @param IL10N $l
*/
public function __construct(TrustedServers $trustedServers, IL10N $l) {
$this->trustedServers = $trustedServers;
$this->l = $l;
}
/**
@ -63,4 +74,12 @@ class Admin implements ISettings {
public function getPriority() {
return 30;
}
public function getName(): ?string {
return $this->l->t("Trusted servers");
}
public function getAuthorizedAppConfig(): array {
return []; // Handled by custom controller
}
}

@ -26,6 +26,7 @@ namespace OCA\Federation\Tests\Settings;
use OCA\Federation\Settings\Admin;
use OCA\Federation\TrustedServers;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N;
use Test\TestCase;
class AdminTest extends TestCase {
@ -38,7 +39,8 @@ class AdminTest extends TestCase {
parent::setUp();
$this->trustedServers = $this->getMockBuilder('\OCA\Federation\TrustedServers')->disableOriginalConstructor()->getMock();
$this->admin = new Admin(
$this->trustedServers
$this->trustedServers,
$this->createMock(IL10N::class)
);
}

@ -26,12 +26,19 @@ declare(strict_types=1);
*/
namespace OCA\Provisioning_API\Controller;
use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Settings\IDelegatedSettings;
use OCP\Settings\IManager;
class AppConfigController extends OCSController {
@ -41,6 +48,18 @@ class AppConfigController extends OCSController {
/** @var IAppConfig */
protected $appConfig;
/** @var IUserSession */
private $userSession;
/** @var IL10N */
private $l10n;
/** @var IGroupManager */
private $groupManager;
/** @var IManager */
private $settingManager;
/**
* @param string $appName
* @param IRequest $request
@ -50,10 +69,18 @@ class AppConfigController extends OCSController {
public function __construct(string $appName,
IRequest $request,
IConfig $config,
IAppConfig $appConfig) {
IAppConfig $appConfig,
IUserSession $userSession,
IL10N $l10n,
IGroupManager $groupManager,
IManager $settingManager) {
parent::__construct($appName, $request);
$this->config = $config;
$this->appConfig = $appConfig;
$this->userSession = $userSession;
$this->l10n = $l10n;
$this->groupManager = $groupManager;
$this->settingManager = $settingManager;
}
/**
@ -99,12 +126,23 @@ class AppConfigController extends OCSController {
/**
* @PasswordConfirmationRequired
* @NoSubAdminRequired
* @NoAdminRequired
* @param string $app
* @param string $key
* @param string $value
* @return DataResponse
*/
public function setValue(string $app, string $key, string $value): DataResponse {
$user = $this->userSession->getUser();
if ($user === null) {
throw new \Exception("User is not logged in."); // Should not happen, since method is guarded by middleware
}
if (!$this->isAllowedToChangedKey($user, $app, $key)) {
throw new NotAdminException($this->l10n->t('Logged in user must be an admin or have authorization to edit this setting.'));
}
try {
$this->verifyAppId($app);
$this->verifyConfigKey($app, $key, $value);
@ -170,4 +208,30 @@ class AppConfigController extends OCSController {
throw new \InvalidArgumentException('The given key can not be set, unlimited quota is forbidden on this instance');
}
}
private function isAllowedToChangedKey(IUser $user, string $app, string $key): bool {
// Admin right verification
$isAdmin = $this->groupManager->isAdmin($user->getUID());
if ($isAdmin) {
return true;
}
$settings = $this->settingManager->getAllAllowedAdminSettings($user);
foreach ($settings as $setting) {
if (!($setting instanceof IDelegatedSettings)) {
continue;
}
$allowedKeys = $setting->getAuthorizedAppConfig();
if (!array_key_exists($app, $allowedKeys)) {
continue;
}
foreach ($allowedKeys[$app] as $regex) {
if ($regex === $key
|| (str_starts_with($regex, '/') && preg_match($regex, $key) === 1)) {
return true;
}
}
}
return false;
}
}

@ -96,9 +96,10 @@ class GroupsController extends AUserData {
}
/**
* returns a list of groups details with ids and displaynames
* Returns a list of groups details with ids and displaynames
*
* @NoAdminRequired
* @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\Sharing)
*
* @param string $search
* @param int $limit

@ -70,7 +70,8 @@ class ProvisioningApiMiddleware extends Middleware {
* @throws NotSubAdminException
*/
public function beforeController($controller, $methodName) {
if (!$this->isAdmin && !$this->reflector->hasAnnotation('NoSubAdminRequired') && !$this->isSubAdmin) {
// If AuthorizedAdminSetting, the check will be done in the SecurityMiddleware
if (!$this->isAdmin && !$this->reflector->hasAnnotation('NoSubAdminRequired') && !$this->isSubAdmin && !$this->reflector->hasAnnotation('AuthorizedAdminSetting')) {
throw new NotSubAdminException();
}
}

@ -30,7 +30,12 @@ use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Settings\IManager;
use Test\TestCase;
/**
@ -44,12 +49,24 @@ class AppConfigControllerTest extends TestCase {
private $config;
/** @var IAppConfig|\PHPUnit\Framework\MockObject\MockObject */
private $appConfig;
/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
private $userSession;
/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
private $l10n;
/** @var IManager|\PHPUnit\Framework\MockObject\MockObject */
private $settingManager;
/** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
private $groupManager;
protected function setUp(): void {
parent::setUp();
$this->config = $this->createMock(IConfig::class);
$this->appConfig = $this->createMock(IAppConfig::class);
$this->userSession = $this->createMock(IUserSession::class);
$this->l10n = $this->createMock(IL10N::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->settingManager = $this->createMock(IManager::class);
}
/**
@ -64,7 +81,11 @@ class AppConfigControllerTest extends TestCase {
'provisioning_api',
$request,
$this->config,
$this->appConfig
$this->appConfig,
$this->userSession,
$this->l10n,
$this->groupManager,
$this->settingManager
);
} else {
return $this->getMockBuilder(AppConfigController::class)
@ -73,6 +94,10 @@ class AppConfigControllerTest extends TestCase {
$request,
$this->config,
$this->appConfig,
$this->userSession,
$this->l10n,
$this->groupManager,
$this->settingManager
])
->setMethods($methods)
->getMock();
@ -200,6 +225,18 @@ class AppConfigControllerTest extends TestCase {
* @param int $status
*/
public function testSetValue($app, $key, $value, $appThrows, $keyThrows, $status) {
$adminUser = $this->createMock(IUser::class);
$adminUser->expects($this->once())
->method('getUid')
->willReturn('admin');
$this->userSession->expects($this->once())
->method('getUser')
->willReturn($adminUser);
$this->groupManager->expects($this->once())
->method('isAdmin')
->with('admin')
->willReturn(true);
$api = $this->getInstance(['verifyAppId', 'verifyConfigKey']);
if ($appThrows instanceof \Exception) {
$api->expects($this->once())

@ -45,13 +45,20 @@ class ProvisioningApiMiddlewareTest extends TestCase {
public function dataAnnotation() {
return [
[false, false, false, false],
[false, false, true, false],
[false, true, true, false],
[ true, false, false, true],
[ true, false, true, false],
[ true, true, false, false],
[ true, true, true, false],
[false, false, false, false, false],
[false, false, true, false, false],
[false, true, true, false, false],
[ true, false, false, false, true],
[ true, false, true, false, false],
[ true, true, false, false, false],
[ true, true, true, false, false],
[false, false, false, true, false],
[false, false, true, true, false],
[false, true, true, true, false],
[ true, false, false, true, false],
[ true, false, true, true, false],
[ true, true, false, true, false],
[ true, true, true, true, false],
];
}
@ -63,7 +70,7 @@ class ProvisioningApiMiddlewareTest extends TestCase {
* @param bool $isSubAdmin
* @param bool $shouldThrowException
*/
public function testBeforeController($subadminRequired, $isAdmin, $isSubAdmin, $shouldThrowException) {
public function testBeforeController($subadminRequired, $isAdmin, $isSubAdmin, $hasSettingAuthorizationAnnotation, $shouldThrowException) {
$middleware = new ProvisioningApiMiddleware(
$this->reflector,
$isAdmin,
@ -71,8 +78,15 @@ class ProvisioningApiMiddlewareTest extends TestCase {
);
$this->reflector->method('hasAnnotation')
->with('NoSubAdminRequired')
->willReturn(!$subadminRequired);
->willReturnCallback(function ($annotation) use ($subadminRequired, $hasSettingAuthorizationAnnotation) {
if ($annotation === 'NoSubAdminRequired') {
return !$subadminRequired;
}
if ($annotation === 'AuthorizedAdminSetting') {
return $hasSettingAuthorizationAnnotation;
}
return false;
});
try {
$middleware->beforeController(

@ -24,15 +24,20 @@
namespace OCA\ShareByMail\Settings;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Settings\ISettings;
use OCP\IL10N;
use OCP\Settings\IDelegatedSettings;
class Admin implements ISettings {
class Admin implements IDelegatedSettings {
/** @var SettingsManager */
private $settingsManager;
public function __construct(SettingsManager $settingsManager) {
/** @var IL10N */
private $l;
public function __construct(SettingsManager $settingsManager, IL10N $l) {
$this->settingsManager = $settingsManager;
$this->l = $l;
}
/**
@ -64,4 +69,14 @@ class Admin implements ISettings {
public function getPriority() {
return 40;
}
public function getName(): ?string {
return $this->l->t('Share by mail');
}
public function getAuthorizedAppConfig(): array {
return [
'sharebymail' => ['s/(sendpasswordmail|replyToInitiator)/'],
];
}
}

@ -124,6 +124,7 @@ class ThemingController extends Controller {
}
/**
* @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
* @param string $setting
* @param string $value
* @return DataResponse
@ -208,6 +209,7 @@ class ThemingController extends Controller {
}
/**
* @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
* @return DataResponse
* @throws NotPermittedException
*/
@ -278,6 +280,7 @@ class ThemingController extends Controller {
/**
* Revert setting to default value
* @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
*
* @param string $setting setting which should be reverted
* @return DataResponse

@ -33,9 +33,9 @@ use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Settings\ISettings;
use OCP\Settings\IDelegatedSettings;
class Admin implements ISettings {
class Admin implements IDelegatedSettings {
/** @var IConfig */
private $config;
/** @var IL10N */
@ -106,4 +106,14 @@ class Admin implements ISettings {
public function getPriority(): int {
return 5;
}
public function getName(): ?string {
return null; // Only one setting in this section
}
public function getAuthorizedAppConfig(): array {
return [
'theming' => '/.*/',
];
}
}

@ -76,7 +76,7 @@ class ConfigAPIController extends OCSController {
}
/**
* creates a new (empty) configuration and returns the resulting prefix
* Creates a new (empty) configuration and returns the resulting prefix
*
* Example: curl -X POST -H "OCS-APIREQUEST: true" -u $admin:$password \
* https://nextcloud.server/ocs/v2.php/apps/user_ldap/api/v1/config
@ -110,6 +110,7 @@ class ConfigAPIController extends OCSController {
*
* For JSON output provide the format=json parameter
*
* @AuthorizedAdminSetting(settings=OCA\User_LDAP\Settings\Admin)
* @return DataResponse
* @throws OCSException
*/
@ -142,6 +143,7 @@ class ConfigAPIController extends OCSController {
* <data/>
* </ocs>
*
* @AuthorizedAdminSetting(settings=OCA\User_LDAP\Settings\Admin)
* @param string $configID
* @return DataResponse
* @throws OCSBadRequestException
@ -164,7 +166,7 @@ class ConfigAPIController extends OCSController {
}
/**
* modifies a configuration
* Modifies a configuration
*
* Example:
* curl -X PUT -d "configData[ldapHost]=ldaps://my.ldap.server&configData[ldapPort]=636" \
@ -181,6 +183,7 @@ class ConfigAPIController extends OCSController {
* <data/>
* </ocs>
*
* @AuthorizedAdminSetting(settings=OCA\User_LDAP\Settings\Admin)
* @param string $configID
* @param array $configData
* @return DataResponse
@ -216,7 +219,7 @@ class ConfigAPIController extends OCSController {
}
/**
* retrieves a configuration
* Retrieves a configuration
*
* <?xml version="1.0"?>
* <ocs>
@ -280,6 +283,7 @@ class ConfigAPIController extends OCSController {
* </data>
* </ocs>
*
* @AuthorizedAdminSetting(settings=OCA\User_LDAP\Settings\Admin)
* @param string $configID
* @param bool|string $showPassword
* @return DataResponse
@ -311,8 +315,9 @@ class ConfigAPIController extends OCSController {
}
/**
* if the given config ID is not available, an exception is thrown
* If the given config ID is not available, an exception is thrown
*
* @AuthorizedAdminSetting(settings=OCA\User_LDAP\Settings\Admin)
* @param string $configID
* @throws OCSNotFoundException
*/

@ -29,10 +29,10 @@ use OCA\User_LDAP\Configuration;
use OCA\User_LDAP\Helper;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N;
use OCP\Settings\ISettings;
use OCP\Settings\IDelegatedSettings;
use OCP\Template;
class Admin implements ISettings {
class Admin implements IDelegatedSettings {
/** @var IL10N */
private $l;
@ -98,4 +98,12 @@ class Admin implements ISettings {
public function getPriority() {
return 5;
}
public function getName(): ?string {
return null; // Only one setting in this section
}
public function getAuthorizedAppConfig(): array {
return []; // Custom controller
}
}

Loading…
Cancel
Save