Ovh::getHeader()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 11
rs 9.9666
cc 1
nc 1
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @author Jordan Bieder <[email protected]>
7
 *
8
 * Nextcloud - Two-factor Gateway for Ovh
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OCA\TwoFactorGateway\Service\Gateway\SMS\Provider;
25
26
use Exception;
27
use OCA\TwoFactorGateway\Exception\InvalidSmsProviderException;
28
use OCA\TwoFactorGateway\Exception\SmsTransmissionException;
29
use OCP\Http\Client\IClient;
30
use OCP\Http\Client\IClientService;
31
32
class Ovh implements IProvider {
33
	public const PROVIDER_ID = 'ovh';
34
35
	/** @var IClient */
36
	private $client;
37
38
	/** @var OvhConfig */
39
	private $config;
40
41
	/**
42
	 * Url to communicate with Ovh API
43
	 *
44
	 * @var array
45
	 */
46
	private $endpoints = [
47
		'ovh-eu' => 'https://api.ovh.com/1.0',
48
		'ovh-us' => 'https://api.us.ovhcloud.com/1.0',
49
		'ovh-ca' => 'https://ca.api.ovh.com/1.0',
50
		'kimsufi-eu' => 'https://eu.api.kimsufi.com/1.0',
51
		'kimsufi-ca' => 'https://ca.api.kimsufi.com/1.0',
52
		'soyoustart-eu' => 'https://eu.api.soyoustart.com/1.0',
53
		'soyoustart-ca' => 'https://ca.api.soyoustart.com/1.0',
54
		'runabove-ca' => 'https://api.runabove.com/1.0',
55
	];
56
57
	/**
58
	 * Array of the 4 needed parameters to connect to the API
59
	 * @var array
60
	 */
61
	private $attrs = [
62
		'AK' => null,
63
		'AS' => null,
64
		'CK' => null,
65
		'endpoint' => null,
66
		'timedelta' => null
67
	];
68
69
70
	public function __construct(IClientService $clientService,
71
		OvhConfig $config) {
72
		$this->client = $clientService->newClient();
73
		$this->config = $config;
74
	}
75
76
	/**
77
	 * @param string $identifier
78
	 * @param string $message
79
	 *
80
	 * @throws SmsTransmissionException
81
	 */
82
	public function send(string $identifier, string $message) {
83
		$config = $this->getConfig();
84
		$endpoint = $config->getEndpoint();
85
		$sender = $config->getSender();
86
		$smsAccount = $config->getAccount();
87
88
		$this->attrs['AK'] = $config->getApplicationKey();
89
		$this->attrs['AS'] = $config->getApplicationSecret();
90
		$this->attrs['CK'] = $config->getConsumerKey();
91
		if (!isset($this->endpoints[$endpoint])) {
92
			throw new InvalidSmsProviderException("Endpoint $endpoint not found");
93
		}
94
		$this->attrs['endpoint'] = $this->endpoints[$endpoint];
95
96
		$this->getTimeDelta();
97
98
		$header = $this->getHeader('GET', $this->attrs['endpoint'].'/sms');
99
		$response = $this->client->get($this->attrs['endpoint'].'/sms', [
100
			'headers' => $header,
101
		]);
102
		$smsServices = json_decode($response->getBody(), true);
0 ignored issues
show
Bug introduced by
It seems like $response->getBody() can also be of type resource; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

102
		$smsServices = json_decode(/** @scrutinizer ignore-type */ $response->getBody(), true);
Loading history...
103
104
		$smsAccountFound = false;
105
		foreach ($smsServices as $smsService) {
106
			if ($smsService === $smsAccount) {
107
				$smsAccountFound = true;
108
				break;
109
			}
110
		}
111
		if ($smsAccountFound === false) {
112
			throw new InvalidSmsProviderException("SMS account $smsAccount not found");
113
		}
114
		$content = [
115
			"charset" => "UTF-8",
116
			"message" => $message,
117
			"noStopClause" => true,
118
			"priority" => "high",
119
			"receivers" => [ $identifier ],
120
			"senderForResponse" => false,
121
			"sender" => $sender,
122
			"validityPeriod" => 3600
123
		];
124
		$body = json_encode($content);
125
126
		$header = $this->getHeader('POST', $this->attrs['endpoint']."/sms/$smsAccount/jobs", $body);
127
		$response = $this->client->post($this->attrs['endpoint']."/sms/$smsAccount/jobs", [
128
			'headers' => $header,
129
			'json' => $content,
130
		]);
131
		$resultPostJob = json_decode($response->getBody(), true);
132
133
		if (count($resultPostJob["validReceivers"]) === 0) {
134
			throw new SmsTransmissionException("Bad receiver $identifier");
135
		}
136
	}
137
138
	/**
139
	 * @return OvhConfig
140
	 */
141
	public function getConfig(): IProviderConfig {
142
		return $this->config;
143
	}
144
145
	/**
146
	 * Compute time delta between this server and OVH endpoint
147
	 * @throws InvalidSmsProviderException
148
	 */
149
	private function getTimeDelta() {
150
		if (!isset($this->attrs['timedelta'])) {
151
			if (!isset($this->attrs['endpoint'])) {
152
				throw new InvalidSmsProviderException('Need to set the endpoint');
153
			}
154
			try {
155
				$response = $this->client->get($this->attrs['endpoint'].'/auth/time');
156
				$serverTimestamp = (int)$response->getBody();
157
				$this->attrs['timedelta'] = $serverTimestamp - time();
158
			} catch (Exception $ex) {
159
				throw new InvalidSmsProviderException('Unable to calculate time delta:'.$ex->getMessage());
160
			}
161
		}
162
	}
163
164
	/**
165
	 * Make header for Ovh
166
	 * @param string $method The methode use for the query : GET, POST, PUT, DELETE
167
	 * @param string $query The fulle URI for the query: https://eu.api.ovh.com/1.0/......
168
	 * @param string $body JSON encoded body content for the POST request
169
	 * @return array $header Contains the data for the request need by OVH
170
	 */
171
	private function getHeader($method, $query, $body = '') {
172
		$timestamp = time() + $this->attrs['timedelta'];
173
		$prehash = $this->attrs['AS'].'+'.$this->attrs['CK'].'+'.$method.'+'.$query.'+'.$body.'+'.$timestamp;
174
		$header = [
175
			'Content-Type' => 'application/json; charset=utf-8',
176
			'X-Ovh-Application' => $this->attrs['AK'],
177
			'X-Ovh-Timestamp' => $timestamp,
178
			'X-Ovh-Signature' => '$1$'.sha1($prehash),
179
			'X-Ovh-Consumer' => $this->attrs['CK'],
180
		];
181
		return $header;
182
	}
183
}
184