Passed
Pull Request — master (#686)
by Vitor
04:21
created

Ovh::createSettings()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 28
ccs 0
cts 28
cp 0
rs 9.568
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * SPDX-FileCopyrightText: 2024 Jordan Bieder <[email protected]>
7
 * SPDX-License-Identifier: AGPL-3.0-or-later
8
 */
9
10
namespace OCA\TwoFactorGateway\Provider\Channel\SMS\Provider\Drivers;
11
12
use Exception;
13
use OCA\TwoFactorGateway\Exception\InvalidProviderException;
14
use OCA\TwoFactorGateway\Exception\MessageTransmissionException;
15
use OCA\TwoFactorGateway\Provider\Channel\SMS\Provider\AProvider;
16
use OCA\TwoFactorGateway\Provider\Settings;
17
use OCP\Http\Client\IClient;
18
use OCP\Http\Client\IClientService;
19
20
/**
21
 * @method string getApplicationKey()
22
 * @method static setApplicationKey(string $applicationKey)
23
 * @method string getApplicationSecret()
24
 * @method static setApplicationSecret(string $applicationSecret)
25
 * @method string getConsumerKey()
26
 * @method static setConsumerKey(string $consumerKey)
27
 * @method string getEndpoint()
28
 * @method static setEndpoint(string $endpoint)
29
 * @method string getAccount()
30
 * @method static setAccount(string $account)
31
 * @method string getSender()
32
 * @method static setSender(string $sender)
33
 */
34
class Ovh extends AProvider {
35
	private IClient $client;
36
37
	/**
38
	 * Url to communicate with Ovh API
39
	 */
40
	private array $endpoints = [
41
		'ovh-eu' => 'https://api.ovh.com/1.0',
42
		'ovh-us' => 'https://api.us.ovhcloud.com/1.0',
43
		'ovh-ca' => 'https://ca.api.ovh.com/1.0',
44
		'kimsufi-eu' => 'https://eu.api.kimsufi.com/1.0',
45
		'kimsufi-ca' => 'https://ca.api.kimsufi.com/1.0',
46
		'soyoustart-eu' => 'https://eu.api.soyoustart.com/1.0',
47
		'soyoustart-ca' => 'https://ca.api.soyoustart.com/1.0',
48
		'runabove-ca' => 'https://api.runabove.com/1.0',
49
	];
50
51
	/**
52
	 * Array of the 4 needed parameters to connect to the API
53
	 */
54
	private array $attrs = [
55
		'AK' => null,
56
		'AS' => null,
57
		'CK' => null,
58
		'endpoint' => null,
59
		'timedelta' => null
60
	];
61
62
63
	public function __construct(
64
		IClientService $clientService,
65
	) {
66
		$this->client = $clientService->newClient();
67
	}
68
69
	public function createSettings(): Settings {
70
		return new Settings(
71
			id: 'ovh',
72
			name: 'OVH',
73
			fields: [
74
				new \OCA\TwoFactorGateway\Provider\FieldDefinition(
75
					field: 'endpoint',
76
					prompt: 'Please enter the endpoint (ovh-eu, ovh-us, ovh-ca, soyoustart-eu, soyoustart-ca, kimsufi-eu, kimsufi-ca, runabove-ca):',
77
				),
78
				new \OCA\TwoFactorGateway\Provider\FieldDefinition(
79
					field: 'application_key',
80
					prompt: 'Please enter your application key:',
81
				),
82
				new \OCA\TwoFactorGateway\Provider\FieldDefinition(
83
					field: 'application_secret',
84
					prompt: 'Please enter your application secret:',
85
				),
86
				new \OCA\TwoFactorGateway\Provider\FieldDefinition(
87
					field: 'consumer_key',
88
					prompt: 'Please enter your consumer key:',
89
				),
90
				new \OCA\TwoFactorGateway\Provider\FieldDefinition(
91
					field: 'account',
92
					prompt: 'Please enter your account (sms-*****):',
93
				),
94
				new \OCA\TwoFactorGateway\Provider\FieldDefinition(
95
					field: 'sender',
96
					prompt: 'Please enter your sender:',
97
				),
98
			]
99
		);
100
	}
101
102
	#[\Override]
103
	public function send(string $identifier, string $message) {
104
		$endpoint = $this->getEndpoint();
105
		$sender = $this->getSender();
106
		$smsAccount = $this->getAccount();
107
108
		$this->attrs['AK'] = $this->getApplicationKey();
109
		$this->attrs['AS'] = $this->getApplicationSecret();
110
		$this->attrs['CK'] = $this->getConsumerKey();
111
		if (!isset($this->endpoints[$endpoint])) {
112
			throw new InvalidProviderException("Endpoint $endpoint not found");
113
		}
114
		$this->attrs['endpoint'] = $this->endpoints[$endpoint];
115
116
		$this->getTimeDelta();
117
118
		$header = $this->getHeader('GET', $this->attrs['endpoint'] . '/sms');
119
		$response = $this->client->get($this->attrs['endpoint'] . '/sms', [
120
			'headers' => $header,
121
		]);
122
		$body = (string)$response->getBody();
123
		$smsServices = json_decode($body, true);
124
125
		$smsAccountFound = false;
126
		foreach ($smsServices as $smsService) {
127
			if ($smsService === $smsAccount) {
128
				$smsAccountFound = true;
129
				break;
130
			}
131
		}
132
		if ($smsAccountFound === false) {
133
			throw new InvalidProviderException("SMS account $smsAccount not found");
134
		}
135
		$content = [
136
			'charset' => 'UTF-8',
137
			'message' => $message,
138
			'noStopClause' => true,
139
			'priority' => 'high',
140
			'receivers' => [ $identifier ],
141
			'senderForResponse' => false,
142
			'sender' => $sender,
143
			'validityPeriod' => 3600
144
		];
145
		$body = json_encode($content);
146
147
		$header = $this->getHeader('POST', $this->attrs['endpoint'] . "/sms/$smsAccount/jobs", $body);
148
		$response = $this->client->post($this->attrs['endpoint'] . "/sms/$smsAccount/jobs", [
149
			'headers' => $header,
150
			'json' => $content,
151
		]);
152
		$body = (string)$response->getBody();
153
		$resultPostJob = json_decode($body, true);
154
155
		if (count($resultPostJob['validReceivers']) === 0) {
156
			throw new MessageTransmissionException("Bad receiver $identifier");
157
		}
158
	}
159
160
	/**
161
	 * Compute time delta between this server and OVH endpoint
162
	 *
163
	 * @throws InvalidProviderException
164
	 */
165
	private function getTimeDelta(): void {
166
		if (!isset($this->attrs['timedelta'])) {
167
			if (!isset($this->attrs['endpoint'])) {
168
				throw new InvalidProviderException('Need to set the endpoint');
169
			}
170
			try {
171
				$response = $this->client->get($this->attrs['endpoint'] . '/auth/time');
172
				$serverTimestamp = (int)$response->getBody();
173
				$this->attrs['timedelta'] = $serverTimestamp - time();
174
			} catch (Exception $ex) {
175
				throw new InvalidProviderException('Unable to calculate time delta:' . $ex->getMessage());
176
			}
177
		}
178
	}
179
180
	/**
181
	 * Make header for Ovh
182
	 * @param string $method The methode use for the query : GET, POST, PUT, DELETE
183
	 * @param string $query The fulle URI for the query: https://eu.api.ovh.com/1.0/......
184
	 * @param string $body JSON encoded body content for the POST request
185
	 * @return array $header Contains the data for the request need by OVH
186
	 */
187
	private function getHeader($method, $query, $body = '') {
188
		$timestamp = time() + $this->attrs['timedelta'];
189
		$prehash = $this->attrs['AS'] . '+' . $this->attrs['CK'] . '+' . $method . '+' . $query . '+' . $body . '+' . $timestamp;
190
		$header = [
191
			'Content-Type' => 'application/json; charset=utf-8',
192
			'X-Ovh-Application' => $this->attrs['AK'],
193
			'X-Ovh-Timestamp' => $timestamp,
194
			'X-Ovh-Signature' => '$1$' . sha1($prehash),
195
			'X-Ovh-Consumer' => $this->attrs['CK'],
196
		];
197
		return $header;
198
	}
199
}
200