ApiController.php 13 KB

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