AppConfigControllerTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
  4. *
  5. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Morris Jobke <hey@morrisjobke.de>
  8. * @author Roeland Jago Douma <roeland@famdouma.nl>
  9. *
  10. * @license GNU AGPL version 3 or any later version
  11. *
  12. * This program is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Affero General Public License as
  14. * published by the Free Software Foundation, either version 3 of the
  15. * License, or (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU Affero General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Affero General Public License
  23. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  24. *
  25. */
  26. namespace OCA\Provisioning_API\Tests\Controller;
  27. use OC\AppConfig;
  28. use OCA\Provisioning_API\Controller\AppConfigController;
  29. use OCP\AppFramework\Http;
  30. use OCP\AppFramework\Http\DataResponse;
  31. use OCP\Exceptions\AppConfigUnknownKeyException;
  32. use OCP\IAppConfig;
  33. use OCP\IGroupManager;
  34. use OCP\IL10N;
  35. use OCP\IRequest;
  36. use OCP\IUser;
  37. use OCP\IUserSession;
  38. use OCP\Settings\IManager;
  39. use Test\TestCase;
  40. use function json_decode;
  41. use function json_encode;
  42. /**
  43. * Class AppConfigControllerTest
  44. *
  45. * @package OCA\Provisioning_API\Tests
  46. */
  47. class AppConfigControllerTest extends TestCase {
  48. /** @var IAppConfig|\PHPUnit\Framework\MockObject\MockObject */
  49. private $appConfig;
  50. /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
  51. private $userSession;
  52. /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
  53. private $l10n;
  54. /** @var IManager|\PHPUnit\Framework\MockObject\MockObject */
  55. private $settingManager;
  56. /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
  57. private $groupManager;
  58. protected function setUp(): void {
  59. parent::setUp();
  60. $this->appConfig = $this->createMock(AppConfig::class);
  61. $this->userSession = $this->createMock(IUserSession::class);
  62. $this->l10n = $this->createMock(IL10N::class);
  63. $this->groupManager = $this->createMock(IGroupManager::class);
  64. $this->settingManager = $this->createMock(IManager::class);
  65. }
  66. /**
  67. * @param string[] $methods
  68. * @return AppConfigController|\PHPUnit\Framework\MockObject\MockObject
  69. */
  70. protected function getInstance(array $methods = []) {
  71. $request = $this->createMock(IRequest::class);
  72. if (empty($methods)) {
  73. return new AppConfigController(
  74. 'provisioning_api',
  75. $request,
  76. $this->appConfig,
  77. $this->userSession,
  78. $this->l10n,
  79. $this->groupManager,
  80. $this->settingManager
  81. );
  82. } else {
  83. return $this->getMockBuilder(AppConfigController::class)
  84. ->setConstructorArgs([
  85. 'provisioning_api',
  86. $request,
  87. $this->appConfig,
  88. $this->userSession,
  89. $this->l10n,
  90. $this->groupManager,
  91. $this->settingManager
  92. ])
  93. ->setMethods($methods)
  94. ->getMock();
  95. }
  96. }
  97. public function testGetApps() {
  98. $this->appConfig->expects($this->once())
  99. ->method('getApps')
  100. ->willReturn(['apps']);
  101. $result = $this->getInstance()->getApps();
  102. $this->assertInstanceOf(DataResponse::class, $result);
  103. $this->assertSame(Http::STATUS_OK, $result->getStatus());
  104. $this->assertEquals(['data' => ['apps']], $result->getData());
  105. }
  106. public function dataGetKeys() {
  107. return [
  108. ['app1 ', null, new \InvalidArgumentException('error'), Http::STATUS_FORBIDDEN],
  109. ['app2', ['keys'], null, Http::STATUS_OK],
  110. ];
  111. }
  112. /**
  113. * @dataProvider dataGetKeys
  114. * @param string $app
  115. * @param array|null $keys
  116. * @param \Exception|null $throws
  117. * @param int $status
  118. */
  119. public function testGetKeys($app, $keys, $throws, $status) {
  120. $api = $this->getInstance(['verifyAppId']);
  121. if ($throws instanceof \Exception) {
  122. $api->expects($this->once())
  123. ->method('verifyAppId')
  124. ->with($app)
  125. ->willThrowException($throws);
  126. $this->appConfig->expects($this->never())
  127. ->method('getKeys');
  128. } else {
  129. $api->expects($this->once())
  130. ->method('verifyAppId')
  131. ->with($app);
  132. $this->appConfig->expects($this->once())
  133. ->method('getKeys')
  134. ->with($app)
  135. ->willReturn($keys);
  136. }
  137. $result = $api->getKeys($app);
  138. $this->assertInstanceOf(DataResponse::class, $result);
  139. $this->assertSame($status, $result->getStatus());
  140. if ($throws instanceof \Exception) {
  141. $this->assertEquals(['data' => ['message' => $throws->getMessage()]], $result->getData());
  142. } else {
  143. $this->assertEquals(['data' => $keys], $result->getData());
  144. }
  145. }
  146. public function dataGetValue() {
  147. return [
  148. ['app1', 'key', 'default', null, new \InvalidArgumentException('error'), Http::STATUS_FORBIDDEN],
  149. ['app2', 'key', 'default', 'return', null, Http::STATUS_OK],
  150. ];
  151. }
  152. /**
  153. * @dataProvider dataGetValue
  154. * @param string $app
  155. * @param string|null $key
  156. * @param string|null $default
  157. * @param string|null $return
  158. * @param \Exception|null $throws
  159. * @param int $status
  160. */
  161. public function testGetValue($app, $key, $default, $return, $throws, $status) {
  162. $api = $this->getInstance(['verifyAppId']);
  163. if ($throws instanceof \Exception) {
  164. $api->expects($this->once())
  165. ->method('verifyAppId')
  166. ->with($app)
  167. ->willThrowException($throws);
  168. } else {
  169. $api->expects($this->once())
  170. ->method('verifyAppId')
  171. ->with($app);
  172. $this->appConfig->expects($this->once())
  173. ->method('getValueMixed')
  174. ->with($app, $key, $default)
  175. ->willReturn($return);
  176. }
  177. $result = $api->getValue($app, $key, $default);
  178. $this->assertInstanceOf(DataResponse::class, $result);
  179. $this->assertSame($status, $result->getStatus());
  180. if ($throws instanceof \Exception) {
  181. $this->assertEquals(['data' => ['message' => $throws->getMessage()]], $result->getData());
  182. } else {
  183. $this->assertEquals(['data' => $return], $result->getData());
  184. }
  185. }
  186. public function dataSetValue() {
  187. return [
  188. ['app1', 'key', 'default', new \InvalidArgumentException('error1'), null, Http::STATUS_FORBIDDEN],
  189. ['app2', 'key', 'default', null, new \InvalidArgumentException('error2'), Http::STATUS_FORBIDDEN],
  190. ['app2', 'key', 'default', null, null, Http::STATUS_OK],
  191. ['app2', 'key', '1', null, null, Http::STATUS_OK, IAppConfig::VALUE_BOOL],
  192. ['app2', 'key', '42', null, null, Http::STATUS_OK, IAppConfig::VALUE_INT],
  193. ['app2', 'key', '4.2', null, null, Http::STATUS_OK, IAppConfig::VALUE_FLOAT],
  194. ['app2', 'key', '42', null, null, Http::STATUS_OK, IAppConfig::VALUE_STRING],
  195. ['app2', 'key', 'secret', null, null, Http::STATUS_OK, IAppConfig::VALUE_STRING | IAppConfig::VALUE_SENSITIVE],
  196. ['app2', 'key', json_encode([4, 2]), null, null, Http::STATUS_OK, IAppConfig::VALUE_ARRAY],
  197. ['app2', 'key', json_encode([4, 2]), null, null, Http::STATUS_OK, new AppConfigUnknownKeyException()],
  198. ];
  199. }
  200. /**
  201. * @dataProvider dataSetValue
  202. * @param string $app
  203. * @param string|null $key
  204. * @param string|null $value
  205. * @param \Exception|null $appThrows
  206. * @param \Exception|null $keyThrows
  207. * @param int|\Throwable $status
  208. */
  209. public function testSetValue($app, $key, $value, $appThrows, $keyThrows, $status, int|\Throwable $type = IAppConfig::VALUE_MIXED) {
  210. $adminUser = $this->createMock(IUser::class);
  211. $adminUser->expects($this->once())
  212. ->method('getUid')
  213. ->willReturn('admin');
  214. $this->userSession->expects($this->once())
  215. ->method('getUser')
  216. ->willReturn($adminUser);
  217. $this->groupManager->expects($this->once())
  218. ->method('isAdmin')
  219. ->with('admin')
  220. ->willReturn(true);
  221. $api = $this->getInstance(['verifyAppId', 'verifyConfigKey']);
  222. if ($appThrows instanceof \Exception) {
  223. $api->expects($this->once())
  224. ->method('verifyAppId')
  225. ->with($app)
  226. ->willThrowException($appThrows);
  227. $api->expects($this->never())
  228. ->method('verifyConfigKey');
  229. $this->appConfig->expects($this->never())
  230. ->method('setValueMixed');
  231. } elseif ($keyThrows instanceof \Exception) {
  232. $api->expects($this->once())
  233. ->method('verifyAppId')
  234. ->with($app);
  235. $api->expects($this->once())
  236. ->method('verifyConfigKey')
  237. ->with($app, $key)
  238. ->willThrowException($keyThrows);
  239. $this->appConfig->expects($this->never())
  240. ->method('setValueMixed');
  241. } else {
  242. $api->expects($this->once())
  243. ->method('verifyAppId')
  244. ->with($app);
  245. $api->expects($this->once())
  246. ->method('verifyConfigKey')
  247. ->with($app, $key);
  248. if ($type instanceof \Throwable) {
  249. $this->appConfig->expects($this->once())
  250. ->method('getDetails')
  251. ->with($app, $key)
  252. ->willThrowException($type);
  253. } else {
  254. $this->appConfig->expects($this->once())
  255. ->method('getDetails')
  256. ->with($app, $key)
  257. ->willReturn([
  258. 'app' => $app,
  259. 'key' => $key,
  260. 'value' => '', // 🤷
  261. 'type' => $type,
  262. 'lazy' => false,
  263. 'typeString' => (string)$type, // this is not accurate, but acceptable
  264. 'sensitive' => ($type & IAppConfig::VALUE_SENSITIVE) !== 0,
  265. ]);
  266. }
  267. $configValueSetter = match ($type) {
  268. IAppConfig::VALUE_BOOL => 'setValueBool',
  269. IAppConfig::VALUE_FLOAT => 'setValueFloat',
  270. IAppConfig::VALUE_INT => 'setValueInt',
  271. IAppConfig::VALUE_STRING => 'setValueString',
  272. IAppConfig::VALUE_ARRAY => 'setValueArray',
  273. default => 'setValueMixed',
  274. };
  275. $this->appConfig->expects($this->once())
  276. ->method($configValueSetter)
  277. ->with($app, $key, $configValueSetter === 'setValueArray' ? json_decode($value, true) : $value);
  278. }
  279. $result = $api->setValue($app, $key, $value);
  280. $this->assertInstanceOf(DataResponse::class, $result);
  281. $this->assertSame($status, $result->getStatus());
  282. if ($appThrows instanceof \Exception) {
  283. $this->assertEquals(['data' => ['message' => $appThrows->getMessage()]], $result->getData());
  284. } elseif ($keyThrows instanceof \Exception) {
  285. $this->assertEquals(['data' => ['message' => $keyThrows->getMessage()]], $result->getData());
  286. } else {
  287. $this->assertEquals([], $result->getData());
  288. }
  289. }
  290. public function dataDeleteValue() {
  291. return [
  292. ['app1', 'key', new \InvalidArgumentException('error1'), null, Http::STATUS_FORBIDDEN],
  293. ['app2', 'key', null, new \InvalidArgumentException('error2'), Http::STATUS_FORBIDDEN],
  294. ['app2', 'key', null, null, Http::STATUS_OK],
  295. ];
  296. }
  297. /**
  298. * @dataProvider dataDeleteValue
  299. * @param string $app
  300. * @param string|null $key
  301. * @param \Exception|null $appThrows
  302. * @param \Exception|null $keyThrows
  303. * @param int $status
  304. */
  305. public function testDeleteValue($app, $key, $appThrows, $keyThrows, $status) {
  306. $api = $this->getInstance(['verifyAppId', 'verifyConfigKey']);
  307. if ($appThrows instanceof \Exception) {
  308. $api->expects($this->once())
  309. ->method('verifyAppId')
  310. ->with($app)
  311. ->willThrowException($appThrows);
  312. $api->expects($this->never())
  313. ->method('verifyConfigKey');
  314. $this->appConfig->expects($this->never())
  315. ->method('deleteKey');
  316. } elseif ($keyThrows instanceof \Exception) {
  317. $api->expects($this->once())
  318. ->method('verifyAppId')
  319. ->with($app);
  320. $api->expects($this->once())
  321. ->method('verifyConfigKey')
  322. ->with($app, $key)
  323. ->willThrowException($keyThrows);
  324. $this->appConfig->expects($this->never())
  325. ->method('deleteKey');
  326. } else {
  327. $api->expects($this->once())
  328. ->method('verifyAppId')
  329. ->with($app);
  330. $api->expects($this->once())
  331. ->method('verifyConfigKey')
  332. ->with($app, $key);
  333. $this->appConfig->expects($this->once())
  334. ->method('deleteKey')
  335. ->with($app, $key);
  336. }
  337. $result = $api->deleteKey($app, $key);
  338. $this->assertInstanceOf(DataResponse::class, $result);
  339. $this->assertSame($status, $result->getStatus());
  340. if ($appThrows instanceof \Exception) {
  341. $this->assertEquals(['data' => ['message' => $appThrows->getMessage()]], $result->getData());
  342. } elseif ($keyThrows instanceof \Exception) {
  343. $this->assertEquals(['data' => ['message' => $keyThrows->getMessage()]], $result->getData());
  344. } else {
  345. $this->assertEquals([], $result->getData());
  346. }
  347. }
  348. public function testVerifyAppId() {
  349. $api = $this->getInstance();
  350. $this->invokePrivate($api, 'verifyAppId', ['activity']);
  351. $this->addToAssertionCount(1);
  352. }
  353. public function dataVerifyAppIdThrows() {
  354. return [
  355. ['activity..'],
  356. ['activity/'],
  357. ['activity\\'],
  358. ['activity\0'],
  359. ];
  360. }
  361. /**
  362. * @dataProvider dataVerifyAppIdThrows
  363. * @param string $app
  364. */
  365. public function testVerifyAppIdThrows($app) {
  366. $this->expectException(\InvalidArgumentException::class);
  367. $api = $this->getInstance();
  368. $this->invokePrivate($api, 'verifyAppId', [$app]);
  369. }
  370. public function dataVerifyConfigKey() {
  371. return [
  372. ['activity', 'abc', ''],
  373. ['dav', 'public_route', ''],
  374. ['files', 'remote_route', ''],
  375. ['core', 'encryption_enabled', 'yes'],
  376. ];
  377. }
  378. /**
  379. * @dataProvider dataVerifyConfigKey
  380. * @param string $app
  381. * @param string $key
  382. * @param string $value
  383. */
  384. public function testVerifyConfigKey($app, $key, $value) {
  385. $api = $this->getInstance();
  386. $this->invokePrivate($api, 'verifyConfigKey', [$app, $key, $value]);
  387. $this->addToAssertionCount(1);
  388. }
  389. public function dataVerifyConfigKeyThrows() {
  390. return [
  391. ['activity', 'installed_version', ''],
  392. ['calendar', 'enabled', ''],
  393. ['contacts', 'types', ''],
  394. ['core', 'encryption_enabled', 'no'],
  395. ['core', 'encryption_enabled', ''],
  396. ['core', 'public_files', ''],
  397. ['core', 'public_dav', ''],
  398. ['core', 'remote_files', ''],
  399. ['core', 'remote_dav', ''],
  400. ];
  401. }
  402. /**
  403. * @dataProvider dataVerifyConfigKeyThrows
  404. * @param string $app
  405. * @param string $key
  406. * @param string $value
  407. */
  408. public function testVerifyConfigKeyThrows($app, $key, $value) {
  409. $this->expectException(\InvalidArgumentException::class);
  410. $api = $this->getInstance();
  411. $this->invokePrivate($api, 'verifyConfigKey', [$app, $key, $value]);
  412. }
  413. }