Test Failed
Push — master ( 77d308...e79ab4 )
by Artem
01:53
created

CodeManager   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 126
Duplicated Lines 0 %

Test Coverage

Coverage 97.92%

Importance

Changes 0
Metric Value
eloc 44
dl 0
loc 126
ccs 47
cts 48
cp 0.9792
rs 10
c 0
b 0
f 0
wmc 14

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A generate() 0 18 2
A verify() 0 25 4
A generateOTP() 0 15 3
A checkCreationLimit() 0 19 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Prozorov\DataVerification;
6
7
use Prozorov\DataVerification\Exceptions\{LimitException, VerificationException};
8
use Prozorov\DataVerification\Models\Code;
9
use Prozorov\DataVerification\Types\Address;
10
11
class CodeManager
12
{
13
    /**
14
     * @var Configuration $config
15
     */
16
    protected $config;
17
18 8
    public function __construct(Configuration $config)
19
    {
20 8
        $this->config = $config;
21 8
    }
22
23
    /**
24
     * generate.
25
     *
26
     * @access	public
27
     * @param	Address	$address
28
     * @param	array  	$data    	Default: null
29
     * @param	string 	$fakeCode	Default: null
30
     * @return	Code
31
     */
32 4
    public function generate(Address $address, array $data = null, string $fakeCode = null): Code
33
    {
34 4
        $this->checkCreationLimit($address);
35
36 2
        $verificationCode = md5((string) strtotime('now'));
37
38 2
        $code = new Code();
39 2
        $code->setVerificationCode($verificationCode)
40 2
            ->setOneTimePass($this->generateOTP($fakeCode))
41 2
            ->setAddress($address);
42
43 2
        if (!empty($data)) {
44 1
            $code->setVerificationData($data);
45
        }
46
47 2
        $this->config->getCodeRepo()->save($code);
48
49 2
        return $code;
50
    }
51
52
    /**
53
     * verify.
54
     *
55
     * @access	public
56
     * @param	string	$verificationCode	
57
     * @param	string	$pass            	
58
     * @return	Code
59
     */
60 4
    public function verify(string $verificationCode, string $pass): Code
61
    {
62 4
        $seconds = $this->config->getPasswordValidationPeriod();
63
64 4
        $createdAfter = (new \Datetime())->sub(new \DateInterval('PT'.$seconds.'S'));
65 4
        $code = $this->config->getCodeRepo()->getOneUnvalidatedByCode($verificationCode, $createdAfter);
66
67 4
        if (!$code) {
68 1
            throw new \OutOfBoundsException('Данные не найдены');
69
        }
70
71 3
        if ($code->getOneTimePass() !== $pass) {
72 1
            $code->incrementAttempts();
73 1
            throw new VerificationException('Некорректно указан код');
74
        }
75
76 2
        if ($this->config->getAttempts() <= $code->getAttempts()) {
77 1
            throw new LimitException('Превышен лимит');
78
        }
79
80 1
        $code->setValidated();
81
        
82 1
        $this->config->getCodeRepo()->save($code);
83
84 1
        return $code;
85
    }
86
87
    /**
88
     * generateOTP.
89
     *
90
     * @access	protected
91
     * @param	string	$fakeCode	Default: null
92
     * @return	string
93
     */
94 2
    protected function generateOTP(string $fakeCode = null): string
95
    {
96 2
        if (! empty($fakeCode)) {
97
            return $fakeCode;
98
        }
99
100 2
        $symbols = $this->config->getAllowedSymbols();
101 2
        $length = $this->config->getPassLength();
102 2
        $otp = '';
103
104 2
        for ($i = 1; $i <= $length; $i++) {
105 2
            $otp .= $symbols[rand(0, (count($symbols) - 1))];
106
        }
107
108 2
        return $otp;
109
    }
110
111
    /**
112
     * checkCreationLimit.
113
     *
114
     * @access	protected
115
     * @param	Address	$address	
116
     * @return	void
117
     */
118 4
    protected function checkCreationLimit(Address $address): void
119
    {
120 4
        $threshold = $this->config->getCreationCodeThreshold();
121
122 4
        $createdAfter = (new \Datetime())->sub(new \DateInterval('PT'.$threshold.'S'));
123
124 4
        if ($this->config->getCodeRepo()->getLastCodeForAddress($address, $createdAfter)) {
125 1
            throw new LimitException('Превышен лимит');
126
        }
127
128 3
        $createdAfter = (new \Datetime())->sub(new \DateInterval('PT3600S'));
129
130 3
        $attempts = $this->config->getCodeRepo()->getCodesCountForAddress($address, $createdAfter);
131
132 3
        if ($attempts > 0) {
133 2
            $limitPerHour = $this->config->getLimitPerHour();
134
135 2
            if ($limitPerHour < $attempts) {
136 1
                throw new LimitException('Превышен лимит обращений в час');
137
            }
138
        }
139 2
    }
140
}
141