Passed
Push — devel-3.0 ( 4622e9...e92637 )
by Rubén
03:18
created

AccountService::getByFilter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 8
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\Services\Account;
26
27
use Defuse\Crypto\Exception\CryptoException;
28
use SP\Core\Crypt\Crypt;
29
use SP\Core\Exceptions\QueryException;
30
use SP\Core\Exceptions\SPException;
31
use SP\DataModel\AccountData;
32
use SP\DataModel\AccountHistoryData;
33
use SP\DataModel\AccountPassData;
34
use SP\DataModel\AccountSearchVData;
35
use SP\DataModel\Dto\AccountDetailsResponse;
36
use SP\DataModel\Dto\AccountHistoryCreateDto;
37
use SP\DataModel\ItemSearchData;
38
use SP\Repositories\Account\AccountRepository;
39
use SP\Repositories\Account\AccountToTagRepository;
40
use SP\Repositories\Account\AccountToUserGroupRepository;
41
use SP\Repositories\Account\AccountToUserRepository;
42
use SP\Repositories\NoSuchItemException;
43
use SP\Services\Config\ConfigService;
44
use SP\Services\Service;
45
use SP\Services\ServiceException;
46
use SP\Services\ServiceItemTrait;
47
use SP\Storage\Database\QueryResult;
48
49
/**
50
 * Class AccountService
51
 *
52
 * @package SP\Services\Account
53
 */
