Passed
Push — 3.0 ( 5e1ed3...5a5495 )
by Rubén
04:22
created

AccountFileController::getSearchGrid()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * sysPass
4
 *
5
 * @author    nuxsmin
6
 * @link      https://syspass.org
7
 * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
8
 *
9
 * This file is part of sysPass.
10
 *
11
 * sysPass is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation, either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * sysPass is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 *  along with sysPass.  If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace SP\Modules\Web\Controllers;
26
27
use SP\Core\Acl\Acl;
28
use SP\Core\Events\Event;
29
use SP\Core\Events\EventMessage;
30
use SP\Core\Exceptions\SPException;
31
use SP\DataModel\FileData;
32
use SP\Html\Html;
33
use SP\Http\JsonResponse;
34
use SP\Modules\Web\Controllers\Helpers\Grid\FileGrid;
35
use SP\Modules\Web\Controllers\Traits\ItemTrait;
36
use SP\Modules\Web\Controllers\Traits\JsonTrait;
37
use SP\Mvc\Controller\CrudControllerInterface;
38
use SP\Services\Account\AccountFileService;
39
use SP\Services\Account\AccountService;
40
use SP\Storage\File\FileException;
41
use SP\Storage\File\FileHandler;
42
use SP\Util\ErrorUtil;
43
use SP\Util\FileUtil;
44
45
/**
46
 * Class AccountFileController
47
 *
48
 * @package SP\Modules\Web\Controllers
49
 */
