PluginProbe
User Access Manager / trunk
User Access Manager vtrunk
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
← All changes | src/Controller/Frontend/RedirectController.php +120 -36 2.3.14trunk View file →
@@ -9,12 +9,13 @@
9 9 use UserAccessManager\Cache\Cache;
10 10 use UserAccessManager\Config\MainConfig;
11 11 use UserAccessManager\Config\WordpressConfig;
12 12 use UserAccessManager\Controller\Controller;
13 +use UserAccessManager\Controller\Frontend\Authentication\LoginControllerTrait;
13 14 use UserAccessManager\Database\Database;
14 15 use UserAccessManager\File\FileHandler;
15 -use UserAccessManager\File\FileObject;
16 -use UserAccessManager\File\FileObjectFactory;
16 +use UserAccessManager\File\Delivery\FileObject;
17 +use UserAccessManager\File\Delivery\FileObjectFactory;
17 18 use UserAccessManager\Object\ObjectHandler;
18 19 use UserAccessManager\UserGroup\UserGroupTypeException;
19 20 use UserAccessManager\Util\Util;
20 21 use UserAccessManager\Wrapper\Php;
@@ -24,8 +25,9 @@
24 25 {
25 26 use LoginControllerTrait;
26 27
27 28 public const POST_URL_CACHE_KEY = 'PostUrls';
29 + public const REDIRECT_TO_PARAMETER = 'redirect_to';
28 30
29 31 public function __construct(
30 32 Php $php,
31 33 Wordpress $wordpress,
@@ -72,48 +74,126 @@
72 74
73 75 return $postUrls[$url];
74 76 }
75 77
76 - private function getFileSettingsByType(string $objectType, string $objectUrl): ?FileObject
78 + private function normalizeAttachmentUrl(array $uploadDirs, string $objectUrl): string
77 79 {
78 - $fileObject = null;
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']);
79 84
80 - if ($objectType === ObjectHandler::ATTACHMENT_OBJECT_TYPE) {
81 - $uploadDirs = $this->wordpress->getUploadDir();
82 - $uploadDir = str_replace(ABSPATH, '/', $uploadDirs['basedir']);
83 - $regex = '/.*' . str_replace('/', '\/', $uploadDir) . '\//i';
84 - $cleanObjectUrl = preg_replace($regex, '', $objectUrl);
85 - $uploadUrl = str_replace('/files', $uploadDir, $uploadDirs['baseurl']);
86 - $objectUrl = rtrim($uploadUrl, '/') . '/' . ltrim($cleanObjectUrl, '/');
85 + return rtrim($uploadUrl, '/') . '/' . ltrim($cleanObjectUrl, '/');
86 + }
87 87
88 - $post = $this->objectHandler->getPost($this->getPostIdByUrl($objectUrl));
89 - $postType = $post->post_type ?? '';
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'] ?? []) : [];
90 92
91 - if ($postType === ObjectHandler::ATTACHMENT_OBJECT_TYPE) {
92 - $multiPath = str_replace('/files', $uploadDir, $uploadDirs['baseurl']);
93 + return in_array($fileName, array_column($sizes, 'file'), true);
94 + }
93 95
94 - $fileObject = $this->fileObjectFactory->createFileObject(
95 - $post->ID,
96 - $objectType,
97 - $uploadDirs['basedir'] . str_replace($multiPath, '', $objectUrl),
98 - $this->wordpress->attachmentIsImage($post->ID)
99 - );
100 - }
101 - } else {
102 - $extraParameter = $this->getRequestParameter('uamextra');
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;
103 111
104 - $fileObject = $this->wordpress->applyFilters(
105 - 'uam_get_file_settings_by_type',
106 - $fileObject,
107 - $objectType,
108 - $objectUrl,
109 - $extraParameter
110 - );
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;
111 119 }
112 120
113 - return $fileObject;
121 + $isImage = $this->wordpress->attachmentIsImage($attachmentId);
122 +
123 + return $attachedFile;
114 124 }
115 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 + // Unfiltered, because the plugin denies the path through the get_attached_file filter for
143 + // users without access. Filtered it would hide the file from the access check below, which
144 + // then could not answer the request with the no rights page any more.
145 + $attachedFile = $this->wordpress->getAttachedFile($post->ID, true);
146 +
147 + if ($attachedFile === false
148 + || $this->isInsideUploadDirectory($attachedFile, $uploadDirs['basedir']) === false
149 + ) {
150 + return null;
151 + }
152 +
153 + $file = $this->getAttachmentFile(
154 + $post->ID,
155 + $attachedFile,
156 + $requestedUrl,
157 + $uploadDirs['basedir'],
158 + $isImage
159 + );
160 +
161 + return $this->fileObjectFactory->createFileObject(
162 + $post->ID,
163 + ObjectHandler::ATTACHMENT_OBJECT_TYPE,
164 + $file,
165 + $isImage
166 + );
167 + }
168 +
169 + private function isInsideUploadDirectory(string $file, string $uploadBaseDir): bool
170 + {
171 + $realFile = $this->php->realpath($file);
172 + $realUploadBaseDir = $this->php->realpath($uploadBaseDir);
173 +
174 + return $realFile !== false
175 + && $realUploadBaseDir !== false
176 + && str_starts_with($realFile, $realUploadBaseDir . DIRECTORY_SEPARATOR);
177 + }
178 +
179 + private function getFileSettingsByType(string $objectType, string $objectUrl): ?FileObject
180 + {
181 + if ($objectType === ObjectHandler::ATTACHMENT_OBJECT_TYPE) {
182 + return $this->getAttachmentFileObject($objectUrl);
183 + }
184 +
185 + $extraParameter = $this->getRequestParameter('uamextra');
186 +
187 + return $this->wordpress->applyFilters(
188 + 'uam_get_file_settings_by_type',
189 + null,
190 + $objectType,
191 + $objectUrl,
192 + $extraParameter
193 + );
194 + }
195 +
116 196 /**
117 197 * @throws UserGroupTypeException
118 198 */
119 199 public function getFile(string $objectType, string $objectUrl): void
@@ -187,8 +267,12 @@
187 267 $url = $this->getRedirectUrlAndPermalink($permalink);
188 268 $currentUrl = $this->util->getCurrentUrl();
189 269
190 270 if ($url !== null && $url !== $currentUrl && $permalink !== $currentUrl) {
271 + if ($this->mainConfig->appendRedirectToParameter() === true) {
272 + $url = $this->wordpress->addQueryArg([self::REDIRECT_TO_PARAMETER => $currentUrl], $url);
273 + }
274 +
191 275 $this->wordpress->wpRedirect($url);
192 276 $this->php->callExit();
193 277 }
194 278 }
@@ -197,12 +281,12 @@
197 281 {
198 282 $postableTypes = implode('\',\'', $this->objectHandler->getPostTypes());
199 283
200 284 $query = $this->database->prepare(
201 - "SELECT ID
202 - FROM {$this->database->getPostsTable()}
203 - WHERE post_name = %s
204 - AND post_type IN ('$postableTypes')",
285 + "SELECT `ID`
286 + FROM `{$this->database->getPostsTable()}`
287 + WHERE `post_name` = %s
288 + AND `post_type` IN ('$postableTypes')",
205 289 $name
206 290 );
207 291
208 292 return (int) $this->database->getVariable($query);