1 | <?php |
||||
2 | /** |
||||
3 | * |
||||
4 | * (c) Copyright Ascensio System SIA 2023 |
||||
5 | * |
||||
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
||||
7 | * you may not use this file except in compliance with the License. |
||||
8 | * You may obtain a copy of the License at |
||||
9 | * |
||||
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
||||
11 | * |
||||
12 | * Unless required by applicable law or agreed to in writing, software |
||||
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
||||
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
15 | * See the License for the specific language governing permissions and |
||||
16 | * limitations under the License. |
||||
17 | * |
||||
18 | */ |
||||
19 | |||||
20 | require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
21 | |||||
22 | use ChamiloSession as Session; |
||||
23 | use \Firebase\JWT\JWT; |
||||
24 | use \Firebase\JWT\Key; |
||||
25 | |||||
26 | /** |
||||
27 | * Status of the document |
||||
28 | */ |
||||
29 | const TrackerStatus_Editing = 1; |
||||
30 | const TrackerStatus_MustSave = 2; |
||||
31 | const TrackerStatus_Corrupted = 3; |
||||
32 | const TrackerStatus_Closed = 4; |
||||
33 | const TrackerStatus_ForceSave = 6; |
||||
34 | const TrackerStatus_CorruptedForceSave = 7; |
||||
35 | |||||
36 | $plugin = OnlyofficePlugin::create(); |
||||
37 | |||||
38 | if (isset($_GET["hash"]) && !empty($_GET["hash"])) { |
||||
39 | $callbackResponseArray = []; |
||||
40 | @header( 'Content-Type: application/json; charset==utf-8'); |
||||
0 ignored issues
–
show
Are you sure the usage of
header('Content-Type: ap.../json; charset==utf-8') is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
41 | @header( 'X-Robots-Tag: noindex' ); |
||||
0 ignored issues
–
show
Are you sure the usage of
header('X-Robots-Tag: noindex') is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
42 | @header( 'X-Content-Type-Options: nosniff' ); |
||||
0 ignored issues
–
show
Are you sure the usage of
header('X-Content-Type-Options: nosniff') is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
43 | |||||
44 | list ($hashData, $error) = Crypt::ReadHash($_GET["hash"]); |
||||
45 | if ($hashData === null) { |
||||
46 | $callbackResponseArray["status"] = "error"; |
||||
47 | $callbackResponseArray["error"] = $error; |
||||
48 | die(json_encode($callbackResponseArray)); |
||||
49 | } |
||||
50 | |||||
51 | $type = $hashData->type; |
||||
52 | $courseId = $hashData->courseId; |
||||
53 | $userId = $hashData->userId; |
||||
54 | $docId = $hashData->docId; |
||||
55 | $groupId = $hashData->groupId; |
||||
56 | $sessionId = $hashData->sessionId; |
||||
57 | |||||
58 | $courseInfo = api_get_course_info_by_id($courseId); |
||||
59 | $courseCode = $courseInfo["code"]; |
||||
60 | |||||
61 | if (!empty($userId)) { |
||||
62 | $userInfo = api_get_user_info($userId); |
||||
63 | } else { |
||||
64 | $result["error"] = "User not found"; |
||||
65 | die (json_encode($result)); |
||||
66 | } |
||||
67 | |||||
68 | if (api_is_anonymous()) { |
||||
69 | $loggedUser = [ |
||||
70 | "user_id" => $userInfo["id"], |
||||
71 | "status" => $userInfo["status"], |
||||
72 | "uidReset" => true, |
||||
73 | ]; |
||||
74 | |||||
75 | Session::write("_user", $loggedUser); |
||||
76 | Login::init_user($loggedUser["user_id"], true); |
||||
77 | } else { |
||||
78 | $userId = api_get_user_id(); |
||||
79 | } |
||||
80 | |||||
81 | switch($type) { |
||||
82 | case "track": |
||||
83 | $callbackResponseArray = track(); |
||||
84 | die (json_encode($callbackResponseArray)); |
||||
85 | case "download": |
||||
86 | $callbackResponseArray = download(); |
||||
0 ignored issues
–
show
Are you sure the assignment to
$callbackResponseArray is correct as download() seems to always return null .
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
87 | die (json_encode($callbackResponseArray)); |
||||
88 | default: |
||||
89 | $callbackResponseArray["status"] = "error"; |
||||
90 | $callbackResponseArray["error"] = "404 Method not found"; |
||||
91 | die(json_encode($callbackResponseArray)); |
||||
92 | } |
||||
93 | } |
||||
94 | |||||
95 | /** |
||||
96 | * Handle request from the document server with the document status information |
||||
97 | */ |
||||
98 | function track(): array |
||||
99 | { |
||||
100 | $result = []; |
||||
101 | |||||
102 | global $plugin; |
||||
103 | global $courseCode; |
||||
104 | global $userId; |
||||
105 | global $docId; |
||||
106 | global $groupId; |
||||
107 | global $sessionId; |
||||
108 | global $courseInfo; |
||||
109 | |||||
110 | if (($body_stream = file_get_contents("php://input")) === false) { |
||||
111 | $result["error"] = "Bad Request"; |
||||
112 | return $result; |
||||
113 | } |
||||
114 | |||||
115 | $data = json_decode($body_stream, true); |
||||
116 | |||||
117 | if ($data === null) { |
||||
118 | $result["error"] = "Bad Response"; |
||||
119 | return $result; |
||||
120 | } |
||||
121 | |||||
122 | if (!empty($plugin->getDocumentServerSecret())) { |
||||
123 | |||||
124 | if (!empty($data["token"])) { |
||||
125 | try { |
||||
126 | $payload = JWT::decode($data["token"], new Key($plugin->getDocumentServerSecret(), "HS256")); |
||||
127 | } catch (\UnexpectedValueException $e) { |
||||
128 | $result["status"] = "error"; |
||||
129 | $result["error"] = "403 Access denied"; |
||||
130 | return $result; |
||||
131 | } |
||||
132 | } else { |
||||
133 | $token = substr(getallheaders()[$plugin->getJwtHeader()], strlen("Bearer ")); |
||||
134 | try { |
||||
135 | $decodeToken = JWT::decode($token, new Key($plugin->getDocumentServerSecret(), "HS256")); |
||||
136 | $payload = $decodeToken->payload; |
||||
137 | } catch (\UnexpectedValueException $e) { |
||||
138 | $result["status"] = "error"; |
||||
139 | $result["error"] = "403 Access denied"; |
||||
140 | return $result; |
||||
141 | } |
||||
142 | } |
||||
143 | |||||
144 | $data["url"] = isset($payload->url) ? $payload->url : null; |
||||
145 | $data["status"] = $payload->status; |
||||
146 | } |
||||
147 | |||||
148 | $status = $data["status"]; |
||||
149 | |||||
150 | $track_result = 1; |
||||
151 | switch ($status) { |
||||
152 | case TrackerStatus_MustSave: |
||||
153 | case TrackerStatus_Corrupted: |
||||
154 | |||||
155 | $downloadUri = $data["url"]; |
||||
156 | $downloadUri = $plugin->replaceDocumentServerUrlToInternal($downloadUri); |
||||
157 | |||||
158 | if (!empty($docId) && !empty($courseCode)) { |
||||
159 | $docInfo = DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId); |
||||
160 | |||||
161 | if ($docInfo === false) { |
||||
162 | $result["error"] = "File not found"; |
||||
163 | return $result; |
||||
164 | } |
||||
165 | |||||
166 | $filePath = $docInfo["absolute_path"]; |
||||
167 | } else { |
||||
168 | $result["error"] = "Bad Request"; |
||||
169 | return $result; |
||||
170 | } |
||||
171 | |||||
172 | list ($isAllowToEdit, $isMyDir, $isGroupAccess, $isReadonly) = getPermissions($docInfo, $userId, $courseCode, $groupId, $sessionId); |
||||
173 | |||||
174 | if ($isReadonly) { |
||||
175 | break; |
||||
176 | } |
||||
177 | |||||
178 | if (($new_data = file_get_contents($downloadUri)) === false) { |
||||
179 | break; |
||||
180 | } |
||||
181 | |||||
182 | if ($isAllowToEdit || $isMyDir || $isGroupAccess) { |
||||
183 | $groupInfo = GroupManager::get_group_properties($groupId); |
||||
184 | |||||
185 | if ($fp = @fopen($filePath, "w")) { |
||||
186 | fputs($fp, $new_data); |
||||
187 | fclose($fp); |
||||
188 | api_item_property_update($courseInfo, |
||||
189 | TOOL_DOCUMENT, |
||||
190 | $docId, |
||||
191 | "DocumentUpdated", |
||||
192 | $userId, |
||||
193 | $groupInfo, |
||||
194 | null, |
||||
195 | null, |
||||
196 | null, |
||||
197 | $sessionId); |
||||
198 | update_existing_document($courseInfo, |
||||
199 | $docId, |
||||
200 | filesize($filePath), |
||||
201 | false); |
||||
202 | $track_result = 0; |
||||
203 | break; |
||||
204 | } |
||||
205 | } |
||||
206 | |||||
207 | case TrackerStatus_Editing: |
||||
208 | case TrackerStatus_Closed: |
||||
209 | |||||
210 | $track_result = 0; |
||||
211 | break; |
||||
212 | } |
||||
213 | |||||
214 | $result["error"] = $track_result; |
||||
215 | return $result; |
||||
216 | } |
||||
217 | |||||
218 | /** |
||||
219 | * Downloading file by the document service |
||||
220 | */ |
||||
221 | function download() |
||||
222 | { |
||||
223 | global $plugin; |
||||
224 | global $courseCode; |
||||
225 | global $userId; |
||||
226 | global $docId; |
||||
227 | global $groupId; |
||||
228 | global $sessionId; |
||||
229 | global $courseInfo; |
||||
230 | |||||
231 | if (!empty($plugin->getDocumentServerSecret())) { |
||||
232 | $token = substr(getallheaders()[$plugin->getJwtHeader()], strlen("Bearer ")); |
||||
233 | try { |
||||
234 | $payload = JWT::decode($token, new Key($plugin->getDocumentServerSecret(), "HS256")); |
||||
235 | |||||
236 | } catch (\UnexpectedValueException $e) { |
||||
237 | $result["status"] = "error"; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
238 | $result["error"] = "403 Access denied"; |
||||
239 | return $result; |
||||
240 | } |
||||
241 | } |
||||
242 | |||||
243 | if (!empty($docId) && !empty($courseCode)) { |
||||
244 | $docInfo = DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId); |
||||
245 | |||||
246 | if ($docInfo === false) { |
||||
247 | $result["error"] = "File not found"; |
||||
248 | return $result; |
||||
249 | } |
||||
250 | |||||
251 | $filePath = $docInfo["absolute_path"]; |
||||
252 | } else { |
||||
253 | $result["error"] = "File not found"; |
||||
254 | return $result; |
||||
255 | } |
||||
256 | |||||
257 | @header("Content-Type: application/octet-stream"); |
||||
0 ignored issues
–
show
It seems like you do not handle an error condition for
header() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Are you sure the usage of
header('Content-Type: application/octet-stream') is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
258 | @header("Content-Disposition: attachment; filename=" . $docInfo["title"]); |
||||
0 ignored issues
–
show
Are you sure the usage of
header('Content-Disposit...=' . $docInfo['title']) is correct as it seems to always return null .
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||||
259 | |||||
260 | readfile($filePath); |
||||
261 | exit(); |
||||
0 ignored issues
–
show
|
|||||
262 | } |
||||
263 | |||||
264 | /** |
||||
265 | * Method checks access rights to document and returns permissions |
||||
266 | */ |
||||
267 | function getPermissions(array $docInfo, int $userId, string $courseCode, int $groupId = null, int $sessionId = null): array |
||||
268 | { |
||||
269 | $isAllowToEdit = api_is_allowed_to_edit(true, true); |
||||
270 | $isMyDir = DocumentManager::is_my_shared_folder($userId, $docInfo["absolute_parent_path"], $sessionId); |
||||
271 | |||||
272 | $isGroupAccess = false; |
||||
273 | if (!empty($groupId)) { |
||||
274 | $courseInfo = api_get_course_info($courseCode); |
||||
275 | Session::write("_real_cid", $courseInfo["real_id"]); |
||||
276 | $groupProperties = GroupManager::get_group_properties($groupId); |
||||
277 | $docInfoGroup = api_get_item_property_info($courseInfo["real_id"], "document", $docInfo["id"], $sessionId); |
||||
278 | $isGroupAccess = GroupManager::allowUploadEditDocument($userId, $courseCode, $groupProperties, $docInfoGroup); |
||||
279 | } |
||||
280 | |||||
281 | $isReadonly = $docInfo["readonly"]; |
||||
282 | |||||
283 | return [$isAllowToEdit, $isMyDir, $isGroupAccess, $isReadonly]; |
||||
284 | } |
||||
285 |
If you suppress an error, we recommend checking for the error condition explicitly: