PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/applications/dashboard/controllers/api/ConfigApiController.php

http://github.com/vanillaforums/Garden
PHP | 217 lines | 124 code | 28 blank | 65 comment | 10 complexity | e2e30d2b7e219e9cee10273a0b6c623e MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-3-Clause, MIT
  1. <?php
  2. /**
  3. * @author Todd Burry <todd@vanillaforums.com>
  4. * @copyright 2009-2020 Vanilla Forums Inc.
  5. * @license GPL-2.0-only
  6. */
  7. namespace Vanilla\Dashboard\Controllers\API;
  8. use Garden\Schema\Schema;
  9. use Garden\Web\Data;
  10. use Vanilla\Contracts\ConfigurationInterface;
  11. use Vanilla\FileUtils;
  12. use Vanilla\OpenAPIBuilder;
  13. use Vanilla\Utility\ArrayUtils;
  14. use Vanilla\Web\Controller;
  15. /**
  16. * API endpoints for the /config resource.
  17. */
  18. final class ConfigApiController extends Controller {
  19. public const PERM_PUBLIC = 'public';
  20. public const PERM_MEMBER = 'member';
  21. public const PERM_MODERATOR = 'community.moderate';
  22. public const PERM_COMMUNITY_MANAGER = 'community.manage';
  23. public const PERM_ADMIN = 'site.manage';
  24. /**
  25. * All of the permissions that are valid for config reading.
  26. */
  27. public const READ_PERMS = [
  28. self::PERM_PUBLIC,
  29. self::PERM_MEMBER,
  30. self::PERM_MODERATOR,
  31. self::PERM_COMMUNITY_MANAGER,
  32. self::PERM_ADMIN,
  33. ];
  34. /**
  35. * All of the permissions that are valid for config writing.
  36. */
  37. public const WRITE_PERMS = [
  38. self::PERM_COMMUNITY_MANAGER,
  39. self::PERM_ADMIN,
  40. ];
  41. /**
  42. * @var OpenAPIBuilder
  43. */
  44. private $apiBuilder;
  45. /**
  46. * @var ConfigurationInterface
  47. */
  48. private $config;
  49. /**
  50. * @var string
  51. */
  52. private $cachePath;
  53. /**
  54. * ConfigApiController constructor.
  55. *
  56. * @param OpenAPIBuilder $apiBuilder
  57. * @param ConfigurationInterface $config
  58. * @param string $cachePath
  59. */
  60. public function __construct(OpenAPIBuilder $apiBuilder, ConfigurationInterface $config, string $cachePath = '') {
  61. $this->apiBuilder = $apiBuilder;
  62. $this->config = $config;
  63. $this->cachePath = $cachePath ?: PATH_CACHE.'/config-schema.php';
  64. }
  65. /**
  66. * The GET /api/v2/config endpoint.
  67. *
  68. * @param array $query
  69. * @return Data
  70. */
  71. public function get(array $query = []): Data {
  72. $in = $this->schema([
  73. 'select?' => [
  74. 'type' => 'array',
  75. 'items' => [
  76. 'type' => 'string'
  77. ],
  78. 'style' => 'form',
  79. ]
  80. ], 'in');
  81. $query = $in->validate($query);
  82. $select = $query['select'] ?? null;
  83. $out = $this->getConfigSchema();
  84. $result = [];
  85. foreach ($out->getField('properties') as $key => $item) {
  86. if (is_array($select)) {
  87. $matched = false;
  88. foreach ($select as $pattern) {
  89. if (fnmatch($pattern, $key)) {
  90. $matched = true;
  91. break;
  92. }
  93. }
  94. if (!$matched) {
  95. continue;
  96. }
  97. }
  98. $permission = $this->realPermissionName($item['x-read'] ?? self::PERM_ADMIN);
  99. if ($permission === 'public' ||
  100. ($permission === self::PERM_MEMBER && $this->getSession()->isValid()) ||
  101. $this->getSession()->checkPermission($permission)
  102. ) {
  103. $configKey = $item['x-key'] ?? $key;
  104. $result[$key] = $this->config->get($configKey, $item['default'] ?? null);
  105. }
  106. }
  107. return new Data($result);
  108. }
  109. /**
  110. * The PATCH /api/v2/config endpoint
  111. *
  112. * @param array $body
  113. * @return Data
  114. */
  115. public function patch(array $body): Data {
  116. $in = $this->getConfigSchema();
  117. // ApiKey => ConfigKey
  118. $propertyMapping = [];
  119. // Make sure the user has all necessary permissions.
  120. $permissions = [];
  121. foreach ($in->getField('properties') as $key => $item) {
  122. if (array_key_exists($key, $body)) {
  123. $permissions[$this->realPermissionName($item['x-write'] ?? self::PERM_ADMIN)] = true;
  124. }
  125. $actualConfigKey = $item['x-key'] ?? $key;
  126. if ($actualConfigKey !== $key) {
  127. $propertyMapping[$key] = $actualConfigKey;
  128. }
  129. }
  130. $this->permission(array_keys($permissions));
  131. $in->setFlag(Schema::VALIDATE_EXTRA_PROPERTY_EXCEPTION, true);
  132. $valid = $in->validate($body, true);
  133. $mapped = [];
  134. foreach ($valid as $key => $value) {
  135. $configKey = $propertyMapping[$key] ?? $key;
  136. $mapped[$configKey] = $value;
  137. }
  138. $this->config->saveToConfig($mapped);
  139. return new Data(null);
  140. }
  141. /**
  142. * Return the config data schema.
  143. *
  144. * @return Data
  145. */
  146. public function get_schema(): Data {
  147. $this->permission('Garden.Settings.Manage');
  148. $schema = $this->getConfigSchema();
  149. $r = new Data($schema->getSchemaArray());
  150. return $r;
  151. }
  152. /**
  153. * Get the config schema.
  154. *
  155. * @return Schema
  156. */
  157. private function getConfigSchema(): Schema {
  158. if (file_exists($this->cachePath)) {
  159. $r = FileUtils::getExport($this->cachePath);
  160. } else {
  161. $r = $this->buildConfigSchemaArray();
  162. FileUtils::putExport($this->cachePath, $r);
  163. }
  164. return new Schema($r);
  165. }
  166. /**
  167. * Build the config schema.
  168. *
  169. * @return array
  170. */
  171. private function buildConfigSchemaArray(): array {
  172. $openAPI = $this->apiBuilder->getFullOpenAPI();
  173. $config = $openAPI['components']['schemas']['Config'];
  174. ksort($config['properties']);
  175. return $config;
  176. }
  177. /**
  178. * Get the real permission name corresponding to the permission requested.
  179. *
  180. * @param string $permission
  181. * @return string
  182. */
  183. private function realPermissionName(string $permission) {
  184. if (in_array($permission, [self::PERM_PUBLIC, self::PERM_MEMBER])) {
  185. return $permission;
  186. }
  187. return $this->getSession()->getPermissions()->untranslatePermission($permission);
  188. }
  189. }