Failed Conditions
Push — master ( 893dc8...0c1f2a )
by Adrien
06:01
created

UserRepository::getAllNonFamilyOwnersWithAccount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 8
ccs 6
cts 6
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Repository;
6
7
use Application\DBAL\Types\BookingTypeType;
8
use Application\Model\User;
9
use Cake\Chronos\Chronos;
10
use Doctrine\DBAL\Connection;
11
use Ecodev\Felix\Repository\LimitedAccessSubQuery;
12
13
class UserRepository extends AbstractRepository implements LimitedAccessSubQuery
14
{
15
    /**
16
     * Returns pure SQL to get ID of all objects that are accessible to given user.
17
     */
18 58
    public function getAccessibleSubQuery(?\Ecodev\Felix\Model\User $user): string
19
    {
20 58
        if (!$user) {
21 5
            return '-1';
22
        }
23
24 53
        return $this->getAllIdsQuery();
25
    }
26
27
    /**
28
     * Returns the user authenticated by its email and password.
29
     */
30 2
    public function getOneByLoginPassword(string $login, string $password): ?User
31
    {
32
        /** @var null|User $user */
33 2
        $user = $this->getOneByLogin($login);
34
35 2
        if (!$user) {
36 1
            return null;
37
        }
38
39
        // Check user status
40 2
        if (!in_array($user->getStatus(), [User::STATUS_ACTIVE, User::STATUS_INACTIVE, User::STATUS_NEW], true)) {
41
            return null;
42
        }
43
44 2
        $hashFromDb = $user->getPassword();
45 2
        $isMd5 = mb_strlen($hashFromDb) === 32 && ctype_xdigit($hashFromDb);
46
47
        // If we found a user and he has a correct MD5 or correct new hash, then return the user
48 2
        if (($isMd5 && md5($password) === $hashFromDb) || password_verify($password, $hashFromDb)) {
49
50
            // Update the hash in DB, if we are still MD5, or if PHP default options changed
51 2
            if ($isMd5 || password_needs_rehash($hashFromDb, PASSWORD_DEFAULT)) {
52 2
                $user->setPassword($password);
53
            }
54 2
            $user->revokeToken();
55 2
            _em()->flush();
56
57 2
            return $user;
58
        }
59
60 1
        return null;
61
    }
62
63
    /**
64
     * Unsecured way to get a user from its ID.
65
     *
66
     * This should only be used in tests or controlled environment.
67
     */
68 23
    public function getOneById(int $id): ?User
69
    {
70 23
        $user = $this->getAclFilter()->runWithoutAcl(fn () => $this->findOneById($id));
71
72 23
        return $user;
73
    }
74
75
    /**
76
     * Unsecured way to get a user from its login.
77
     *
78
     * This should only be used in tests or controlled environment.
79
     */
80 103
    public function getOneByLogin(?string $login): ?User
81
    {
82 103
        $user = $this->getAclFilter()->runWithoutAcl(fn () => $this->findOneByLogin($login));
83
84 103
        return $user;
85
    }
86
87
    /**
88
     * Get all administrators to notify by email.
89
     *
90
     * @return User[]
91
     */
92 2
    public function getAllAdministratorsToNotify(): array
93
    {
94 2
        $qb = $this->createQueryBuilder('user')
95 2
            ->andWhere('user.status = :status')
96 2
            ->andWhere('user.role = :role')
97 2
            ->andWhere("user.email IS NOT NULL AND user.email != ''")
98 2
            ->setParameter('status', User::STATUS_ACTIVE)
99 2
            ->setParameter('role', User::ROLE_ADMINISTRATOR);
100
101 2
        $result = $this->getAclFilter()->runWithoutAcl(fn () => $qb->getQuery()->getResult());
102
103 2
        return $result;
104
    }
105
106 4
    public function getAllToQueueBalanceMessage(bool $onlyNegativeBalance = false): array
107
    {
108 4
        $qb = $this->createQueryBuilder('user')
109 4
            ->addSelect('account')
110 4
            ->addSelect('booking')
111 4
            ->addSelect('bookable')
112 4
            ->join('user.accounts', 'account')
113 4
            ->join('user.bookings', 'booking')
114 4
            ->join('booking.bookable', 'bookable')
115 4
            ->andWhere('user.status != :status')
116 4
            ->andWhere("user.email IS NOT NULL AND user.email != ''")
117 4
            ->andWhere('bookable.bookingType IN (:bookingType)')
118 4
            ->andWhere('bookable.isActive = true')
119 4
            ->andWhere('bookable.periodicPrice != 0')
120 4
            ->setParameter('bookingType', [BookingTypeType::MANDATORY, BookingTypeType::ADMIN_ASSIGNED], Connection::PARAM_STR_ARRAY)
121 4
            ->setParameter('status', User::STATUS_ARCHIVED)
122 4
            ->addOrderBy('user.id')
123 4
            ->addOrderBy('bookable.name');
124
125 4
        if ($onlyNegativeBalance) {
126 2
            $qb->andWhere('account.balance < 0');
127
        }
128
129 4
        $result = $this->getAclFilter()->runWithoutAcl(fn () => $qb->getQuery()->getResult());
130
131 4
        return $result;
132
    }
133
134
    /**
135
     * Return all users that are family owners (and should have Account).
136
     *
137
     * @return User[]
138
     */
139 1
    public function getAllFamilyOwners(): array
140
    {
141 1
        $qb = $this->createQueryBuilder('user')
142 1
            ->addSelect('account')
143 1
            ->leftJoin('user.accounts', 'account')
144 1
            ->andWhere('user.owner IS NULL')
145 1
            ->addOrderBy('user.id');
146
147 1
        return $qb->getQuery()->getResult();
148
    }
149
150
    /**
151
     * Return all users that are not family owners but still have an Account.
152
     *
153
     * @return User[]
154
     */
155 1
    public function getAllNonFamilyOwnersWithAccount(): array
156
    {
157 1
        $qb = $this->createQueryBuilder('user')
158 1
            ->join('user.accounts', 'account')
159 1
            ->andWhere('user.owner IS NOT NULL')
160 1
            ->addOrderBy('user.id');
161
162 1
        return $qb->getQuery()->getResult();
163
    }
164
165
    public function exists(string $login, ?int $excludedId): bool
166
    {
167
        if (null === $excludedId) {
168
            $excludedId = -1;
169
        }
170
171
        $sql = 'SELECT 1 FROM user WHERE login = :login AND id != :excludedId LIMIT 1';
172
        $params = [
173
            'login' => $login,
174
            'excludedId' => $excludedId,
175
        ];
176
177
        $result = $this->getEntityManager()->getConnection()->executeQuery($sql, $params)->fetchOne();
178
179
        return (bool) $result;
180
    }
181
182
    /**
183
     * Delete unconfirmed registrations older than a few days (user + account).
184
     *
185
     * @return int number of deleted users
186
     */
187
    public function deleteOldRegistrations(): int
188
    {
189
        $qb = $this->createQueryBuilder('user')
190
            ->addSelect('account')
191
            ->andWhere('user.login IS NULL AND user.creationDate < :creationDate')
192
            ->leftJoin('user.accounts', 'account')
193
            ->setParameter('creationDate', (new Chronos())->subDay(3));
194
195
        $users = $qb->getQuery()->getResult();
196
197
        foreach ($users as $user) {
198
            $account = $user->getAccount();
199
            if ($account) {
200
                _em()->remove($account);
201
            }
202
            _em()->remove($user);
203
        }
204
        _em()->flush();
205
206
        return count($users);
207
    }
208
}
209