Passed
Push — master ( b215e3...34e661 )
by Tim
11:08
created

OTPClient::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * Utilities for sending OTP text messages
5
 *
6
 * @package tvdijen/simplesamlphp-module-cmdotcom
7
 */
8
9
declare(strict_types=1);
10
11
namespace SimpleSAML\Module\cmdotcom\Utils;
12
13
use GuzzleHttp\Client as GuzzleClient;
14
use Psr\Http\Message\ResponseInterface;
15
use SimpleSAML\{Configuration, Session};
16
use SimpleSAML\Assert\Assert;
17
18
class OTPClient
19
{
20
    /** @var string */
21
    public const API_BASE = 'https://api.cmtelecom.com';
22
23
    /** @var string */
24
    public const HEADER = 'X-CM-ProductToken';
25
26
    /** @var \SimpleSAML\Configuration */
27
    protected Configuration $config;
28
29
30
    /**
31
     * @param \SimpleSAML\Configuration $config The configuration to use.
32
     */
33
    public function __construct(Configuration $config)
34
    {
35
        $this->config = $config;
36
    }
37
38
39
    /**
40
     * Send OTP code
41
     *
42
     * @param array $state
43
     * @return \Psr\Http\Message\ResponseInterface
44
     */
45
    public function sendCode(array $state): ResponseInterface
46
    {
47
        Assert::keyExists($state, 'cmdotcom:productToken', 'Missing required REST API key for the cm.com service.');
48
        Assert::keyExists($state, 'cmdotcom:recipient');
49
        Assert::keyExists($state, 'cmdotcom:originator');
50
        Assert::keyExists($state, 'cmdotcom:codeLength');
51
        Assert::keyExists($state, 'cmdotcom:validFor');
52
        Assert::keyExists($state, 'cmdotcom:message');
53
        Assert::keyExists($state, 'cmdotcom:allowPush');
54
55
        // Validate product token
56
        $productToken = $state['cmdotcom:productToken'];
57
        Assert::notNull(
58
            $productToken,
59
            'Missing required REST API key for the cm.com service.',
60
        );
61
        Assert::uuid($productToken);
62
63
        // Validate appKey
64
        $allowPush = $state['cmdotcom:allowPush'];
65
        if ($allowPush === true) {
66
            $appKey = $state['cmdotcom:appKey'];
67
            Assert::notNull(
68
                $appKey,
69
                'Missing required appKey for use with push notification.',
70
            );
71
            Assert::uuid($appKey);
72
        }
73
74
        // Validate originator
75
        $originator = $state['cmdotcom:originator'];
76
        if (is_numeric($originator)) {
77
            Assert::maxLength(
78
                $originator,
79
                16,
80
                'A numeric originator must represent a phonenumber and can contain a maximum of 16 digits.',
81
            );
82
        } else {
83
            // TODO: figure out what characters are allowed and write a regex.
84
            // So far 'A-Z', 'a-z', '0-9', ' ' and '-' are known to be accepted
85
            //Assert::alnum(str_replace(' ', '', $originator));
86
            Assert::lengthBetween(
87
                $originator,
88
                3,
89
                11,
90
                'An alphanumeric originator can contain a minimum of 2 and a maximum of 11 characters.',
91
            );
92
        }
93
94
        // Validate OTP length
95
        $codeLength = $state['cmdotcom:codeLength'];
96
        Assert::range($codeLength, 4, 10);
97
98
        // Validate recipient
99
        $recipient = $state['cmdotcom:recipient'];
100
        Assert::numeric($recipient);
101
        Assert::maxLength(
102
            $recipient,
103
            16,
104
            'A recipient must represent a phonenumber and can contain a maximum of 16 digits.',
105
        );
106
107
        // Validate validFor
108
        $validFor = $state['cmdotcom:validFor'];
109
        Assert::positiveInteger(
110
            $validFor,
111
            'validFor must be a positive integer.',
112
        );
113
114
        // Validate message
115
        $message = $state['cmdotcom:message'];
116
        Assert::contains($message, '{code}');
117
118
        $options = [
119
            'base_uri' => self::API_BASE,
120
            //'debug' => true,
121
            'headers' => [
122
                'Content-Type' => 'application/json',
123
                self::HEADER => $productToken,
124
            ],
125
            'http_errors' => false,
126
            'timeout' => 3.0,
127
        ];
128
129
        $client = new GuzzleClient($options);
130
        $json = [
131
            'recipient' => $recipient,
132
            'sender' => $originator,
133
            'length' => $codeLength,
134
            'expiry' => $validFor,
135
            'message' => $message,
136
        ];
137
138
        if ($allowPush === true) {
139
            $json += ['allowPush' => $allowPush, 'appKey' => $appKey];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $appKey does not seem to be defined for all execution paths leading up to this point.
Loading history...
140
        }
141
142
        return $client->request(
143
            'POST',
144
            '/v1.0/otp/generate',
145
            [
146
                'json' => $json,
147
            ],
148
        );
149
    }
150
151
152
    /**
153
     * Verify OTP code
154
     *
155
     * @param array $state
156
     * @param string $code
157
     * @return \Psr\Http\Message\ResponseInterface
158
     */
159
    public function verifyCode(array $state, string $code): ResponseInterface
160
    {
161
        Assert::keyExists($state, 'cmdotcom:reference');
162
        Assert::keyExists($state, 'cmdotcom:productToken');
163
164
        // Validate reference
165
        $reference = $state['cmdotcom:reference'];
166
        Assert::uuid($reference);
167
168
        // Validate product token
169
        $productToken = $state['cmdotcom:productToken'];
170
        Assert::notNull(
171
            $productToken,
172
            'Missing required REST API key for the cm.com service.',
173
        );
174
        Assert::uuid($productToken);
175
176
        $options = [
177
            'base_uri' => self::API_BASE,
178
            //'debug' => true,
179
            'headers' => [
180
                'Content-Type' => 'application/json',
181
                self::HEADER => $productToken,
182
            ],
183
            'http_errors' => false,
184
            'timeout' => 3.0,
185
        ];
186
187
        $proxy = $this->config->getString('proxy', null);
188
        if ($proxy !== null) {
189
            $options += ['proxy' => ['http' => $proxy, 'https' => $proxy]];
190
        }
191
192
        $client = new GuzzleClient($options);
193
        $json = [
194
            'id' => $reference,
195
            'code' => $code,
196
        ];
197
198
        return $response = $client->request(
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
199
            'POST',
200
            '/v1.0/otp/verify',
201
            [
202
                'json' => $json,
203
            ],
204
        );
205
    }
206
}
207