Skip to content

Commit 1a6acd4

Browse files
committed
Add CustomSMS a generic provider to send SMS using any webservice gateway
1 parent 93ea4ac commit 1a6acd4

File tree

5 files changed

+336
-1
lines changed

5 files changed

+336
-1
lines changed

doc/Admin Documentation.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,23 @@
44

55
Here you can find the configuration instructions for the currently supported gateways.
66

7+
## CustomSMS
8+
URL: any endpoint
9+
Stability: Experimental
10+
11+
This provider allow to send sms to any sms api by setting:
12+
The Webservice `URL`.
13+
The Webservice `method` accept (GET or POST).
14+
The `mobile number` parameter .
15+
The `message` parameter .
16+
The Webservice required http headers for sending.
17+
And any others static parameters.
18+
19+
Interactive admin configuration:
20+
```bash
21+
occ twofactorauth:gateway:configure sms
22+
```
23+
724
### playSMS
825
URL: https://playsms.org/
926
Stability: Experimental

lib/Command/Configure.php

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
use OCA\TwoFactorGateway\Service\Gateway\SMS\GatewayConfig as SMSConfig;
3131
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\ClickSendConfig;
3232
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\ClockworkSMSConfig;
33+
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\CustomSMS;
34+
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\CustomSMSConfig;
3335
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\EcallSMSConfig;
3436
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\PlaySMSConfig;
3537
use OCA\TwoFactorGateway\Service\Gateway\SMS\Provider\Sms77IoConfig;
@@ -110,12 +112,101 @@ private function configureSignal(InputInterface $input, OutputInterface $output)
110112
private function configureSms(InputInterface $input, OutputInterface $output) {
111113
$helper = $this->getHelper('question');
112114

113-
$providerQuestion = new Question('Please choose a SMS provider (websms, playsms, clockworksms, puzzelsms, ecallsms, voipms, voipbuster, huawei_e3531, spryng, sms77io, ovh, clickatellcentral, clicksend): ', 'websms');
115+
$providerQuestion = new Question('Please choose a SMS provider (customsms, websms, playsms, clockworksms, puzzelsms, ecallsms, voipms, voipbuster, huawei_e3531, spryng, sms77io, ovh, clickatellcentral, clicksend): ', 'customsms');
114116
$provider = $helper->ask($input, $output, $providerQuestion);
115117

116118
/** @var SMSConfig $config */
117119
$config = $this->smsGateway->getConfig();
118120
switch ($provider) {
121+
case 'customsms':
122+
$config->setProvider($provider);
123+
/** @var CustomSMSConfig $providerConfig */
124+
$providerConfig = $config->getProvider()->getConfig();
125+
126+
ReTypeUrl:
127+
$urlQuestion = new Question('Please enter the web service URL: ');
128+
$url = $helper->ask($input, $output, $urlQuestion);
129+
130+
if(!filter_var($url, FILTER_VALIDATE_URL))
131+
{
132+
$output->writeln('Invalid URL '.$url);
133+
goto ReTypeUrl;
134+
}
135+
136+
ReTypeMethod:
137+
$methodQuestion = new Question('Please enter the web service method (GET or POST): ');
138+
$method = (string)$helper->ask($input, $output, $methodQuestion);
139+
140+
if(strtolower($method) != 'get' && strtolower($method) != 'post')
141+
{
142+
$output->writeln('Invalid method '.$method);
143+
goto ReTypeMethod;
144+
}
145+
146+
ReTypeIdentifier:
147+
$identifierQuestion = new Question('Please enter the identifier parameter for mobile number (Eg. mobile or number): ');
148+
$identifier = $helper->ask($input, $output, $identifierQuestion);
149+
150+
if(empty($identifier))
151+
{
152+
$output->writeln('Mobile number parameter cannot be empty');
153+
goto ReTypeIdentifier;
154+
}
155+
156+
ReTypeMessage:
157+
$messageQuestion = new Question('Please enter the message parameter (Eg. msg, sms, message): ');
158+
$message = $helper->ask($input, $output, $messageQuestion);
159+
160+
if(empty($message))
161+
{
162+
$output->writeln('Message parameter cannot be empty');
163+
goto ReTypeMessage;
164+
}
165+
166+
ReTypeHeaders:
167+
$headersQuestion = new Question('Please enter any http headers (Eg. x-api-key=123456789&x-api-token=ABCD12345 etc.): ');
168+
$headers = $helper->ask($input, $output, $headersQuestion);
169+
170+
if(!empty($headers))
171+
{
172+
parse_str($headers, $headers_array);
173+
if(!is_array($headers_array))
174+
{
175+
$output->writeln('headers is invalid');
176+
goto ReTypeHeaders;
177+
}
178+
}
179+
else
180+
{
181+
$headers='';
182+
}
183+
184+
ReTypeParameters:
185+
$parametersQuestion = new Question('Please enter any extra parameters (Eg. sender=nextcloud&username=nextcloud&password=1234 etc.): ');
186+
$parameters = $helper->ask($input, $output, $parametersQuestion);
187+
188+
if(!empty($parameters))
189+
{
190+
parse_str($parameters, $parameters_array);
191+
if(!is_array($parameters_array))
192+
{
193+
$output->writeln('Extra parameters is invalid');
194+
goto ReTypeParameters;
195+
}
196+
}
197+
else
198+
{
199+
$parameters='';
200+
}
201+
202+
$providerConfig->setUrl($url);
203+
$providerConfig->setMethod($method);
204+
$providerConfig->setIdentifier($identifier);
205+
$providerConfig->setMessage($message);
206+
$providerConfig->setHeaders($headers);
207+
$providerConfig->setParameters($parameters);
208+
209+
break;
119210
case 'websms':
120211
$config->setProvider($provider);
121212
/** @var WebSmsConfig $providerConfig */
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @author Daif Alazmi <[email protected]>
7+
*
8+
* Nextcloud - Two-factor Gateway
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\SmsTransmissionException;
28+
use OCP\Http\Client\IClient;
29+
use OCP\Http\Client\IClientService;
30+
31+
class CustomSMS implements IProvider {
32+
public const PROVIDER_ID = 'customsms';
33+
34+
/** @var IClient */
35+
private $client;
36+
37+
/** @var CustomSMSConfig */
38+
private $config;
39+
40+
public function __construct(IClientService $clientService,
41+
CustomSMSConfig $config) {
42+
$this->client = $clientService->newClient();
43+
$this->config = $config;
44+
}
45+
46+
/**
47+
* @param string $identifier
48+
* @param string $message
49+
*
50+
* @throws SmsTransmissionException
51+
*/
52+
public function send(string $identifier, string $message) {
53+
$config = $this->getConfig();
54+
55+
try {
56+
57+
if(strtolower($config->getMethod()) == 'get')
58+
{
59+
$options = [
60+
'query'=> [
61+
$config->getIdentifier()=>$identifier,
62+
$config->getMessage()=>$message,
63+
]
64+
];
65+
parse_str($config->getHeaders(), $headers);
66+
if(!empty($headers))
67+
{
68+
$options['headers'] = $headers;
69+
}
70+
parse_str($config->getParameters(), $parameters);
71+
if(!empty($parameters))
72+
{
73+
$options['query'] = $options['query'] + $parameters;
74+
}
75+
$response = $this->client->get($config->getUrl(),$options);
76+
}
77+
78+
if(strtolower($config->getMethod()) == 'post')
79+
{
80+
$options = [
81+
'body'=> [
82+
$config->getIdentifier()=>$identifier,
83+
$config->getMessage()=>$message,
84+
]
85+
];
86+
parse_str($config->getHeaders(), $headers);
87+
if(!empty($headers))
88+
{
89+
$options['headers'] = $headers;
90+
}
91+
parse_str($config->getParameters(), $parameters);
92+
if(!empty($parameters))
93+
{
94+
$options['body'] = $options['body'] + $parameters;
95+
}
96+
$this->client->post($config->getUrl(),$options);
97+
}
98+
99+
} catch (Exception $ex) {
100+
throw new SmsTransmissionException();
101+
}
102+
}
103+
104+
/**
105+
* @return CustomSMSConfig
106+
*/
107+
public function getConfig(): IProviderConfig {
108+
return $this->config;
109+
}
110+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @author Daif Alazmi <[email protected]>
7+
*
8+
* Nextcloud - Two-factor Gateway
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 function array_intersect;
27+
use OCA\TwoFactorGateway\AppInfo\Application;
28+
use OCA\TwoFactorGateway\Exception\ConfigurationException;
29+
use OCP\IConfig;
30+
31+
class CustomSMSConfig implements IProviderConfig {
32+
33+
const expected = [
34+
'customsms_url',
35+
'customsms_method',
36+
'customsms_identifier',
37+
'customsms_message',
38+
'customsms_headers',
39+
'customsms_parameters',
40+
];
41+
42+
/** @var IConfig */
43+
private $config;
44+
45+
public function __construct(IConfig $config) {
46+
$this->config = $config;
47+
}
48+
49+
private function getOrFail(string $key): string {
50+
$val = $this->config->getAppValue(Application::APP_NAME, $key, null);
51+
if (is_null($val)) {
52+
throw new ConfigurationException();
53+
}
54+
return $val;
55+
}
56+
57+
public function getUrl(): string {
58+
return $this->getOrFail('customsms_url');
59+
}
60+
61+
public function setUrl(string $url) {
62+
$this->config->setAppValue(Application::APP_NAME, 'customsms_url', $url);
63+
}
64+
65+
public function getMethod(): string {
66+
return $this->getOrFail('customsms_method');
67+
}
68+
69+
public function setMethod(string $method) {
70+
$this->config->setAppValue(Application::APP_NAME, 'customsms_method', $method);
71+
}
72+
73+
public function getIdentifier(): string {
74+
return $this->getOrFail('customsms_identifier');
75+
}
76+
77+
public function setIdentifier(string $identifier) {
78+
$this->config->setAppValue(Application::APP_NAME, 'customsms_identifier', $identifier);
79+
}
80+
81+
public function getMessage(): string {
82+
return $this->getOrFail('customsms_message');
83+
}
84+
85+
public function setMessage(string $message) {
86+
$this->config->setAppValue(Application::APP_NAME, 'customsms_message', $message);
87+
}
88+
89+
public function getHeaders(): string {
90+
return $this->getOrFail('customsms_headers');
91+
}
92+
93+
public function setHeaders(string $headers) {
94+
$this->config->setAppValue(Application::APP_NAME, 'customsms_headers', $headers);
95+
}
96+
97+
public function getParameters(): string {
98+
return $this->getOrFail('customsms_parameters');
99+
}
100+
101+
public function setParameters(string $parameters) {
102+
$this->config->setAppValue(Application::APP_NAME, 'customsms_parameters', $parameters);
103+
}
104+
105+
public function isComplete(): bool {
106+
$set = $this->config->getAppKeys(Application::APP_NAME);
107+
return count(array_intersect($set, self::expected)) === count(self::expected);
108+
}
109+
110+
public function remove() {
111+
foreach(self::expected as $key) {
112+
$this->config->deleteAppValue(Application::APP_NAME, $key);
113+
}
114+
}
115+
}

lib/Service/Gateway/SMS/Provider/ProviderFactory.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public function getProvider(string $id): IProvider {
4545
return $this->container->query(WebSms::class);
4646
case ClockworkSMS::PROVIDER_ID:
4747
return $this->container->query(ClockworkSMS::class);
48+
case CustomSMS::PROVIDER_ID:
49+
return $this->container->query(CustomSMS::class);
4850
case EcallSMS::PROVIDER_ID:
4951
return $this->container->query(EcallSMS::class);
5052
case VoipMs::PROVIDER_ID:

0 commit comments

Comments
 (0)