Completed
Push — master ( e2b148...0bc0e6 )
by Derek Stephen
06:06
created

UserService::authenticate()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7.7656

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 15
cts 20
cp 0.75
rs 8.4426
c 0
b 0
f 0
cc 7
nc 7
nop 2
crap 7.7656
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 Zend\Crypt\Password\Bcrypt;
20
21
class UserService
22
{
23
    /** @var EntityManager $em */
24
    protected $em;
25
26
    /** @var  PersonService */
27
    private $personSvc;
28
29
    /** @var string $userClass */
30
    private $userClass;
31
32 24
    public function __construct(EntityManager $entityManager,  PersonService $personService)
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $entityManager. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
33
    {
34 24
        $this->em = $entityManager;
35 24
        $this->personSvc = $personService;
36 24
        $this->setUserClass(User::class);
37 24
    }
38
39
    /**
40
     * @param array $data
41
     * @return UserInterface
42
     * @throws \Exception
43
     */
44 12
    public function createFromArray(array $data): UserInterface
45
    {
46
        /** @var UserInterface $user */
47 12
        $user = new $this->userClass();
48 12
        $person = isset($data['person']) ? $data['person'] : new Person();
49 12
        $user->setPerson($person);
50 12
        isset($data['id']) ? $user->setId($data['id']) : null;
51 12
        isset($data['email']) ? $user->setEmail($data['email']) : null;
52 12
        isset($data['password']) ? $user->setPassword($data['password']) : null;
53 12
        isset($data['state']) ? $user->setState(new State($data['state'])) : null;
54 12
        isset($data['registrationDate']) ? $user->setRegistrationDate(new DateTime($data['registrationDate'])) : null;
55 12
        isset($data['lastLogin']) ? $user->setLastLogin(new DateTime($data['lastLogin'])) : null;
56
57 12
        return $user;
58
    }
59
60
    /**
61
     * @return array
62
     */
63 1
    public function toArray(UserInterface $user): array
64
    {
65
        return array
66
        (
67 1
            'id' => $user->getID(),
68 1
            'email' => $user->getEmail(),
69 1
            'person' => $user->getPerson(),
70 1
            'password' => $user->getPassword(),
71 1
            'state' => $user->getState()->getValue(),
72 1
            'registrationDate' => $user->getRegistrationDate() === null ? null : $user->getRegistrationDate()->format('Y-m-d H:i:s'),
73 1
            'lastLoginDate' => $user->getLastLoginDate() === null ? null : $user->getLastLoginDate()->format('Y-m-d H:i:s'),
74
        );
75
    }
76
77
    /**
78
     * @param UserInterface $user
79
     * @return UserInterface
80
     * @throws \Doctrine\ORM\OptimisticLockException
81
     */
82 11
    public function saveUser(UserInterface $user): UserInterface
83
    {
84 11
        return $this->getUserRepository()->save($user);
85
    }
86
87
    /**
88
     * @param int $id
89
     * @return UserInterface|null
90
     */
91 1
    public function findUserById(int $id): ?UserInterface
92
    {
93 1
        $criteria = new UserCriteria();
94 1
        $criteria->setId($id);
95 1
        $results = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
96 1
        return (count($results)) ? $results[0] : null;
97
    }
98
99
    /**
100
     * @param string $email
101
     * @return UserInterface|null
102
     */
103 1
    public function findUserByEmail(string $email): ?UserInterface
104
    {
105 1
        $criteria = new UserCriteria();
106 1
        $criteria->setEmail($email);
107 1
        $result = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
108 1
        return count($result) ? $result[0] : null;
109
    }
110
111
    /**
112
     * @return \Doctrine\Common\Persistence\ObjectRepository|\Doctrine\ORM\EntityRepository
113
     */
114 11
    private function getUserRepository(): UserRepository
115
    {
116 11
        return $this->em->getRepository($this->userClass);
117
    }
118
119
    /**
120
     * @return \Doctrine\Common\Persistence\ObjectRepository|\Doctrine\ORM\EntityRepository
121
     */
122 2
    private function getEmailLinkRepository(): EmailLinkRepository
123
    {
124 2
        return $this->em->getRepository(EmailLink::class);
125
    }
126
127
    /**
128
     * @param array $data
129
     * @return UserInterface
130
     * @throws UserException
131
     * @throws \Doctrine\ORM\OptimisticLockException
132
     */
133 1
    public function registerUser(array $data): UserInterface
134
    {
135 1
        if (!isset($data['email']) || !isset($data['password']) || !isset($data['confirm'])) {
136
            throw new InvalidArgumentException('Required fields missing', 400);
137
        }
138 1
        if ($data['password'] !== $data['confirm']) {
139
            throw new UserException(UserException::WRONG_PASSWORD);
140
        }
141
142 1
        $criteria = new UserCriteria();
143 1
        $criteria->setEmail($data['email']);
144 1
        $user = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
145 1
        if(!empty($user)) {
146
            throw new UserException(UserException::USER_EXISTS);
147
        }
148
149 1
        $person = new Person();
150
        /** @var UserInterface $user */
151 1
        $user = new $this->userClass();
152 1
        $state = new State(State::STATE_UNACTIVATED);
153 1
        $user->setPerson($person);
154 1
        $user->setEmail($data['email']);
155 1
        $user->setRegistrationDate(new DateTime());
156 1
        $user->setState($state);
157
158 1
        $bcrypt = new Bcrypt();
159 1
        $bcrypt->setCost(14);
160 1
        $encryptedPassword = $bcrypt->create($data['password']);
161 1
        $user->setPassword($encryptedPassword);
162 1
        $this->saveUser($user);
163
164 1
        return $user;
165
    }
166
167
    /**
168
     * @param UserInterface $user
169
     * @param $password
170
     * @return UserInterface
171
     * @throws \Doctrine\ORM\OptimisticLockException
172
     */
173 3
    public function changePassword(UserInterface $user, string $password): UserInterface
174
    {
175 3
        $bcrypt = new Bcrypt();
176 3
        $bcrypt->setCost(14);
177
178 3
        $encryptedPassword = $bcrypt->create($password);
179 3
        $user->setPassword($encryptedPassword);
180 3
        $this->saveUser($user);
181
182 3
        return $user;
183
    }
184
185
    /**
186
     * @param UserInterface $user
187
     * @param int $expiry_days
188
     * @return EmailLink
189
     * @throws \Doctrine\ORM\OptimisticLockException
190
     */
191 2
    public function generateEmailLink(UserInterface $user, int $expiry_days = 7): EmailLink
192
    {
193 2
        $date = new DateTime();
194 2
        $date->modify('+'.$expiry_days.' days');
195 2
        $token = md5(uniqid(rand(), true));
196 2
        $link = new EmailLink();
197 2
        $link->setUser($user);
198 2
        $link->setToken($token);
199 2
        $link->setExpiryDate($date);
200
201 2
        return $this->getEmailLinkRepository()->save($link);
202
    }
203
204
    /**
205
     * @param EmailLink $link
206
     * @throws \Doctrine\ORM\ORMException
207
     */
208 2
    public function deleteEmailLink(EmailLink $link): void
209
    {
210
        /** @var EmailLink $link */
211 2
        $link = $this->em->merge($link);
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ORM\EntityManager::merge() has been deprecated with message: 2.7 This method is being removed from the ORM and won't have any replacement

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

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

Loading history...
212 2
        $this->getEmailLinkRepository()->delete($link);
213 2
    }
214
215
    /**
216
     * @param UserInterface $user
217
     * @param bool $deletePerson
218
     * @throws \Doctrine\ORM\OptimisticLockException
219
     */
220 11
    public function deleteUser(UserInterface $user, bool $deletePerson = false):  void
221
    {
222 11
        $this->getUserRepository()->delete($user,$deletePerson);
223 11
    }
224
225
    /**
226
     * @param string $email
227
     * @param string $token
228
     * @return EmailLink
229
     * @throws EmailLinkException
230
     */
231 1
    public function findEmailLink(string $email, string $token): EmailLink
232
    {
233 1
        $link = $this->getEmailLinkRepository()->findByToken($token);
0 ignored issues
show
Bug introduced by
The method findByToken() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
234 1
        if(!$link) {
235
            throw new EmailLinkException(EmailLinkException::LINK_NOT_FOUND);
236
        }
237 1
        if($link->getUser()->getEmail() != $email) {
238
            throw new EmailLinkException(EmailLinkException::LINK_NO_MATCH);
239
        }
240 1
        if($link->getExpiryDate() < new DateTime()) {
241
            throw new EmailLinkException(EmailLinkException::LINK_EXPIRED);
242
        }
243 1
        return $link;
244
    }
245
246
    /**
247
     * @param string $email
248
     * @param string $password
249
     * @return int
250
     * @throws UserException
251
     */
252 1
    public function authenticate(string $email, string $password): int
253
    {
254 1
        $criteria = new UserCriteria();
255 1
        $criteria->setEmail($email);
256
257 1
        $user = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
258
259 1
        if(empty($user)) {
260
            throw new UserException(UserException::USER_NOT_FOUND);
261
        }
262
263
        /** @var UserInterface $user  */
264 1
        $user = $user[0];
265
266 1
        switch($user->getState()->getValue()) {
267 1
            case State::STATE_UNACTIVATED:
268
                throw new UserException(UserException::USER_UNACTIVATED);
269 1
            case State::STATE_DISABLED:
270 1
            case State::STATE_SUSPENDED:
271
                throw new UserException(UserException::USER_DISABLED);
272 1
            case State::STATE_BANNED:
273
                throw new UserException(UserException::USER_BANNED);
274
        }
275
276 1
        $bcrypt = new Bcrypt();
277 1
        $bcrypt->setCost(14);
278
279 1
        if(!$bcrypt->verify($password, $user->getPassword()))
280
        {
281
            throw new UserException(UserException::WRONG_PASSWORD);
282
        }
283
284 1
        return $user->getID();
285
    }
286
287
    /**
288
     * @param UserCriteria $criteria
289
     * @return array
290
     */
291 1
    public function findByCriteria(UserCriteria $criteria): array
292
    {
293 1
        return $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
294
    }
295
296
    /**
297
     * @param UserCriteria $criteria
298
     * @return UserInterface|null
299
     */
300 1
    public function findOneByCriteria(UserCriteria $criteria): ?UserInterface
301
    {
302 1
        $results = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
303 1
        return count($results) > 0 ? $results[0] : null;
304
    }
305
306
    /**
307
     * @param UserInterface $user
308
     * @param $password
309
     * @return bool
310
     */
311 1
    public function checkPassword(UserInterface $user, string $password): bool
312
    {
313 1
        $bcrypt = new Bcrypt();
314 1
        $bcrypt->setCost(14);
315
316 1
        return $bcrypt->verify($password, $user->getPassword());
317
    }
318
319
    /**
320
     * @param string $fullyQualifiedClassName
321
     */
322 24
    public function setUserClass(string $fullyQualifiedClassName): void
323
    {
324 24
        $this->userClass = $fullyQualifiedClassName;
325 24
    }
326
327
    /**
328
     * @return PersonService
329
     */
330 1
    public function getPersonSvc(): PersonService
331
    {
332 1
        return $this->personSvc;
333
    }
334
}