50
final class AccountFileController extends ControllerBase implements CrudControllerInterface
51
{
52
    const MIME_VIEW = ['text/plain'];
53
54
    use JsonTrait, ItemTrait;
0 ignored issues
show
introduced by
The trait SP\Modules\Web\Controllers\Traits\ItemTrait requires some properties which are not provided by SP\Modules\Web\Controllers\AccountFileController: $data, $key
Loading history...
55
56
    /**
57
     * @var AccountFileService
58
     */
59
    protected $accountFileService;
60
61
    /**
62
     * View action
63
     *
64
     * @param $id
65
     *
66
     * @return bool
67
     */
68
    public function viewAction($id)
69
    {
70
        try {
71
            $this->checkSecurityToken($this->previousSk, $this->request);
72
73
            if (null === ($fileData = $this->accountFileService->getById($id))) {
74
                throw new SPException(__u('File does not exist'), SPException::INFO);
75
            }
76
77
            $this->view->addTemplate('file', 'itemshow');
78
79
            if (FileUtil::isImage($fileData)) {
80
                $this->view->assign('data', chunk_split(base64_encode($fileData->getContent())));
81
                $this->view->assign('fileData', $fileData);
82
                $this->view->assign('isImage', 1);
83
84
                $this->eventDispatcher->notifyEvent('show.accountFile',
85
                    new Event($this,
86
                        EventMessage::factory()
87
                            ->addDescription(__u('File viewed'))
88
                            ->addDetail(__u('File'), $fileData->getName()))
89
                );
90
91
                return $this->returnJsonResponseData(['html' => $this->render()]);
92
            }
93
94
            $type = strtolower($fileData->getType());
95
96
            if (in_array($type, self::MIME_VIEW)) {
97
                $this->view->assign('mime', $type);
98
                $this->view->assign('data', htmlentities($fileData->getContent()));
99
100
                $this->eventDispatcher->notifyEvent('show.accountFile',
101
                    new Event($this,
102
                        EventMessage::factory()
103
                            ->addDescription(__u('File viewed'))
104
                            ->addDetail(__u('File'), $fileData->getName()))
105
                );
106
107
                return $this->returnJsonResponseData(['html' => $this->render()]);
108
            }
109
        } catch (\Exception $e) {
110
            processException($e);
111
112
            $this->eventDispatcher->notifyEvent('exception', new Event($e));
113
114
            return $this->returnJsonResponseException($e);
115
        }
116
117
        return $this->returnJsonResponse(JsonResponse::JSON_WARNING, __u('File not supported for preview'));
118
    }
119
120
    /**
121
     * Download action
122
     *
123
     * @param $id
124
     *
125
     * @return string
126
     */
127
    public function downloadAction($id)
128
    {
129
        try {
130
            $this->checkSecurityToken($this->previousSk, $this->request);
131
132
            // Set the security toke to its previous value because we can't tell
133
            // the browser which will be the new security token (not so good...)
134
            $this->session->setSecurityKey($this->previousSk);
135
136
            if (null === ($fileData = $this->accountFileService->getById($id))) {
137
                throw new SPException(__u('File does not exist'), SPException::INFO);
138
            }
139
140
            $this->eventDispatcher->notifyEvent('download.accountFile',
141
                new Event($this, EventMessage::factory()
142
                    ->addDescription(__u('File downloaded'))
143
                    ->addDetail(__u('File'), $fileData->getName()))
144
            );
145
146
            $response = $this->router->response();
147
            $response->header('Cache-Control', 'max-age=60, must-revalidate');
148
            $response->header('Content-length', $fileData->getSize());
149
            $response->header('Content-type', $fileData->getType());
150
            $response->header('Content-Description', ' sysPass file');
151
            $response->header('Content-transfer-encoding', 'binary');
152
153
            $type = strtolower($fileData->getType());
154
155
            if ($type === 'application/pdf') {
156
                $response->header('Content-Disposition', 'inline; filename="' . $fileData->getName() . '"');
157
            } else {
158
                $response->header('Set-Cookie', 'fileDownload=true; path=/');
159
                $response->header('Content-Disposition', 'attachment; filename="' . $fileData->getName() . '"');
160
            }
161
162
            $response->body($fileData->getContent());
163
            $response->send(true);
164
        } catch (\Exception $e) {
165
            processException($e);
166
167
            $this->eventDispatcher->notifyEvent('exception', new Event($e));
168
        }
169
170
        return '';
171
    }
172
173
    /**
174
     * Upload action
175
     *
176
     * @param int $accountId
177
     *
178
     * @return bool
179
     */
180
    public function uploadAction($accountId)
181
    {
182
        try {
183
            $this->checkSecurityToken($this->previousSk, $this->request);
184
185
            $file = $this->router->request()->files()->get('inFile');
186
187
            if ($accountId === 0 || null === $file) {
188
                throw new SPException(__u('INVALID QUERY'), SPException::ERROR);
189
            }
190
191
            $filesAllowedMime = $this->configData->getFilesAllowedMime();
192
193
            if (empty($filesAllowedMime)) {
194
                throw new SPException(__u('There aren\'t any allowed MIME types'));
195
            }
196
197
            try {
198
                $fileHandler = new FileHandler($file['tmp_name']);
199
200
                $fileData = new FileData();
201
                $fileData->setAccountId($accountId);
202
                $fileData->setName(Html::sanitize($file['name']));
0 ignored issues
show
Bug introduced by
It seems like SP\Html\Html::sanitize($file['name']) can also be of type false; however, parameter $name of SP\DataModel\FileData::setName() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

202
                $fileData->setName(/** @scrutinizer ignore-type */ Html::sanitize($file['name']));
Loading history...
203
                $fileData->setSize($file['size']);
204
                $fileData->setType($file['type']);
205
                $fileData->setExtension(mb_strtoupper(pathinfo($fileData->getName(), PATHINFO_EXTENSION)));
206
207
                if ($fileData->getName() === '') {
208
                    throw new SPException(
209
                        __u('Invalid file'),
210
                        SPException::ERROR,
211
                        sprintf(__u('File: %s'), $fileData->getName())
212
                    );
213
                }
214
215
                $fileHandler->checkFileExists();
216
217
                $fileData->setType($this->checkAllowedMimeType($fileData, $fileHandler));
0 ignored issues
show
Bug introduced by
$this->checkAllowedMimeT...fileData, $fileHandler) of type string is incompatible with the type integer expected by parameter $type of SP\DataModel\FileData::setType(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

217
                $fileData->setType(/** @scrutinizer ignore-type */ $this->checkAllowedMimeType($fileData, $fileHandler));
Loading history...
218
219
                $allowedSize = $this->configData->getFilesAllowedSize();
220
221
                if ($fileData->getSize() > ($allowedSize * 1000)) {
222
                    throw new SPException(
223
                        __u('File size exceeded'),
224
                        SPException::ERROR,
225
                        sprintf(__u('Maximum size: %d KB'), $fileData->getRoundSize())
226
                    );
227
                }
228
229
                $fileData->setContent($fileHandler->readToString());
230
            } catch (FileException $e) {
231
                throw new SPException(__u('Internal error while reading the file'));
232
            }
233
234
            $this->accountFileService->create($fileData);
235
236
            $account = $this->dic->get(AccountService::class)
237
                ->getById($accountId)
238
                ->getAccountVData();
239
240
            $this->eventDispatcher->notifyEvent('upload.accountFile',
241
                new Event($this,
242
                    EventMessage::factory()
243
                        ->addDescription(__u('File saved'))
244
                        ->addDetail(__u('File'), $fileData->getName())
245
                        ->addDetail(__u('Account'), $account->getName())
246
                        ->addDetail(__u('Client'), $account->getClientName())
247
                        ->addDetail(__u('Type'), $fileData->getType())
248
                        ->addDetail(__u('Size'), $fileData->getRoundSize() . 'KB')
249
                )
250
            );
251
252
            return $this->returnJsonResponse(0, __u('File saved'));
253
        } catch (SPException $e) {
254
            processException($e);
255
256
            $this->eventDispatcher->notifyEvent('exception', new Event($e));
257
258
            return $this->returnJsonResponse(1, $e->getMessage(), [$e->getHint()]);
259
        } catch (\Exception $e) {
260
            processException($e);
261
262
            $this->eventDispatcher->notifyEvent('exception', new Event($e));
263
264
            return $this->returnJsonResponseException($e);
265
        }
266
    }
267
268
    /**
269
     * @param FileData    $fileData
270
     *
271
     * @param FileHandler $fileHandler
272
     *
273
     * @return string
274
     * @throws SPException
275
     * @throws \SP\Storage\File\FileException
276
     */
277
    private function checkAllowedMimeType(FileData $fileData, FileHandler $fileHandler)
278
    {
279
        if (in_array($fileData->getType(), $this->configData->getFilesAllowedMime())) {
280
            return $fileData->getType();
281
        } elseif (in_array($fileHandler->getFileType(), $this->configData->getFilesAllowedMime())) {
282
            return $fileHandler->getFileType();
283
284
        }
285
286
        throw new SPException(
287
            __u('File type not allowed'),
288
            SPException::ERROR,
289
            sprintf(__('MIME type: %s'), $fileData->getType())
290
        );
291
    }
292
293
    /**
294
     * Search action
295
     *
296
     * @return bool
297
     * @throws \DI\DependencyException
298
     * @throws \DI\NotFoundException
299
     * @throws \SP\Core\Exceptions\ConstraintException
300
     * @throws \SP\Core\Exceptions\QueryException
301
     * @throws SPException
302
     */
303
    public function searchAction()
304
    {
305
        $this->checkSecurityToken($this->previousSk, $this->request);
306
307
        if (!$this->acl->checkUserAccess(Acl::ACCOUNT_FILE_SEARCH)) {
308
            return $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('You don\'t have permission to do this operation'));
309
        }
310
311
        $this->view->addTemplate('datagrid-table', 'grid');
312
        $this->view->assign('index', $this->request->analyzeInt('activetab', 0));
313
        $this->view->assign('data', $this->getSearchGrid());
314
315
        return $this->returnJsonResponseData(['html' => $this->render()]);
316
    }
317
318
    /**
319
     * getSearchGrid
320
     *
321
     * @return $this
322
     * @throws \DI\DependencyException
323
     * @throws \DI\NotFoundException
324
     * @throws \SP\Core\Exceptions\ConstraintException
325
     * @throws \SP\Core\Exceptions\QueryException
326
     */
327
    protected function getSearchGrid()
328
    {
329
        $itemSearchData = $this->getSearchData($this->configData->getAccountCount(), $this->request);
330
331
        $fileGrid = $this->dic->get(FileGrid::class);
332
333
        return $fileGrid->updatePager($fileGrid->getGrid($this->accountFileService->search($itemSearchData)), $itemSearchData);
334
    }
335
336
    /**
337
     * Create action
338
     */
339
    public function createAction()
340
    {
341
        throw new \RuntimeException('Not implemented');
342
    }
343
344
    /**
345
     * Edit action
346
     *
347
     * @param $id
348
     */
349
    public function editAction($id)
350
    {
351
        throw new \RuntimeException('Not implemented');
352
    }
353
354
    /**
355
     * Delete action
356
     *
357
     * @param $id
358
     *
359
     * @return bool
360
     */
361
    public function deleteAction($id = null)
362
    {
363
        try {
364
            $this->checkSecurityToken($this->previousSk, $this->request);
365
366
            if ($id === null) {
367
                $this->accountFileService->deleteByIdBatch($this->getItemsIdFromRequest($this->request));
0 ignored issues
show
Bug introduced by
It seems like $this->getItemsIdFromRequest($this->request) can also be of type null; however, parameter $ids of SP\Services\Account\Acco...vice::deleteByIdBatch() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

367
                $this->accountFileService->deleteByIdBatch(/** @scrutinizer ignore-type */ $this->getItemsIdFromRequest($this->request));
Loading history...
368
369
                $this->eventDispatcher->notifyEvent('delete.accountFile.selection',
370
                    new Event($this, EventMessage::factory()
371
                        ->addDescription(__u('Files deleted')))
372
                );
373
374
                return $this->returnJsonResponse(0, __u('Files deleted'));
375
            }
376
377
            $this->eventDispatcher->notifyEvent('delete.accountFile',
378
                new Event($this, EventMessage::factory()
379
                    ->addDescription(__u('File deleted'))
380
                    ->addDetail(__u('File'), $id))
381
            );
382
383
            $this->accountFileService->delete($id);
384
385
            return $this->returnJsonResponse(0, __u('File Deleted'));
386
        } catch (\Exception $e) {
387
            processException($e);
388
389
            $this->eventDispatcher->notifyEvent('exception', new Event($e));
390
391
            return $this->returnJsonResponseException($e);
392
        }
393
    }
