Passed
Push — devel-3.0 ( 5f7f30...cd1038 )
by Rubén
03:44
created

AccountService::addDefaultPermissions()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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