PluginProbe
User Access Manager / 2.3.18
User Access Manager v2.3.18
2.3.20 2.3.19 2.3.18 2.3.17 2.3.16 2.3.15 2.3.14 2.3.13 trunk 0.6 0.6.1 0.6.2 0.7 0.7 Beta 0.7.0.1 0.8 0.8.0.1 0.8.0.2 0.9 0.9.1 0.9.1.1 0.9.1.2 0.9.1.3 0.9.1.4 1.0 All 136 releases
user-access-manager / src / Controller / Frontend / RedirectController.php

RedirectController.php in User Access Manager 2.3.18, at src/Controller/Frontend/RedirectController.php

386 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace UserAccessManager\Controller\Frontend;
6
7 use JetBrains\PhpStorm\NoReturn;
8 use UserAccessManager\Access\AccessHandler;
9 use UserAccessManager\Cache\Cache;
10 use UserAccessManager\Config\MainConfig;
11 use UserAccessManager\Config\WordpressConfig;
12 use UserAccessManager\Controller\Controller;
13 use UserAccessManager\Controller\Frontend\Authentication\LoginControllerTrait;
14 use UserAccessManager\Database\Database;
15 use UserAccessManager\File\FileHandler;
16 use UserAccessManager\File\Delivery\FileObject;
17 use UserAccessManager\File\Delivery\FileObjectFactory;
18 use UserAccessManager\Object\ObjectHandler;
19 use UserAccessManager\UserGroup\UserGroupTypeException;
20 use UserAccessManager\Util\Util;
21 use UserAccessManager\Wrapper\Php;
22 use UserAccessManager\Wrapper\Wordpress;
23
24 class RedirectController extends Controller
25 {
26 use LoginControllerTrait;
27
28 public const POST_URL_CACHE_KEY = 'PostUrls';
29 public const REDIRECT_TO_PARAMETER = 'redirect_to';
30
31 public function __construct(
32 Php $php,
33 Wordpress $wordpress,
34 WordpressConfig $wordpressConfig,
35 private MainConfig $mainConfig,
36 private Database $database,
37 private Util $util,
38 private Cache $cache,
39 private ObjectHandler $objectHandler,
40 private AccessHandler $accessHandler,
41 private FileHandler $fileHandler,
42 private FileObjectFactory $fileObjectFactory
43 ) {
44 parent::__construct($php, $wordpress, $wordpressConfig);
45 }
46
47 protected function getWordpress(): Wordpress
48 {
49 return $this->wordpress;
50 }
51
52 public function getPostIdByUrl(string $url): int
53 {
54 $postUrls = (array)$this->cache->getFromRuntimeCache(self::POST_URL_CACHE_KEY);
55
56 if (isset($postUrls[$url]) === true) {
57 return $postUrls[$url];
58 }
59
60 //Filter size
61 $newUrlPieces = preg_split('/-[0-9]+x[0-9]+(_[a-z])?/', $url);
62 $newUrl = (count($newUrlPieces) === 2) ? $newUrlPieces[0] . $newUrlPieces[1] : $newUrlPieces[0];
63 $newUrl = preg_replace('/-pdf\.jpg$/', '.pdf', $newUrl);
64
65 $postId = $this->wordpress->attachmentUrlToPostId($newUrl);
66
67 if ($postId === 0) {
68 $newUrl = preg_replace('/(\\.[^.\\s]{3,4})$/', '-scaled$1', $newUrl);
69 $postId = $this->wordpress->attachmentUrlToPostId($newUrl);
70 }
71
72 $postUrls[$url] = $postId;
73 $this->cache->addToRuntimeCache(self::POST_URL_CACHE_KEY, $postUrls);
74
75 return $postUrls[$url];
76 }
77
78 private function normalizeAttachmentUrl(array $uploadDirs, string $objectUrl): string
79 {
80 $uploadDir = str_replace(ABSPATH, '/', $uploadDirs['basedir']);
81 $regex = '/.*' . str_replace('/', '\/', $uploadDir) . '\//i';
82 $cleanObjectUrl = preg_replace($regex, '', $objectUrl);
83 $uploadUrl = str_replace('/files', $uploadDir, $uploadDirs['baseurl']);
84
85 return rtrim($uploadUrl, '/') . '/' . ltrim($cleanObjectUrl, '/');
86 }
87
88 private function isRegisteredSize(int $attachmentId, string $fileName): bool
89 {
90 $metaData = $this->wordpress->getAttachmentMetadata($attachmentId);
91 $sizes = is_array($metaData) === true ? (array) ($metaData['sizes'] ?? []) : [];
92
93 return in_array($fileName, array_column($sizes, 'file'), true);
94 }
95
96 /**
97 * Returns the file the request asks for and tells through $isImage whether it can be shown as image.
98 * A requested generated size is only used if it is registered at the attachment and stored inside the
99 * upload directory, so no arbitrary and no missing file becomes reachable. Generated sizes are always
100 * images, also for documents like PDFs, where they are the preview images shown in the media library.
101 */
102 private function getAttachmentFile(
103 int $attachmentId,
104 string $attachedFile,
105 string $requestedUrl,
106 string $uploadBaseDir,
107 ?bool &$isImage
108 ): string {
109 $requestedFileName = basename((string) parse_url($requestedUrl, PHP_URL_PATH));
110 $sizeFile = dirname($attachedFile) . DIRECTORY_SEPARATOR . $requestedFileName;
111
112 if ($requestedFileName !== basename($attachedFile)
113 && $this->isRegisteredSize($attachmentId, $requestedFileName) === true
114 && $this->isInsideUploadDirectory($sizeFile, $uploadBaseDir) === true
115 ) {
116 $isImage = true;
117
118 return $sizeFile;
119 }
120
121 $isImage = $this->wordpress->attachmentIsImage($attachmentId);
122
123 return $attachedFile;
124 }
125
126 private function getAttachmentFileObject(string $objectUrl): ?FileObject
127 {
128 $uploadDirs = $this->wordpress->getUploadDir();
129 $requestedUrl = $this->normalizeAttachmentUrl($uploadDirs, $objectUrl);
130 $postId = $this->getPostIdByUrl($requestedUrl);
131
132 if ($postId < 1) {
133 return null;
134 }
135
136 $post = $this->objectHandler->getPost($postId);
137
138 if (($post->post_type ?? '') !== ObjectHandler::ATTACHMENT_OBJECT_TYPE) {
139 return null;
140 }
141
142 $attachedFile = $this->wordpress->getAttachedFile($post->ID);
143
144 if ($attachedFile === false
145 || $this->isInsideUploadDirectory($attachedFile, $uploadDirs['basedir']) === false
146 ) {
147 return null;
148 }
149
150 $file = $this->getAttachmentFile(
151 $post->ID,
152 $attachedFile,
153 $requestedUrl,
154 $uploadDirs['basedir'],
155 $isImage
156 );
157
158 return $this->fileObjectFactory->createFileObject(
159 $post->ID,
160 ObjectHandler::ATTACHMENT_OBJECT_TYPE,
161 $file,
162 $isImage
163 );
164 }
165
166 private function isInsideUploadDirectory(string $file, string $uploadBaseDir): bool
167 {
168 $realFile = $this->php->realpath($file);
169 $realUploadBaseDir = $this->php->realpath($uploadBaseDir);
170
171 return $realFile !== false
172 && $realUploadBaseDir !== false
173 && str_starts_with($realFile, $realUploadBaseDir . DIRECTORY_SEPARATOR);
174 }
175
176 private function getFileSettingsByType(string $objectType, string $objectUrl): ?FileObject
177 {
178 if ($objectType === ObjectHandler::ATTACHMENT_OBJECT_TYPE) {
179 return $this->getAttachmentFileObject($objectUrl);
180 }
181
182 $extraParameter = $this->getRequestParameter('uamextra');
183
184 return $this->wordpress->applyFilters(
185 'uam_get_file_settings_by_type',
186 null,
187 $objectType,
188 $objectUrl,
189 $extraParameter
190 );
191 }
192
193 /**
194 * @throws UserGroupTypeException
195 */
196 public function getFile(string $objectType, string $objectUrl): void
197 {
198 $fileObject = $this->getFileSettingsByType($objectType, $objectUrl);
199
200 if ($fileObject === null) {
201 return;
202 }
203
204 if ($this->accessHandler->checkObjectAccess($fileObject->getType(), $fileObject->getId()) === true) {
205 $file = $fileObject->getFile();
206 } elseif ($fileObject->isImage() === true) {
207 if ($this->mainConfig->getNoAccessImageType() === 'custom') {
208 $file = $this->mainConfig->getCustomNoAccessImage();
209 } else {
210 $realPath = $this->wordpressConfig->getRealPath();
211 $file = $realPath . 'assets' . DIRECTORY_SEPARATOR . 'gfx' . DIRECTORY_SEPARATOR . 'noAccessPic.png';
212 }
213 } else {
214 $this->wordpress->wpDie(TXT_UAM_NO_RIGHTS_MESSAGE, TXT_UAM_NO_RIGHTS_TITLE, ['response' => 403]);
215 return;
216 }
217
218 $this->fileHandler->getFile($file, $fileObject->isImage());
219 }
220
221 private function getRedirectUrlAndPermalink(?string &$permalink): ?string
222 {
223 $permalink = null;
224 $redirect = $this->mainConfig->getRedirect();
225
226 if ($redirect === 'custom_page') {
227 $redirectCustomPage = $this->mainConfig->getRedirectCustomPage();
228 $post = $this->objectHandler->getPost($redirectCustomPage);
229 $url = null;
230
231 if ($post !== false) {
232 $url = $post->guid;
233 $permalink = $this->wordpress->getPageLink($post);
234 }
235 } elseif ($redirect === 'custom_url') {
236 $url = $this->mainConfig->getRedirectCustomUrl();
237 } elseif ($redirect === 'login') {
238 $url = $this->getLoginUrl();
239 } elseif ($redirect === 'origin') {
240 $referer = $this->wordpress->getReferer();
241 $url = $referer !== false ? $referer : $this->wordpress->getHomeUrl('/');
242 } else {
243 $url = $this->wordpress->getHomeUrl('/');
244 }
245
246 return $url;
247 }
248
249 /**
250 * @throws UserGroupTypeException
251 */
252 public function redirectUser(bool $checkPosts = true): void
253 {
254 if ($checkPosts === true) {
255 $posts = $this->wordpress->getWpQuery()->get_posts();
256
257 foreach ($posts as $post) {
258 if ($this->accessHandler->checkObjectAccess($post->post_type, $post->ID)) {
259 return;
260 }
261 }
262 }
263
264 $url = $this->getRedirectUrlAndPermalink($permalink);
265 $currentUrl = $this->util->getCurrentUrl();
266
267 if ($url !== null && $url !== $currentUrl && $permalink !== $currentUrl) {
268 if ($this->mainConfig->appendRedirectToParameter() === true) {
269 $url = $this->wordpress->addQueryArg([self::REDIRECT_TO_PARAMETER => $currentUrl], $url);
270 }
271
272 $this->wordpress->wpRedirect($url);
273 $this->php->callExit();
274 }
275 }
276
277 private function getPostIdByName(string $name): int
278 {
279 $postableTypes = implode('\',\'', $this->objectHandler->getPostTypes());
280
281 $query = $this->database->prepare(
282 "SELECT ID
283 FROM {$this->database->getPostsTable()}
284 WHERE post_name = %s
285 AND post_type IN ('$postableTypes')",
286 $name
287 );
288
289 return (int) $this->database->getVariable($query);
290 }
291
292 private function extractObjectTypeAndId(mixed $pageParams, ?string &$objectType, int|string|null &$objectId): void
293 {
294 $objectType = null;
295 $objectId = null;
296
297 $simpleTypes = [
298 'p' => ObjectHandler::GENERAL_POST_OBJECT_TYPE,
299 'page_id' => ObjectHandler::GENERAL_POST_OBJECT_TYPE,
300 'cat_id' => ObjectHandler::GENERAL_TERM_OBJECT_TYPE
301 ];
302
303 foreach ($simpleTypes as $queryVar => $newObjectType) {
304 if (isset($pageParams->query_vars[$queryVar]) === true) {
305 $objectType = $newObjectType;
306 $objectId = $pageParams->query_vars[$queryVar];
307 }
308 }
309
310 if (isset($pageParams->query_vars['name']) === true) {
311 $objectType = ObjectHandler::GENERAL_POST_OBJECT_TYPE;
312 $objectId = $this->getPostIdByName($pageParams->query_vars['name']);
313 } elseif (isset($pageParams->query_vars['pagename']) === true) {
314 $object = $this->wordpress->getPageByPath($pageParams->query_vars['pagename']);
315
316 if ($object !== null) {
317 $objectType = $object->post_type ?? null;
318 $objectId = $object->ID ?? null;
319 }
320 }
321 }
322
323 /**
324 * @throws UserGroupTypeException
325 */
326 public function redirect(?array $headers, mixed $pageParams): ?array
327 {
328 $fileUrl = $this->getRequestParameter('uamgetfile');
329 $fileType = $this->getRequestParameter('uamfiletype');
330
331 if ($fileUrl !== null && $fileType !== null) {
332 $this->getFile($fileType, $fileUrl);
333 } elseif ($this->wordpressConfig->atAdminPanel() === false
334 && $this->mainConfig->getRedirect() !== 'false'
335 ) {
336 $this->extractObjectTypeAndId($pageParams, $objectType, $objectId);
337
338 if ($this->accessHandler->checkObjectAccess($objectType, $objectId) === false) {
339 $this->redirectUser(false);
340 }
341 }
342
343 return $headers;
344 }
345
346 public function getFileUrl(string $url, int|string|null $id): string
347 {
348 // Nginx always supports real urls so we need the new urls only
349 // if we don't use nginx and mod_rewrite is disabled
350 if ($this->mainConfig->lockFile() === true
351 && $this->wordpress->isNginx() === false
352 && $this->wordpress->gotModRewrite() === false
353 ) {
354 $post = $this->objectHandler->getPost($id);
355
356 if ($post !== false) {
357 $type = explode('/', $post->post_mime_type);
358 $type = $type[1] ?? $type[0];
359
360 $lockedFileTypes = $this->mainConfig->getLockedFiles();
361 $fileTypes = explode(',', $lockedFileTypes);
362
363 if ($lockedFileTypes === 'all' || in_array($type, $fileTypes) === true) {
364 $url = $this->wordpress->getHomeUrl('/') . '?uamfiletype=attachment&uamgetfile=' . $url;
365 }
366 }
367 }
368
369 return $url;
370 }
371
372 public function cachePostLinks(string $url, object $post): string
373 {
374 $postUrls = (array) $this->cache->getFromRuntimeCache(self::POST_URL_CACHE_KEY);
375 $postUrls[$url] = $post->ID;
376 $this->cache->addToRuntimeCache(self::POST_URL_CACHE_KEY, $postUrls);
377 return $url;
378 }
379
380 #[NoReturn]
381 public function testXSendFile(): void
382 {
383 $this->fileHandler->deliverXSendFileTestFile();
384 }
385 }
386