Skip to content

Commit 1013c47

Browse files
committed
Add user_id to token map to prevent overwrites
1 parent d3a2800 commit 1013c47

2 files changed

Lines changed: 54 additions & 5 deletions

File tree

notification/method/webpush.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public function notify()
158158
];
159159
$data = self::clean_data($data);
160160
$insert_buffer->insert($data);
161-
$this->push_token_map[$notification->notification_type_id][$notification->item_id] = $data['push_token'];
161+
$this->push_token_map[$notification->notification_type_id][$notification->item_id][$notification->user_id] = $data['push_token'];
162162
}
163163

164164
$insert_buffer->flush();
@@ -239,7 +239,7 @@ protected function notify_using_webpush(): void
239239
'type_id' => $notification->notification_type_id,
240240
'user_id' => $notification->user_id,
241241
'version' => $this->config['assets_version'],
242-
'token' => hash('sha256', $user['user_form_salt'] . $this->push_token_map[$notification->notification_type_id][$notification->item_id]),
242+
'token' => hash('sha256', $user['user_form_salt'] . $this->push_token_map[$notification->notification_type_id][$notification->item_id][$notification->user_id]),
243243
];
244244
$json_data = json_encode($data);
245245

tests/notification/notification_method_webpush_test.php

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919
require_once __DIR__ . '/../../../../../../tests/notification/base.php';
2020
require_once __DIR__ . '/../../vendor/autoload.php'; // load the composer dependencies for this extension
2121

22-
/**
23-
* @group slow
24-
*/
2522
class notification_method_webpush_test extends \phpbb_tests_notification_base
2623
{
2724
/** @var string[] VAPID keys for testing purposes */
@@ -648,6 +645,58 @@ public function test_get_type(): void
648645
$this->assertEquals('notification.method.phpbb.wpn.webpush', $this->notification_method_webpush->get_type());
649646
}
650647

648+
public function test_push_token_map_is_per_user(): void
649+
{
650+
// Verifies that when multiple users are notified about the same item,
651+
// each user's push token is stored and used independently.
652+
// Previously the map was keyed [type_id][item_id], so the last user's
653+
// token overwrote all others, making every other user's token invalid.
654+
$subscription_info = [];
655+
$expected_users = [2 => ['user_id' => '2'], 3 => ['user_id' => '3'], 4 => ['user_id' => '4']];
656+
foreach ($expected_users as $user_id => $user_data)
657+
{
658+
$subscription_info[$user_id] = $this->create_subscription_for_user($user_id);
659+
}
660+
661+
$post_data = [
662+
'post_time' => 1349413322,
663+
'poster_id' => 1,
664+
'topic_title' => '',
665+
'post_subject' => '',
666+
'post_username' => '',
667+
'forum_name' => '',
668+
'forum_id' => '1',
669+
'post_id' => '2',
670+
'topic_id' => '1',
671+
];
672+
673+
$this->notifications->add_notifications('notification.type.post', $post_data);
674+
675+
// Fetch the stored rows only for users we created subscriptions for
676+
$webpush_table = $this->container->getParameter('tables.phpbb.wpn.notification_push');
677+
$sql = 'SELECT user_id, push_token FROM ' . $webpush_table . ' WHERE ' . $this->db->sql_in_set('user_id', array_keys($expected_users)) . ' ORDER BY user_id ASC';
678+
$result = $this->db->sql_query($sql);
679+
$rows = $this->db->sql_fetchrowset($result);
680+
$this->db->sql_freeresult($result);
681+
682+
$this->assertCount(count($expected_users), $rows, 'Each expected user must have a notification row');
683+
$tokens = array_column($rows, 'push_token');
684+
$this->assertEquals(count($tokens), count(array_unique($tokens)), 'Each user must have a unique push_token');
685+
686+
// Verify each message payload contains the token hashed with that specific user's salt
687+
foreach ($rows as $row)
688+
{
689+
$user_id = (int) $row['user_id'];
690+
$client_hash = basename($subscription_info[$user_id]['endpoint']);
691+
$messages = $this->get_messages_for_subscription($client_hash);
692+
$this->assertNotEmpty($messages);
693+
$payload = json_decode($messages[0], true);
694+
$user = $this->user_loader->get_user($user_id);
695+
$expected_token = hash('sha256', $user['user_form_salt'] . $row['push_token']);
696+
$this->assertEquals($expected_token, $payload['token'], 'Token in push payload must match hash of that user\'s salt + their own push_token');
697+
}
698+
}
699+
651700
public function test_get_ucp_template_data_uses_millisecond_expiration_time(): void
652701
{
653702
$this->user->data['user_id'] = 2;

0 commit comments

Comments
 (0)