Skip to content

Commit

Permalink
Merge pull request #316 from piti-diablotin/master
Browse files Browse the repository at this point in the history
Add OVH SMS gateway
  • Loading branch information
ChristophWurst authored Feb 3, 2020
2 parents 7be6c65 + c1e40fb commit dd953d2
Show file tree
Hide file tree
Showing 6 changed files with 363 additions and 6 deletions.
10 changes: 5 additions & 5 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions doc/Admin Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,32 @@ Interactive admin configuration:
occ twofactorauth:gateway:configure sms
```

### OVH
URL: https://www.ovhtelecom.fr/sms/
Stability: Experimental

Use the SMS gateway provided by OVH for sending SMS.

1. First create an application key, an application secret and a consumer key with the [createToken](https://eu.api.ovh.com/createToken/index.cgi?GET=/sms&GET=/sms/*/jobs&POST=/sms/*/jobs) page.

2. Go to you OVH account manager and get an SMS plan. You should see on the sidebar menu the SMS submenu with the account name: *sms-#######*

3. Create a "sender". On the main page of the SMS account, you should see a *Create a sender* link.

4. Interactive admin configuration:
```bash
occ twofactorauth:gateway:configure sms
```

* Choose the `ovh` SMS provider.
* Choose the endpoint connexion.
* Enter successively the application key, the application secret, the consumer key, the account, and the sender.

5. Try to send a test with
```bash
occ twofactorauth:gateway:test <uid> sms <receiver>
```

[User Documentation]: https://nextcloud-twofactor-gateway.readthedocs.io/en/latest/User%20Documentation/

### Spryng
Expand Down
37 changes: 36 additions & 1 deletion lib/Command/Configure.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\EcallSMSConfig;
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\PlaySMSConfig;
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\Sms77IoConfig;
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\OvhConfig;
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\WebSmsConfig;
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\PuzzelSMSConfig;
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\HuaweiE3531Config;
Expand Down Expand Up @@ -105,7 +106,8 @@ private function configureSignal(InputInterface $input, OutputInterface $output)

private function configureSms(InputInterface $input, OutputInterface $output) {
$helper = $this->getHelper('question');
$providerQuestion = new Question('Please choose a SMS provider (websms, playsms, clockworksms, puzzelsms, ecallsms, voipms, huawei_e3531, spryng, sms77io): ', 'websms');

$providerQuestion = new Question('Please choose a SMS provider (websms, playsms, clockworksms, puzzelsms, ecallsms, voipms, huawei_e3531, spryng, sms77io, ovh', 'websms');
$provider = $helper->ask($input, $output, $providerQuestion);

/** @var SMSConfig $config */
Expand Down Expand Up @@ -247,6 +249,39 @@ private function configureSms(InputInterface $input, OutputInterface $output) {
$providerConfig->setApiKey($apiKey);
break;

case 'ovh':
$config->setProvider($provider);

/** @var OvhConfig $providerConfig */
$providerConfig = $config->getProvider()->getConfig();

$endpointQ = new Question('Please enter the endpoint to use (ovh-eu, ovh-us, ovh-ca, soyoustart-eu, soyoustart-ca, kimsufi-eu, kimsufi-ca, runabove-ca): ');
$endpoint = $helper->ask($input, $output, $endpointQ);

$appKeyQ = new Question('Please enter your application key: ');
$appKey = $helper->ask($input, $output, $appKeyQ);

$appSecretQ = new Question('Please enter your application secret: ');
$appSecret = $helper->ask($input, $output, $appSecretQ);

$consumerKeyQ = new Question('Please enter your consumer key: ');
$consumerKey = $helper->ask($input, $output, $consumerKeyQ);

$accountQ = new Question('Please enter your account (sms-*****): ');
$account = $helper->ask($input, $output, $accountQ);

$senderQ = new Question('Please enter your sender: ');
$sender = $helper->ask($input, $output, $senderQ);

$providerConfig->setApplicationKey($appKey);
$providerConfig->setApplicationSecret($appSecret);
$providerConfig->setConsumerKey($consumerKey);
$providerConfig->setEndpoint($endpoint);
$providerConfig->setAccount($account);
$providerConfig->setSender($sender);
break;


default:
$output->writeln("Invalid provider $provider");
break;
Expand Down
185 changes: 185 additions & 0 deletions lib/Service/Gateway/SMS/Provider/Ovh.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
<?php

declare(strict_types=1);

/**
* @author Jordan Bieder <[email protected]>
*
* Nextcloud - Two-factor Gateway for Ovh
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OCA\TwoFactorGateway\Service\Gateway\SMS\Provider;

use Exception;
use OCA\TwoFactorGateway\Exception\SmsTransmissionException;
use OCA\TwoFactorGateway\Exception\InvalidSmsProviderException;
use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService;

class Ovh implements IProvider {

const PROVIDER_ID = 'ovh';

/** @var IClient */
private $client;

/** @var OvhConfig */
private $config;

/**
* Url to communicate with Ovh API
*
* @var array
*/
private $endpoints = [
'ovh-eu' => 'https://api.ovh.com/1.0',
'ovh-us' => 'https://api.us.ovhcloud.com/1.0',
'ovh-ca' => 'https://ca.api.ovh.com/1.0',
'kimsufi-eu' => 'https://eu.api.kimsufi.com/1.0',
'kimsufi-ca' => 'https://ca.api.kimsufi.com/1.0',
'soyoustart-eu' => 'https://eu.api.soyoustart.com/1.0',
'soyoustart-ca' => 'https://ca.api.soyoustart.com/1.0',
'runabove-ca' => 'https://api.runabove.com/1.0',
];

/**
* Array of the 4 needed parameters to connect to the API
* @var array
*/
private $attrs = [
'AK' => null,
'AS' => null,
'CK' => null,
'endpoint' => null,
'timedelta' => null
];


public function __construct(IClientService $clientService,
OvhConfig $config) {
$this->client = $clientService->newClient();
$this->config = $config;
}

/**
* @param string $identifier
* @param string $message
*
* @throws SmsTransmissionException
*/
public function send(string $identifier, string $message) {
$config = $this->getConfig();
$endpoint = $config->getEndpoint();
$sender = $config->getSender();
$smsAccount = $config->getAccount();

$this->attrs['AK'] = $config->getApplicationKey();
$this->attrs['AS'] = $config->getApplicationSecret();
$this->attrs['CK'] = $config->getConsumerKey();
if (!isset($this->endpoints[$endpoint]))
throw new InvalidSmsProviderException("Endpoint $endpoint not found");
$this->attrs['endpoint'] = $this->endpoints[$endpoint];

$this->getTimeDelta();

$header = $this->getHeader('GET',$this->attrs['endpoint'].'/sms');
$response = $this->client->get($this->attrs['endpoint'].'/sms',[
'headers' => $header,
]);
$smsServices = json_decode($response->getBody(),true);

$smsAccountFound = false;
foreach ($smsServices as $smsService) {
if ($smsService === $smsAccount) {
$smsAccountFound = true;
break;
}
}
if ($smsAccountFound === false) {
throw new InvalidSmsProviderException("SMS account $smsAccount not found");
}
$content = [
"charset"=> "UTF-8",
"message"=> $message,
"noStopClause"=> true,
"priority"=> "high",
"receivers"=> [ $identifier ],
"senderForResponse"=> false,
"sender"=> $sender,
"validityPeriod"=> 3600
];
$body = json_encode($content);

$header = $this->getHeader('POST',$this->attrs['endpoint']."/sms/$smsAccount/jobs",$body);
$response = $this->client->post($this->attrs['endpoint']."/sms/$smsAccount/jobs",[
'headers' => $header,
'json' => $content,
]);
$resultPostJob = json_decode($response->getBody(),true);

if (count($resultPostJob["validReceivers"]) === 0) {
throw new SmsTransmissionException("Bad receiver $identifier");
}
}

/**
* @return OvhConfig
*/
public function getConfig(): IProviderConfig {
return $this->config;
}

/**
* Compute time delta between this server and OVH endpoint
* @throws InvalidSmsProviderException
*/
private function getTimeDelta() {
if (!isset($this->attrs['timedelta'])) {
if (!isset($this->attrs['endpoint']))
throw new InvalidSmsProviderException('Need to set the endpoint');
try {
$response = $this->client->get($this->attrs['endpoint'].'/auth/time');
$serverTimestamp = (int)$response->getBody();
$this->attrs['timedelta'] = $serverTimestamp - time();
}
catch (Exception $ex) {
throw new InvalidSmsProviderException('Unable to calculate time delta:'.$ex->getMessage());
}
}
}

/**
* Make header for Ovh
* @param string $method The methode use for the query : GET, POST, PUT, DELETE
* @param string $query The fulle URI for the query: https://eu.api.ovh.com/1.0/......
* @param string $body JSON encoded body content for the POST request
* @return array $header Contains the data for the request need by OVH
*/
private function getHeader($method,$query,$body='') {
$timestamp = time() + $this->attrs['timedelta'];
$prehash = $this->attrs['AS'].'+'.$this->attrs['CK'].'+'.$method.'+'.$query.'+'.$body.'+'.$timestamp;
$header = [
'Content-Type' => 'application/json; charset=utf-8',
'X-Ovh-Application' => $this->attrs['AK'],
'X-Ovh-Timestamp' => $timestamp,
'X-Ovh-Signature' => '$1$'.sha1($prehash),
'X-Ovh-Consumer' => $this->attrs['CK'],
];
return $header;
}


}
Loading

0 comments on commit dd953d2

Please sign in to comment.