Passed
Push — 1.11.x ( 31fff3...86e7ae )
by Yannick
15:37 queued 10s
created

getPermissions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 11
nc 2
nop 5
dl 0
loc 17
rs 9.9
c 1
b 0
f 0
1
<?php
2
/**
3
 *
4
 * (c) Copyright Ascensio System SIA 2021
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
24
/**
25
 * Status of the document
26
 */
27
const TrackerStatus_Editing = 1;
28
const TrackerStatus_MustSave = 2;
29
const TrackerStatus_Corrupted = 3;
30
const TrackerStatus_Closed = 4;
31
const TrackerStatus_ForceSave = 6;
32
const TrackerStatus_CorruptedForceSave = 7;
33
34
$plugin = OnlyofficePlugin::create();
35
36
if (isset($_GET["hash"]) && !empty($_GET["hash"])) {
37
    $callbackResponseArray = [];
38
    @header( 'Content-Type: application/json; charset==utf-8');
0 ignored issues
show
Security Best Practice introduced by
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 ignore-unhandled  annotation

38
    /** @scrutinizer ignore-unhandled */ @header( 'Content-Type: application/json; charset==utf-8');

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...
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
39
    @header( 'X-Robots-Tag: noindex' );
0 ignored issues
show
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
40
    @header( 'X-Content-Type-Options: nosniff' );
0 ignored issues
show
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
41
42
    list ($hashData, $error) = Crypt::ReadHash($_GET["hash"]);
43
    if ($hashData === null) {
44
        $callbackResponseArray["status"] = "error";
45
        $callbackResponseArray["error"] = $error;
46
        die(json_encode($callbackResponseArray));
47
    }
48
49
    $type = $hashData->type;
50
    $courseId = $hashData->courseId;
51
    $userId = $hashData->userId;
52
    $docId = $hashData->docId;
53
    $groupId = $hashData->groupId;
54
    $sessionId = $hashData->sessionId;
55
56
    $courseInfo = api_get_course_info_by_id($courseId);
57
    $courseCode = $courseInfo["code"];
58
59
    if (!empty($userId)) {
60
        $userInfo = api_get_user_info($userId);
61
    } else {
62
        $result["error"] = "User not found";
63
        die (json_encode($result));
64
    }
65
66
    if (api_is_anonymous()) {
67
        $loggedUser = [
68
            "user_id" => $userInfo["id"],
69
            "status" => $userInfo["status"],
70
            "uidReset" => true,
71
        ];
72
73
        Session::write("_user", $loggedUser);
74
        Login::init_user($loggedUser["user_id"], true);
75
    } else {
76
        $userId = api_get_user_id();
77
    }
78
79
    switch($type) {
80
        case "track":
81
            $callbackResponseArray = track();
82
            die (json_encode($callbackResponseArray));
83
        case "download":
84
            $callbackResponseArray = download();
0 ignored issues
show
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
85
            die (json_encode($callbackResponseArray));
86
        default:
87
            $callbackResponseArray["status"] = "error";
88
            $callbackResponseArray["error"] = "404 Method not found";
89
            die(json_encode($callbackResponseArray));
90
    }
91
}
92
93
/**
94
 * Handle request from the document server with the document status information
95
 */
