1<?php 2 3declare(strict_types=1); 4 5/** 6 * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> 7 * 8 * @author Joas Schilling <coding@schilljs.com> 9 * @author Roeland Jago Douma <roeland@famdouma.nl> 10 * 11 * @license GNU AGPL version 3 or any later version 12 * 13 * This program is free software: you can redistribute it and/or modify 14 * it under the terms of the GNU Affero General Public License as 15 * published by the Free Software Foundation, either version 3 of the 16 * License, or (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU Affero General Public License for more details. 22 * 23 * You should have received a copy of the GNU Affero General Public License 24 * along with this program. If not, see <http://www.gnu.org/licenses/>. 25 * 26 */ 27namespace OCA\Provisioning_API\Controller; 28 29use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException; 30use OCP\AppFramework\Http; 31use OCP\AppFramework\Http\DataResponse; 32use OCP\AppFramework\OCSController; 33use OCP\IAppConfig; 34use OCP\IConfig; 35use OCP\IGroupManager; 36use OCP\IL10N; 37use OCP\IRequest; 38use OCP\IUser; 39use OCP\IUserSession; 40use OCP\Settings\IDelegatedSettings; 41use OCP\Settings\IManager; 42 43class AppConfigController extends OCSController { 44 45 /** @var IConfig */ 46 protected $config; 47 48 /** @var IAppConfig */ 49 protected $appConfig; 50 51 /** @var IUserSession */ 52 private $userSession; 53 54 /** @var IL10N */ 55 private $l10n; 56 57 /** @var IGroupManager */ 58 private $groupManager; 59 60 /** @var IManager */ 61 private $settingManager; 62 63 /** 64 * @param string $appName 65 * @param IRequest $request 66 * @param IConfig $config 67 * @param IAppConfig $appConfig 68 */ 69 public function __construct(string $appName, 70 IRequest $request, 71 IConfig $config, 72 IAppConfig $appConfig, 73 IUserSession $userSession, 74 IL10N $l10n, 75 IGroupManager $groupManager, 76 IManager $settingManager) { 77 parent::__construct($appName, $request); 78 $this->config = $config; 79 $this->appConfig = $appConfig; 80 $this->userSession = $userSession; 81 $this->l10n = $l10n; 82 $this->groupManager = $groupManager; 83 $this->settingManager = $settingManager; 84 } 85 86 /** 87 * @return DataResponse 88 */ 89 public function getApps(): DataResponse { 90 return new DataResponse([ 91 'data' => $this->appConfig->getApps(), 92 ]); 93 } 94 95 /** 96 * @param string $app 97 * @return DataResponse 98 */ 99 public function getKeys(string $app): DataResponse { 100 try { 101 $this->verifyAppId($app); 102 } catch (\InvalidArgumentException $e) { 103 return new DataResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_FORBIDDEN); 104 } 105 return new DataResponse([ 106 'data' => $this->config->getAppKeys($app), 107 ]); 108 } 109 110 /** 111 * @param string $app 112 * @param string $key 113 * @param string $defaultValue 114 * @return DataResponse 115 */ 116 public function getValue(string $app, string $key, string $defaultValue = ''): DataResponse { 117 try { 118 $this->verifyAppId($app); 119 } catch (\InvalidArgumentException $e) { 120 return new DataResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_FORBIDDEN); 121 } 122 return new DataResponse([ 123 'data' => $this->config->getAppValue($app, $key, $defaultValue), 124 ]); 125 } 126 127 /** 128 * @PasswordConfirmationRequired 129 * @NoSubAdminRequired 130 * @NoAdminRequired 131 * @param string $app 132 * @param string $key 133 * @param string $value 134 * @return DataResponse 135 */ 136 public function setValue(string $app, string $key, string $value): DataResponse { 137 $user = $this->userSession->getUser(); 138 if ($user === null) { 139 throw new \Exception("User is not logged in."); // Should not happen, since method is guarded by middleware 140 } 141 142 if (!$this->isAllowedToChangedKey($user, $app, $key)) { 143 throw new NotAdminException($this->l10n->t('Logged in user must be an administrator or have authorization to edit this setting.')); 144 } 145 146 try { 147 $this->verifyAppId($app); 148 $this->verifyConfigKey($app, $key, $value); 149 } catch (\InvalidArgumentException $e) { 150 return new DataResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_FORBIDDEN); 151 } 152 153 $this->config->setAppValue($app, $key, $value); 154 return new DataResponse(); 155 } 156 157 /** 158 * @PasswordConfirmationRequired 159 * @param string $app 160 * @param string $key 161 * @return DataResponse 162 */ 163 public function deleteKey(string $app, string $key): DataResponse { 164 try { 165 $this->verifyAppId($app); 166 $this->verifyConfigKey($app, $key, ''); 167 } catch (\InvalidArgumentException $e) { 168 return new DataResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_FORBIDDEN); 169 } 170 171 $this->config->deleteAppValue($app, $key); 172 return new DataResponse(); 173 } 174 175 /** 176 * @param string $app 177 * @throws \InvalidArgumentException 178 */ 179 protected function verifyAppId(string $app) { 180 if (\OC_App::cleanAppId($app) !== $app) { 181 throw new \InvalidArgumentException('Invalid app id given'); 182 } 183 } 184 185 /** 186 * @param string $app 187 * @param string $key 188 * @param string $value 189 * @throws \InvalidArgumentException 190 */ 191 protected function verifyConfigKey(string $app, string $key, string $value) { 192 if (in_array($key, ['installed_version', 'enabled', 'types'])) { 193 throw new \InvalidArgumentException('The given key can not be set'); 194 } 195 196 if ($app === 'core' && $key === 'encryption_enabled' && $value !== 'yes') { 197 throw new \InvalidArgumentException('The given key can not be set'); 198 } 199 200 if ($app === 'core' && (strpos($key, 'public_') === 0 || strpos($key, 'remote_') === 0)) { 201 throw new \InvalidArgumentException('The given key can not be set'); 202 } 203 204 if ($app === 'files' 205 && $key === 'default_quota' 206 && $value === 'none' 207 && $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '0') { 208 throw new \InvalidArgumentException('The given key can not be set, unlimited quota is forbidden on this instance'); 209 } 210 } 211 212 private function isAllowedToChangedKey(IUser $user, string $app, string $key): bool { 213 // Admin right verification 214 $isAdmin = $this->groupManager->isAdmin($user->getUID()); 215 if ($isAdmin) { 216 return true; 217 } 218 219 $settings = $this->settingManager->getAllAllowedAdminSettings($user); 220 foreach ($settings as $setting) { 221 if (!($setting instanceof IDelegatedSettings)) { 222 continue; 223 } 224 $allowedKeys = $setting->getAuthorizedAppConfig(); 225 if (!array_key_exists($app, $allowedKeys)) { 226 continue; 227 } 228 foreach ($allowedKeys[$app] as $regex) { 229 if ($regex === $key 230 || (str_starts_with($regex, '/') && preg_match($regex, $key) === 1)) { 231 return true; 232 } 233 } 234 } 235 return false; 236 } 237} 238