Passed
Push — master ( aab8f3...c0357c )
by Derek Stephen
03:21
created

UserService::deleteEmailLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 1
eloc 2
c 2
b 0
f 1
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Del\Service;
4
5
use DateTime;
6
use Del\Criteria\UserCriteria;
7
use Del\Entity\EmailLink;
8
use Del\Entity\User;
9
use Del\Person\Entity\Person;
10
use Del\Entity\UserInterface;
11
use Del\Exception\EmailLinkException;
12
use Del\Exception\UserException;
13
use Del\Repository\EmailLink as EmailLinkRepository;
14
use Del\Repository\UserRepository;
15
use Del\Person\Service\PersonService;
16
use Del\Value\User\State;
17
use Doctrine\ORM\EntityManager;
18
use InvalidArgumentException;
19
use Laminas\Crypt\Password\Bcrypt;
20
21
class UserService
22
{
23
    protected EntityManager $em;
24
    private PersonService $personSvc;
25
    private string $userClass;
26
27
    private UserRepository $userRepository;
28
29 28
    public function __construct(EntityManager $entityManager,  PersonService $personService)
30
    {
31 28
        $this->em = $entityManager;
32 28
        $this->personSvc = $personService;
33 28
        $this->setUserClass(User::class);
34 28
    }
35
36
    /**
37
     * @param array $data
38
     * @return UserInterface
39
     * @throws \Exception
40
     */
41 8
    public function createFromArray(array $data): UserInterface
42
    {
43
        /** @var UserInterface $user */
44 8
        $user = new $this->userClass();
45 8
        $person = isset($data['person']) ? $data['person'] : new Person();
46 8
        $user->setPerson($person);
47 8
        isset($data['id']) ? $user->setId($data['id']) : null;
48 8
        isset($data['email']) ? $user->setEmail($data['email']) : null;
49 8
        isset($data['password']) ? $user->setPassword($data['password']) : null;
50 8
        isset($data['state']) ? $user->setState(new State($data['state'])) : null;
51 8
        isset($data['registrationDate']) ? $user->setRegistrationDate(new DateTime($data['registrationDate'])) : null;
52 8
        isset($data['lastLogin']) ? $user->setLastLogin(new DateTime($data['lastLogin'])) : null;
53
54 8
        return $user;
55
    }
56
57
    /**
58
     * @return array
59
     */
60 1
    public function toArray(UserInterface $user): array
61
    {
62
        return [
63 1
            'id' => $user->getID(),
64 1
            'email' => $user->getEmail(),
65 1
            'person' => $user->getPerson(),
66 1
            'password' => $user->getPassword(),
67 1
            'state' => $user->getState()->getValue(),
68 1
            'registrationDate' => $user->getRegistrationDate() === null ? null : $user->getRegistrationDate()->format('Y-m-d H:i:s'),
69 1
            'lastLoginDate' => $user->getLastLoginDate() === null ? null : $user->getLastLoginDate()->format('Y-m-d H:i:s'),
70
        ];
71
    }
72
73
    /**
74
     * @param UserInterface $user
75
     * @return UserInterface
76
     * @throws \Doctrine\ORM\OptimisticLockException
77
     */
78 26
    public function saveUser(UserInterface $user): UserInterface
79
    {
80 26
        return $this->getUserRepository()->save($user);
81
    }
82
83
    /**
84
     * @param int $id
85
     * @return UserInterface|null
86
     */
87 1
    public function findUserById(int $id): ?UserInterface
88
    {
89 1
        $criteria = new UserCriteria();
90 1
        $criteria->setId($id);
91 1
        $results = $this->getUserRepository()->findByCriteria($criteria);
92
93 1
        return (count($results)) ? $results[0] : null;
94
    }
95
96
    /**
97
     * @param string $email
98
     * @return UserInterface|null
99
     */
100 1
    public function findUserByEmail(string $email): ?UserInterface
101
    {
102 1
        $criteria = new UserCriteria();
103 1
        $criteria->setEmail($email);
104 1
        $result = $this->getUserRepository()->findByCriteria($criteria);
105 1
        return count($result) ? $result[0] : null;
106
    }
107
108 27
    protected function getUserRepository(): UserRepository
109
    {
110 27
        if (!isset($this->userRepository)) {
111 27
            $this->userRepository = $this->em->getRepository($this->userClass);
112
        }
113
114 27
        return $this->userRepository;
115
    }
116
117 5
    private function getEmailLinkRepository(): EmailLinkRepository
118
    {
119 5
        return $this->em->getRepository(EmailLink::class);
120
    }
121
122 1
    public function hasProfile(UserInterface $user): bool
123
    {
124 1
        $has = false;
125 1
        $person = $user->getPerson();
126
127 1
        if (!empty($person->getFirstname()) && !empty($person->getLastname())) {
128 1
            $has = true;
129
        }
130
131 1
        return $has;
132
    }
133
134
    /**
135
     * @param array $data
136
     * @return UserInterface
137
     * @throws UserException
138
     * @throws \Doctrine\ORM\OptimisticLockException
139
     */
140 4
    public function registerUser(array $data): UserInterface
141
    {
142 4
        if (!isset($data['email']) || !isset($data['password']) || !isset($data['confirm'])) {
143 1
            throw new InvalidArgumentException('Required fields missing', 400);
144
        }
145 3
        if ($data['password'] !== $data['confirm']) {
146 1
            throw new UserException(UserException::WRONG_PASSWORD);
147
        }
148
149 2
        $criteria = new UserCriteria();
150 2
        $criteria->setEmail($data['email']);
151 2
        $user = $this->getUserRepository()->findByCriteria($criteria);
152
153 2
        if(!empty($user)) {
154 1
            throw new UserException(UserException::USER_EXISTS);
155
        }
156
157 1
        $person = new Person();
158
        /** @var UserInterface $user */
159 1
        $user = new $this->userClass();
160 1
        $state = new State(State::STATE_UNACTIVATED);
161 1
        $user->setPerson($person);
162 1
        $user->setEmail($data['email']);
163 1
        $user->setRegistrationDate(new DateTime());
164 1
        $user->setState($state);
165
166 1
        $bcrypt = new Bcrypt();
167 1
        $bcrypt->setCost(14);
168 1
        $encryptedPassword = $bcrypt->create($data['password']);
169 1
        $user->setPassword($encryptedPassword);
170 1
        $this->saveUser($user);
171
172 1
        return $user;
173
    }
174
175
    /**
176
     * @param UserInterface $user
177
     * @param $password
178
     * @return UserInterface
179
     * @throws \Doctrine\ORM\OptimisticLockException
180
     */
181 26
    public function changePassword(UserInterface $user, string $password): UserInterface
182
    {
183 26
        $bcrypt = new Bcrypt();
184 26
        $bcrypt->setCost(14);
185
186 26
        $encryptedPassword = $bcrypt->create($password);
187 26
        $user->setPassword($encryptedPassword);
188 26
        $this->saveUser($user);
189
190 26
        return $user;
191
    }
192
193
    /**
194
     * @param UserInterface $user
195
     * @param int $expiry_days
196
     * @return EmailLink
197
     * @throws \Doctrine\ORM\OptimisticLockException
198
     */
199 2
    public function generateEmailLink(UserInterface $user, int $expiry_days = 7): EmailLink
200
    {
201 2
        $date = new DateTime();
202 2
        $date->modify('+'.$expiry_days.' days');
203 2
        $token = md5(uniqid(rand(), true));
204 2
        $link = new EmailLink();
205 2
        $link->setUser($user);
206 2
        $link->setToken($token);
207 2
        $link->setExpiryDate($date);
208
209 2
        return $this->getEmailLinkRepository()->save($link);
210
    }
211
212
    /**
213
     * @param EmailLink $link
214
     * @throws \Doctrine\ORM\ORMException
215
     */
216 2
    public function deleteEmailLink(EmailLink $link): void
217
    {
218
        /** @var EmailLink $link */
219 2
        $link = $this->em->merge($link);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\ORM\EntityManager::merge() has been deprecated: 2.7 This method is being removed from the ORM and won't have any replacement ( Ignorable by Annotation )

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

219
        $link = /** @scrutinizer ignore-deprecated */ $this->em->merge($link);

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...
220 2
        $this->getEmailLinkRepository()->delete($link);
221 2
    }
222
223
    /**
224
     * @param UserInterface $user
225
     * @param bool $deletePerson
226
     * @throws \Doctrine\ORM\OptimisticLockException
227
     */
228 7
    public function deleteUser(UserInterface $user, bool $deletePerson = false):  void
229
    {
230 7
        $this->getUserRepository()->delete($user,$deletePerson);
231 7
    }
232
233
    /**
234
     * @param string $email
235
     * @param string $token
236
     * @return EmailLink
237
     * @throws EmailLinkException
238
     */
239 4
    public function findEmailLink(string $email, string $token): EmailLink
240
    {
241 4
        $link = $this->getEmailLinkRepository()->findByToken($token);
242 4
        if(!$link) {
243 1
            throw new EmailLinkException(EmailLinkException::LINK_NOT_FOUND);
244
        }
245 3
        if($link->getUser()->getEmail() != $email) {
246 1
            throw new EmailLinkException(EmailLinkException::LINK_NO_MATCH);
247
        }
248 2
        if($link->getExpiryDate() < new DateTime()) {
249 1
            throw new EmailLinkException(EmailLinkException::LINK_EXPIRED);
250
        }
251 1
        return $link;
252
    }
253
254
    /**
255
     * @param string $email
256
     * @param string $password
257
     * @return int
258
     * @throws UserException
259
     */
260 6
    public function authenticate(string $email, string $password): int
261
    {
262 6
        $criteria = new UserCriteria();
263 6
        $criteria->setEmail($email);
264
265 6
        $user = $this->getUserRepository()->findByCriteria($criteria);
266
267 6
        if(empty($user)) {
268 1
            throw new UserException(UserException::USER_NOT_FOUND);
269
        }
270
271
        /** @var UserInterface $user  */
272 5
        $user = $user[0];
273
274 5
        switch($user->getState()->getValue()) {
275 5
            case State::STATE_UNACTIVATED:
276 1
                throw new UserException(UserException::USER_UNACTIVATED);
277 4
            case State::STATE_DISABLED:
278 3
            case State::STATE_SUSPENDED:
279 1
                throw new UserException(UserException::USER_DISABLED);
280 3
            case State::STATE_BANNED:
281 1
                throw new UserException(UserException::USER_BANNED);
282
        }
283
284 2
        $bcrypt = new Bcrypt();
285 2
        $bcrypt->setCost(14);
286
287 2
        if(!$bcrypt->verify($password, $user->getPassword()))
288
        {
289 1
            throw new UserException(UserException::WRONG_PASSWORD);
290
        }
291
292 1
        return $user->getID();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $user->getID() could return the type null which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
293
    }
294
295
    /**
296
     * @param UserCriteria $criteria
297
     * @return array
298
     */
299 1
    public function findByCriteria(UserCriteria $criteria): array
300
    {
301 1
        return $this->getUserRepository()->findByCriteria($criteria);
302
    }
303
304
    /**
305
     * @param UserCriteria $criteria
306
     * @return UserInterface|null
307
     */
308 3
    public function findOneByCriteria(UserCriteria $criteria): ?UserInterface
309
    {
310 3
        $results = $this->getUserRepository()->findByCriteria($criteria);
311
312 3
        return count($results) > 0 ? $results[0] : null;
313
    }
314
315
    /**
316
     * @param UserInterface $user
317
     * @param $password
318
     * @return bool
319
     */
320 1
    public function checkPassword(UserInterface $user, string $password): bool
321
    {
322 1
        $bcrypt = new Bcrypt();
323 1
        $bcrypt->setCost(14);
324
325 1
        return $bcrypt->verify($password, $user->getPassword());
326
    }
327
328
    /**
329
     * @param string $fullyQualifiedClassName
330
     */
331 28
    public function setUserClass(string $fullyQualifiedClassName): void
332
    {
333 28
        $this->userClass = $fullyQualifiedClassName;
334 28
    }
335
336
    /**
337
     * @return PersonService
338
     */
339 1
    public function getPersonSvc(): PersonService
340
    {
341 1
        return $this->personSvc;
342
    }
343
}
344