ApiController.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <?php
  2. namespace Opentalent\OtAdmin\Http;
  3. use Opentalent\OtAdmin\Controller\ScanController;
  4. use Opentalent\OtAdmin\Controller\SiteController;
  5. use Psr\Log\LoggerAwareInterface;
  6. use Psr\Log\LoggerAwareTrait;
  7. use TYPO3\CMS\Core\Database\ConnectionPool;
  8. use TYPO3\CMS\Core\Http\JsonResponse;
  9. use TYPO3\CMS\Core\Http\ServerRequest;
  10. use TYPO3\CMS\Core\Utility\GeneralUtility;
  11. /**
  12. * Actions for Http API calls
  13. *
  14. * @package Opentalent\OtAdmin\Http
  15. */
  16. class ApiController implements LoggerAwareInterface
  17. {
  18. use LoggerAwareTrait;
  19. const array ALLOWED_IPS = [
  20. '/^127\.0\.0\.[0-1]$/', // Localhost
  21. '/^localhost$/', // Localhost
  22. '/^10\.8\.0\.\d{1,3}$/', // 10.8.0.[0-255] - VPN
  23. '/^141\.94\.117\.((3[3-9])|(4\d)|(5\d)|(6[0-1]))$/', // 141.94.117.[33-61] - Opentalent hosts public ips
  24. '/^172\.16\.0.\d{1,3}$/', // 172.16.0.[0-255] - Opentalent hosts private ips
  25. '/^172\.20\.\d{1,3}\.\d{1,3}$/', // 172.20.[0-255].[0-255] - Docker
  26. ];
  27. public function __construct(
  28. private readonly SiteController $siteController
  29. ) {}
  30. /**
  31. * Returns true if the client Ip is allowed
  32. *
  33. * @param string $clientIp
  34. * @return bool
  35. */
  36. public static function isIpAllowed(string $clientIp): bool
  37. {
  38. foreach (self::ALLOWED_IPS as $ipRule) {
  39. if (preg_match($ipRule, $clientIp)) {
  40. return true;
  41. }
  42. }
  43. return false;
  44. }
  45. /**
  46. * Check that the client Ip is allowed, else throw a Runtime error
  47. *
  48. * @return bool
  49. */
  50. private function assertIpAllowed(): bool
  51. {
  52. $clientIp = $_SERVER['REMOTE_ADDR'];
  53. if (!self::isIpAllowed($clientIp)){
  54. $route = $_REQUEST['route'];
  55. $this->logger->error(sprintf(
  56. "OtAdmin API: an attempt was made to call the route " .
  57. $route . " from an non-allowed IP (" . $clientIp . ")"));
  58. throw new \RuntimeException("Not allowed");
  59. }
  60. return true;
  61. }
  62. /**
  63. * Retrieve the organization's id from the given request parameters
  64. *
  65. * @param ServerRequest $request
  66. * @return int
  67. */
  68. private function getOrganizationId(ServerRequest $request): int
  69. {
  70. $params = $request->getQueryParams();
  71. $organizationId = $params['organization-id'];
  72. if (!$organizationId) {
  73. throw new \RuntimeException("Missing parameter: 'organization-id'");
  74. }
  75. return (int)$organizationId;
  76. }
  77. /**
  78. * -- Target of the route 'site_infos' --
  79. *
  80. * Return the main informations about the organization's website
  81. *
  82. * @param ServerRequest $request
  83. * @return JsonResponse
  84. * @throws \Exception
  85. */
  86. public function getSiteInfosAction(ServerRequest $request): JsonResponse
  87. {
  88. $this->assertIpAllowed();
  89. $organizationId = $this->getOrganizationId($request);
  90. $infos = $this->siteController->getSiteInfosAction($organizationId);
  91. return new JsonResponse($infos);
  92. }
  93. /**
  94. * -- Target of the route 'site_create' --
  95. * >> Requires a query param named 'organization-id' (int)
  96. *
  97. * Create the organization's website
  98. *
  99. * @param ServerRequest $request
  100. * @return JsonResponse
  101. * @throws \Exception
  102. */
  103. public function createSiteAction(ServerRequest $request): JsonResponse
  104. {
  105. $this->assertIpAllowed();
  106. $organizationId = $this->getOrganizationId($request);
  107. $rootUid = $this->siteController->createSiteAction($organizationId);
  108. $this->logger->info(sprintf(
  109. "OtAdmin API: A new website has been created with root page uid=" . $rootUid .
  110. " for the organization " . $organizationId));
  111. return new JsonResponse(
  112. [
  113. 'organization_id' => $organizationId,
  114. 'msg' => "A new website has been created with root page uid=" . $rootUid,
  115. 'root_uid' => $rootUid
  116. ]
  117. );
  118. }
  119. /**
  120. * -- Target of the route 'site_update' --
  121. * >> Requires a query param named 'organization-id' (int)
  122. *
  123. * Update the settings of the organization's website
  124. *
  125. * @param ServerRequest $request
  126. * @return JsonResponse
  127. * @throws \Exception
  128. */
  129. public function updateSiteConstantsAction(ServerRequest $request): JsonResponse
  130. {
  131. $this->assertIpAllowed();
  132. $organizationId = $this->getOrganizationId($request);
  133. $deep = (isset($queryParams['deep']) && $queryParams['deep']);
  134. $rootUid = $this->siteController->updateSiteAction($organizationId, $deep);
  135. $this->logger->info(sprintf(
  136. "OtAdmin API: The website with root uid " . $rootUid . " has been updated " .
  137. " (organization: " . $organizationId . ")"));
  138. return new JsonResponse(
  139. [
  140. 'organization_id' => $organizationId,
  141. 'msg' => "The website with root uid " . $rootUid . " has been updated",
  142. 'root_uid' => $rootUid
  143. ]
  144. );
  145. }
  146. /**
  147. * -- Target of the route 'redirect_add' --
  148. * >> Requires query params named 'from-domain' (string) and 'to-domain' (string)
  149. *
  150. * Add or update a redirection from 'from-domain' to 'to-domain'
  151. *
  152. * @param ServerRequest $request
  153. * @return JsonResponse
  154. * @throws \Exception
  155. */
  156. public function addRedirectionAction(ServerRequest $request): JsonResponse
  157. {
  158. $this->assertIpAllowed();
  159. $fromDomain = (isset($queryParams['from-domain']) && $queryParams['from-domain']);
  160. $toDomain = (isset($queryParams['to-domain']) && $queryParams['to-domain']);
  161. $res = $this->siteController->addRedirection($fromDomain, $toDomain);
  162. if ($res === SiteController::REDIRECTION_UPDATED) {
  163. $msg = "An existing redirection has been updated ";
  164. } elseif ($res === SiteController::REDIRECTION_CREATED) {
  165. $msg = "A redirection has been added ";
  166. }
  167. $this->logger->info(sprintf(
  168. "OtAdmin API: " . $msg . " from " . $fromDomain . " to " . $toDomain
  169. ));
  170. return new JsonResponse(
  171. [
  172. 'msg' => $msg . " from " . $fromDomain . " to " . $toDomain,
  173. ]
  174. );
  175. }
  176. /**
  177. * -- Target of the route 'site_delete' --
  178. * >> Requires a query param named 'organization-id' (int)
  179. *
  180. * Proceeds to a soft-deletion of the organization's website
  181. *
  182. * @param ServerRequest $request
  183. * @return JsonResponse
  184. * @throws \Exception
  185. */
  186. public function deleteSiteAction(ServerRequest $request): JsonResponse
  187. {
  188. $this->assertIpAllowed();
  189. $organizationId = $this->getOrganizationId($request);
  190. $params = $request->getQueryParams();
  191. $rootUid = $this->siteController->deleteSiteAction($organizationId);
  192. $this->logger->info(sprintf(
  193. "OtAdmin API: The website with root uid " . $rootUid . " has been soft-deleted " .
  194. " (organization: " . $organizationId . ")"));
  195. return new JsonResponse(
  196. [
  197. 'organization_id' => $organizationId,
  198. 'msg' => "The website with root uid " . $rootUid . " has been soft-deleted. Use the /site/undelete route to restore it.",
  199. 'root_uid' => $rootUid
  200. ]
  201. );
  202. }
  203. /**
  204. * -- Target of the route 'site_undelete' --
  205. * >> Requires a query param named 'organization-id' (int)
  206. *
  207. * Restore a soft-deleted organization's website
  208. *
  209. * @param ServerRequest $request
  210. * @return JsonResponse
  211. * @throws \Exception
  212. */
  213. public function undeleteSiteAction(ServerRequest $request): JsonResponse
  214. {
  215. $this->assertIpAllowed();
  216. $organizationId = $this->getOrganizationId($request);
  217. $rootUid = $this->siteController->undeleteSiteAction($organizationId);
  218. $this->logger->info(sprintf(
  219. "OtAdmin API: The website with root uid " . $rootUid . " has been restored " .
  220. " (organization: " . $organizationId . ")"));
  221. return new JsonResponse(
  222. [
  223. 'organization_id' => $organizationId,
  224. 'msg' => "The website with root uid " . $rootUid . " has been restored",
  225. 'root_uid' => $rootUid
  226. ]
  227. );
  228. }
  229. /**
  230. * -- Target of the route 'site_clearcache' --
  231. * >> Requires a query param named 'organization-id' (int)
  232. *
  233. * Clear the cache of the organization's website
  234. *
  235. * @param ServerRequest $request
  236. * @return JsonResponse
  237. * @throws \Exception
  238. */
  239. public function clearSiteCacheAction(ServerRequest $request): JsonResponse
  240. {
  241. $this->assertIpAllowed();
  242. $organizationId = $this->getOrganizationId($request);
  243. $queryParams = $request->getQueryParams();
  244. $clearAll = (isset($queryParams['all']) && $queryParams['all']);;
  245. $rootUid = $this->siteController->clearSiteCacheAction($organizationId, $clearAll);
  246. return new JsonResponse(
  247. [
  248. 'organization_id' => $organizationId,
  249. 'msg' => "The cache has been cleared for the website with root uid " . $rootUid . "",
  250. 'root_uid' => $rootUid
  251. ]
  252. );
  253. }
  254. /**
  255. * -- Target of the route 'site_setdomain' --
  256. * >> Requires a query param named 'organization-id' (int)
  257. * and a parameter named 'domain' (string)
  258. *
  259. * Set a new domain for the organization website
  260. *
  261. * @param ServerRequest $request
  262. * @return JsonResponse
  263. * @throws \Exception
  264. */
  265. public function setSiteCustomDomainAction(ServerRequest $request): JsonResponse
  266. {
  267. $this->assertIpAllowed();
  268. $organizationId = $this->getOrganizationId($request);
  269. $queryParams = $request->getQueryParams();
  270. $domain = $queryParams['domain'];
  271. if (!$domain) {
  272. throw new \RuntimeException("Missing 'domain' parameter");
  273. }
  274. $redirect = (isset($queryParams['redirect']) && $queryParams['redirect']);
  275. $rootUid = $this->siteController->setSiteCustomDomainAction($organizationId, $domain, $redirect);
  276. return new JsonResponse(
  277. [
  278. 'organization_id' => $organizationId,
  279. 'msg' => "The cache has been cleared for the website with root uid " . $rootUid . "",
  280. 'root_uid' => $rootUid
  281. ]
  282. );
  283. }
  284. /**
  285. * -- Target of the route 'site_resetperms' --
  286. * >> Requires a query param named 'organization-id' (int)
  287. *
  288. * Reset the permissions of the website be users (admin, editors...)
  289. *
  290. * @param ServerRequest $request
  291. * @return JsonResponse
  292. * @throws \Exception
  293. */
  294. public function resetBeUserPermsAction(ServerRequest $request): JsonResponse
  295. {
  296. $this->assertIpAllowed();
  297. $organizationId = $this->getOrganizationId($request);
  298. $rootUid = $this->siteController->resetBeUserPermsAction($organizationId);
  299. return new JsonResponse(
  300. [
  301. 'organization_id' => $organizationId,
  302. 'msg' => "The website with root uid " . $rootUid . " had its be users permissions reset",
  303. 'root_uid' => $rootUid
  304. ]
  305. );
  306. }
  307. /**
  308. * -- Target of the route 'site_status' --
  309. * >> Requires a query param named 'organization-id' (int)
  310. *
  311. * Returns the current status of the website
  312. *
  313. * @param ServerRequest $request
  314. * @return JsonResponse
  315. * @throws \Exception
  316. */
  317. public function getSiteStatusAction(ServerRequest $request): JsonResponse
  318. {
  319. $this->assertIpAllowed();
  320. $organizationId = $this->getOrganizationId($request);
  321. $queryParams = $request->getQueryParams();
  322. $full = (isset($queryParams['full']) && $queryParams['full']);
  323. $status = $this->siteController->getSiteStatusAction($organizationId, $full);
  324. return new JsonResponse($status->toArray());
  325. }
  326. /**
  327. * -- Target of the route 'scan' --
  328. *
  329. * Scan the whole Typo3 database and return the results
  330. *
  331. * @param ServerRequest $request
  332. * @return JsonResponse
  333. * @throws \Exception
  334. */
  335. public function scanAllAction(ServerRequest $request): JsonResponse
  336. {
  337. $this->assertIpAllowed();
  338. $queryParams = $request->getQueryParams();
  339. $full = (isset($queryParams['full']) && $queryParams['full']);
  340. $results = $this->siteController->scanAllAction($full);
  341. return new JsonResponse($results);
  342. }
  343. }