394
395
    /**
396
     * Saves create action
397
     */
398
    public function saveCreateAction()
399
    {
400
        throw new \RuntimeException('Not implemented');
401
    }
402
403
    /**
404
     * Saves edit action
405
     *
406
     * @param $id
407
     */
408
    public function saveEditAction($id)
409
    {
410
        throw new \RuntimeException('Not implemented');
411
    }
412
413
    /**
414
     * Obtener los datos para la vista de archivos de una cuenta
415
     *
416
     * @param int $accountId Account's ID
417
     *
418
     * @throws \Psr\Container\ContainerExceptionInterface
419
     */
420
    public function listAction($accountId)
421
    {
422
        if (!$this->configData->isFilesEnabled()) {
423
            echo __('Files management disabled');
424
            return;
425
        }
426
427
        try {
428
            $this->checkSecurityToken($this->previousSk, $this->request);
429
430
            $this->view->addTemplate('files-list', 'account');
431
432
            $this->view->assign('deleteEnabled', $this->request->analyzeInt('del', false));
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer|null expected by parameter $default of SP\Http\Request::analyzeInt(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

432
            $this->view->assign('deleteEnabled', $this->request->analyzeInt('del', /** @scrutinizer ignore-type */ false));
Loading history...
433
            $this->view->assign('files', $this->dic->get(AccountFileService::class)->getByAccountId($accountId));
434
            $this->view->assign('sk', $this->session->getSecurityKey());
435
            $this->view->assign('fileViewRoute', Acl::getActionRoute(Acl::ACCOUNT_FILE_VIEW));
436
            $this->view->assign('fileDownloadRoute', Acl::getActionRoute(Acl::ACCOUNT_FILE_DOWNLOAD));
437
            $this->view->assign('fileDeleteRoute', Acl::getActionRoute(Acl::ACCOUNT_FILE_DELETE));
438
439
            if (!is_array($this->view->files) || count($this->view->files) === 0) {
0 ignored issues
show
Bug Best Practice introduced by
The property files does not exist on SP\Mvc\View\Template. Since you implemented __get, consider adding a @property annotation.
Loading history...
introduced by
The condition is_array($this->view->files) is always false.
Loading history...
440
                $this->view->addTemplate('no_records_found', '_partials');
441
442
                $this->view->assign('message', __('There are no linked files for the account'));
443
444
                $this->view();
445
446
                return;
447
            }
448
449
            $this->eventDispatcher->notifyEvent('list.accountFile', new Event($this));
450
        } catch (\Exception $e) {
451
            processException($e);
452
453
            ErrorUtil::showErrorInView($this->view, ErrorUtil::ERR_EXCEPTION, true, 'files-list');
454
        }
455
456
        $this->view();
457
    }
458
459
    /**
460
     * Initialize class
461
     *
462
     * @throws \Psr\Container\ContainerExceptionInterface
463
     * @throws \Psr\Container\NotFoundExceptionInterface
464
     * @throws \SP\Services\Auth\AuthException
465
     */
466
    protected function initialize()
467
    {
468
        $this->checkLoggedIn();
469
470
        $this->accountFileService = $this->dic->get(AccountFileService::class);
471
    }
472
}
473