Failed Conditions
Push — master ( 3358c7...2378df )
by Sam
04:40
created

UserRepository::deleteOldRegistrations()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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