Failed Conditions
Push — master ( 7ae6e5...0246af )
by Sylvain
09:29
created

UserRepository::getOneByLogin()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 41
    public function getAccessibleSubQuery(?\Ecodev\Felix\Model\User $user): string
19
    {
20 41
        if (!$user) {
21 3
            return '-1';
22
        }
23
24 38
        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 20
    public function getOneById(int $id): ?User
69
    {
70 20
        $user = $this->getAclFilter()->runWithoutAcl(function () use ($id) {
71 20
            return $this->findOneById($id);
72 20
        });
73
74 20
        return $user;
75
    }
76
77
    /**
78
     * Unsecured way to get a user from its login.
79
     *
80
     * This should only be used in tests or controlled environment.
81
     */
82 94
    public function getOneByLogin(?string $login): ?User
83
    {
84 94
        $user = $this->getAclFilter()->runWithoutAcl(function () use ($login) {
85 94
            return $this->findOneByLogin($login);
86 94
        });
87
88 94
        return $user;
89
    }
90
91
    /**
92
     * Get all administrators to notify by email
93
     *
94
     * @return User[]
95
     */
96 2
    public function getAllAdministratorsToNotify(): array
97
    {
98 2
        $qb = $this->createQueryBuilder('user')
99 2
            ->andWhere('user.status = :status')
100 2
            ->andWhere('user.role = :role')
101 2
            ->andWhere("user.email IS NOT NULL AND user.email != ''")
102 2
            ->setParameter('status', User::STATUS_ACTIVE)
103 2
            ->setParameter('role', User::ROLE_ADMINISTRATOR);
104
105 2
        $result = $this->getAclFilter()->runWithoutAcl(function () use ($qb) {
106 2
            return $qb->getQuery()->getResult();
107 2
        });
108
109 2
        return $result;
110
    }
111
112 4
    public function getAllToQueueBalanceMessage(bool $onlyNegativeBalance = false): array
113
    {
114 4
        $qb = $this->createQueryBuilder('user')
115 4
            ->addSelect('account')
116 4
            ->addSelect('booking')
117 4
            ->addSelect('bookable')
118 4
            ->join('user.accounts', 'account')
119 4
            ->join('user.bookings', 'booking')
120 4
            ->join('booking.bookable', 'bookable')
121 4
            ->andWhere('user.status != :status')
122 4
            ->andWhere("user.email IS NOT NULL AND user.email != ''")
123 4
            ->andWhere('bookable.bookingType IN (:bookingType)')
124 4
            ->andWhere('bookable.isActive = true')
125 4
            ->andWhere('bookable.periodicPrice != 0')
126 4
            ->setParameter('bookingType', [BookingTypeType::MANDATORY, BookingTypeType::ADMIN_ONLY], Connection::PARAM_STR_ARRAY)
127 4
            ->setParameter('status', User::STATUS_ARCHIVED)
128 4
            ->addOrderBy('user.id')
129 4
            ->addOrderBy('bookable.name');
130
131 4
        if ($onlyNegativeBalance) {
132 2
            $qb->andWhere('account.balance < 0');
133
        }
134
135 4
        $result = $this->getAclFilter()->runWithoutAcl(function () use ($qb) {
136 4
            return $qb->getQuery()->getResult();
137 4
        });
138
139 4
        return $result;
140
    }
141
142
    /**
143
     * Return all users that are family owners (and should have Account)
144
     *
145
     * @return User[]
146
     */
147 1
    public function getAllFamilyOwners(): array
148
    {
149 1
        $qb = $this->createQueryBuilder('user')
150 1
            ->andWhere('user.owner IS NULL')
151 1
            ->addOrderBy('user.id');
152
153 1
        return $qb->getQuery()->getResult();
154
    }
155
156
    /**
157
     * Return all users that are not family owners but still have an Account
158
     *
159
     * @return User[]
160
     */
161 1
    public function getAllNonFamilyOwnersWithAccount(): array
162
    {
163 1
        $qb = $this->createQueryBuilder('user')
164 1
            ->join('user.accounts', 'account')
165 1
            ->andWhere('user.owner IS NOT NULL')
166 1
            ->addOrderBy('user.id');
167
168 1
        return $qb->getQuery()->getResult();
169
    }
170
171
    public function exists(string $login, ?int $excludedId): bool
172
    {
173
        if (!$excludedId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $excludedId of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
174
            $excludedId = -1;
175
        }
176
177
        $sql = 'SELECT 1 FROM user WHERE login = :login AND id != :excludedId LIMIT 1';
178
        $params = [
179
            'login' => $login,
180
            'excludedId' => $excludedId,
181
        ];
182
183
        $result = $this->getEntityManager()->getConnection()->executeQuery($sql, $params)->fetchColumn();
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Driver\Res...tatement::fetchColumn() has been deprecated: Use fetchOne() instead. ( Ignorable by Annotation )

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

183
        $result = /** @scrutinizer ignore-deprecated */ $this->getEntityManager()->getConnection()->executeQuery($sql, $params)->fetchColumn();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
184
185
        return (bool) $result;
186
    }
187
188
    /**
189
     * Delete unconfirmed registrations older than a few days (user + account)
190
     *
191
     * @return int number of deleted users
192
     */
193
    public function deleteOldRegistrations(): int
194
    {
195
        $qb = $this->createQueryBuilder('user')
196
            ->addSelect('account')
197
            ->andWhere('user.login IS NULL AND user.creationDate < :creationDate')
198
            ->leftJoin('user.accounts', 'account')
199
            ->setParameter('creationDate', (new Chronos())->subDay(3));
200
201
        $users = $qb->getQuery()->getResult();
202
203
        foreach ($users as $user) {
204
            $account = $user->getAccount();
205
            if ($account) {
206
                _em()->remove($account);
207
            }
208
            _em()->remove($user);
209
        }
210
        _em()->flush();
211
212
        return count($users);
213
    }
214
}
215