54
final class AccountService extends Service implements AccountServiceInterface
55
{
56
    use ServiceItemTrait;
57
58
    /**
59
     * @var AccountRepository
60
     */
61
    protected $accountRepository;
62
    /**
63
     * @var AccountToUserGroupRepository
64
     */
65
    protected $accountToUserGroupRepository;
66
    /**
67
     * @var AccountToUserRepository
68
     */
69
    protected $accountToUserRepository;
70
    /**
71
     * @var AccountToTagRepository
72
     */
73
    protected $accountToTagRepository;
74
75
    /**
76
     * @param int $id
77
     *
78
     * @return AccountDetailsResponse
79
     * @throws QueryException
80
     * @throws \SP\Repositories\NoSuchItemException
81
     * @throws \SP\Core\Exceptions\ConstraintException
82
     */
83
    public function getById($id)
84
    {
85
        $result = $this->accountRepository->getById($id);
86
87
        if ($result->getNumRows() === 0) {
88
            throw new NoSuchItemException(__u('La cuenta no existe'));
89
        }
90
91
        return new AccountDetailsResponse($id, $result->getData());
92
    }
93
94
    /**
95
     * @param AccountDetailsResponse $accountDetailsResponse
96
     *
97
     * @return AccountService
98
     * @throws QueryException
99
     * @throws \SP\Core\Exceptions\ConstraintException
100
     */
101
    public function withUsersById(AccountDetailsResponse $accountDetailsResponse)
102
    {
103
        $accountDetailsResponse->setUsers($this->accountToUserRepository->getUsersByAccountId($accountDetailsResponse->getId()));
104
105
        return $this;
106
    }
107
108
    /**
109
     * @param AccountDetailsResponse $accountDetailsResponse
110
     *
111
     * @return AccountService
112
     * @throws QueryException
113
     * @throws \SP\Core\Exceptions\ConstraintException
114
     */
115
    public function withUserGroupsById(AccountDetailsResponse $accountDetailsResponse)
116
    {
117
        $accountDetailsResponse->setUserGroups($this->accountToUserGroupRepository->getUserGroupsByAccountId($accountDetailsResponse->getId())->getDataAsArray());
118
119
        return $this;
120
    }
121
122
    /**
123
     * @param AccountDetailsResponse $accountDetailsResponse
124
     *
125
     * @return AccountService
126
     * @throws QueryException
127
     * @throws \SP\Core\Exceptions\ConstraintException
128
     */
129
    public function withTagsById(AccountDetailsResponse $accountDetailsResponse)
130
    {
131
        $accountDetailsResponse->setTags($this->accountToTagRepository->getTagsByAccountId($accountDetailsResponse->getId())->getDataAsArray());
132
133
        return $this;
134
    }
135
136
    /**
137
     * @param $id
138
     *
139
     * @return bool
140
     * @throws QueryException
141
     * @throws \SP\Core\Exceptions\ConstraintException
142
     */
143
    public function incrementViewCounter($id)
144
    {
145
        return $this->accountRepository->incrementViewCounter($id);
146
    }
147
148
    /**
149
     * @param $id
150
     *
151
     * @return bool
152
     * @throws QueryException
153
     * @throws \SP\Core\Exceptions\ConstraintException
154
     */
155
    public function incrementDecryptCounter($id)
156
    {
157
        return $this->accountRepository->incrementDecryptCounter($id);
158
    }
159
160
    /**
161
     * @param $id
162
     *
163
     * @return \SP\DataModel\AccountPassData
164
     * @throws QueryException
165
     * @throws \SP\Core\Exceptions\ConstraintException
166
     * @throws NoSuchItemException
167
     */
168
    public function getPasswordForId($id)
169
    {
170
        $queryFilter = $this->dic->get(AccountFilterUser::class)->getFilter();
171
172
        $result = $this->accountRepository->getPasswordForId($id, $queryFilter);
173
174
        if ($result->getNumRows() === 0) {
175
            throw new NoSuchItemException(__u('Cuenta no encontrada'));
176
        }
177
178
        return $result->getData();
179
    }
180
181
    /**
182
     * @param AccountRequest $accountRequest
183
     *
184
     * @return int
185
     * @throws QueryException
186
     * @throws SPException
187
     * @throws \SP\Core\Exceptions\ConstraintException
188
     */
189
    public function create(AccountRequest $accountRequest)
190
    {
191
        $accountRequest->changePermissions = AccountAclService::getShowPermission($this->context->getUserData(), $this->context->getUserProfile());
192
        $accountRequest->userGroupId = $accountRequest->userGroupId ?: $this->context->getUserData()->getUserGroupId();
193
194
        if (empty($accountRequest->key)) {
195
            $pass = $this->getPasswordEncrypted($accountRequest->pass);
196
197
            $accountRequest->pass = $pass['pass'];
198
            $accountRequest->key = $pass['key'];
199
        }
200
201
        $accountRequest->id = $this->accountRepository->create($accountRequest);
202
203
        $this->addItems($accountRequest);
204
205
        return $accountRequest->id;
206
    }
207
208
    /**
209
     * Devolver los datos de la clave encriptados
210
     *
211
     * @param string $pass
212
     * @param string $masterPass Clave maestra a utilizar
213
     *
214
     * @return array
215
     * @throws ServiceException
216
     */
217
    public function getPasswordEncrypted($pass, $masterPass = null)
218
    {
219
        try {
220
            if ($masterPass === null) {
221
                $masterPass = $this->getMasterKeyFromContext();
222
            }
223
224
            if (empty($masterPass)) {
225
                throw new ServiceException(__u('Clave maestra no establecida'));
226
            }
227
228
            $out['key'] = Crypt::makeSecuredKey($masterPass);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$out was never initialized. Although not strictly required by PHP, it is generally a good practice to add $out = array(); before regardless.
Loading history...
229
            $out['pass'] = Crypt::encrypt($pass, $out['key'], $masterPass);
230
231
            if (strlen($out['pass']) > 1000 || strlen($out['key']) > 1000) {
232
                throw new ServiceException(__u('Error interno'));
233
            }
234
235
            return $out;
236
        } catch (CryptoException $e) {
237
            throw new ServiceException(__u('Error interno'));
238
        }
239
    }
240
241
    /**
242
     * Adds external items to the account
243
     *
244
     * @param AccountRequest $accountRequest
245
     */
246
    protected function addItems(AccountRequest $accountRequest)
247
    {
248
        try {
249
            if ($accountRequest->changePermissions) {
250
                if (is_array($accountRequest->userGroupsView) && !empty($accountRequest->userGroupsView)) {
251
                    $this->accountToUserGroupRepository->add($accountRequest);
252
                }
253
254
                if (is_array($accountRequest->userGroupsEdit) && !empty($accountRequest->userGroupsEdit)) {
255
                    $this->accountToUserGroupRepository->addEdit($accountRequest);
256
                }
257
258
                if (is_array($accountRequest->usersView) && !empty($accountRequest->usersView)) {
259
                    $this->accountToUserRepository->add($accountRequest);
260
                }
261
262
                if (is_array($accountRequest->usersEdit) && !empty($accountRequest->usersEdit)) {
263
                    $this->accountToUserRepository->addEdit($accountRequest);
264
                }
265
            }
266
267
            if (is_array($accountRequest->tags) && !empty($accountRequest->tags)) {
268
                $this->accountToTagRepository->add($accountRequest);
269
            }
270
        } catch (SPException $e) {
271
            logger($e->getMessage());
272
        }
273
    }
274
275
    /**
276
     * @param AccountHistoryData $data
277
     *
278
     * @return int
279
     * @throws QueryException
280
     * @throws \SP\Core\Exceptions\ConstraintException
281
     */
282
    public function createFromHistory(AccountHistoryData $data)
283
    {
284
        $accountRequest = new AccountRequest();
285
        $accountRequest->name = $data->getName();
286
        $accountRequest->categoryId = $data->getCategoryId();
287
        $accountRequest->clientId = $data->getClientId();
288
        $accountRequest->url = $data->getUrl();
289
        $accountRequest->login = $data->getLogin();
290
        $accountRequest->pass = $data->getPass();
291
        $accountRequest->key = $data->getKey();
292
        $accountRequest->notes = $data->getNotes();
293
        $accountRequest->userId = $data->getUserId();
294
        $accountRequest->userGroupId = $data->getUserGroupId();
295
        $accountRequest->passDateChange = $data->getPassDateChange();
296
        $accountRequest->parentId = $data->getParentId();
297
        $accountRequest->isPrivate = $data->getIsPrivate();
298
        $accountRequest->isPrivateGroup = $data->getIsPrivateGroup();
299
300
        return $this->accountRepository->create($accountRequest);
301
    }
302
303
    /**
304
     * Updates external items for the account
305
     *
306
     * @param AccountRequest $accountRequest
307
     *
308
     * @throws \Exception
309
     */
310
    public function update(AccountRequest $accountRequest)
311
    {
312
        $this->transactionAware(function () use ($accountRequest) {
313
            $accountRequest->changePermissions = AccountAclService::getShowPermission($this->context->getUserData(), $this->context->getUserProfile());
314
315
            // Cambiar el grupo principal si el usuario es Admin
316
            $accountRequest->changeUserGroup = ($accountRequest->userGroupId !== 0
317
                && ($this->context->getUserData()->getIsAdminApp() || $this->context->getUserData()->getIsAdminAcc()));
318
319
            $this->addHistory($accountRequest->id);
320
321
            $this->accountRepository->update($accountRequest);
322
323
            $this->updateItems($accountRequest);
324
        });
325
    }
326
327
    /**
328
     * @param int  $accountId
329
     * @param bool $isDelete
330
     *
331
     * @return bool
332
     * @throws NoSuchItemException
333
     * @throws QueryException
334
     * @throws ServiceException
335
     * @throws \SP\Core\Exceptions\ConstraintException
336
     */
337
    protected function addHistory($accountId, $isDelete = false)
338
    {
339
        $accountHistoryRepository = $this->dic->get(AccountHistoryService::class);
340
        $configService = $this->dic->get(ConfigService::class);
341
342
        return $accountHistoryRepository->create(
343
            new AccountHistoryCreateDto(
344
                $accountId,
345
                !$isDelete,
346
                $isDelete,
347
                $configService->getByParam('masterPwd'))
348
        );
349
    }
350
351
    /**
352
     * Updates external items for the account
353
     *
354
     * @param AccountRequest $accountRequest
355
     *
356
     * @throws QueryException
357
     * @throws \SP\Core\Exceptions\ConstraintException
358
     */
359
    protected function updateItems(AccountRequest $accountRequest)
360
    {
361
        if ($accountRequest->changePermissions) {
362
            if ($accountRequest->updateUserGroupPermissions) {
363
                if (!empty($accountRequest->userGroupsView)) {
364
                    $this->accountToUserGroupRepository->update($accountRequest);
365
                } else {
366
                    $this->accountToUserGroupRepository->deleteByAccountId($accountRequest->id);
367
                }
368
369
                if (!empty($accountRequest->userGroupsEdit)) {
370
                    $this->accountToUserGroupRepository->updateEdit($accountRequest);
371
                } else {
372
                    $this->accountToUserGroupRepository->deleteEditByAccountId($accountRequest->id);
373
                }
374
            }
375
376
            if ($accountRequest->updateUserPermissions) {
377
                if (!empty($accountRequest->usersView)) {
378
                    $this->accountToUserRepository->update($accountRequest);
379
                } else {
380
                    $this->accountToUserRepository->deleteByAccountId($accountRequest->id);
381
                }
382
383
                if (!empty($accountRequest->usersEdit)) {
384
                    $this->accountToUserRepository->updateEdit($accountRequest);
385
                } else {
386
                    $this->accountToUserRepository->deleteEditByAccountId($accountRequest->id);
387
                }
388
            }
389
        }
390
391
        if ($accountRequest->updateTags) {
392
            if (!empty($accountRequest->tags)) {
393
                $this->accountToTagRepository->update($accountRequest);
394
            } else {
395
                $this->accountToTagRepository->deleteByAccountId($accountRequest->id);
396
            }
397
        }
398
    }
399
400
    /**
401
     * @param AccountRequest $accountRequest
402
     *
403
     * @throws \Exception
404
     */
405
    public function editPassword(AccountRequest $accountRequest)
406
    {
407
        $this->transactionAware(function () use ($accountRequest) {
408
            $this->addHistory($accountRequest->id);
409
410
            $pass = $this->getPasswordEncrypted($accountRequest->pass);
411
412
            $accountRequest->pass = $pass['pass'];
413
            $accountRequest->key = $pass['key'];
414
415
            $this->accountRepository->editPassword($accountRequest);
416
        });
417
    }
418
419
    /**
420
     * Updates an already encrypted password data from a master password changing action
421
     *
422
     * @param AccountPasswordRequest $accountRequest
423
     *
424
     * @return bool
425
     * @throws \SP\Core\Exceptions\ConstraintException
426
     * @throws QueryException
427
     */
428
    public function updatePasswordMasterPass(AccountPasswordRequest $accountRequest)
429
    {
430
        return $this->accountRepository->updatePassword($accountRequest);
431
    }
432
433
    /**
434
     * @param $historyId
435
     * @param $accountId
436
     *
437
     * @throws \Exception
438
     */
439
    public function editRestore($historyId, $accountId)
440
    {
441
        $this->transactionAware(function () use ($historyId, $accountId) {
442
            $this->addHistory($accountId);
443
444
            if (!$this->accountRepository->editRestore($historyId, $this->context->getUserData()->getId())) {
445
                throw new ServiceException(__u('Error al restaurar cuenta'));
446
            }
447
        });
448
    }
449
450
    /**
451
     * @param $id
452
     *
453
     * @return AccountService
454
     * @throws ServiceException
455
     */
456
    public function delete($id)
457
    {
458
        $this->transactionAware(function () use ($id) {
459
            $this->addHistory($id, 1);
460
461
            if ($this->accountRepository->delete($id) === 0) {
462
                throw new NoSuchItemException(__u('Cuenta no encontrada'));
463
            }
464
465
        });
466
467
        return $this;
468
    }
469
470
    /**
471
     * @param array $ids
472
     *
473
     * @return AccountService
474
     * @throws SPException
475
     * @throws ServiceException
476
     */
477
    public function deleteByIdBatch(array $ids)
478
    {
479
        if ($this->accountRepository->deleteByIdBatch($ids) === 0) {
480
            throw new ServiceException(__u('Error al eliminar las cuentas'));
481
        }
482
483
        return $this;
484
    }
485
486
    /**
487
     * @param $accountId
488
     *
489
     * @return array
490
     * @throws QueryException
491
     * @throws \SP\Core\Exceptions\ConstraintException
492
     */
493
    public function getForUser($accountId = null)
494
    {
495
        $queryFilter = $this->dic->get(AccountFilterUser::class)->getFilter();
496
497
        if (null !== $accountId) {
498
            $queryFilter->addFilter('Account.id <> ? AND (Account.parentId = 0 OR Account.parentId IS NULL)', [$accountId]);
499
        }
500
501
        return $this->accountRepository->getForUser($queryFilter)->getDataAsArray();
502
    }
503
504
    /**
505
     * @param $accountId
506
     *
507
     * @return array
508
     * @throws QueryException
509
     * @throws \SP\Core\Exceptions\ConstraintException
510
     */
511
    public function getLinked($accountId)
512
    {
513
        $queryFilter = $this->dic->get(AccountFilterUser::class)->getFilter();
514
515
        $queryFilter->addFilter('Account.parentId = ?', [$accountId]);
516
517
        return $this->accountRepository->getLinked($queryFilter)->getDataAsArray();
518
    }
519
520
    /**
521
     * @param $id
522
     *
523
     * @return AccountPassData
524
     * @throws QueryException
525
     * @throws \SP\Core\Exceptions\ConstraintException
526
     * @throws NoSuchItemException
527
     */
528
    public function getPasswordHistoryForId($id)
529
    {
530
        $queryFilter = $this->dic->get(AccountFilterUser::class)->getFilterHistory();
531
        $queryFilter->addFilter('AccountHistory.id = ?', [$id]);
532
533
        $result = $this->accountRepository->getPasswordHistoryForId($queryFilter);
534
535
        if ($result->getNumRows() === 0) {
536
            throw new NoSuchItemException(__u('La cuenta no existe'));
537
        }
538
539
        return $result->getData();
540
    }
541
542
    /**
543
     * @return AccountData[]
544
     * @throws QueryException
545
     * @throws \SP\Core\Exceptions\ConstraintException
546
     */
547
    public function getAllBasic()
548
    {
549
        return $this->accountRepository->getAll()->getDataAsArray();
550
    }
551
552
    /**
553
     * @param ItemSearchData $itemSearchData
554
     *
555
     * @return QueryResult
556
     * @throws QueryException
557
     * @throws \SP\Core\Exceptions\ConstraintException
558
     */
559
    public function search(ItemSearchData $itemSearchData)
560
    {
561
        return $this->accountRepository->search($itemSearchData);
562
    }
563
564
    /**
565
     * Devolver el número total de cuentas
566
     *
567
     * @return \stdClass
568
     * @throws QueryException
569
     * @throws \SP\Core\Exceptions\ConstraintException
570
     */
571
    public function getTotalNumAccounts()
572
    {
573
        return $this->accountRepository->getTotalNumAccounts()->num;
574
    }
575
576
    /**
577
     * Obtener los datos de una cuenta.
578
     *
579
     * @param $id
580
     *
581
     * @return \SP\DataModel\AccountExtData
582
     * @throws QueryException
583
     * @throws \SP\Repositories\NoSuchItemException
584
     * @throws \SP\Core\Exceptions\ConstraintException
585
     */
586
    public function getDataForLink($id)
587
    {
588
        $result = $this->accountRepository->getDataForLink($id);
589
590
        if ($result->getNumRows() === 0) {
591
            throw new NoSuchItemException(__u('La cuenta no existe'));
592
        }
593
594
        return $result->getData();
595
    }
596
597
    /**
598
     * Obtener los datos relativos a la clave de todas las cuentas.
599
     *
600
     * @return array Con los datos de la clave
601
     * @throws QueryException
602
     * @throws \SP\Core\Exceptions\ConstraintException
603
     */
604
    public function getAccountsPassData()
605
    {
606
        return $this->accountRepository->getAccountsPassData();
607
    }
608
609
    /**
610
     * Obtener las cuentas de una búsqueda.
611
     *
612
     * @param AccountSearchFilter $accountSearchFilter
613
     *
614
     * @return AccountSearchVData[]
615
     * @throws QueryException
616
     * @throws SPException
617
     * @throws \SP\Core\Exceptions\ConstraintException
618
     */
619
    public function getByFilter(AccountSearchFilter $accountSearchFilter)
620
    {
621
        $accountFilterUser = $this->dic->get(AccountFilterUser::class);
622
623
        return $this->accountRepository->getByFilter(
624
            $accountSearchFilter,
625
            $accountFilterUser->getFilter($accountSearchFilter->getGlobalSearch())
626
        )->getDataAsArray();
627
    }
628
629
    /**
630
     * @throws \Psr\Container\ContainerExceptionInterface
631
     * @throws \Psr\Container\NotFoundExceptionInterface
632
     */
633
    protected function initialize()
634
    {
635
        $this->accountRepository = $this->dic->get(AccountRepository::class);
636
        $this->accountToUserRepository = $this->dic->get(AccountToUserRepository::class);
637
        $this->accountToUserGroupRepository = $this->dic->get(AccountToUserGroupRepository::class);
638
        $this->accountToTagRepository = $this->dic->get(AccountToTagRepository::class);
639
    }
640
}