Skip to content

Commit a88e572

Browse files
authored
Merge pull request #850 from nextcloud/feat/notify-admin-when-whatsapp-is-unauthenticated
feat: notify admin when whatsapp is unauthenticated
2 parents 974ac3f + 8dc07d2 commit a88e572

File tree

6 files changed

+177
-0
lines changed

6 files changed

+177
-0
lines changed

lib/AppInfo/Application.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
namespace OCA\TwoFactorGateway\AppInfo;
1111

12+
use OCA\TwoFactorGateway\Events\WhatsAppAuthenticationErrorEvent;
13+
use OCA\TwoFactorGateway\Listener\NotificationListener;
14+
use OCA\TwoFactorGateway\Notification\Notifier;
1215
use OCA\TwoFactorGateway\Provider\Factory;
1316
use OCP\AppFramework\App;
1417
use OCP\AppFramework\Bootstrap\IBootContext;
@@ -25,6 +28,9 @@ public function __construct(array $urlParams = []) {
2528

2629
#[\Override]
2730
public function register(IRegistrationContext $context): void {
31+
$context->registerNotifierService(Notifier::class);
32+
$context->registerEventListener(WhatsAppAuthenticationErrorEvent::class, NotificationListener::class);
33+
2834
$providerFactory = Server::get(Factory::class);
2935
$fqcn = $providerFactory->getFqcnList();
3036
foreach ($fqcn as $class) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\TwoFactorGateway\Events;
11+
12+
use OCP\EventDispatcher\Event;
13+
14+
class WhatsAppAuthenticationErrorEvent extends Event {
15+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\TwoFactorGateway\Listener;
11+
12+
use OCA\TwoFactorGateway\AppInfo\Application;
13+
use OCA\TwoFactorGateway\Events\WhatsAppAuthenticationErrorEvent;
14+
use OCP\AppFramework\Utility\ITimeFactory;
15+
use OCP\EventDispatcher\Event;
16+
use OCP\EventDispatcher\IEventListener;
17+
use OCP\IGroupManager;
18+
use OCP\Notification\IManager;
19+
use Psr\Log\LoggerInterface;
20+
21+
/**
22+
* @template-implements IEventListener<Event>
23+
*/
24+
class NotificationListener implements IEventListener {
25+
public function __construct(
26+
private IManager $notificationManager,
27+
private IGroupManager $groupManager,
28+
private ITimeFactory $timeFactory,
29+
private LoggerInterface $logger,
30+
) {
31+
}
32+
33+
#[\Override]
34+
public function handle(Event $event): void {
35+
if (!($event instanceof WhatsAppAuthenticationErrorEvent)) {
36+
return;
37+
}
38+
39+
$this->notifyAdmins();
40+
}
41+
42+
private function notifyAdmins(): void {
43+
try {
44+
$adminGroup = $this->groupManager->get('admin');
45+
if ($adminGroup === null) {
46+
$this->logger->error('Admin group not found');
47+
return;
48+
}
49+
50+
$admins = $adminGroup->getUsers();
51+
$this->logger->info('Found ' . count($admins) . ' admins to notify');
52+
53+
foreach ($admins as $user) {
54+
try {
55+
$notification = $this->notificationManager->createNotification();
56+
$notification
57+
->setApp(Application::APP_ID)
58+
->setDateTime($this->timeFactory->getDateTime())
59+
->setObject('whatsapp_error', 'authentication')
60+
->setSubject('whatsapp_auth_error')
61+
->setUser($user->getUID());
62+
63+
$this->logger->info('About to notify user: ' . $user->getUID());
64+
$this->notificationManager->notify($notification);
65+
$this->logger->info('WhatsApp auth error notification sent to ' . $user->getUID());
66+
} catch (\Exception $e) {
67+
$this->logger->error('Failed to notify user ' . $user->getUID() . ': ' . $e->getMessage(), [
68+
'exception' => $e,
69+
]);
70+
}
71+
}
72+
} catch (\Exception $e) {
73+
$this->logger->error('Error notifying admins about WhatsApp auth error: ' . $e->getMessage(), [
74+
'exception' => $e,
75+
]);
76+
}
77+
}
78+
}

lib/Notification/Notifier.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\TwoFactorGateway\Notification;
11+
12+
use OCA\TwoFactorGateway\AppInfo\Application;
13+
use OCP\IURLGenerator;
14+
use OCP\L10N\IFactory;
15+
use OCP\Notification\INotification;
16+
use OCP\Notification\INotifier;
17+
use OCP\Notification\UnknownNotificationException;
18+
use Override;
19+
use Psr\Log\LoggerInterface;
20+
21+
class Notifier implements INotifier {
22+
public function __construct(
23+
private IFactory $factory,
24+
private IURLGenerator $url,
25+
private LoggerInterface $logger,
26+
) {
27+
}
28+
29+
#[Override]
30+
public function getID(): string {
31+
return Application::APP_ID;
32+
}
33+
34+
#[Override]
35+
public function getName(): string {
36+
return $this->factory->get(Application::APP_ID)->t('Two-Factor Gateway');
37+
}
38+
39+
#[Override]
40+
public function prepare(INotification $notification, string $languageCode): INotification {
41+
if ($notification->getApp() !== Application::APP_ID) {
42+
throw new UnknownNotificationException();
43+
}
44+
45+
$l = $this->factory->get(Application::APP_ID, $languageCode);
46+
47+
if ($notification->getSubject() === 'whatsapp_auth_error') {
48+
$this->logger->debug('Preparing WhatsApp auth error notification for user ' . $notification->getUser());
49+
return $this->parseWhatsAppAuthError($notification, $l);
50+
}
51+
52+
$this->logger->warning('Unknown notification subject: ' . $notification->getSubject());
53+
throw new UnknownNotificationException();
54+
}
55+
56+
private function parseWhatsAppAuthError(INotification $notification, \OCP\IL10N $l): INotification {
57+
$notification
58+
->setParsedSubject($l->t('Two-Factor Gateway: WhatsApp API authentication failed'))
59+
->setParsedMessage($l->t('The authentication with WhatsApp API has failed. The Two-Factor Gateway (WhatsApp) will not work until this is resolved. Please reconfigure the WhatsApp gateway using the command line tool.'))
60+
->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/error.svg')))
61+
->setLink($this->url->linkToRouteAbsolute('settings.AdminSettings.index', ['section' => 'overview']));
62+
63+
return $notification;
64+
}
65+
}

lib/Provider/Channel/GoWhatsApp/Gateway.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111

1212
use GuzzleHttp\Exception\BadResponseException;
1313
use GuzzleHttp\Exception\RequestException;
14+
use OCA\TwoFactorGateway\Events\WhatsAppAuthenticationErrorEvent;
1415
use OCA\TwoFactorGateway\Exception\MessageTransmissionException;
1516
use OCA\TwoFactorGateway\Provider\FieldDefinition;
1617
use OCA\TwoFactorGateway\Provider\Gateway\AGateway;
1718
use OCA\TwoFactorGateway\Provider\Settings;
19+
use OCP\EventDispatcher\IEventDispatcher;
1820
use OCP\Http\Client\IClient;
1921
use OCP\Http\Client\IClientService;
2022
use OCP\IAppConfig;
@@ -60,6 +62,7 @@ public function __construct(
6062
private IClientService $clientService,
6163
private IL10N $l10n,
6264
private LoggerInterface $logger,
65+
private IEventDispatcher $eventDispatcher,
6366
) {
6467
parent::__construct($appConfig);
6568
$this->client = $this->clientService->newClient();
@@ -649,6 +652,8 @@ private function checkUserOnWhatsApp(string $phoneNumber): bool {
649652
$data = json_decode($body, true);
650653

651654
if ($status === 401 || ($data['code'] ?? '') === 'AUTHENTICATION_ERROR') {
655+
$this->eventDispatcher->dispatchTyped(new WhatsAppAuthenticationErrorEvent());
656+
652657
throw new MessageTransmissionException(
653658
$this->l10n->t('Authentication failed with WhatsApp API. Please verify username/password or log in again.'),
654659
self::CODE_AUTHENTICATION,

tests/psalm-baseline.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22
<files psalm-version="6.14.3@d0b040a91f280f071c1abcb1b77ce3822058725a">
33
<file src="lib/Provider/Channel/GoWhatsApp/Gateway.php">
44
<UndefinedClass>
5+
<code><![CDATA[$e]]></code>
6+
<code><![CDATA[$e]]></code>
7+
<code><![CDATA[$e]]></code>
8+
<code><![CDATA[$e]]></code>
9+
<code><![CDATA[$e]]></code>
10+
<code><![CDATA[$e]]></code>
511
<code><![CDATA[$e]]></code>
612
<code><![CDATA[$e]]></code>
713
<code><![CDATA[$e]]></code>
814
<code><![CDATA[BadResponseException]]></code>
15+
<code><![CDATA[BadResponseException]]></code>
16+
<code><![CDATA[RequestException]]></code>
917
<code><![CDATA[RequestException]]></code>
1018
<code><![CDATA[\GuzzleHttp\Exception\ConnectException]]></code>
1119
</UndefinedClass>

0 commit comments

Comments
 (0)