/
TokenRepository.php
155 lines (129 loc) · 4.78 KB
/
TokenRepository.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<?php
declare(strict_types=1);
namespace Wearesho\Yii\Repositories;
use Wearesho\Yii\Exceptions\DeliveryLimitReachedException;
use Wearesho\Yii\Exceptions\InvalidRecipientException;
use Wearesho\Yii\Exceptions\InvalidTokenException;
use Wearesho\Yii\Interfaces\TokenableEntityInterface;
use Wearesho\Yii\Interfaces\TokenGeneratorInterface;
use Wearesho\Yii\Interfaces\TokenInterface;
use Wearesho\Yii\Interfaces\TokenRecordInterface;
use Wearesho\Yii\Interfaces\TokenRepositoryInterface;
use Wearesho\Yii\Interfaces\TokenRepositoryConfigInterface;
use Horat1us\Yii\Validation;
use Wearesho\Delivery;
class TokenRepository implements TokenRepositoryInterface
{
protected TokenRecordInterface $model;
protected TokenRepositoryConfigInterface $config;
protected TokenGeneratorInterface $generator;
protected Delivery\ServiceInterface $deliveryService;
/**
* TokensRepository constructor.
* @param TokenRecordInterface|null $model
* @param TokenRepositoryConfigInterface $config
* @param TokenGeneratorInterface $generator
* @param Delivery\ServiceInterface $service
*/
public function __construct(
TokenRecordInterface $model,
TokenRepositoryConfigInterface $config,
TokenGeneratorInterface $generator,
Delivery\ServiceInterface $service
) {
$this->model = $model;
$this->generator = $generator;
$this->config = $config;
$this->deliveryService = $service;
}
/**
* Creating token (for example, when we receive first-stage data)
* Will increase sending counter
*
* @param TokenableEntityInterface $entity
* @return TokenInterface|TokenRecordInterface
* @throws Validation\Failure
*/
public function push(TokenableEntityInterface $entity): TokenInterface
{
$record = $this->pull($entity->getTokenRecipient());
if (!$record instanceof TokenRecordInterface) {
$class = get_class($this->model);
/** @var TokenRecordInterface $record */
$record = new $class;
$record->setRecipient($entity->getTokenRecipient());
$record->setToken($this->generator->getToken());
}
$record->setData($entity->getTokenData());
Validation\Exception::saveOrThrow($record);
return $record;
}
/**
* Creating and sending token
* Internal should use push() method
*
* @param TokenableEntityInterface $entity
* @throws DeliveryLimitReachedException
* @throws Delivery\Exception
* @throws Validation\Failure
* @todo: preventing two write operations (update+update, insert+update)
*
*/
public function send(TokenableEntityInterface $entity): void
{
$token = $this->push($entity);
if ($token->getDeliveryCount() >= $this->config->getDeliveryLimit()) {
throw new DeliveryLimitReachedException(
$token->getDeliveryCount(),
$this->config->getExpirePeriod()
);
}
$this->deliveryService->send($token);
if ($token instanceof TokenRecordInterface) {
$token->increaseDeliveryCount();
Validation\Exception::saveOrThrow($token);
}
}
/**
* Pulling active token to process it (for example, sending sms)
*/
public function pull(string $tokenRecipient): ?TokenRecordInterface
{
/** @noinspection PhpIncompatibleReturnTypeInspection */
return $this->model->find()
->notExpired($this->config->getExpirePeriod())
->whereRecipient($tokenRecipient)
->one();
}
/**
* Will return token with data to process registration if token valid
* Or throw one of exceptions
* Should delete token from storage if token valid to prevent double validation for single token
*
* @return TokenInterface
* @throws DeliveryLimitReachedException
* @throws InvalidRecipientException
* @throws InvalidTokenException
* @throws Validation\Failure
*/
public function verify(string $tokenRecipient, string $token): TokenInterface
{
$record = $this->pull($tokenRecipient);
if (!$record instanceof TokenRecordInterface) {
throw new InvalidRecipientException($tokenRecipient);
}
$record->increaseVerifyCount();
Validation\Exception::saveOrThrow($record);
if ($this->config->getVerifyLimit() < $record->getVerifyCount()) {
throw new DeliveryLimitReachedException(
$record->getVerifyCount(),
$this->config->getExpirePeriod()
);
}
if ($record->getToken() !== $token) {
throw new InvalidTokenException($token);
}
$record->delete();
return $record;
}
}