96
function track(): array
97
{
98
    $result = [];
99
100
    global $plugin;
101
    global $courseCode;
102
    global $userId;
103
    global $docId;
104
    global $groupId;
105
    global $sessionId;
106
    global $courseInfo;
107
108
    if (($body_stream = file_get_contents("php://input")) === false) {
109
        $result["error"] = "Bad Request";
110
        return $result;
111
    }
112
113
    $data = json_decode($body_stream, true);
114
115
    if ($data === null) {
116
        $result["error"] = "Bad Response";
117
        return $result;
118
    }
119
120
    if (!empty($plugin->get("jwt_secret"))) {
121
122
        if (!empty($data["token"])) {
123
            try {
124
                $payload = \Firebase\JWT\JWT::decode($data["token"], $plugin->get("jwt_secret"), array("HS256"));
125
            } catch (\UnexpectedValueException $e) {
126
                $result["status"] = "error";
127
                $result["error"] = "403 Access denied";
128
                return $result;
129
            }
130
        } else {
131
            $token = substr($_SERVER[AppConfig::JwtHeader()], strlen("Bearer "));
132
            try {
133
                $decodeToken = \Firebase\JWT\JWT::decode($token, $plugin->get("jwt_secret"), array("HS256"));
134
                $payload = $decodeToken->payload;
135
            } catch (\UnexpectedValueException $e) {
136
                $result["status"] = "error";
137
                $result["error"] = "403 Access denied";
138
                return $result;
139
            }
140
        }
141
142
        $data["url"] = isset($payload->url) ? $payload->url : null;
143
        $data["status"] = $payload->status;
144
    }
145
146
    $status = $data["status"];
147
148
    $track_result = 1;
149
    switch ($status) {
150
        case TrackerStatus_MustSave:
151
        case TrackerStatus_Corrupted:
152
153
            $downloadUri = $data["url"];
154
155
            if (!empty($docId) && !empty($courseCode)) {
156
                $docInfo = DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId);
157
158
                if ($docInfo === false) {
159
                    $result["error"] = "File not found";
160
                    return $result;
161
                }
162
163
                $filePath = $docInfo["absolute_path"];
164
            } else {
165
                $result["error"] = "Bad Request";
166
                return $result;
167
            }
168
169
            list ($isAllowToEdit, $isMyDir, $isGroupAccess, $isReadonly) = getPermissions($docInfo, $userId, $courseCode, $groupId, $sessionId);
170
171
            if ($isReadonly) {
172
                break;
173
            }
174
175
            if (($new_data = file_get_contents($downloadUri)) === false) {
176
                break;
177
            }
178
179
            if ($isAllowToEdit || $isMyDir || $isGroupAccess) {
180
                $groupInfo = GroupManager::get_group_properties($groupId);
181
182
                if ($fp = @fopen($filePath, "w")) {
183
                    fputs($fp, $new_data);
184
                    fclose($fp);
185
                    api_item_property_update($courseInfo,
186
                                                TOOL_DOCUMENT,
187
                                                $docId,
188
                                                "DocumentUpdated",
189
                                                $userId,
190
                                                $groupInfo,
191
                                                null,
192
                                                null,
193
                                                null,
194
                                                $sessionId);
195
                    update_existing_document($courseInfo,
196
                                                $docId,
197
                                                filesize($filePath),
198
                                                false);
199
                    $track_result = 0;
200
                    break;
201
                }
202
            }
203
204
        case TrackerStatus_Editing:
205
        case TrackerStatus_Closed:
206
207
            $track_result = 0;
208
            break;
209
    }
210
211
    $result["error"] = $track_result;
212
    return $result;
213
}
214
215
/**
216
 * Downloading file by the document service
217
 */
218
function download()
219
{
220
    global $plugin;
221
    global $courseCode;
222
    global $userId;
223
    global $docId;
224
    global $groupId;
225
    global $sessionId;
226
    global $courseInfo;
227
228
    if (!empty($plugin->get("jwt_secret"))) {
229
        $token = substr($_SERVER[AppConfig::JwtHeader()], strlen("Bearer "));
230
        try {
231
            $payload = \Firebase\JWT\JWT::decode($token, $plugin->get("jwt_secret"), array("HS256"));
232
233
        } catch (\UnexpectedValueException $e) {
234
            $result["status"] = "error";
0 ignored issues
show
Comprehensibility Best Practice introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.
Loading history...
235
            $result["error"] = "403 Access denied";
236
            return $result;
237
        }
238
    }
239
240
    if (!empty($docId) && !empty($courseCode)) {
241
        $docInfo = DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId);
242
243
        if ($docInfo === false) {
244
            $result["error"] = "File not found";
245
            return $result;
246
        }
247
248
        $filePath = $docInfo["absolute_path"];
249
    } else {
250
        $result["error"] = "File not found";
251
        return $result;
252
    }
253
254
    @header("Content-Type: application/octet-stream");
0 ignored issues
show
Security Best Practice introduced by
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 ignore-unhandled  annotation

254
    /** @scrutinizer ignore-unhandled */ @header("Content-Type: application/octet-stream");

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...
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
255
    @header("Content-Disposition: attachment; filename=" . $docInfo["title"]);
0 ignored issues
show
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
256
257
    readfile($filePath);
258
}
259
260
/**
261
 * Method checks access rights to document and returns permissions
262
 */
263
function getPermissions(array $docInfo, int $userId, string $courseCode, int $groupId = null, int $sessionId = null): array
264
{
265
    $isAllowToEdit = api_is_allowed_to_edit(true, true);
266
    $isMyDir = DocumentManager::is_my_shared_folder($userId, $docInfo["absolute_parent_path"], $sessionId);
267
268
    $isGroupAccess = false;
269
    if (!empty($groupId)) {
270
        $courseInfo = api_get_course_info($courseCode);
271
        Session::write("_real_cid", $courseInfo["real_id"]);
272
        $groupProperties = GroupManager::get_group_properties($groupId);
273
        $docInfoGroup = api_get_item_property_info($courseInfo["real_id"], "document", $docInfo["id"], $sessionId);
274
        $isGroupAccess = GroupManager::allowUploadEditDocument($userId, $courseCode, $groupProperties, $docInfoGroup);
275
    }
276
277
    $isReadonly = $docInfo["readonly"];
278
279
    return [$isAllowToEdit, $isMyDir, $isGroupAccess, $isReadonly];
280
}
281