From 464f37fe46074074633ec6d65daa60d15b37c542 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Thu, 28 Jul 2022 21:09:57 +0200 Subject: [PATCH 01/63] [ticket/17010] Add dummy for web push notification method PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 163 ++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 phpBB/phpbb/notification/method/webpush.php diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php new file mode 100644 index 0000000000..f88cfc7cf9 --- /dev/null +++ b/phpBB/phpbb/notification/method/webpush.php @@ -0,0 +1,163 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\notification\method; + +use phpbb\config\config; +use phpbb\db\driver\driver_interface; +use phpbb\notification\type\type_interface; +use phpbb\user; +use phpbb\user_loader; + +/** +* Web push notification method class +* This class handles sending push messages for notifications +*/ + +class webpush extends \phpbb\notification\method\messenger_base +{ + /** @var config */ + protected $config; + + /** @var driver_interface */ + protected $db; + + /** @var user */ + protected $user; + + /** @var string Notification web push table */ + protected $notification_webpush_table; + + /** + * Notification Method web push constructor + * + * @param user_loader $user_loader + * @param user $user + * @param config $config + * @param driver_interface $db + * @param string $phpbb_root_path + * @param string $php_ext + * @param string $notification_webpush_table + */ + public function __construct(user_loader $user_loader, user $user, config $config, driver_interface $db, string $phpbb_root_path, string $php_ext, string $notification_webpush_table) + { + parent::__construct($user_loader, $phpbb_root_path, $php_ext); + + $this->user = $user; + $this->config = $config; + $this->db = $db; + $this->notification_webpush_table = $notification_webpush_table; + } + + /** + * {@inheritDoc} + */ + public function get_type(): string + { + return 'notification.method.webpush'; + } + + /** + * {@inheritDoc} + */ + public function is_available(type_interface $notification_type = null): bool + { + return parent::is_available($notification_type) && $this->config['webpush_enable'] && !empty($this->user->data['user_push_subscriptions']); + } + + /** + * {@inheritdoc} + */ + public function get_notified_users($notification_type_id, array $options): array + { + $notified_users = []; + + $sql = 'SELECT user_id + FROM ' . $this->notification_webpush_table . ' + WHERE notification_type_id = ' . (int) $notification_type_id . + (isset($options['item_id']) ? ' AND item_id = ' . (int) $options['item_id'] : '') . + (isset($options['item_parent_id']) ? ' AND item_parent_id = ' . (int) $options['item_parent_id'] : '') . + (isset($options['user_id']) ? ' AND user_id = ' . (int) $options['user_id'] : ''); + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + $notified_users[$row['user_id']] = $row; + } + $this->db->sql_freeresult($result); + + return $notified_users; + } + + /** + * Parse the queue and notify the users + */ + public function notify() + { + $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, $this->notification_webpush_table); + + /** @var type_interface $notification */ + foreach ($this->queue as $notification) + { + $data = self::clean_data($notification->get_insert_array()); + $insert_buffer->insert($data); + } + + $insert_buffer->flush(); + + // @todo: add actual web push code + + return false; + } + + /** + * {@inheritdoc} + */ + public function mark_notifications($notification_type_id, $item_id, $user_id, $time = false, $mark_read = true) + { + $sql = 'DELETE FROM ' . $this->notification_webpush_table . ' + WHERE ' . ($notification_type_id !== false ? $this->db->sql_in_set('notification_type_id', $notification_type_id) : '1=1') . + ($user_id !== false ? ' AND ' . $this->db->sql_in_set('user_id', $user_id) : '') . + ($item_id !== false ? ' AND ' . $this->db->sql_in_set('item_id', $item_id) : ''); + $this->db->sql_query($sql); + } + + /** + * {@inheritdoc} + */ + public function mark_notifications_by_parent($notification_type_id, $item_parent_id, $user_id, $time = false, $mark_read = true) + { + $sql = 'DELETE FROM ' . $this->notification_webpush_table . ' + WHERE ' . ($notification_type_id !== false ? $this->db->sql_in_set('notification_type_id', $notification_type_id) : '1=1') . + ($user_id !== false ? ' AND ' . $this->db->sql_in_set('user_id', $user_id) : '') . + ($item_parent_id !== false ? ' AND ' . $this->db->sql_in_set('item_parent_id', $item_parent_id, false, true) : ''); + $this->db->sql_query($sql); + } + + /** + * Clean data to contain only what we need for webpush notifications table + * + * @param array $data Notification data + * @return array Cleaned notification data + */ + public static function clean_data(array $data) + { + $row = [ + 'notification_type_id' => null, + 'item_id' => null, + 'item_parent_id' => null, + 'user_id' => null, + ]; + + return array_intersect_key($data, $row); + } +} From 07d3376612db776548d584f09397733c7414ddc9 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Thu, 28 Jul 2022 22:19:53 +0200 Subject: [PATCH 02/63] [ticket/17010] Add web-push-lib dependency PHPBB3-17010 --- phpBB/composer.json | 1 + phpBB/composer.lock | 1367 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 1196 insertions(+), 172 deletions(-) diff --git a/phpBB/composer.json b/phpBB/composer.json index da6875f9bc..6d59cefe26 100644 --- a/phpBB/composer.json +++ b/phpBB/composer.json @@ -40,6 +40,7 @@ "google/recaptcha": "~1.1", "guzzlehttp/guzzle": "~6.3", "marc1706/fast-image-size": "^1.1", + "minishlink/web-push": "^8.0", "s9e/text-formatter": "^2.0", "symfony/config": "^6.3", "symfony/console": "^6.3", diff --git a/phpBB/composer.lock b/phpBB/composer.lock index 36d1379632..287f56b08b 100644 --- a/phpBB/composer.lock +++ b/phpBB/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "75425a269d37301eb11d494437c2f5ff", + "content-hash": "26143c95732859f6f0849abd2fe74ae5", "packages": [ { "name": "bantu/ini-get-wrapper", @@ -40,6 +40,66 @@ }, "time": "2014-09-15T13:12:35+00:00" }, + { + "name": "brick/math", + "version": "0.12.1", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.12.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-11-29T23:19:16+00:00" + }, { "name": "carlos-mg89/oauth", "version": "0.8.15", @@ -1974,6 +2034,276 @@ }, "time": "2022-01-12T16:29:39+00:00" }, + { + "name": "minishlink/web-push", + "version": "v8.0.0", + "source": { + "type": "git", + "url": "https://github.com/web-push-libs/web-push-php.git", + "reference": "ec034f1e287cd1e74235e349bd017d71a61e9d8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-push-libs/web-push-php/zipball/ec034f1e287cd1e74235e349bd017d71a61e9d8d", + "reference": "ec034f1e287cd1e74235e349bd017d71a61e9d8d", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^7.0.1|^6.2", + "php": ">=8.0", + "spomky-labs/base64url": "^2.0", + "web-token/jwt-key-mgmt": "^2.0|^3.0.2", + "web-token/jwt-signature": "^2.0|^3.0.2", + "web-token/jwt-signature-algorithm-ecdsa": "^2.0|^3.0.2", + "web-token/jwt-util-ecc": "^2.0|^3.0.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^v3.13.2", + "phpstan/phpstan": "^1.9.8", + "phpunit/phpunit": "^9.5.27" + }, + "suggest": { + "ext-gmp": "Optional for performance." + }, + "type": "library", + "autoload": { + "psr-4": { + "Minishlink\\WebPush\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Louis Lagrange", + "email": "lagrange.louis@gmail.com", + "homepage": "https://github.com/Minishlink" + } + ], + "description": "Web Push library for PHP", + "homepage": "https://github.com/web-push-libs/web-push-php", + "keywords": [ + "Push API", + "WebPush", + "notifications", + "push", + "web" + ], + "support": { + "issues": "https://github.com/web-push-libs/web-push-php/issues", + "source": "https://github.com/web-push-libs/web-push-php/tree/v8.0.0" + }, + "time": "2023-01-10T17:14:44+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "58c3f47f650c94ec05a151692652a868995d2938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2022-06-14T06:56:20+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "paragonie/sodium_compat", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/sodium_compat.git", + "reference": "e592a3e06d1fa0d43988c7c7d9948ca836f644b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/e592a3e06d1fa0d43988c7c7d9948ca836f644b6", + "reference": "e592a3e06d1fa0d43988c7c7d9948ca836f644b6", + "shasum": "" + }, + "require": { + "paragonie/random_compat": ">=1", + "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^3|^4|^5|^6|^7|^8|^9" + }, + "suggest": { + "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", + "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." + }, + "type": "library", + "autoload": { + "files": [ + "autoload.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + }, + { + "name": "Frank Denis", + "email": "jedisct1@pureftpd.org" + } + ], + "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", + "keywords": [ + "Authentication", + "BLAKE2b", + "ChaCha20", + "ChaCha20-Poly1305", + "Chapoly", + "Curve25519", + "Ed25519", + "EdDSA", + "Edwards-curve Digital Signature Algorithm", + "Elliptic Curve Diffie-Hellman", + "Poly1305", + "Pure-PHP cryptography", + "RFC 7748", + "RFC 8032", + "Salpoly", + "Salsa20", + "X25519", + "XChaCha20-Poly1305", + "XSalsa20-Poly1305", + "Xchacha20", + "Xsalsa20", + "aead", + "cryptography", + "ecdh", + "elliptic curve", + "elliptic curve cryptography", + "encryption", + "libsodium", + "php", + "public-key cryptography", + "secret-key cryptography", + "side-channel resistant" + ], + "support": { + "issues": "https://github.com/paragonie/sodium_compat/issues", + "source": "https://github.com/paragonie/sodium_compat/tree/v1.20.0" + }, + "time": "2023-04-30T00:54:53+00:00" + }, { "name": "psr/cache", "version": "3.0.0", @@ -2023,6 +2353,54 @@ }, "time": "2021-02-03T23:26:27+00:00" }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, { "name": "psr/container", "version": "2.0.2", @@ -2126,6 +2504,113 @@ }, "time": "2019-01-08T18:20:26+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + }, + "time": "2023-04-10T20:10:41+00:00" + }, { "name": "psr/http-message", "version": "1.1", @@ -2680,6 +3165,182 @@ }, "time": "2023-09-03T09:24:00+00:00" }, + { + "name": "spomky-labs/base64url", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/base64url.git", + "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/base64url/zipball/7752ce931ec285da4ed1f4c5aa27e45e097be61d", + "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.11|^0.12", + "phpstan/phpstan-beberlei-assert": "^0.11|^0.12", + "phpstan/phpstan-deprecation-rules": "^0.11|^0.12", + "phpstan/phpstan-phpunit": "^0.11|^0.12", + "phpstan/phpstan-strict-rules": "^0.11|^0.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Base64Url\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky-Labs/base64url/contributors" + } + ], + "description": "Base 64 URL Safe Encoding/Decoding PHP Library", + "homepage": "https://github.com/Spomky-Labs/base64url", + "keywords": [ + "base64", + "rfc4648", + "safe", + "url" + ], + "support": { + "issues": "https://github.com/Spomky-Labs/base64url/issues", + "source": "https://github.com/Spomky-Labs/base64url/tree/v2.0.4" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-11-03T09:10:25+00:00" + }, + { + "name": "spomky-labs/pki-framework", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/pki-framework.git", + "reference": "86102bdd19379b2c6e5b0feb94fd490d40e7d133" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/86102bdd19379b2c6e5b0feb94fd490d40e7d133", + "reference": "86102bdd19379b2c6e5b0feb94fd490d40e7d133", + "shasum": "" + }, + "require": { + "brick/math": "^0.10|^0.11|^0.12", + "ext-mbstring": "*", + "php": ">=8.1" + }, + "require-dev": { + "ekino/phpstan-banned-code": "^1.0", + "ext-gmp": "*", + "ext-openssl": "*", + "infection/infection": "^0.27", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-beberlei-assert": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^10.1", + "rector/rector": "^0.19", + "roave/security-advisories": "dev-latest", + "symfony/phpunit-bridge": "^6.4|^7.0", + "symfony/string": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symplify/easy-coding-standard": "^12.0" + }, + "suggest": { + "ext-bcmath": "For better performance (or GMP)", + "ext-gmp": "For better performance (or BCMath)", + "ext-openssl": "For OpenSSL based cyphering" + }, + "type": "library", + "autoload": { + "psr-4": { + "SpomkyLabs\\Pki\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joni Eskelinen", + "email": "jonieske@gmail.com", + "role": "Original developer" + }, + { + "name": "Florent Morselli", + "email": "florent.morselli@spomky-labs.com", + "role": "Spomky-Labs PKI Framework developer" + } + ], + "description": "A PHP framework for managing Public Key Infrastructures. It comprises X.509 public key certificates, attribute certificates, certification requests and certification path validation.", + "homepage": "https://github.com/spomky-labs/pki-framework", + "keywords": [ + "DER", + "Private Key", + "ac", + "algorithm identifier", + "asn.1", + "asn1", + "attribute certificate", + "certificate", + "certification request", + "cryptography", + "csr", + "decrypt", + "ec", + "encrypt", + "pem", + "pkcs", + "public key", + "rsa", + "sign", + "signature", + "verify", + "x.509", + "x.690", + "x509", + "x690" + ], + "support": { + "issues": "https://github.com/Spomky-Labs/pki-framework/issues", + "source": "https://github.com/Spomky-Labs/pki-framework/tree/1.1.1" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2024-02-05T20:37:46+00:00" + }, { "name": "symfony/config", "version": "v6.4.3", @@ -3355,6 +4016,177 @@ ], "time": "2023-10-31T17:30:12+00:00" }, + { + "name": "symfony/http-client", + "version": "v6.4.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "a9034bc119fab8238f76cf49c770f3135f3ead86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/a9034bc119fab8238f76cf49c770f3135f3ead86", + "reference": "a9034bc119fab8238f76cf49c770f3135f3ead86", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "^3", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v6.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T15:01:07+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "1ee70e699b41909c209a0c930f11034b93578654" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1ee70e699b41909c209a0c930f11034b93578654", + "reference": "1ee70e699b41909c209a0c930f11034b93578654", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-30T20:28:31+00:00" + }, { "name": "symfony/http-foundation", "version": "v6.4.3", @@ -5282,6 +6114,368 @@ } ], "time": "2023-11-21T18:54:41+00:00" + }, + { + "name": "web-token/jwt-key-mgmt", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-key-mgmt.git", + "reference": "4d2a5a1a86477dd50b89aff76962816ddbd64590" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-key-mgmt/zipball/4d2a5a1a86477dd50b89aff76962816ddbd64590", + "reference": "4d2a5a1a86477dd50b89aff76962816ddbd64590", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=8.1", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "web-token/jwt-library": "^3.3" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "[DEPRECATED] Please use web-token/jwt-library instead.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-key-mgmt/tree/3.3.0" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "abandoned": "web-token/jwt-library", + "time": "2024-02-22T07:19:34+00:00" + }, + { + "name": "web-token/jwt-library", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-library.git", + "reference": "5edf0f193425bb9c695a433180ddf9d263f55063" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-library/zipball/5edf0f193425bb9c695a433180ddf9d263f55063", + "reference": "5edf0f193425bb9c695a433180ddf9d263f55063", + "shasum": "" + }, + "require": { + "brick/math": "^0.9|^0.10|^0.11|^0.12", + "ext-json": "*", + "ext-mbstring": "*", + "paragonie/constant_time_encoding": "^2.6", + "paragonie/sodium_compat": "^1.20", + "php": ">=8.1", + "psr/clock": "^1.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "spomky-labs/pki-framework": "^1.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/polyfill-mbstring": "^1.12" + }, + "conflict": { + "spomky-labs/jose": "*" + }, + "suggest": { + "ext-bcmath": "GMP or BCMath is highly recommended to improve the library performance", + "ext-gmp": "GMP or BCMath is highly recommended to improve the library performance", + "ext-openssl": "For key management (creation, optimization, etc.) and some algorithms (AES, RSA, ECDSA, etc.)", + "ext-sodium": "Sodium is required for OKP key creation, EdDSA signature algorithm and ECDH-ES key encryption with OKP keys", + "paragonie/sodium_compat": "Sodium is required for OKP key creation, EdDSA signature algorithm and ECDH-ES key encryption with OKP keys", + "spomky-labs/aes-key-wrap": "For all Key Wrapping algorithms (A128KW, A192KW, A256KW, A128GCMKW, A192GCMKW, A256GCMKW, PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW...)", + "symfony/http-client": "To enable JKU/X5U support." + }, + "type": "library", + "autoload": { + "psr-4": { + "Jose\\Component\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "JWT library", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "issues": "https://github.com/web-token/jwt-library/issues", + "source": "https://github.com/web-token/jwt-library/tree/3.3.0" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2024-02-22T08:15:45+00:00" + }, + { + "name": "web-token/jwt-signature", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature.git", + "reference": "eccfd59e658d4118414cf6d14229aa52eec387e7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature/zipball/eccfd59e658d4118414cf6d14229aa52eec387e7", + "reference": "eccfd59e658d4118414cf6d14229aa52eec387e7", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "web-token/jwt-library": "^3.3" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "[DEPRECATED] Please use web-token/jwt-library instead.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature/tree/3.3.0" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "abandoned": "web-token/jwt-library", + "time": "2024-02-22T07:19:34+00:00" + }, + { + "name": "web-token/jwt-signature-algorithm-ecdsa", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature-algorithm-ecdsa.git", + "reference": "28516e170f6ee6d13766d9e2b912c2853e1ac5e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature-algorithm-ecdsa/zipball/28516e170f6ee6d13766d9e2b912c2853e1ac5e4", + "reference": "28516e170f6ee6d13766d9e2b912c2853e1ac5e4", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=8.1", + "web-token/jwt-library": "^3.3" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "[DEPRECATED] Please use web-token/jwt-library instead.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature-algorithm-ecdsa/tree/3.3.0" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "abandoned": "web-token/jwt-library", + "time": "2024-02-22T07:19:34+00:00" + }, + { + "name": "web-token/jwt-util-ecc", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-util-ecc.git", + "reference": "667934c5c6e37238f4e67d51aa3ba55abc703e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-util-ecc/zipball/667934c5c6e37238f4e67d51aa3ba55abc703e1a", + "reference": "667934c5c6e37238f4e67d51aa3ba55abc703e1a", + "shasum": "" + }, + "require": { + "brick/math": "^0.9|^0.10|^0.11|^0.12", + "php": ">=8.1", + "web-token/jwt-library": "^3.3" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "[DEPRECATED] Please use web-token/jwt-library instead.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-util-ecc/tree/3.3.0" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "abandoned": "web-token/jwt-library", + "time": "2024-02-22T07:19:34+00:00" } ], "packages-dev": [ @@ -8614,177 +9808,6 @@ ], "time": "2024-01-29T15:02:55+00:00" }, - { - "name": "symfony/http-client", - "version": "v6.4.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "a9034bc119fab8238f76cf49c770f3135f3ead86" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/a9034bc119fab8238f76cf49c770f3135f3ead86", - "reference": "a9034bc119fab8238f76cf49c770f3135f3ead86", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client-contracts": "^3", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.3" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "3.0" - }, - "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", - "homepage": "https://symfony.com", - "keywords": [ - "http" - ], - "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T15:01:07+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v3.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "1ee70e699b41909c209a0c930f11034b93578654" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1ee70e699b41909c209a0c930f11034b93578654", - "reference": "1ee70e699b41909c209a0c930f11034b93578654", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-30T20:28:31+00:00" - }, { "name": "theseer/tokenizer", "version": "1.2.2", From 5c51f2ef7f56a4faee684999a701ef75a2e6bda2 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Wed, 17 Aug 2022 22:29:23 +0200 Subject: [PATCH 03/63] [ticket/17010] Add webpush to container definition PHPBB3-17010 --- .../default/container/services_notification.yml | 12 ++++++++++++ phpBB/config/default/container/tables.yml | 1 + tests/notification/base.php | 1 + .../notification/notification_method_email_test.php | 1 + tests/notification/submit_post_base.php | 1 + 5 files changed, 16 insertions(+) diff --git a/phpBB/config/default/container/services_notification.yml b/phpBB/config/default/container/services_notification.yml index a037ad7352..ebde7bcf5a 100644 --- a/phpBB/config/default/container/services_notification.yml +++ b/phpBB/config/default/container/services_notification.yml @@ -243,3 +243,15 @@ services: - '%core.php_ext%' tags: - { name: notification.method } + + notification.method.webpush: + class: phpbb\notification\method\webpush + shared: false + arguments: + - '@user_loader' + - '@user' + - '@config' + - '@dbal.conn' + - '%core.root_path%' + - '%core.php_ext%' + - '%tables.notification_push%' diff --git a/phpBB/config/default/container/tables.yml b/phpBB/config/default/container/tables.yml index 005f3ba927..f3a4e1b4ab 100644 --- a/phpBB/config/default/container/tables.yml +++ b/phpBB/config/default/container/tables.yml @@ -38,6 +38,7 @@ parameters: tables.modules: '%core.table_prefix%modules' tables.notification_emails: '%core.table_prefix%notification_emails' tables.notification_types: '%core.table_prefix%notification_types' + tables.notification_push: '%core.table_prefix%notification_push' tables.notifications: '%core.table_prefix%notifications' tables.poll_options: '%core.table_prefix%poll_options' tables.poll_votes: '%core.table_prefix%poll_votes' diff --git a/tests/notification/base.php b/tests/notification/base.php index 545508eedd..eb888cb0ae 100644 --- a/tests/notification/base.php +++ b/tests/notification/base.php @@ -124,6 +124,7 @@ abstract class phpbb_tests_notification_base extends phpbb_database_test_case $phpbb_container->setParameter('tables.user_notifications', 'phpbb_user_notifications'); $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types'); $phpbb_container->setParameter('tables.notification_emails', 'phpbb_notification_emails'); + $phpbb_container->setParameter('tables.notification_push', 'phpbb_notification_push'); $this->notifications = new phpbb_notification_manager_helper( array(), diff --git a/tests/notification/notification_method_email_test.php b/tests/notification/notification_method_email_test.php index d9b52cae2d..efcac83035 100644 --- a/tests/notification/notification_method_email_test.php +++ b/tests/notification/notification_method_email_test.php @@ -91,6 +91,7 @@ class notification_method_email_test extends phpbb_tests_notification_base $phpbb_container->setParameter('tables.user_notifications', 'phpbb_user_notifications'); $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types'); $phpbb_container->setParameter('tables.notification_emails', 'phpbb_notification_emails'); + $phpbb_container->setParameter('tables.notification_push', 'phpbb_notification_push'); $phpbb_container->set( 'text_formatter.s9e.mention_helper', new \phpbb\textformatter\s9e\mention_helper( diff --git a/tests/notification/submit_post_base.php b/tests/notification/submit_post_base.php index e8ad00e5ef..cb5b8d0d2a 100644 --- a/tests/notification/submit_post_base.php +++ b/tests/notification/submit_post_base.php @@ -154,6 +154,7 @@ abstract class phpbb_notification_submit_post_base extends phpbb_database_test_c $phpbb_container->setParameter('tables.user_notifications', 'phpbb_user_notifications'); $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types'); $phpbb_container->setParameter('tables.notification_emails', 'phpbb_notification_emails'); + $phpbb_container->setParameter('tables.notification_push', 'phpbb_notification_push'); $phpbb_container->set('content.visibility', new \phpbb\content_visibility($auth, $config, $phpbb_dispatcher, $db, $user, $phpbb_root_path, $phpEx, FORUMS_TABLE, POSTS_TABLE, TOPICS_TABLE, USERS_TABLE)); $phpbb_container->addCompilerPass(new phpbb\di\pass\markpublic_pass()); $phpbb_container->compile(); From 3feeb237caaab696657c01624261d8fdf3d294d6 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 19 Aug 2022 22:43:52 +0200 Subject: [PATCH 04/63] [ticket/17010] Add migration for notificatio_push table PHPBB3-17010 --- .../data/v400/add_notification_push_table.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php diff --git a/phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php b/phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php new file mode 100644 index 0000000000..0d71f5fb60 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php @@ -0,0 +1,55 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\db\migration\data\v400; + +use phpbb\db\migration\migration; + +class add_notification_push_table extends migration +{ + public static function depends_on(): array + { + return [ + '\phpbb\db\migration\data\v400\dev', + ]; + } + + public function effectively_installed(): bool + { + return $this->db_tools->sql_table_exists($this->table_prefix . 'notification_push'); + } + + public function update_schema(): array + { + return [ + 'add_tables' => [ + $this->table_prefix . 'notification_push' => [ + 'COLUMNS' => [ + 'notification_type_id' => ['USINT', 0], + 'item_id' => ['ULINT', 0], + 'item_parent_id' => ['ULINT', 0], + 'user_id' => ['ULINT', 0], + ], + 'PRIMARY_KEY' => ['notification_type_id', 'item_id', 'item_parent_id', 'user_id'], + ], + ], + ]; + } + + public function revert_schema(): array + { + return [ + 'drop_tables' => [$this->table_prefix . 'notification_push'], + ]; + } +} From 769f5bc3977df5579c2d70812efabecc71f32087 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 20 Aug 2022 22:56:34 +0200 Subject: [PATCH 05/63] [ticket/17010] Add settings and some more notification code PHPBB3-17010 --- phpBB/includes/acp/acp_board.php | 13 +++++ phpBB/includes/acp/info/acp_board.php | 1 + .../data/v400/add_notification_push_table.php | 27 ++++++++++ phpBB/phpbb/notification/method/webpush.php | 53 +++++++++++++++++++ 4 files changed, 94 insertions(+) diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index df7c8bf657..d6ce8f23c4 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -485,6 +485,19 @@ class acp_board ); break; + case 'webpush': + $display_vars = [ + 'title' => 'ACP_WEBPUSH_SETTINGS', + 'vars' => [ + 'legend1' => 'GENERAL_SETTINGS', + 'webpush_vapid_public' => ['lang' => 'WEBPUSH_VAPID_PUBLIC', 'validate' => 'string', 'type' => 'text:25:255', 'explain' => true], + 'webpush_vapid_private' => ['lang' => 'WEBPUSH_VAPID_PUBLIC', 'validate' => 'string', 'type' => 'text:25:255', 'explain' => true], + + 'legend3' => 'ACP_SUBMIT_CHANGES', + ], + ]; + break; + default: trigger_error('NO_MODE', E_USER_ERROR); break; diff --git a/phpBB/includes/acp/info/acp_board.php b/phpBB/includes/acp/info/acp_board.php index 1a3ee7b6be..a1d9f4fdd1 100644 --- a/phpBB/includes/acp/info/acp_board.php +++ b/phpBB/includes/acp/info/acp_board.php @@ -30,6 +30,7 @@ class acp_board_info 'auth' => array('title' => 'ACP_AUTH_SETTINGS', 'auth' => 'acl_a_server', 'cat' => array('ACP_CLIENT_COMMUNICATION')), 'email' => array('title' => 'ACP_EMAIL_SETTINGS', 'auth' => 'acl_a_server', 'cat' => array('ACP_CLIENT_COMMUNICATION')), + 'webpush' => array('title' => 'ACP_WEBPUSH_SETTINGS', 'auth' => 'acl_a_server', 'cat' => array('ACP_CLIENT_COMMUNICATION')), 'cookie' => array('title' => 'ACP_COOKIE_SETTINGS', 'auth' => 'acl_a_server', 'cat' => array('ACP_SERVER_CONFIGURATION')), 'server' => array('title' => 'ACP_SERVER_SETTINGS', 'auth' => 'acl_a_server', 'cat' => array('ACP_SERVER_CONFIGURATION')), diff --git a/phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php b/phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php index 0d71f5fb60..e822a39405 100644 --- a/phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php +++ b/phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php @@ -52,4 +52,31 @@ class add_notification_push_table extends migration 'drop_tables' => [$this->table_prefix . 'notification_push'], ]; } + + public function update_data(): array + { + return [ + ['config.add', ['webpush_vapid_public', '']], + ['config.add', ['webpush_vapid_private', '']], + ['module.add', [ + 'acp', + 'ACP_BOARD_CONFIGURATION', + [ + 'module_basename' => 'acp_board', + 'module_langname' => 'ACP_WEBPUSH_SETTINGS', + 'module_mode' => 'webpush', + 'module_auth' => 'acl_a_board', + 'after' => ['settings', 'ACP_JABBER_SETTINGS'], + ], + ]], + ]; + } + + public function revert_data(): array + { + return [ + ['config.remove', ['webpush_vapid_public']], + ['config.remove', ['webpush_vapid_private']], + ]; + } } diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index f88cfc7cf9..48787901b0 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -115,10 +115,63 @@ class webpush extends \phpbb\notification\method\messenger_base $insert_buffer->flush(); // @todo: add actual web push code + $this->notify_using_webpush(); return false; } + protected function notify_using_webpush() + { + if (empty($this->queue)) + { + return; + } + + // Load all users we want to notify (we need their email address) + $user_ids = []; + foreach ($this->queue as $notification) + { + $user_ids[] = $notification->user_id; + } + + // We do not send emails to banned users + if (!function_exists('phpbb_get_banned_user_ids')) + { + include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext); + } + $banned_users = phpbb_get_banned_user_ids($user_ids); + + // Load all the users we need + $this->user_loader->load_users(array_diff($user_ids, $banned_users), array(USER_IGNORE)); + + // Time to go through the queue and send emails + /** @var type_interface $notification */ + foreach ($this->queue as $notification) + { + $user = $this->user_loader->get_user($notification->user_id); + + if ($user['user_type'] == USER_INACTIVE && $user['user_inactive_reason'] == INACTIVE_MANUAL) + { + continue; + } + + // add actual web push data + $data['data'] = [ + 'badge' => '', // @todo: to be filled? + 'body' => $notification->get_title(), + 'icon' => '', // @todo: to be filled? + 'image' => '', // @todo: to be filled? + 'title' => $this->config['sitename'], + 'url' => $notification->get_url(), + ]; + + // @todo: start implementing actual web push code + } + + // We're done, empty the queue + $this->empty_queue(); + } + /** * {@inheritdoc} */ From d05e2dd043a6fe17517bb980062079b61dccc643 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Mon, 22 Aug 2022 21:31:04 +0200 Subject: [PATCH 06/63] [ticket/17010] Add first web push settings to ACP PHPBB3-17010 --- phpBB/includes/acp/acp_board.php | 2 +- phpBB/language/en/acp/board.php | 8 ++++++++ phpBB/language/en/acp/common.php | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index d6ce8f23c4..2530c3e254 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -491,7 +491,7 @@ class acp_board 'vars' => [ 'legend1' => 'GENERAL_SETTINGS', 'webpush_vapid_public' => ['lang' => 'WEBPUSH_VAPID_PUBLIC', 'validate' => 'string', 'type' => 'text:25:255', 'explain' => true], - 'webpush_vapid_private' => ['lang' => 'WEBPUSH_VAPID_PUBLIC', 'validate' => 'string', 'type' => 'text:25:255', 'explain' => true], + 'webpush_vapid_private' => ['lang' => 'WEBPUSH_VAPID_PRIVATE', 'validate' => 'string', 'type' => 'password:25:255', 'explain' => true], 'legend3' => 'ACP_SUBMIT_CHANGES', ], diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index ec2d0236ec..37303d481a 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -599,6 +599,14 @@ $lang = array_merge($lang, array( 'USE_SMTP_EXPLAIN' => 'Select “Yes” if you want or have to send email via a named server instead of the local mail function.', )); +$lang = array_merge($lang, [ + 'ACP_WEBPUSH_SETTINGS_EXPLAIN' => 'Here you can enable and control the use of Webpush for board notifications. Webpush is a simple protocol for the delivery of real-time events to user agents, more commonly known as push messages. It is supported by most modern browsers on desktop and mobile devices.', + 'WEBPUSH_VAPID_PUBLIC' => 'VAPID public key', + 'WEBPUSH_VAPID_PUBLIC_EXPLAIN' => 'The VAPID public key will be shared to authenticate push messages sent by your site.
Warning: Changing the VAPID public key will automatically invalidate all webpush subscriptions.', + 'WEBPUSH_VAPID_PRIVATE' => 'VAPID private key', + 'WEBPUSH_VAPID_PRIVATE_EXPLAIN' => 'The VAPID private key will be used to create authenticated push messages sent by your site. The VAPID private key must be a valid public-private key pair with the VAPID public key.
Warning: Changing the VAPID private key will automatically invalidate all webpush subscriptions.', +]); + // Jabber settings $lang = array_merge($lang, array( 'ACP_JABBER_SETTINGS_EXPLAIN' => 'Here you can enable and control the use of Jabber for instant messaging and board notifications. Jabber is an open source protocol and therefore available for use by anyone. Some Jabber servers include gateways or transports which allow you to contact users on other networks. Not all servers offer all transports and changes in protocols can prevent transports from operating. Please be sure to enter already registered account details - phpBB will use the details you enter here as is.', diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php index aeab0af262..84c047af54 100644 --- a/phpBB/language/en/acp/common.php +++ b/phpBB/language/en/acp/common.php @@ -219,6 +219,7 @@ $lang = array_merge($lang, array( 'ACP_VIEW_GLOBAL_MOD_PERMISSIONS' => 'View global moderation permissions', 'ACP_VIEW_USER_PERMISSIONS' => 'View user-based permissions', + 'ACP_WEBPUSH_SETTINGS' => 'Webpush settings', 'ACP_WORDS' => 'Word censoring', 'ACTION' => 'Action', From 873a22176dbe477ba6e3f4af2481feb0e6f81bf1 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 30 Aug 2022 20:21:03 +0200 Subject: [PATCH 07/63] [ticket/17010] Add Webpush settings and language entries PHPBB3-17010 --- phpBB/includes/acp/acp_board.php | 23 +++++++++++++++++++ phpBB/install/schemas/schema_data.sql | 3 +++ phpBB/language/en/acp/board.php | 2 ++ ...ication_push_table.php => add_webpush.php} | 6 ++++- .../migrations_check_config_added_test.php | 4 ++-- 5 files changed, 35 insertions(+), 3 deletions(-) rename phpBB/phpbb/db/migration/data/v400/{add_notification_push_table.php => add_webpush.php} (88%) diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index 2530c3e254..ceb7f47d58 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -19,6 +19,7 @@ * @ignore */ +use Minishlink\WebPush\VAPID; use phpbb\config\config; use phpbb\language\language; use phpbb\user; @@ -490,6 +491,7 @@ class acp_board 'title' => 'ACP_WEBPUSH_SETTINGS', 'vars' => [ 'legend1' => 'GENERAL_SETTINGS', + 'webpush_enable' => ['lang' => 'WEBPUSH_ENABLE', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true], 'webpush_vapid_public' => ['lang' => 'WEBPUSH_VAPID_PUBLIC', 'validate' => 'string', 'type' => 'text:25:255', 'explain' => true], 'webpush_vapid_private' => ['lang' => 'WEBPUSH_VAPID_PRIVATE', 'validate' => 'string', 'type' => 'password:25:255', 'explain' => true], @@ -537,6 +539,27 @@ class acp_board } } + if ($mode == 'webpush') + { + // Create VAPID keys if keys are empty and web push is enabled + if ($submit && $cfg_array['webpush_enable'] && $cfg_array['webpush_enable'] != $config['webpush_enable'] + && empty($cfg_array['webpush_vapid_public']) && empty($cfg_array['webpush_vapid_private']) + && empty($config['webpush_vapid_public']) && empty($config['webpush_vapid_private'])) + { + try + { + $vapid_keys = VAPID::createVapidKeys(); + $cfg_array['webpush_vapid_public'] = $vapid_keys['publicKey']; + $cfg_array['webpush_vapid_private'] = $vapid_keys['privateKey']; + } + catch (\ErrorException $exception) + { + // Nothing we can do about this, user will have to follow the + // documentation and manually create these. + } + } + } + // We validate the complete config if wished validate_config_vars($display_vars['vars'], $cfg_array, $error); diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index 844ba402b3..b924f70603 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -326,6 +326,9 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\avatar\pro INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\avatar\config\path', 'images/avatars/upload'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\backup\provider', 'phpbb\storage\provider\local'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\backup\config\path', 'store'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('webpush_enable', '0'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('webpush_vapid_public', ''); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('webpush_vapid_private', ''); INSERT INTO phpbb_config (config_name, config_value, is_dynamic) VALUES ('cache_last_gc', '0', 1); INSERT INTO phpbb_config (config_name, config_value, is_dynamic) VALUES ('cron_lock', '0', 1); diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index 37303d481a..8fe9f832f2 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -601,6 +601,8 @@ $lang = array_merge($lang, array( $lang = array_merge($lang, [ 'ACP_WEBPUSH_SETTINGS_EXPLAIN' => 'Here you can enable and control the use of Webpush for board notifications. Webpush is a simple protocol for the delivery of real-time events to user agents, more commonly known as push messages. It is supported by most modern browsers on desktop and mobile devices.', + 'WEBPUSH_ENABLE' => 'Enable Webpush', + 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow receiving notifications via Webpush.
Note: If VAPID keys have not been set, phpBB will try to automatically create them when enabling Webpush.', 'WEBPUSH_VAPID_PUBLIC' => 'VAPID public key', 'WEBPUSH_VAPID_PUBLIC_EXPLAIN' => 'The VAPID public key will be shared to authenticate push messages sent by your site.
Warning: Changing the VAPID public key will automatically invalidate all webpush subscriptions.', 'WEBPUSH_VAPID_PRIVATE' => 'VAPID private key', diff --git a/phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php b/phpBB/phpbb/db/migration/data/v400/add_webpush.php similarity index 88% rename from phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php rename to phpBB/phpbb/db/migration/data/v400/add_webpush.php index e822a39405..3230acfc8d 100644 --- a/phpBB/phpbb/db/migration/data/v400/add_notification_push_table.php +++ b/phpBB/phpbb/db/migration/data/v400/add_webpush.php @@ -13,9 +13,10 @@ namespace phpbb\db\migration\data\v400; +use Minishlink\WebPush\VAPID; use phpbb\db\migration\migration; -class add_notification_push_table extends migration +class add_webpush extends migration { public static function depends_on(): array { @@ -56,6 +57,7 @@ class add_notification_push_table extends migration public function update_data(): array { return [ + ['config.add', ['webpush_enable', false]], ['config.add', ['webpush_vapid_public', '']], ['config.add', ['webpush_vapid_private', '']], ['module.add', [ @@ -75,8 +77,10 @@ class add_notification_push_table extends migration public function revert_data(): array { return [ + ['config.remove', ['webpush_enable']], ['config.remove', ['webpush_vapid_public']], ['config.remove', ['webpush_vapid_private']], + ['module.remove', ['acp', 'ACP_BOARD_CONFIGURATION', 'ACP_WEBPUSH_SETTINGS']] ]; } } diff --git a/tests/migrations/migrations_check_config_added_test.php b/tests/migrations/migrations_check_config_added_test.php index 969a0f5e79..1472f9e370 100644 --- a/tests/migrations/migrations_check_config_added_test.php +++ b/tests/migrations/migrations_check_config_added_test.php @@ -142,7 +142,7 @@ class migrations_check_config_added_test extends phpbb_test_case continue; } - // Fill error entries for configuration options which were not added to shema_data.sql + // Fill error entries for configuration options which were not added to schema_data.sql if (!isset($config_names[$config_name])) { $config_names[$config_name] = [$config_name, $class]; @@ -160,7 +160,7 @@ class migrations_check_config_added_test extends phpbb_test_case */ public function test_config_option_exists_in_schema_data($config_name, $class) { - $message = 'Migration: %1$s, config_name: %2$s; not added to shema_data.sql'; + $message = 'Migration: %1$s, config_name: %2$s; not added to schema_data.sql'; $this->assertNotFalse(strpos($this->schema_data, $config_name), sprintf($message, $class, $config_name) From 1c64c695a9832b8d00ff9546443e68b0ff6c4821 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 30 Aug 2022 20:49:07 +0200 Subject: [PATCH 08/63] [ticket/17010] Implement basic logic for webpush notifications PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 46 ++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 48787901b0..eab1dac681 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -15,6 +15,7 @@ namespace phpbb\notification\method; use phpbb\config\config; use phpbb\db\driver\driver_interface; +use phpbb\json\sanitizer; use phpbb\notification\type\type_interface; use phpbb\user; use phpbb\user_loader; @@ -72,7 +73,8 @@ class webpush extends \phpbb\notification\method\messenger_base */ public function is_available(type_interface $notification_type = null): bool { - return parent::is_available($notification_type) && $this->config['webpush_enable'] && !empty($this->user->data['user_push_subscriptions']); + return parent::is_available($notification_type) && $this->config['webpush_enable'] + && !empty($this->config['webpush_vapid_public']) && !empty($this->config['webpush_vapid_private']); } /** @@ -114,13 +116,17 @@ class webpush extends \phpbb\notification\method\messenger_base $insert_buffer->flush(); - // @todo: add actual web push code $this->notify_using_webpush(); return false; } - protected function notify_using_webpush() + /** + * Notify using web push + * + * @return void + */ + protected function notify_using_webpush(): void { if (empty($this->queue)) { @@ -144,28 +150,58 @@ class webpush extends \phpbb\notification\method\messenger_base // Load all the users we need $this->user_loader->load_users(array_diff($user_ids, $banned_users), array(USER_IGNORE)); + $web_push = new \Minishlink\WebPush\WebPush(); + // Time to go through the queue and send emails /** @var type_interface $notification */ foreach ($this->queue as $notification) { $user = $this->user_loader->get_user($notification->user_id); - if ($user['user_type'] == USER_INACTIVE && $user['user_inactive_reason'] == INACTIVE_MANUAL) + $user_subscriptions = sanitizer::decode($this->user->data['user_push_subscriptions']); + + if ($user['user_type'] == USER_INACTIVE && $user['user_inactive_reason'] == INACTIVE_MANUAL + || empty($user_subscriptions)) { continue; } // add actual web push data $data['data'] = [ - 'badge' => '', // @todo: to be filled? 'body' => $notification->get_title(), 'icon' => '', // @todo: to be filled? 'image' => '', // @todo: to be filled? 'title' => $this->config['sitename'], 'url' => $notification->get_url(), + 'user_id' => $notification->user_id, ]; + $json_data = json_encode($data); // @todo: start implementing actual web push code + + foreach ($user_subscriptions as $subscription) + { + try + { + $push_subscription = \Minishlink\WebPush\Subscription::create($subscription); + $web_push->queueNotification($push_subscription, $json_data); + } + catch (\ErrorException $exception) + { + // @todo: decide whether we want to remove invalid subscriptions directly? + // Might need too many resources ... + } + } + } + + // @todo: Try offloading to after request + try + { + $web_push->flush(); + } + catch (\ErrorException $exception) + { + // @todo: Add to error log if we can't flush ... } // We're done, empty the queue From 83cf915758f830c0544165b12411f6f8b8d385a8 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Wed, 31 Aug 2022 20:06:07 +0200 Subject: [PATCH 09/63] [ticket/17010] Add column for user push subscriptions PHPBB3-17010 --- phpBB/phpbb/db/migration/data/v400/add_webpush.php | 7 +++++++ phpBB/phpbb/notification/method/webpush.php | 4 +--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/phpBB/phpbb/db/migration/data/v400/add_webpush.php b/phpBB/phpbb/db/migration/data/v400/add_webpush.php index 3230acfc8d..abc0ffc079 100644 --- a/phpBB/phpbb/db/migration/data/v400/add_webpush.php +++ b/phpBB/phpbb/db/migration/data/v400/add_webpush.php @@ -44,6 +44,13 @@ class add_webpush extends migration 'PRIMARY_KEY' => ['notification_type_id', 'item_id', 'item_parent_id', 'user_id'], ], ], + 'add_columns' => [ + $this->table_prefix . 'users' => [ + 'COLUMNS' => [ + 'user_push_subscriptions' => ['MTEXT_UNI', ''] + ], + ], + ], ]; } diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index eab1dac681..2532246624 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -168,17 +168,15 @@ class webpush extends \phpbb\notification\method\messenger_base // add actual web push data $data['data'] = [ + 'title' => $this->config['sitename'], 'body' => $notification->get_title(), 'icon' => '', // @todo: to be filled? 'image' => '', // @todo: to be filled? - 'title' => $this->config['sitename'], 'url' => $notification->get_url(), 'user_id' => $notification->user_id, ]; $json_data = json_encode($data); - // @todo: start implementing actual web push code - foreach ($user_subscriptions as $subscription) { try From af3108044576586efb1d9b67697319fb1bba89a2 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 18 Sep 2022 21:28:25 +0200 Subject: [PATCH 10/63] [ticket/17010] Add web-push-testing for implementing web push tests PHPBB3-17010 --- package-lock.json | 1627 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- 2 files changed, 1628 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7471a2fec2..5801393ff3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,8 @@ "postcss": "^8.4.31", "postcss-sorting": "^7.0.1", "stylelint": "^14.7.0", - "stylelint-order": "^5.0.0" + "stylelint-order": "^5.0.0", + "web-push-testing": "^1.0.0" } }, "node_modules/@babel/code-frame": { @@ -325,6 +326,19 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", @@ -471,6 +485,12 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -537,6 +557,12 @@ "node": ">=0.10.0" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, "node_modules/array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", @@ -819,6 +845,45 @@ "file-uri-to-path": "1.0.0" } }, + "node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -894,12 +959,27 @@ "node": ">=0.4.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true + }, "node_modules/buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -1377,6 +1457,53 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, + "node_modules/connected-domain": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/connected-domain/-/connected-domain-1.0.0.tgz", + "integrity": "sha512-lHlohUiJxlpunvDag2Y0pO20bnvarMjnrdciZeuJUqRwrf/5JHNhdpiPIr5GQ8IkqrFj5TDMQwcCjblGo1oeuA==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -1386,6 +1513,21 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, "node_modules/copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", @@ -1754,6 +1896,25 @@ "node": ">=0.10.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -1885,6 +2046,21 @@ "node": ">=0.10.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, "node_modules/electron-to-chromium": { "version": "1.4.111", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.111.tgz", @@ -1897,6 +2073,15 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1983,6 +2168,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2170,6 +2361,15 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -2317,6 +2517,83 @@ "node": ">=0.10.0" } }, + "node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -2549,6 +2826,39 @@ "node": ">=0.10.0" } }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -2664,6 +2974,15 @@ "node": ">=0.10.0" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -2689,6 +3008,15 @@ "node": ">=0.10.0" } }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs-mkdirp-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", @@ -3396,6 +3724,46 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/http_ece": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.1.0.tgz", + "integrity": "sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==", + "dev": true, + "dependencies": { + "urlsafe-base64": "~1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -3506,6 +3874,15 @@ "node": ">=0.10.0" } }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -3830,12 +4207,64 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dev": true, + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/just-debounce": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==", "dev": true }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", @@ -4065,6 +4494,12 @@ "lodash.restparam": "^3.0.0" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true + }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -4077,6 +4512,36 @@ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", "dev": true }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, "node_modules/lodash.keys": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", @@ -4100,6 +4565,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true + }, "node_modules/lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", @@ -4251,6 +4722,15 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/memoizee": { "version": "0.4.15", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", @@ -4305,6 +4785,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4314,6 +4800,15 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -4384,6 +4879,39 @@ "node": ">=0.10.0" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -4405,6 +4933,12 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, "node_modules/minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", @@ -4465,6 +4999,18 @@ "node": ">=0.10.0" } }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4579,12 +5125,78 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true }, + "node_modules/node-persist": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-persist/-/node-persist-2.1.0.tgz", + "integrity": "sha512-NI30KmynAIpKtvl3XaLE/Q/hPUNfh2bFM0U9zgWyIVzBUL/fh1EMk2/rTAqWY6KXrX8jqusVA6avPJ6I2S9B4w==", + "dev": true, + "dependencies": { + "is-absolute": "^0.2.6", + "mkdirp": "~0.5.1", + "q": "~1.1.1" + } + }, + "node_modules/node-persist/node_modules/is-absolute": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", + "integrity": "sha512-7Kr05z5LkcOpoMvxHN1PC11WbPabdNFmMYYo0eZvWu3BfVS0T03yoqYDczoCBx17xqk2x1XAZrcKiFVL88jxlQ==", + "dev": true, + "dependencies": { + "is-relative": "^0.2.1", + "is-windows": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-persist/node_modules/is-relative": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", + "integrity": "sha512-9AMzjRmLqcue629b4ezEVSK6kJsYJlUIhMcygmYORUgwUNJiavHcC3HkaGx0XYpyVKQSOqFbMEZmW42cY87sYw==", + "dev": true, + "dependencies": { + "is-unc-path": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-persist/node_modules/is-unc-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", + "integrity": "sha512-HhLc5VDMH4pu3oMtIuunz/DFQUIoR561kMME3U3Afhj8b7vH085vkIkemrz1kLXCEIuoMAmO3yVmafWdSbGW8w==", + "dev": true, + "dependencies": { + "unc-path-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-persist/node_modules/is-windows": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", + "integrity": "sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/node-releases": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", @@ -4775,6 +5387,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -4867,6 +5488,18 @@ "node": ">=0.10.0" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5024,6 +5657,15 @@ "node": ">=0.10.0" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -5093,6 +5735,12 @@ "node": ">=0.10.0" } }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -5759,6 +6407,28 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ps-node": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ps-node/-/ps-node-0.1.6.tgz", + "integrity": "sha512-w7QJhUTbu70hpDso0YXDRNKCPNuchV8UTUZsAv0m7Qj5g85oHOJfr9drA1EjvK4nQK/bG8P97W4L6PJ3IQLoOA==", + "dev": true, + "dependencies": { + "table-parser": "^0.1.3" + } + }, "node_modules/pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", @@ -5789,6 +6459,31 @@ "node": ">=6" } }, + "node_modules/q": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz", + "integrity": "sha512-ROtylwux7Vkc4C07oKE/ReigUmb33kVoLtcR4SJ1QVqwaZkBEDL3vX4/kwFzIERQ5PfCl0XafbU8u2YUhyGgVA==", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5818,6 +6513,30 @@ "node": ">=8" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -6326,6 +7045,12 @@ "ret": "~0.1.10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -6353,6 +7078,66 @@ "node": ">= 0.10" } }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -6386,6 +7171,12 @@ "node": ">=0.10.0" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6407,6 +7198,20 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -6862,6 +7667,15 @@ "node": ">=0.10.0" } }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", @@ -7275,6 +8089,15 @@ "node": ">=10.0.0" } }, + "node_modules/table-parser": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/table-parser/-/table-parser-0.1.3.tgz", + "integrity": "sha512-LCYeuvqqoPII3lzzYaXKbC3Forb+d2u4bNwhk/9FlivuGRxPE28YEWAYcujeSlLLDlMfvy29+WPybFJZFiKMYg==", + "dev": true, + "dependencies": { + "connected-domain": "^1.0.0" + } + }, "node_modules/table/node_modules/ajv": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", @@ -7506,6 +8329,15 @@ "xtend": "~4.0.1" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -7545,6 +8377,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -7630,6 +8475,15 @@ "through2-filter": "^3.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -7716,6 +8570,12 @@ "node": ">=0.10.0" } }, + "node_modules/urlsafe-base64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz", + "integrity": "sha512-RtuPeMy7c1UrHwproMZN9gN6kiZ0SvJwRaEzwZY0j9MypEkFqyBaKv176jvlPtg58Zh36bOkS0NFABXMHvvGCA==", + "dev": true + }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -7731,6 +8591,15 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -7768,6 +8637,15 @@ "node": ">= 0.10" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vinyl": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", @@ -7871,6 +8749,27 @@ "node": ">=0.10.0" } }, + "node_modules/web-push-testing": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.0.0.tgz", + "integrity": "sha512-p14iWiQSPY4kjpbaz59QfczaxEyD4uWg1aBjUS+Y9LAzQPU4Z95Jnq3FiOIazrvrfY0/1uDBbQkjXVxHq6wI/Q==", + "dev": true, + "dependencies": { + "arg": "^5.0.1", + "express": "^4.17.2", + "http_ece": "^1.1.0", + "jsonwebtoken": "^8.5.1", + "node-persist": "^2.1.0", + "ps-node": "^0.1.6", + "urlsafe-base64": "^1.0.0" + }, + "bin": { + "web-push-testing": "src/bin/cli.js" + }, + "engines": { + "node": ">=15.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8447,6 +9346,16 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, "acorn": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", @@ -8556,6 +9465,12 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -8604,6 +9519,12 @@ "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", "dev": true }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, "array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", @@ -8813,6 +9734,43 @@ "file-uri-to-path": "1.0.0" } }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -8866,12 +9824,24 @@ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", "dev": true }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -9255,6 +10225,35 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, + "connected-domain": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/connected-domain/-/connected-domain-1.0.0.tgz", + "integrity": "sha512-lHlohUiJxlpunvDag2Y0pO20bnvarMjnrdciZeuJUqRwrf/5JHNhdpiPIr5GQ8IkqrFj5TDMQwcCjblGo1oeuA==", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -9264,6 +10263,18 @@ "safe-buffer": "~5.1.1" } }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", @@ -9545,6 +10556,18 @@ "isobject": "^3.0.1" } }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -9645,6 +10668,21 @@ } } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, "electron-to-chromium": { "version": "1.4.111", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.111.tgz", @@ -9657,6 +10695,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -9739,6 +10783,12 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -9871,6 +10921,12 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, "event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -9997,6 +11053,68 @@ "homedir-polyfill": "^1.0.1" } }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, "ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -10196,6 +11314,38 @@ "to-regex-range": "^2.1.0" } }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -10289,6 +11439,12 @@ "for-in": "^1.0.1" } }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -10304,6 +11460,12 @@ "map-cache": "^0.2.2" } }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, "fs-mkdirp-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", @@ -10865,6 +12027,37 @@ "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", "dev": true }, + "http_ece": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.1.0.tgz", + "integrity": "sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==", + "dev": true, + "requires": { + "urlsafe-base64": "~1.0.0" + } + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -10945,6 +12138,12 @@ "integrity": "sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0=", "dev": true }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -11202,12 +12401,59 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dev": true, + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, "just-debounce": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==", "dev": true }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", @@ -11405,6 +12651,12 @@ "lodash.restparam": "^3.0.0" } }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true + }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -11417,6 +12669,36 @@ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", "dev": true }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, "lodash.keys": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", @@ -11440,6 +12722,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true + }, "lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", @@ -11561,6 +12849,12 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true + }, "memoizee": { "version": "0.4.15", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", @@ -11605,12 +12899,24 @@ } } }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -11668,6 +12974,27 @@ } } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -11683,6 +13010,12 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, "minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", @@ -11732,6 +13065,15 @@ } } }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -11818,12 +13160,65 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, "next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true }, + "node-persist": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-persist/-/node-persist-2.1.0.tgz", + "integrity": "sha512-NI30KmynAIpKtvl3XaLE/Q/hPUNfh2bFM0U9zgWyIVzBUL/fh1EMk2/rTAqWY6KXrX8jqusVA6avPJ6I2S9B4w==", + "dev": true, + "requires": { + "is-absolute": "^0.2.6", + "mkdirp": "~0.5.1", + "q": "~1.1.1" + }, + "dependencies": { + "is-absolute": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", + "integrity": "sha512-7Kr05z5LkcOpoMvxHN1PC11WbPabdNFmMYYo0eZvWu3BfVS0T03yoqYDczoCBx17xqk2x1XAZrcKiFVL88jxlQ==", + "dev": true, + "requires": { + "is-relative": "^0.2.1", + "is-windows": "^0.2.0" + } + }, + "is-relative": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", + "integrity": "sha512-9AMzjRmLqcue629b4ezEVSK6kJsYJlUIhMcygmYORUgwUNJiavHcC3HkaGx0XYpyVKQSOqFbMEZmW42cY87sYw==", + "dev": true, + "requires": { + "is-unc-path": "^0.1.1" + } + }, + "is-unc-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", + "integrity": "sha512-HhLc5VDMH4pu3oMtIuunz/DFQUIoR561kMME3U3Afhj8b7vH085vkIkemrz1kLXCEIuoMAmO3yVmafWdSbGW8w==", + "dev": true, + "requires": { + "unc-path-regex": "^0.1.0" + } + }, + "is-windows": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", + "integrity": "sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q==", + "dev": true + } + } + }, "node-releases": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", @@ -11970,6 +13365,12 @@ } } }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -12038,6 +13439,15 @@ "make-iterator": "^1.0.0" } }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -12156,6 +13566,12 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -12207,6 +13623,12 @@ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -12629,6 +14051,25 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "ps-node": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ps-node/-/ps-node-0.1.6.tgz", + "integrity": "sha512-w7QJhUTbu70hpDso0YXDRNKCPNuchV8UTUZsAv0m7Qj5g85oHOJfr9drA1EjvK4nQK/bG8P97W4L6PJ3IQLoOA==", + "dev": true, + "requires": { + "table-parser": "^0.1.3" + } + }, "pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", @@ -12656,6 +14097,21 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "q": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz", + "integrity": "sha512-ROtylwux7Vkc4C07oKE/ReigUmb33kVoLtcR4SJ1QVqwaZkBEDL3vX4/kwFzIERQ5PfCl0XafbU8u2YUhyGgVA==", + "dev": true + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -12668,6 +14124,24 @@ "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -13072,6 +14546,12 @@ "ret": "~0.1.10" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -13090,6 +14570,64 @@ "sver-compat": "^1.5.0" } }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -13119,6 +14657,12 @@ } } }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -13134,6 +14678,17 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -13514,6 +15069,12 @@ } } }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + }, "stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", @@ -13855,6 +15416,15 @@ } } }, + "table-parser": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/table-parser/-/table-parser-0.1.3.tgz", + "integrity": "sha512-LCYeuvqqoPII3lzzYaXKbC3Forb+d2u4bNwhk/9FlivuGRxPE28YEWAYcujeSlLLDlMfvy29+WPybFJZFiKMYg==", + "dev": true, + "requires": { + "connected-domain": "^1.0.0" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -14044,6 +15614,12 @@ } } }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, "trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -14071,6 +15647,16 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -14145,6 +15731,12 @@ "through2-filter": "^3.0.0" } }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -14215,6 +15807,12 @@ "ip-regex": "^1.0.1" } }, + "urlsafe-base64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz", + "integrity": "sha512-RtuPeMy7c1UrHwproMZN9gN6kiZ0SvJwRaEzwZY0j9MypEkFqyBaKv176jvlPtg58Zh36bOkS0NFABXMHvvGCA==", + "dev": true + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -14227,6 +15825,12 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -14258,6 +15862,12 @@ "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", "dev": true }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, "vinyl": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", @@ -14352,6 +15962,21 @@ } } }, + "web-push-testing": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.0.0.tgz", + "integrity": "sha512-p14iWiQSPY4kjpbaz59QfczaxEyD4uWg1aBjUS+Y9LAzQPU4Z95Jnq3FiOIazrvrfY0/1uDBbQkjXVxHq6wI/Q==", + "dev": true, + "requires": { + "arg": "^5.0.1", + "express": "^4.17.2", + "http_ece": "^1.1.0", + "jsonwebtoken": "^8.5.1", + "node-persist": "^2.1.0", + "ps-node": "^0.1.6", + "urlsafe-base64": "^1.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 932fb51f6d..22103522f3 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "postcss": "^8.4.31", "postcss-sorting": "^7.0.1", "stylelint": "^14.7.0", - "stylelint-order": "^5.0.0" + "stylelint-order": "^5.0.0", + "web-push-testing": "^1.0.0" } } From ec3dc28b5107b19fc409dc8d02268e918e2d11cc Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 18 Sep 2022 21:29:25 +0200 Subject: [PATCH 11/63] [ticket/17010] Switch to using table for tracking push subscriptions PHPBB3-17010 --- .../container/services_notification.yml | 1 + phpBB/config/default/container/tables.yml | 1 + .../db/migration/data/v400/add_webpush.php | 20 ++++-- phpBB/phpbb/notification/method/webpush.php | 62 ++++++++++++++----- 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/phpBB/config/default/container/services_notification.yml b/phpBB/config/default/container/services_notification.yml index ebde7bcf5a..c018416da3 100644 --- a/phpBB/config/default/container/services_notification.yml +++ b/phpBB/config/default/container/services_notification.yml @@ -255,3 +255,4 @@ services: - '%core.root_path%' - '%core.php_ext%' - '%tables.notification_push%' + - '%tables.push_subscriptions%' diff --git a/phpBB/config/default/container/tables.yml b/phpBB/config/default/container/tables.yml index f3a4e1b4ab..8e38c63a4b 100644 --- a/phpBB/config/default/container/tables.yml +++ b/phpBB/config/default/container/tables.yml @@ -51,6 +51,7 @@ parameters: tables.profile_fields_data: '%core.table_prefix%profile_fields_data' tables.profile_fields_options_language: '%core.table_prefix%profile_fields_lang' tables.profile_fields_language: '%core.table_prefix%profile_lang' + tables.push_subscriptions: '%core.table_prefix%push_subscriptions' tables.ranks: '%core.table_prefix%ranks' tables.reports: '%core.table_prefix%reports' tables.reports_reasons: '%core.table_prefix%reports_reasons' diff --git a/phpBB/phpbb/db/migration/data/v400/add_webpush.php b/phpBB/phpbb/db/migration/data/v400/add_webpush.php index abc0ffc079..f426df5b9b 100644 --- a/phpBB/phpbb/db/migration/data/v400/add_webpush.php +++ b/phpBB/phpbb/db/migration/data/v400/add_webpush.php @@ -43,13 +43,18 @@ class add_webpush extends migration ], 'PRIMARY_KEY' => ['notification_type_id', 'item_id', 'item_parent_id', 'user_id'], ], - ], - 'add_columns' => [ - $this->table_prefix . 'users' => [ + $this->table_prefix . 'push_subscriptions' => [ 'COLUMNS' => [ - 'user_push_subscriptions' => ['MTEXT_UNI', ''] + 'subscription_id' => ['ULINT', null, 'auto_increment'], + 'user_id' => ['ULINT', 0], + 'device_name' => ['VCHAR:64', ''], + 'endpoint' => ['TEXT', ''], + 'p256dh' => ['VCHAR', ''], + 'auth' => ['VCHAR', ''], + 'encoding' => ['VCHAR:32', ''], ], - ], + 'PRIMARY_KEY' => ['subscription_id', 'user_id'], + ] ], ]; } @@ -57,7 +62,10 @@ class add_webpush extends migration public function revert_schema(): array { return [ - 'drop_tables' => [$this->table_prefix . 'notification_push'], + 'drop_tables' => [ + $this->table_prefix . 'notification_push', + $this->table_prefix . 'push_subscriptions', + ], ]; } diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 2532246624..0fc704e8de 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -39,6 +39,9 @@ class webpush extends \phpbb\notification\method\messenger_base /** @var string Notification web push table */ protected $notification_webpush_table; + /** @var string Notification push subscriptions table */ + protected $push_subscriptions_table; + /** * Notification Method web push constructor * @@ -49,8 +52,10 @@ class webpush extends \phpbb\notification\method\messenger_base * @param string $phpbb_root_path * @param string $php_ext * @param string $notification_webpush_table + * @param string $push_subscriptions_table */ - public function __construct(user_loader $user_loader, user $user, config $config, driver_interface $db, string $phpbb_root_path, string $php_ext, string $notification_webpush_table) + public function __construct(user_loader $user_loader, user $user, config $config, driver_interface $db,string $phpbb_root_path, + string $php_ext, string $notification_webpush_table, string $push_subscriptions_table) { parent::__construct($user_loader, $phpbb_root_path, $php_ext); @@ -58,6 +63,7 @@ class webpush extends \phpbb\notification\method\messenger_base $this->config = $config; $this->db = $db; $this->notification_webpush_table = $notification_webpush_table; + $this->push_subscriptions_table = $push_subscriptions_table; } /** @@ -148,9 +154,36 @@ class webpush extends \phpbb\notification\method\messenger_base $banned_users = phpbb_get_banned_user_ids($user_ids); // Load all the users we need - $this->user_loader->load_users(array_diff($user_ids, $banned_users), array(USER_IGNORE)); + $notify_users = array_diff($user_ids, $banned_users); + $this->user_loader->load_users($notify_users, array(USER_IGNORE)); - $web_push = new \Minishlink\WebPush\WebPush(); + // Get subscriptions for users + $user_subscription_map = []; + $sql = 'SELECT * FROM ' . $this->push_subscriptions_table . ' + WHERE ' . $this->db->sql_in_set('user_id', $notify_users) . ' + GROUP BY user_id'; + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + if (isset($user_subscriptions[$row['user_id']])) + { + $user_subscription_map[$row['user_id']] += $row; + } + else + { + $user_subscription_map[$row['user_id']] = [$row]; + } + } + + $auth = [ + 'VAPID' => [ + 'subject' => generate_board_url(false), + 'publicKey' => $this->config['webpush_vapid_public'], + 'privateKey' => $this->config['webpush_vapid_private'], + ], + ]; + + $web_push = new \Minishlink\WebPush\WebPush($auth); // Time to go through the queue and send emails /** @var type_interface $notification */ @@ -158,7 +191,7 @@ class webpush extends \phpbb\notification\method\messenger_base { $user = $this->user_loader->get_user($notification->user_id); - $user_subscriptions = sanitizer::decode($this->user->data['user_push_subscriptions']); + $user_subscriptions = $user_subscription_map[$notification->user_id] ?? []; if ($user['user_type'] == USER_INACTIVE && $user['user_inactive_reason'] == INACTIVE_MANUAL || empty($user_subscriptions)) @@ -181,8 +214,17 @@ class webpush extends \phpbb\notification\method\messenger_base { try { - $push_subscription = \Minishlink\WebPush\Subscription::create($subscription); - $web_push->queueNotification($push_subscription, $json_data); + $push_subscription = \Minishlink\WebPush\Subscription::create([ + 'endpoint' => $subscription['endpoint'], + 'keys' => [ + 'p256dh' => $subscription['p256dh'], + 'auth' => $subscription['auth'], + ], + 'contentEncoding' => !empty($subscription['encoding']) ? $subscription['encoding'] : null, + ]); + //$web_push->queueNotification($push_subscription, $json_data); + $foo = $web_push->sendOneNotification($push_subscription, $json_data); + $meh = 2; } catch (\ErrorException $exception) { @@ -193,14 +235,6 @@ class webpush extends \phpbb\notification\method\messenger_base } // @todo: Try offloading to after request - try - { - $web_push->flush(); - } - catch (\ErrorException $exception) - { - // @todo: Add to error log if we can't flush ... - } // We're done, empty the queue $this->empty_queue(); From be21479b2a591e98526bb9d5862c3f0ba9e080f9 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 18 Sep 2022 21:30:01 +0200 Subject: [PATCH 12/63] [ticket/17010] Add tests for push notification type PHPBB3-17010 --- tests/notification/base.php | 1 + .../webpush_notification.type.post.xml | 284 ++++++++++++ .../notification_method_email_test.php | 1 + .../notification_method_webpush_test.php | 433 ++++++++++++++++++ tests/notification/submit_post_base.php | 1 + 5 files changed, 720 insertions(+) create mode 100644 tests/notification/fixtures/webpush_notification.type.post.xml create mode 100644 tests/notification/notification_method_webpush_test.php diff --git a/tests/notification/base.php b/tests/notification/base.php index eb888cb0ae..37718a85a9 100644 --- a/tests/notification/base.php +++ b/tests/notification/base.php @@ -125,6 +125,7 @@ abstract class phpbb_tests_notification_base extends phpbb_database_test_case $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types'); $phpbb_container->setParameter('tables.notification_emails', 'phpbb_notification_emails'); $phpbb_container->setParameter('tables.notification_push', 'phpbb_notification_push'); + $phpbb_container->setParameter('tables.push_subscriptions', 'phpbb_push_subscriptions'); $this->notifications = new phpbb_notification_manager_helper( array(), diff --git a/tests/notification/fixtures/webpush_notification.type.post.xml b/tests/notification/fixtures/webpush_notification.type.post.xml new file mode 100644 index 0000000000..7654ed30aa --- /dev/null +++ b/tests/notification/fixtures/webpush_notification.type.post.xml @@ -0,0 +1,284 @@ + + + + forum_id + user_id + notify_status + + 1 + 6 + 0 + + + 1 + 7 + 0 + + + 1 + 8 + 0 + +
+ + notification_id + notification_type_id + user_id + item_id + item_parent_id + notification_read + notification_data + + 1 + 1 + 5 + 1 + 1 + 0 + + + + 2 + 1 + 8 + 1 + 1 + 0 + + +
+ +
+ + notification_type_id + notification_type_name + notification_type_enabled + + 1 + notification.type.post + 1 + + + 2 + notification.type.forum + 1 + +
+ + post_id + topic_id + forum_id + post_text + + 1 + 1 + 1 + + +
+ + subscription_id + user_id + endpoint +
+ + topic_id + forum_id + + 1 + 1 + + + 2 + 1 + +
+ + topic_id + user_id + notify_status + + 1 + 2 + 0 + + + 2 + 2 + 0 + + + 1 + 3 + 0 + + + 1 + 4 + 0 + + + 1 + 5 + 0 + + + 1 + 6 + 0 + +
+ + user_id + username_clean + user_permissions + user_sig + + 2 + poster + + + + + 3 + test + + + + + 4 + unauthorized + + + + + 5 + notified + + + + + 6 + disabled + + + + + 7 + default + + + + + 8 + latest + + + +
+ + item_type + item_id + user_id + method + notify + + notification.type.post + 0 + 2 + notification.method.webpush + 1 + + + notification.type.post + 0 + 3 + notification.method.webpush + 1 + + + notification.type.post + 0 + 4 + notification.method.webpush + 1 + + + notification.type.post + 0 + 5 + notification.method.webpush + 1 + + + notification.type.post + 0 + 6 + notification.method.webpush + 1 + + + notification.type.post + 0 + 7 + notification.method.webpush + 1 + + + notification.type.post + 0 + 8 + notification.method.webpush + 1 + + + notification.type.forum + 0 + 2 + notification.method.webpush + 1 + + + notification.type.forum + 0 + 3 + notification.method.webpush + 1 + + + notification.type.forum + 0 + 4 + notification.method.webpush + 1 + + + notification.type.forum + 0 + 5 + notification.method.webpush + 1 + + + notification.type.forum + 0 + 6 + notification.method.webpush + 1 + + + notification.type.forum + 0 + 7 + notification.method.webpush + 1 + + + notification.type.forum + 0 + 8 + notification.method.webpush + 1 + +
+
diff --git a/tests/notification/notification_method_email_test.php b/tests/notification/notification_method_email_test.php index efcac83035..77db505c04 100644 --- a/tests/notification/notification_method_email_test.php +++ b/tests/notification/notification_method_email_test.php @@ -92,6 +92,7 @@ class notification_method_email_test extends phpbb_tests_notification_base $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types'); $phpbb_container->setParameter('tables.notification_emails', 'phpbb_notification_emails'); $phpbb_container->setParameter('tables.notification_push', 'phpbb_notification_push'); + $phpbb_container->setParameter('tables.push_subscriptions', 'phpbb_push_subscriptions'); $phpbb_container->set( 'text_formatter.s9e.mention_helper', new \phpbb\textformatter\s9e\mention_helper( diff --git a/tests/notification/notification_method_webpush_test.php b/tests/notification/notification_method_webpush_test.php new file mode 100644 index 0000000000..90e1d14deb --- /dev/null +++ b/tests/notification/notification_method_webpush_test.php @@ -0,0 +1,433 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +use phpbb\notification\method\webpush; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; + +require_once __DIR__ . '/base.php'; + +/** + * @group + */ +class notification_method_webpush_test extends phpbb_tests_notification_base +{ + /** @var string[] VAPID keys for testing purposes */ + public const VAPID_KEYS = [ + 'publicKey' => 'BIcGkq1Ncj3a2-J0UW-1A0NETLjvxZzNLiYBiPVMKNjgwmwPi5jyK87VfS4FZn9n7S9pLMQzjV3LmFuOnRSOvmI', + 'privateKey' => 'SrlbBEVgibWmKHYbDPu4Y2XvDWPjeGcc9fC16jq01xU', + ]; + + /** @var webpush */ + protected $notification_method_webpush; + + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/webpush_notification.type.post.xml'); + } + + protected function get_notification_methods() + { + return [ + 'notification.method.webpush', + ]; + } + + public static function setUpBeforeClass(): void + { + self::start_webpush_testing(); + } + + public static function tearDownAfterClass(): void + { + self::stop_webpush_testing(); + } + + protected static function start_webpush_testing(): void + { + // Stop first to ensure port is available + self::stop_webpush_testing(); + + $process = new \Symfony\Component\Process\Process(['node_modules/.bin/web-push-testing', '--port', '9012', 'start']); + $process->run(); + } + + protected static function stop_webpush_testing(): void + { + $process = new \Symfony\Component\Process\Process(['node_modules/.bin/web-push-testing', '--port', '9012', 'stop']); + $process->run(); + } + + protected function setUp(): void + { + phpbb_database_test_case::setUp(); + + global $phpbb_root_path, $phpEx; + + include_once(__DIR__ . '/ext/test/notification/type/test.' . $phpEx); + + global $db, $config, $user, $auth, $cache, $phpbb_container, $phpbb_dispatcher; + + $avatar_helper = $this->getMockBuilder('\phpbb\avatar\helper') + ->disableOriginalConstructor() + ->getMock(); + $db = $this->db = $this->new_dbal(); + $config = $this->config = new \phpbb\config\config([ + 'allow_privmsg' => true, + 'allow_bookmarks' => true, + 'allow_topic_notify' => true, + 'allow_forum_notify' => true, + 'allow_board_notifications' => true, + 'webpush_vapid_public' => self::VAPID_KEYS['publicKey'], + 'webpush_vapid_private' => self::VAPID_KEYS['privateKey'], + ]); + $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); + $lang = new \phpbb\language\language($lang_loader); + $user = new \phpbb\user($lang, '\phpbb\datetime'); + $this->user = $user; + $this->user_loader = new \phpbb\user_loader($avatar_helper, $this->db, $phpbb_root_path, $phpEx, 'phpbb_users'); + $auth = $this->auth = new phpbb_mock_notifications_auth(); + $this->phpbb_dispatcher = new phpbb_mock_event_dispatcher(); + $phpbb_dispatcher = $this->phpbb_dispatcher; + $cache_driver = new \phpbb\cache\driver\dummy(); + $cache = $this->cache = new \phpbb\cache\service( + $cache_driver, + $this->config, + $this->db, + $this->phpbb_dispatcher, + $phpbb_root_path, + $phpEx + ); + + $phpbb_container = $this->container = new ContainerBuilder(); + $loader = new YamlFileLoader($phpbb_container, new FileLocator(__DIR__ . '/fixtures')); + $loader->load('services_notification.yml'); + $phpbb_container->set('user_loader', $this->user_loader); + $phpbb_container->set('user', $user); + $phpbb_container->set('language', $lang); + $phpbb_container->set('config', $this->config); + $phpbb_container->set('dbal.conn', $this->db); + $phpbb_container->set('auth', $auth); + $phpbb_container->set('cache.driver', $cache_driver); + $phpbb_container->set('cache', $cache); + $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils()); + $phpbb_container->set('dispatcher', $this->phpbb_dispatcher); + $phpbb_container->setParameter('core.root_path', $phpbb_root_path); + $phpbb_container->setParameter('core.php_ext', $phpEx); + $phpbb_container->setParameter('tables.notifications', 'phpbb_notifications'); + $phpbb_container->setParameter('tables.user_notifications', 'phpbb_user_notifications'); + $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types'); + $phpbb_container->setParameter('tables.notification_emails', 'phpbb_notification_emails'); + $phpbb_container->setParameter('tables.notification_push', 'phpbb_notification_push'); + $phpbb_container->setParameter('tables.push_subscriptions', 'phpbb_push_subscriptions'); + $phpbb_container->set( + 'text_formatter.s9e.mention_helper', + new \phpbb\textformatter\s9e\mention_helper( + $this->db, + $auth, + $this->user, + $phpbb_root_path, + $phpEx + ) + ); + + $this->notification_method_webpush = new \phpbb\notification\method\webpush( + $phpbb_container->get('user_loader'), + $phpbb_container->get('user'), + $phpbb_container->get('config'), + $phpbb_container->get('dbal.conn'), + $phpbb_root_path, + $phpEx, + $phpbb_container->getParameter('tables.notification_push'), + $phpbb_container->getParameter('tables.push_subscriptions') + ); + + $phpbb_container->set('notification.method.webpush', $this->notification_method_webpush); + + $this->notifications = new phpbb_notification_manager_helper( + array(), + array(), + $this->container, + $this->user_loader, + $this->phpbb_dispatcher, + $this->db, + $this->cache, + $lang, + $this->user, + 'phpbb_notification_types', + 'phpbb_user_notifications' + ); + + $phpbb_container->set('notification_manager', $this->notifications); + + $phpbb_container->addCompilerPass(new phpbb\di\pass\markpublic_pass()); + + $phpbb_container->compile(); + + $this->notifications->setDependencies($this->auth, $this->config); + + $types = array(); + foreach ($this->get_notification_types() as $type) + { + $class = $this->build_type($type); + + $types[$type] = $class; + } + + $this->notifications->set_var('notification_types', $types); + + $methods = array(); + foreach ($this->get_notification_methods() as $method) + { + $class = $this->container->get($method); + + $methods[$method] = $class; + } + + $this->notifications->set_var('notification_methods', $methods); + } + + public function data_notification_webpush() + { + return [ + /** + * Normal post + * + * User => State description + * 2 => Topic id=1 and id=2 subscribed, should receive a new topics post notification + * 3 => Topic id=1 subscribed, should receive a new topic post notification + * 4 => Topic id=1 subscribed, should receive a new topic post notification + * 5 => Topic id=1 subscribed, post id=1 already notified, should receive a new topic post notification + * 6 => Topic id=1 and forum id=1 subscribed, should receive a new topic/forum post notification + * 7 => Forum id=1 subscribed, should NOT receive a new topic post but a forum post notification + * 8 => Forum id=1 subscribed, post id=1 already notified, should NOT receive a new topic post but a forum post notification + */ + [ + 'notification.type.post', + [ + 'forum_id' => '1', + 'post_id' => '2', + 'topic_id' => '1', + ], + [ + 2 => ['user_id' => '2'], + 3 => ['user_id' => '3'], + 4 => ['user_id' => '4'], + 5 => ['user_id' => '5'], + 6 => ['user_id' => '6'], + ], + ], + [ + 'notification.type.forum', + [ + 'forum_id' => '1', + 'post_id' => '3', + 'topic_id' => '1', + ], + [ + 6 => ['user_id' => '6'], + 7 => ['user_id' => '7'], + 8 => ['user_id' => '8'] + ], + ], + [ + 'notification.type.post', + [ + 'forum_id' => '1', + 'post_id' => '4', + 'topic_id' => '2', + ], + [ + 2 => ['user_id' => '2'], + ], + ], + [ + 'notification.type.forum', + [ + 'forum_id' => '1', + 'post_id' => '5', + 'topic_id' => '2', + ], + [ + 6 => ['user_id' => '6'], + 7 => ['user_id' => '7'], + 8 => ['user_id' => '8'], + ], + ], + [ + 'notification.type.post', + [ + 'forum_id' => '2', + 'post_id' => '6', + 'topic_id' => '3', + ], + [ + ], + ], + [ + 'notification.type.forum', + [ + 'forum_id' => '2', + 'post_id' => '6', + 'topic_id' => '3', + ], + [ + ], + ], + ]; + } + + /** + * @dataProvider data_notification_webpush + */ + public function test_notification_webpush($notification_type, $post_data, $expected_users) + { + $post_data = array_merge([ + 'post_time' => 1349413322, + 'poster_id' => 1, + 'topic_title' => '', + 'post_subject' => '', + 'post_username' => '', + 'forum_name' => '', + ], + + $post_data); + $notification_options = [ + 'item_id' => $post_data['post_id'], + 'item_parent_id' => $post_data['topic_id'], + ]; + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet'); + + $this->notifications->add_notifications($notification_type, $post_data); + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified'); + + $post_data['post_id']++; + $notification_options['item_id'] = $post_data['post_id']; + $post_data['post_time'] = 1349413323; + + $this->notifications->add_notifications($notification_type, $post_data); + + $notified_users2 = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals($expected_users, $notified_users2, 'Assert that expected users stay the same after replying to same topic'); + } + + /** + * @dataProvider data_notification_webpush + */ + public function test_get_subscription($notification_type, $post_data, $expected_users): void + { + $subscription_info = []; + foreach ($expected_users as $user_id => $user_data) + { + $subscription_info[$user_id] = $this->create_subscription_for_user($user_id); + } + + $post_data = array_merge([ + 'post_time' => 1349413322, + 'poster_id' => 1, + 'topic_title' => '', + 'post_subject' => '', + 'post_username' => '', + 'forum_name' => '', + ], + + $post_data); + $notification_options = [ + 'item_id' => $post_data['post_id'], + 'item_parent_id' => $post_data['topic_id'], + ]; + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet'); + + foreach ($expected_users as $user_id => $data) + { + $messages = $this->get_messages_for_subscription($subscription_info[$user_id]['clientHash']); + $this->assertEmpty($messages); + } + + $this->notifications->add_notifications($notification_type, $post_data); + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified'); + + foreach ($expected_users as $user_id => $data) + { + $messages = $this->get_messages_for_subscription($subscription_info[$user_id]['clientHash']); + $this->assertNotEmpty($messages); + } + } + + protected function create_subscription_for_user($user_id): array + { + $client = new \GuzzleHttp\Client(); + try + { + $response = $client->request('POST', 'http://localhost:9012/subscribe', ['form_params' => [ + 'applicationServerKey' => self::VAPID_KEYS['publicKey'], + ]]); + } + catch (\GuzzleHttp\Exception\GuzzleException $exception) + { + $this->fail('Failed getting subscription from web-push-testing client'); + } + + $subscription_return = \phpbb\json\sanitizer::decode((string) $response->getBody()); + $subscription_data = $subscription_return['data']; + $this->assertNotEmpty($subscription_data['endpoint']); + $this->assertStringStartsWith('http://localhost:9012/notify/', $subscription_data['endpoint']); + $this->assertIsArray($subscription_data['keys']); + + // Add subscription data to admin user (user id 2) + + + $push_subscriptions_table = $this->container->getParameter('tables.push_subscriptions'); + + $sql = 'INSERT INTO ' . $push_subscriptions_table . ' ' . $this->db->sql_build_array('INSERT', [ + 'user_id' => $user_id, + 'endpoint' => $subscription_data['endpoint'], + 'p256dh' => $subscription_data['keys']['p256dh'], + 'auth' => $subscription_data['keys']['auth'], + ]); + $this->db->sql_query($sql); + + return $subscription_data; + } + + protected function get_messages_for_subscription($client_hash): array + { + $client = new \GuzzleHttp\Client(); + try + { + $response = $client->request('POST', 'http://localhost:9012/get-notifications', ['form_params' => [ + 'clientHash' => $client_hash, + ]]); + } + catch (\GuzzleHttp\Exception\GuzzleException $exception) + { + $this->fail('Failed getting messages from web-push-testing client'); + } + + $response_data = json_decode($response->getBody()->getContents(), true); + $this->assertNotEmpty($response_data); + $this->assertArrayHasKey('data', $response_data); + $this->assertArrayHasKey('messages', $response_data['data']); + + return $response_data['data']['messages']; + } +} diff --git a/tests/notification/submit_post_base.php b/tests/notification/submit_post_base.php index cb5b8d0d2a..3219c85e27 100644 --- a/tests/notification/submit_post_base.php +++ b/tests/notification/submit_post_base.php @@ -155,6 +155,7 @@ abstract class phpbb_notification_submit_post_base extends phpbb_database_test_c $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types'); $phpbb_container->setParameter('tables.notification_emails', 'phpbb_notification_emails'); $phpbb_container->setParameter('tables.notification_push', 'phpbb_notification_push'); + $phpbb_container->setParameter('tables.push_subscriptions', 'phpbb_push_subscriptions'); $phpbb_container->set('content.visibility', new \phpbb\content_visibility($auth, $config, $phpbb_dispatcher, $db, $user, $phpbb_root_path, $phpEx, FORUMS_TABLE, POSTS_TABLE, TOPICS_TABLE, USERS_TABLE)); $phpbb_container->addCompilerPass(new phpbb\di\pass\markpublic_pass()); $phpbb_container->compile(); From af29f388dab44a8409fb548c2b17dc7a31dbbe71 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Mon, 19 Sep 2022 20:34:42 +0200 Subject: [PATCH 13/63] [ticket/17010] Queue notifications and use flush to send them PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 0fc704e8de..550631ec77 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -185,6 +185,7 @@ class webpush extends \phpbb\notification\method\messenger_base $web_push = new \Minishlink\WebPush\WebPush($auth); + $number_of_notifications = 0; // Time to go through the queue and send emails /** @var type_interface $notification */ foreach ($this->queue as $notification) @@ -222,9 +223,8 @@ class webpush extends \phpbb\notification\method\messenger_base ], 'contentEncoding' => !empty($subscription['encoding']) ? $subscription['encoding'] : null, ]); - //$web_push->queueNotification($push_subscription, $json_data); - $foo = $web_push->sendOneNotification($push_subscription, $json_data); - $meh = 2; + $web_push->queueNotification($push_subscription, $json_data); + $number_of_notifications++; } catch (\ErrorException $exception) { @@ -235,6 +235,21 @@ class webpush extends \phpbb\notification\method\messenger_base } // @todo: Try offloading to after request + try + { + foreach ($web_push->flush($number_of_notifications) as $report) + { + if (!$report->isSuccess()) + { + // @todo: log errors / remove subscription + } + } + } + catch (\ErrorException $exception) + { + // @todo: write to log + } + // We're done, empty the queue $this->empty_queue(); From 5fba4682c34993b8780d389afac524f3c1f0ffc5 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 20 Sep 2022 17:14:15 +0200 Subject: [PATCH 14/63] [ticket/17010] Resolve phing sniff issues PHPBB3-17010 --- phpBB/phpbb/db/migration/data/v400/add_webpush.php | 1 - phpBB/phpbb/notification/method/webpush.php | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/phpBB/phpbb/db/migration/data/v400/add_webpush.php b/phpBB/phpbb/db/migration/data/v400/add_webpush.php index f426df5b9b..f2214fd934 100644 --- a/phpBB/phpbb/db/migration/data/v400/add_webpush.php +++ b/phpBB/phpbb/db/migration/data/v400/add_webpush.php @@ -13,7 +13,6 @@ namespace phpbb\db\migration\data\v400; -use Minishlink\WebPush\VAPID; use phpbb\db\migration\migration; class add_webpush extends migration diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 550631ec77..064ee4507f 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -15,7 +15,6 @@ namespace phpbb\notification\method; use phpbb\config\config; use phpbb\db\driver\driver_interface; -use phpbb\json\sanitizer; use phpbb\notification\type\type_interface; use phpbb\user; use phpbb\user_loader; @@ -54,7 +53,7 @@ class webpush extends \phpbb\notification\method\messenger_base * @param string $notification_webpush_table * @param string $push_subscriptions_table */ - public function __construct(user_loader $user_loader, user $user, config $config, driver_interface $db,string $phpbb_root_path, + public function __construct(user_loader $user_loader, user $user, config $config, driver_interface $db, string $phpbb_root_path, string $php_ext, string $notification_webpush_table, string $push_subscriptions_table) { parent::__construct($user_loader, $phpbb_root_path, $php_ext); @@ -250,7 +249,6 @@ class webpush extends \phpbb\notification\method\messenger_base // @todo: write to log } - // We're done, empty the queue $this->empty_queue(); } From c4a8e32689f624daa503109ef4045d802a460180 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 20 Sep 2022 17:20:12 +0200 Subject: [PATCH 15/63] [ticket/17010] Adjust query to not trigger full group by issue PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 064ee4507f..2d015600c3 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -158,9 +158,9 @@ class webpush extends \phpbb\notification\method\messenger_base // Get subscriptions for users $user_subscription_map = []; - $sql = 'SELECT * FROM ' . $this->push_subscriptions_table . ' - WHERE ' . $this->db->sql_in_set('user_id', $notify_users) . ' - GROUP BY user_id'; + $sql = 'SELECT user_id, endpoint, p256dh, auth, encoding + FROM ' . $this->push_subscriptions_table . ' + WHERE ' . $this->db->sql_in_set('user_id', $notify_users); $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) { From 9bd9962aeafd180fbac258a215968ebf95987ca2 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Thu, 22 Sep 2022 21:08:54 +0200 Subject: [PATCH 16/63] [ticket/17010] Resolve test issues and add node install for webpush testing PHPBB3-17010 --- .github/workflows/tests.yml | 31 +++++++++++++++++++ .../webpush_notification.type.post.xml | 2 ++ .../notification_method_webpush_test.php | 6 +++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 982ca66a2a..761d8e3e87 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -217,6 +217,15 @@ jobs: run: | .github/setup-ldap.sh + - name: Setup node + if: ${{ matrix.SLOWTESTS != 1 }} + uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Setup node dependencies + if: ${{ matrix.SLOWTESTS != 1 }} + run: npm ci + - name: Setup SPHINX run: | .github/setup-sphinx.sh @@ -342,6 +351,13 @@ jobs: run: | .github/setup-database.sh $DB $MYISAM + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Setup node dependencies + run: npm ci + - name: Run unit tests env: DB: ${{steps.database-type.outputs.db}} @@ -447,6 +463,13 @@ jobs: run: | .github/setup-database.sh $DB $MYISAM + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Setup node dependencies + run: npm ci + - name: Run unit tests env: DB: ${{steps.database-type.outputs.db}} @@ -555,6 +578,14 @@ jobs: psql -c 'create database phpbb_tests;' -U postgres Set-MpPreference -ExclusionPath "${env:PGDATA}" # Exclude PGDATA directory from Windows Defender Set-MpPreference -DisableRealtimeMonitoring $true + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Setup node dependencies + run: npm ci + - name: Run unit tests if: ${{ matrix.type == 'unit' }} run: | diff --git a/tests/notification/fixtures/webpush_notification.type.post.xml b/tests/notification/fixtures/webpush_notification.type.post.xml index 7654ed30aa..9d3d72d531 100644 --- a/tests/notification/fixtures/webpush_notification.type.post.xml +++ b/tests/notification/fixtures/webpush_notification.type.post.xml @@ -1,5 +1,7 @@ + +
forum_iduser_id diff --git a/tests/notification/notification_method_webpush_test.php b/tests/notification/notification_method_webpush_test.php index 90e1d14deb..f0e1f1cb50 100644 --- a/tests/notification/notification_method_webpush_test.php +++ b/tests/notification/notification_method_webpush_test.php @@ -61,6 +61,10 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $process = new \Symfony\Component\Process\Process(['node_modules/.bin/web-push-testing', '--port', '9012', 'start']); $process->run(); + if (!$process->isSuccessful()) + { + self::fail('Starting web push testing service failed: ' . $process->getErrorOutput()); + } } protected static function stop_webpush_testing(): void @@ -384,7 +388,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base } catch (\GuzzleHttp\Exception\GuzzleException $exception) { - $this->fail('Failed getting subscription from web-push-testing client'); + $this->fail('Failed getting subscription from web-push-testing client: ' . $exception->getMessage()); } $subscription_return = \phpbb\json\sanitizer::decode((string) $response->getBody()); From 199bc8f964fcdb3da2be089389cdf5f0950c3dd9 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 25 Sep 2022 20:23:21 +0200 Subject: [PATCH 17/63] [ticket/17010] Add first webpush controller and start ucp integration PHPBB3-17010 --- .../container/services_notification.yml | 2 + .../config/default/container/services_ucp.yml | 5 ++ phpBB/config/default/routing/ucp.yml | 8 +++ phpBB/includes/ucp/ucp_notifications.php | 29 +++++++- phpBB/language/en/ucp.php | 4 ++ phpBB/phpbb/ucp/controller/webpush.php | 27 +++++++ .../prosilver/template/ucp_notifications.html | 34 --------- .../template/ucp_notifications_options.html | 71 +++++++++++++++++++ 8 files changed, 145 insertions(+), 35 deletions(-) create mode 100644 phpBB/phpbb/ucp/controller/webpush.php create mode 100644 phpBB/styles/prosilver/template/ucp_notifications_options.html diff --git a/phpBB/config/default/container/services_notification.yml b/phpBB/config/default/container/services_notification.yml index c018416da3..acb136641c 100644 --- a/phpBB/config/default/container/services_notification.yml +++ b/phpBB/config/default/container/services_notification.yml @@ -256,3 +256,5 @@ services: - '%core.php_ext%' - '%tables.notification_push%' - '%tables.push_subscriptions%' + tags: + - { name: notification.method } diff --git a/phpBB/config/default/container/services_ucp.yml b/phpBB/config/default/container/services_ucp.yml index 69ef5ffbf8..1d0535a116 100644 --- a/phpBB/config/default/container/services_ucp.yml +++ b/phpBB/config/default/container/services_ucp.yml @@ -15,3 +15,8 @@ services: - '%tables.users%' - '%core.root_path%' - '%core.php_ext%' + + phpbb.ucp.controller.push_worker: + class: phpbb\ucp\controller\webpush + arguments: + - '@config' diff --git a/phpBB/config/default/routing/ucp.yml b/phpBB/config/default/routing/ucp.yml index 06bd7c3a58..a88b6fc5ae 100644 --- a/phpBB/config/default/routing/ucp.yml +++ b/phpBB/config/default/routing/ucp.yml @@ -5,3 +5,11 @@ phpbb_ucp_reset_password_controller: phpbb_ucp_forgot_password_controller: path: /forgot_password defaults: { _controller: phpbb.ucp.controller.reset_password:request } + +phpbb_ucp_push_worker_controller: + path: /push/worker + defaults: { _controller: phpbb.ucp.controller.webpush:request } + +phpbb_ucp_push_subscribe_controller: + path: /push/subscribe + defaults: { _controller: phpbb.ucp.controller.webpush:request } diff --git a/phpBB/includes/ucp/ucp_notifications.php b/phpBB/includes/ucp/ucp_notifications.php index 7a46d3e5aa..7b07138595 100644 --- a/phpBB/includes/ucp/ucp_notifications.php +++ b/phpBB/includes/ucp/ucp_notifications.php @@ -107,7 +107,12 @@ class ucp_notifications $this->output_notification_types($subscriptions, $phpbb_notifications, $template, $user, $phpbb_dispatcher, 'notification_types'); - $this->tpl_name = 'ucp_notifications'; + /** @var \phpbb\controller\helper $controller_helper */ + $controller_helper = $phpbb_container->get('controller.helper'); + + $template->assign_var('U_WEBPUSH_SUBSCRIBE', $controller_helper->route('phpbb_ucp_push_subscribe_controller')); + + $this->tpl_name = 'ucp_notifications_options'; $this->page_title = 'UCP_NOTIFICATION_OPTIONS'; break; @@ -266,6 +271,11 @@ class ucp_notifications { $notification_methods = $phpbb_notifications->get_subscription_methods(); + if (isset($notification_methods['notification.method.webpush'])) + { + $this->output_webpush_data($template); + } + foreach ($notification_methods as $method => $method_data) { $template->assign_block_vars($block, array( @@ -275,4 +285,21 @@ class ucp_notifications )); } } + + /** + * Output data for webpush + * + * @param \phpbb\template\template $template + * + * @return void + */ + protected function output_webpush_data(\phpbb\template\template $template): void + { + global $config; + + $template->assign_vars([ + 'NOTIFICATIONS_WEBPUSH_ENABLE' => true, // already checked, otherwise we wouldn't be here + 'NOTIFICATIONS_WEBPUSH_VAPID_PUBLIC' => $config['webpush_vapid_public'], + ]); + } } diff --git a/phpBB/language/en/ucp.php b/phpBB/language/en/ucp.php index a4dcdf775d..09bf7824e5 100644 --- a/phpBB/language/en/ucp.php +++ b/phpBB/language/en/ucp.php @@ -332,6 +332,7 @@ $lang = array_merge($lang, array( 'NOTIFICATION_METHOD_BOARD' => 'Notifications', 'NOTIFICATION_METHOD_EMAIL' => 'Email', 'NOTIFICATION_METHOD_JABBER' => 'Jabber', + 'NOTIFICATION_METHOD_WEBPUSH' => 'Web push', 'NOTIFICATION_TYPE' => 'Notification type', 'NOTIFICATION_TYPE_BOOKMARK' => 'Someone replies to a topic you have bookmarked', 'NOTIFICATION_TYPE_GROUP_REQUEST' => 'Someone requests to join a group you lead', @@ -355,6 +356,9 @@ $lang = array_merge($lang, array( 'NOTIFY_METHOD_EXPLAIN' => 'Method for sending messages sent via this board.', 'NOTIFY_METHOD_IM' => 'Jabber only', 'NOTIFY_ON_PM' => 'Notify me on new private messages', + 'NOTIFY_WEBPUSH_ACTIVATE' => 'Activate push notifications', + 'NOTIFY_WEBPUSH_ENABLE' => 'Enable receiving webpush notifications', + 'NOTIFY_WEBPUSH_ENABLE_EXPLAIN' => 'Enable receiving browser-based push notifications.
The notifications can be turned off at any time in your browser settings or by disabling the push notifications below.', 'NOT_ADDED_FRIENDS_ANONYMOUS' => 'You cannot add the anonymous user to your friends list.', 'NOT_ADDED_FRIENDS_BOTS' => 'You cannot add bots to your friends list.', 'NOT_ADDED_FRIENDS_FOES' => 'You cannot add users to your friends list who are on your foes list.', diff --git a/phpBB/phpbb/ucp/controller/webpush.php b/phpBB/phpbb/ucp/controller/webpush.php new file mode 100644 index 0000000000..67ef237d5b --- /dev/null +++ b/phpBB/phpbb/ucp/controller/webpush.php @@ -0,0 +1,27 @@ +config = $config; + } + + /** + * Handle password reset request + * + * @return Response + */ + public function request(): Response + { + return new Response('foo'); + } +} diff --git a/phpBB/styles/prosilver/template/ucp_notifications.html b/phpBB/styles/prosilver/template/ucp_notifications.html index d7019dee1c..7c5715fb41 100644 --- a/phpBB/styles/prosilver/template/ucp_notifications.html +++ b/phpBB/styles/prosilver/template/ucp_notifications.html @@ -12,38 +12,6 @@

{TITLE_EXPLAIN}

- - -
- - - - - - - - - - - - - - - - - - - - - - - - -
{L_NOTIFICATION_TYPE}{notification_methods.NAME}
{notification_types.GROUP_NAME}
- {notification_types.NAME} -
   {notification_types.EXPLAIN} -
checked="checked" disabled="disabled" />
-
diff --git a/phpBB/styles/prosilver/template/ucp_notifications_options.html b/phpBB/styles/prosilver/template/ucp_notifications_options.html new file mode 100644 index 0000000000..6536fb56cd --- /dev/null +++ b/phpBB/styles/prosilver/template/ucp_notifications_options.html @@ -0,0 +1,71 @@ +{% include('ucp_header.html') %} + +
+ +

{{ TITLE }}

+ {% if NOTIFICATIONS_WEBPUSH_ENABLE %} +
+
+
+
+

{{ lang('NOTIFY_WEBPUSH_ENABLE_EXPLAIN') }}
+
+ {{ lang('NOTIFY_WEBPUSH_ACTIVATE') }} +
+
+
+
+
+ {% endif %} +
+
+

{{ TITLE_EXPLAIN }}

+ + + + + + {% for method in notification_methods %} + + {% endfor %} + + + + {% for notification_type in notification_types %} + {% if notification_type.GROUP_NAME %} + + + + {% else %} + + + {% for notification_method in notification_type.notification_methods %} + {% apply spaceless %} + + {% endapply %} + {% endfor %} + + {% endif %} + {% endfor %} + +
{{ lang('NOTIFICATION_TYPE') }}{{ method.NAME }}
{{ notification_type.GROUP_NAME }}
+ {{ notification_type.NAME }} + {% if notification_type.EXPLAIN %}
   {{ notification_type.EXPLAIN }}{% endif %} +
+
+
+
+ + {% if notification_types or notification_list %} +
+ + {{ S_HIDDEN_FIELDS }} + + + {{ S_FORM_TOKEN }} +
+ {% endif %} + +
+ +{% include('ucp_footer.html') %} From ff27401ed2e3eae715fc2386c39c168117023120 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 22 Oct 2022 22:20:39 +0200 Subject: [PATCH 18/63] [ticket/17010] Add javascript for webpush subscribe/unsubscribe PHPBB3-17010 --- phpBB/styles/all/js/webpush.js.twig | 243 ++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 phpBB/styles/all/js/webpush.js.twig diff --git a/phpBB/styles/all/js/webpush.js.twig b/phpBB/styles/all/js/webpush.js.twig new file mode 100644 index 0000000000..1287f133d9 --- /dev/null +++ b/phpBB/styles/all/js/webpush.js.twig @@ -0,0 +1,243 @@ +/* global phpbb */ + +'use strict'; + +function PhpbbWebpush() { + /** @type {string} URL to service worker */ + const serviceWorkerUrl = '{{ U_WEBPUSH_WORKER_URL }}'; + + /** @type {string} URL to subscribe to push */ + const subscribeUrl = '{{ U_WEBPUSH_SUBSCRIBE }}'; + + /** @type {string} URL to unsubscribe from push */ + const unsubscribeUrl = '{{ U_WEBPUSH_UNSUBSCRIBE }}'; + + /** @type {{creationTime: number, formToken: string}} Form tokens */ + this.formTokens = { + creationTime: {{ FORM_TOKENS.creation_time }}, + formToken: '{{ FORM_TOKENS.form_token }}' + }; + + /** @type {string} VAPID public key */ + const VAPID_PUBLIC_KEY = '{{ VAPID_PUBLIC_KEY }}'; + + let subscribeButton, + unsubscribeButton; + + /** + * Init function for phpBB webpush + */ + this.init = function() { + subscribeButton = document.querySelector('#subscribe_webpush'); + unsubscribeButton = document.querySelector('#unsubscribe_webpush'); + let serviceWorkerRegistered = false; + + // Service workers are only supported in secure context + if (window.isSecureContext !== true) { + subscribeButton.disabled = true; + return; + } + + if ('serviceWorker' in navigator && 'PushManager' in window) { + navigator.serviceWorker.register(serviceWorkerUrl) + .then(() => { + serviceWorkerRegistered = true; + }) + .catch(error => { + console.info(error); + }); + } + + if (serviceWorkerRegistered) { + subscribeButton.addEventListener('click', subscribeButtonHandler); + unsubscribeButton.addEventListener('click', unsubscribeButtonHandler); + + updateButtonState(); + } else { + // Service worker could not be registered + subscribeButton.disabled = true; + } + }; + + /** + * Update button state depending on notifications state + * + * @return void + */ + function updateButtonState() { + if (Notification.permission === 'granted') { + navigator.serviceWorker.getRegistration(serviceWorkerUrl) + .then((registration) => { + if (typeof registration === 'undefined') { + return; + } + + registration.pushManager.getSubscription() + .then((subscribed) => { + if (subscribed) { + setSubscriptionState(true); + } + }) + }); + } + } + + /** + * Set subscription state for buttons + * + * @param {boolean} subscribed True if subscribed, false if not + */ + function setSubscriptionState(subscribed) { + if (subscribed) { + subscribeButton.classList.add('hidden'); + unsubscribeButton.classList.remove('hidden'); + } else { + subscribeButton.classList.remove('hidden'); + unsubscribeButton.classList.add('hidden'); + } + } + + /** + * Handler for pushing subscribe button + * + * @param {Object} event Subscribe button push event + * @returns {Promise} + */ + async function subscribeButtonHandler(event) { + event.preventDefault(); + + subscribeButton.addEventListener('click', subscribeButtonHandler); + + // Prevent the user from clicking the subscribe button multiple times. + const result = await Notification.requestPermission(); + if (result === 'denied') { + return; + } + + const registration = await navigator.serviceWorker.getRegistration(serviceWorkerUrl); + if (typeof registration !== 'undefined') { + const subscribed = await registration.pushManager.getSubscription(); + if (subscribed) { + setSubscriptionState(true); + return; + } + } + const newSubscription = await registration.pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY) + }); + + fetch(subscribeUrl, { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest' + }, + body: getFormData(newSubscription) + }) + .then((response) => response.json()) + .then(handleSubscribe) + .catch((error) => { + phpbb.alert({{ lang('AJAX_ERROR_TITLE') }}, error); + }); + } + + /** + * Handler for pushing unsubscribe button + * + * @param {Object} event Unsubscribe button push event + * @returns {Promise} + */ + async function unsubscribeButtonHandler(event) { + event.preventDefault(); + + const registration = await navigator.serviceWorker.getRegistration(serviceWorkerUrl); + if (typeof registration === 'undefined') { + return; + } + + const subscription = await registration.pushManager.getSubscription(); + fetch(unsubscribeUrl, { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest' + }, + body: getFormData({endpoint: subscription.endpoint}) + }).then(() => { + return subscription.unsubscribe(); + }).then((unsubscribed) => { + if (unsubscribed) { + setSubscriptionState(false); + } + }); + } + + /** + * Handle subscribe response + * + * @param {Object} response Response from subscription endpoint + */ + function handleSubscribe(response) { + if (response.success) { + setSubscriptionState(true); + if (response.hasOwnProperty('form_tokens')) { + updateFormTokens(response.form_tokens); + } + } + } + + /** + * Get form data object including form tokens + * + * @param {Object} data Data to create form data from + * @returns {FormData} Form data + */ + function getFormData(data) { + let formData = new FormData(); + formData.append('form_token', phpbb.webpush.formTokens.formToken); + formData.append('creation_time', phpbb.webpush.formTokens.creationTime); + formData.append('data', JSON.stringify(data)); + + return formData; + } + + /** + * Update form tokens with supplied ones + * + * @param {Object} formTokens + */ + function updateFormTokens(formTokens) { + phpbb.webpush.formTokens.creationTime = formTokens.creation_time; + phpbb.webpush.formTokens.formToken = formTokens.form_token; + } + + /** + * Convert a base64 string to Uint8Array + * + * @param base64String + * @returns {Uint8Array} + */ + function urlB64ToUint8Array(base64String) { + const padding = '='.repeat((4 - base64String.length % 4) % 4); + const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); + const rawData = window.atob(base64); + const outputArray = new Uint8Array(rawData.length); + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i); + } + return outputArray; + } +} + +function domReady(callBack) { + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', callBack); + } else { + callBack(); + } +} + +phpbb.webpush = new PhpbbWebpush(); + +domReady(() => { + phpbb.webpush.init(); +}); From 0395c8dbac1f2005164afb8f425a4e3a849c1a00 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 22 Oct 2022 22:25:06 +0200 Subject: [PATCH 19/63] [ticket/17010] Introduce form_helper to allow using form tokens in javascript PHPBB3-17010 --- phpBB/phpbb/form/form_helper.php | 104 +++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 phpBB/phpbb/form/form_helper.php diff --git a/phpBB/phpbb/form/form_helper.php b/phpBB/phpbb/form/form_helper.php new file mode 100644 index 0000000000..ca4ca14b01 --- /dev/null +++ b/phpBB/phpbb/form/form_helper.php @@ -0,0 +1,104 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\form; + +use phpbb\config\config; +use phpbb\request\request_interface; +use phpbb\user; + +class form_helper +{ + /** @var config */ + protected $config; + + /** @var request_interface */ + protected $request; + + /** @var user */ + protected $user; + + /** + * Constructor for form_helper + * + * @param config $config + * @param request_interface $request + * @param user $user + */ + public function __construct(config $config, request_interface $request, user $user) + { + $this->config = $config; + $this->request = $request; + $this->user = $user; + } + + /** + * Get form tokens for form + * + * @param string $form_name Name of form + * @param int $now Token generation time + * @param string $token_sid SID used for form token + * @param string $token Generated token + * + * @return array Array containing form_token and creation_time of form token + */ + public function get_form_tokens(string $form_name, int &$now = 0, string &$token_sid = '', string &$token = ''): array + { + $now = time(); + $token_sid = ($this->user->data['user_id'] == ANONYMOUS && !empty($this->config['form_token_sid_guests'])) ? $this->user->session_id : ''; + $token = sha1($now . $this->user->data['user_form_salt'] . $form_name . $token_sid); + + return [ + 'creation_time' => $now, + 'form_token' => $token, + ]; + } + + /** + * Check form token for form + * + * @param string $form_name Name of form + * @param int|null $timespan Lifetime of token or null if default value should be used + * @return bool True if form token is valid, false if not + */ + public function check_form_tokens(string $form_name, ?int $timespan = null): bool + { + if ($timespan === null) + { + // we enforce a minimum value of half a minute here. + $timespan = ($this->config['form_token_lifetime'] == -1) ? -1 : max(30, $this->config['form_token_lifetime']); + } + + if ($this->request->is_set_post('creation_time') && $this->request->is_set_post('form_token')) + { + $creation_time = abs($this->request->variable('creation_time', 0)); + $token = $this->request->variable('form_token', ''); + + $diff = time() - $creation_time; + + // If creation_time and the time() now is zero we can assume it was not a human doing this (the check for if ($diff)... + if (defined('DEBUG_TEST') || $diff && ($diff <= $timespan || $timespan === -1)) + { + $token_sid = ($this->user->data['user_id'] == ANONYMOUS && !empty($this->config['form_token_sid_guests'])) ? $this->user->session_id : ''; + $key = sha1($creation_time . $this->user->data['user_form_salt'] . $form_name . $token_sid); + + if (hash_equals($key, $token)) + { + return true; + } + } + } + + return false; + } +} From 35259056f0403a1ee36fdfbc85501e3eb104b6a0 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 22 Oct 2022 22:26:18 +0200 Subject: [PATCH 20/63] [ticket/17010] Switch to using form_helper for form tokens PHPBB3-17010 --- phpBB/config/default/container/services.yml | 7 ++++ phpBB/includes/functions.php | 45 +++++---------------- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/phpBB/config/default/container/services.yml b/phpBB/config/default/container/services.yml index d34a1ded9f..14c0d40263 100644 --- a/phpBB/config/default/container/services.yml +++ b/phpBB/config/default/container/services.yml @@ -126,6 +126,13 @@ services: arguments: - '%core.root_path%' + form_helper: + class: phpbb\form\form_helper + arguments: + - '@config' + - '@request' + - '@user' + group_helper: class: phpbb\group\helper arguments: diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index 453e1d3150..2f330096bf 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -2009,16 +2009,14 @@ function check_link_hash($token, $link_name) */ function add_form_key($form_name, $template_variable_suffix = '') { - global $config, $template, $user, $phpbb_dispatcher; + global $phpbb_container, $phpbb_dispatcher, $template; - $now = time(); - $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : ''; - $token = sha1($now . $user->data['user_form_salt'] . $form_name . $token_sid); + /** @var \phpbb\form\form_helper $form_helper */ + $form_helper = $phpbb_container->get('form_helper'); - $s_fields = build_hidden_fields(array( - 'creation_time' => $now, - 'form_token' => $token, - )); + $form_tokens = $form_helper->get_form_tokens($form_name, $now, $token_sid, $token); + + $s_fields = build_hidden_fields($form_tokens); /** * Perform additional actions on creation of the form token @@ -2058,35 +2056,12 @@ function add_form_key($form_name, $template_variable_suffix = '') */ function check_form_key($form_name, $timespan = false) { - global $config, $request, $user; + global $phpbb_container; - if ($timespan === false) - { - // we enforce a minimum value of half a minute here. - $timespan = ($config['form_token_lifetime'] == -1) ? -1 : max(30, $config['form_token_lifetime']); - } + /** @var \phpbb\form\form_helper $form_helper */ + $form_helper = $phpbb_container->get('form_helper'); - if ($request->is_set_post('creation_time') && $request->is_set_post('form_token')) - { - $creation_time = abs($request->variable('creation_time', 0)); - $token = $request->variable('form_token', ''); - - $diff = time() - $creation_time; - - // If creation_time and the time() now is zero we can assume it was not a human doing this (the check for if ($diff)... - if (defined('DEBUG_TEST') || $diff && ($diff <= $timespan || $timespan === -1)) - { - $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : ''; - $key = sha1($creation_time . $user->data['user_form_salt'] . $form_name . $token_sid); - - if ($key === $token) - { - return true; - } - } - } - - return false; + return $form_helper->check_form_tokens($form_name, $timespan !== false ? $timespan : null); } // Message/Login boxes From d4b6ad562096e85783afe237b64afaa3ffd0f6ff Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 28 Oct 2022 20:54:37 +0200 Subject: [PATCH 21/63] [ticket/17010] Add controller methods for subscribe/unsubscribe and others PHPBB3-17010 --- .../config/default/container/services_ucp.yml | 11 +- phpBB/config/default/routing/ucp.yml | 16 +- phpBB/includes/ucp/ucp_notifications.php | 4 +- phpBB/phpbb/ucp/controller/webpush.php | 246 +++++++++++++++++- .../template/ucp_notifications_options.html | 7 +- 5 files changed, 274 insertions(+), 10 deletions(-) diff --git a/phpBB/config/default/container/services_ucp.yml b/phpBB/config/default/container/services_ucp.yml index 1d0535a116..a950ef8e35 100644 --- a/phpBB/config/default/container/services_ucp.yml +++ b/phpBB/config/default/container/services_ucp.yml @@ -16,7 +16,16 @@ services: - '%core.root_path%' - '%core.php_ext%' - phpbb.ucp.controller.push_worker: + phpbb.ucp.controller.webpush: class: phpbb\ucp\controller\webpush arguments: - '@config' + - '@controller.helper' + - '@dbal.conn' + - '@form_helper' + - '@path_helper' + - '@request' + - '@user' + - '@template.twig.environment' + - '%tables.notification_push%' + - '%tables.push_subscriptions%' diff --git a/phpBB/config/default/routing/ucp.yml b/phpBB/config/default/routing/ucp.yml index a88b6fc5ae..dac6461ac5 100644 --- a/phpBB/config/default/routing/ucp.yml +++ b/phpBB/config/default/routing/ucp.yml @@ -6,10 +6,22 @@ phpbb_ucp_forgot_password_controller: path: /forgot_password defaults: { _controller: phpbb.ucp.controller.reset_password:request } +phpbb_ucp_push_get_notification_controller: + path: /push/notification + defaults: { _controller: phpbb.ucp.controller.webpush:notification } + phpbb_ucp_push_worker_controller: path: /push/worker - defaults: { _controller: phpbb.ucp.controller.webpush:request } + defaults: { _controller: phpbb.ucp.controller.webpush:worker } phpbb_ucp_push_subscribe_controller: path: /push/subscribe - defaults: { _controller: phpbb.ucp.controller.webpush:request } + defaults: { _controller: phpbb.ucp.controller.webpush:subscribe } + +phpbb_ucp_push_unsubscribe_controller: + path: /push/unsubscribe + defaults: { _controller: phpbb.ucp.controller.webpush:unsubscribe } + +phpbb_ucp_push_js_controller: + path: /push/js + defaults: { _controller: phpbb.ucp.controller.webpush:js } diff --git a/phpBB/includes/ucp/ucp_notifications.php b/phpBB/includes/ucp/ucp_notifications.php index 7b07138595..9e03e68509 100644 --- a/phpBB/includes/ucp/ucp_notifications.php +++ b/phpBB/includes/ucp/ucp_notifications.php @@ -110,7 +110,9 @@ class ucp_notifications /** @var \phpbb\controller\helper $controller_helper */ $controller_helper = $phpbb_container->get('controller.helper'); - $template->assign_var('U_WEBPUSH_SUBSCRIBE', $controller_helper->route('phpbb_ucp_push_subscribe_controller')); + $template->assign_vars([ + 'T_WEBPUSH_JS_PATH' => $controller_helper->route('phpbb_ucp_push_js_controller'), + ]); $this->tpl_name = 'ucp_notifications_options'; $this->page_title = 'UCP_NOTIFICATION_OPTIONS'; diff --git a/phpBB/phpbb/ucp/controller/webpush.php b/phpBB/phpbb/ucp/controller/webpush.php index 67ef237d5b..9181ad0e83 100644 --- a/phpBB/phpbb/ucp/controller/webpush.php +++ b/phpBB/phpbb/ucp/controller/webpush.php @@ -3,25 +3,263 @@ namespace phpbb\ucp\controller; use phpbb\config\config; +use phpbb\controller\helper as controller_helper; +use phpbb\db\driver\driver_interface; +use phpbb\exception\http_exception; +use phpbb\form\form_helper; +use phpbb\json\sanitizer as json_sanitizer; +use phpbb\path_helper; +use phpbb\request\request_interface; +use phpbb\symfony_request; +use phpbb\user; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; +use Twig\Environment; +use Twig\Error\LoaderError; +use Twig\Error\RuntimeError; +use Twig\Error\SyntaxError; class webpush { + /** @var string UCP form token name */ + private const FORM_TOKEN_UCP = 'ucp_webpush'; + + /** @var string Push worker form token name */ + private const FORM_TOKEN_WORKER = 'webpush_worker'; + /** @var config */ protected $config; - public function __construct(config $config) + /** @var controller_helper */ + protected $controller_helper; + + /** @var driver_interface */ + protected $db; + + /** @var form_helper */ + protected $form_helper; + + /** @var path_helper */ + protected $path_helper; + + /** @var request_interface */ + protected $request; + + /** @var user */ + protected $user; + + /** @var Environment */ + protected $template; + + /** @var string */ + protected $notification_webpush_table; + + /** @var string */ + protected $push_subscriptions_table; + + /** + * Constructor for webpush controller + * + * @param config $config + * @param controller_helper $controller_helper + * @param driver_interface $db + * @param form_helper $form_helper + * @param path_helper $path_helper + * @param request_interface $request + * @param user $user + * @param Environment $template + * @param string $notification_webpush_table + * @param string $push_subscriptions_table + */ + public function __construct(config $config, controller_helper $controller_helper, driver_interface $db, form_helper $form_helper, path_helper $path_helper, + request_interface $request, user $user, Environment $template, string $notification_webpush_table, string $push_subscriptions_table) { $this->config = $config; + $this->controller_helper = $controller_helper; + $this->db = $db; + $this->form_helper = $form_helper; + $this->path_helper = $path_helper; + $this->request = $request; + $this->user = $user; + $this->template = $template; + $this->notification_webpush_table = $notification_webpush_table; + $this->push_subscriptions_table = $push_subscriptions_table; } /** - * Handle password reset request + * Handle request to retrieve notification data + * + * @return JsonResponse + */ + public function notification(): JsonResponse + { + // Subscribe should only be available for logged-in "normal" users + if (!$this->request->is_ajax() || $this->user->id() == ANONYMOUS || $this->user->data['is_bot'] + || $this->user->data['user_type'] == USER_IGNORE || $this->user->data['user_type'] == USER_INACTIVE) + { + throw new http_exception(Response::HTTP_FORBIDDEN, 'Forbidden'); + } + + $item_id = $this->request->variable('item_id', 0); + $type_id = $this->request->variable('type_id', 0); + + $sql = 'SELECT push_data + FROM ' . $this->notification_webpush_table . ' + WHERE user_id = ' . $this->user->id() . ' + AND notification_type_id = ' . $type_id . ' + AND item_id = ' . $item_id; + $result = $this->db->sql_query($sql); + $notification_data = $this->db->sql_fetchfield('push_data'); + $this->db->sql_freeresult($result); + $data = json_decode($notification_data, true); + $data['url'] = isset($data['url']) ? $this->path_helper->update_web_root_path($data['url']) : ''; + + return new JsonResponse($data); + } + + /** + * Handle request to push worker javascript * * @return Response + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError */ - public function request(): Response + public function worker(): Response { - return new Response('foo'); + // @todo: only work for logged in users, no anonymous & bot + $content = $this->template->render('push_worker.js.twig', [ + 'U_WEBPUSH_GET_NOTIFICATION' => $this->controller_helper->route('phpbb_ucp_push_get_notification_controller'), + ]); + + $response = new Response($content); + $response->headers->set('Content-Type', 'text/javascript; charset=UTF-8'); + + if (!empty($this->user->data['is_bot'])) + { + // Let reverse proxies know we detected a bot. + $response->headers->set('X-PHPBB-IS-BOT', 'yes'); + } + + return $response; + } + + /** + * Get template variables for subscribe type pages + * + * @return array + */ + protected function get_subscribe_vars(): array + { + return [ + 'U_WEBPUSH_SUBSCRIBE' => $this->controller_helper->route('phpbb_ucp_push_subscribe_controller'), + 'U_WEBPUSH_UNSUBSCRIBE' => $this->controller_helper->route('phpbb_ucp_push_unsubscribe_controller'), + 'FORM_TOKENS' => $this->form_helper->get_form_tokens(self::FORM_TOKEN_UCP), + ]; + } + + /** + * Check (un)subscribe form for valid link hash + * + * @throws http_exception If form is invalid or user should not request (un)subscription + * @return void + */ + protected function check_subscribe_requests(): void + { + if (!$this->form_helper->check_form_tokens(self::FORM_TOKEN_UCP)) + { + throw new http_exception(Response::HTTP_BAD_REQUEST, 'FORM_INVALID'); + } + + // Subscribe should only be available for logged-in "normal" users + if (!$this->request->is_ajax() || $this->user->id() == ANONYMOUS || $this->user->data['is_bot'] + || $this->user->data['user_type'] == USER_IGNORE || $this->user->data['user_type'] == USER_INACTIVE) + { + throw new http_exception(Response::HTTP_FORBIDDEN, 'NO_AUTH_OPERATION'); + } + } + + /** + * Handle request to web push javascript + * + * @return Response + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError + */ + public function js(): Response + { + // @todo: return forbidden for guest & bot + + $template_data = $this->get_subscribe_vars(); + $template_data += [ + 'VAPID_PUBLIC_KEY' => $this->config['webpush_vapid_public'], + 'U_WEBPUSH_WORKER_URL' => $this->controller_helper->route('phpbb_ucp_push_worker_controller'), + ]; + + $content = $this->template->render('webpush.js.twig', $template_data); + + $response = new Response($content); + $response->headers->set('Content-Type', 'text/javascript; charset=UTF-8'); + + if (!empty($this->user->data['is_bot'])) + { + // Let reverse proxies know we detected a bot. + $response->headers->set('X-PHPBB-IS-BOT', 'yes'); + } + + return $response; + } + + /** + * Handle subscribe requests + * + * @param symfony_request $symfony_request + * @return JsonResponse + */ + public function subscribe(symfony_request $symfony_request): JsonResponse + { + $this->check_subscribe_requests(); + + $data = json_sanitizer::decode($symfony_request->get('data', '')); + + $sql = 'INSERT INTO ' . $this->push_subscriptions_table . ' ' . $this->db->sql_build_array('INSERT', [ + 'user_id' => $this->user->id(), + 'endpoint' => $data['endpoint'], + 'expiration_time' => $data['expiration_time'] ?? 0, + 'p256dh' => $data['keys']['p256dh'], + 'auth' => $data['keys']['auth'], + ]); + $this->db->sql_query($sql); + + return new JsonResponse([ + 'success' => true, + 'form_tokens' => $this->form_helper->get_form_tokens(self::FORM_TOKEN_UCP), + ]); + } + + /** + * Handle unsubscribe requests + * + * @param symfony_request $symfony_request + * @return JsonResponse + */ + public function unsubscribe(symfony_request $symfony_request): JsonResponse + { + $this->check_subscribe_requests(); + + $data = json_sanitizer::decode($symfony_request->get('data', '')); + + $endpoint = $data['endpoint']; + + $sql = 'DELETE FROM ' . $this->push_subscriptions_table . ' + WHERE user_id = ' . $this->user->id() . " + AND endpoint = '" . $this->db->sql_escape($endpoint) . "'"; + $this->db->sql_query($sql); + + return new JsonResponse([ + 'success' => true, + 'form_tokens' => $this->form_helper->get_form_tokens(self::FORM_TOKEN_UCP), + ]); } } diff --git a/phpBB/styles/prosilver/template/ucp_notifications_options.html b/phpBB/styles/prosilver/template/ucp_notifications_options.html index 6536fb56cd..fc85b98af2 100644 --- a/phpBB/styles/prosilver/template/ucp_notifications_options.html +++ b/phpBB/styles/prosilver/template/ucp_notifications_options.html @@ -1,5 +1,7 @@ {% include('ucp_header.html') %} +{% INCLUDEJS(T_WEBPUSH_JS_PATH) %} +

{{ TITLE }}

@@ -8,9 +10,10 @@
-

{{ lang('NOTIFY_WEBPUSH_ENABLE_EXPLAIN') }}
+

{{ lang('NOTIFY_WEBPUSH_ENABLE_EXPLAIN') }}
- {{ lang('NOTIFY_WEBPUSH_ACTIVATE') }} + +
From 7092f24645f9b5504379c675608711335c2834ff Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 28 Oct 2022 20:55:37 +0200 Subject: [PATCH 22/63] [ticket/17010] Change how notification data is passed to push notifications JS PHPBB3-17010 --- .../db/migration/data/v400/add_webpush.php | 6 ++-- phpBB/phpbb/notification/method/webpush.php | 30 ++++++++++++------- .../notification/type/type_interface.php | 2 +- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/phpBB/phpbb/db/migration/data/v400/add_webpush.php b/phpBB/phpbb/db/migration/data/v400/add_webpush.php index f2214fd934..248be387bd 100644 --- a/phpBB/phpbb/db/migration/data/v400/add_webpush.php +++ b/phpBB/phpbb/db/migration/data/v400/add_webpush.php @@ -39,6 +39,8 @@ class add_webpush extends migration 'item_id' => ['ULINT', 0], 'item_parent_id' => ['ULINT', 0], 'user_id' => ['ULINT', 0], + 'push_data' => ['MTEXT', ''], + 'notification_time' => ['TIMESTAMP', 0] ], 'PRIMARY_KEY' => ['notification_type_id', 'item_id', 'item_parent_id', 'user_id'], ], @@ -46,11 +48,11 @@ class add_webpush extends migration 'COLUMNS' => [ 'subscription_id' => ['ULINT', null, 'auto_increment'], 'user_id' => ['ULINT', 0], - 'device_name' => ['VCHAR:64', ''], +// 'device_name' => ['VCHAR:64', ''], 'endpoint' => ['TEXT', ''], + 'expiration_time' => ['TIMESTAMP', 0], 'p256dh' => ['VCHAR', ''], 'auth' => ['VCHAR', ''], - 'encoding' => ['VCHAR:32', ''], ], 'PRIMARY_KEY' => ['subscription_id', 'user_id'], ] diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 2d015600c3..6e6cc7a1eb 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -115,7 +115,18 @@ class webpush extends \phpbb\notification\method\messenger_base /** @var type_interface $notification */ foreach ($this->queue as $notification) { - $data = self::clean_data($notification->get_insert_array()); + $data = $notification->get_insert_array(); + $data += [ + 'push_data' => json_encode([ + 'heading' => $this->config['sitename'], + 'title' => strip_tags($notification->get_title()), + 'text' => strip_tags($notification->get_reference()), + 'url' => $notification->get_url(), + 'avatar' => $notification->get_avatar(), + ]), + 'notification_time' => time(), + ]; + $data = self::clean_data($data); $insert_buffer->insert($data); } @@ -158,7 +169,7 @@ class webpush extends \phpbb\notification\method\messenger_base // Get subscriptions for users $user_subscription_map = []; - $sql = 'SELECT user_id, endpoint, p256dh, auth, encoding + $sql = 'SELECT user_id, endpoint, p256dh, auth FROM ' . $this->push_subscriptions_table . ' WHERE ' . $this->db->sql_in_set('user_id', $notify_users); $result = $this->db->sql_query($sql); @@ -200,13 +211,9 @@ class webpush extends \phpbb\notification\method\messenger_base } // add actual web push data - $data['data'] = [ - 'title' => $this->config['sitename'], - 'body' => $notification->get_title(), - 'icon' => '', // @todo: to be filled? - 'image' => '', // @todo: to be filled? - 'url' => $notification->get_url(), - 'user_id' => $notification->user_id, + $data = [ + 'item_id' => $notification->item_id, + 'type_id' => $notification->notification_type_id, ]; $json_data = json_encode($data); @@ -220,7 +227,6 @@ class webpush extends \phpbb\notification\method\messenger_base 'p256dh' => $subscription['p256dh'], 'auth' => $subscription['auth'], ], - 'contentEncoding' => !empty($subscription['encoding']) ? $subscription['encoding'] : null, ]); $web_push->queueNotification($push_subscription, $json_data); $number_of_notifications++; @@ -283,13 +289,15 @@ class webpush extends \phpbb\notification\method\messenger_base * @param array $data Notification data * @return array Cleaned notification data */ - public static function clean_data(array $data) + public static function clean_data(array $data): array { $row = [ 'notification_type_id' => null, 'item_id' => null, 'item_parent_id' => null, 'user_id' => null, + 'push_data' => null, + 'notification_time' => null, ]; return array_intersect_key($data, $row); diff --git a/phpBB/phpbb/notification/type/type_interface.php b/phpBB/phpbb/notification/type/type_interface.php index 65bd15d4aa..4c45b634ba 100644 --- a/phpBB/phpbb/notification/type/type_interface.php +++ b/phpBB/phpbb/notification/type/type_interface.php @@ -139,7 +139,7 @@ interface type_interface /** * Get the user's avatar (the user who caused the notification typically) * - * @return string + * @return array */ public function get_avatar(); From 06458c95f558b9d05ef420851b4910a8d036bae3 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 28 Oct 2022 20:55:54 +0200 Subject: [PATCH 23/63] [ticket/17010] Add button disabled state CSS PHPBB3-17010 --- phpBB/styles/prosilver/theme/buttons.css | 14 ++++++++++++++ phpBB/styles/prosilver/theme/colours.css | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/phpBB/styles/prosilver/theme/buttons.css b/phpBB/styles/prosilver/theme/buttons.css index 4656128ab9..f4671ff58c 100644 --- a/phpBB/styles/prosilver/theme/buttons.css +++ b/phpBB/styles/prosilver/theme/buttons.css @@ -32,6 +32,20 @@ outline: none; } +.button[disabled], +.button[disabled]:hover, +.button.disabled, +.button.disabled:hover { + background: #eee; + color: #aaa; + border-color: #aaa; + cursor: default; +} + +.button.hidden { + display: none; +} + .caret { border-left: 1px solid; position: relative; diff --git a/phpBB/styles/prosilver/theme/colours.css b/phpBB/styles/prosilver/theme/colours.css index 7bdd5f4b87..ae39635d65 100644 --- a/phpBB/styles/prosilver/theme/colours.css +++ b/phpBB/styles/prosilver/theme/colours.css @@ -697,6 +697,15 @@ dd.profile-warnings { color: #d41142; } +.button[disabled], +.button[disabled]:hover, +.button.disabled, +.button.disabled:hover { + background: #e0e0e0; + color: #9e9e9e; + border-color: #9e9e9e; +} + .button .icon, .button-secondary, .c-button-icon { From e177ee3750d723c9777bc3d9e6f6192558192d28 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 28 Oct 2022 20:56:13 +0200 Subject: [PATCH 24/63] [ticket/17010] Add first version of push worker javascript PHPBB3-17010 --- phpBB/styles/all/js/push_worker.js.twig | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 phpBB/styles/all/js/push_worker.js.twig diff --git a/phpBB/styles/all/js/push_worker.js.twig b/phpBB/styles/all/js/push_worker.js.twig new file mode 100644 index 0000000000..91dbf6f4ad --- /dev/null +++ b/phpBB/styles/all/js/push_worker.js.twig @@ -0,0 +1,59 @@ +/** + * Event listener for push event + */ +self.addEventListener('push', event => { + if (typeof event.data === 'undefined') { + return; + } + + let itemId = 0, + typeId = 0; + try { + const notificationData = event.data.json(); + itemId = notificationData['item_id']; + typeId = notificationData['type_id']; + } catch (e) { + self.registration.showNotification(event.data.text()); + return; + } + + const getNotificationUrl = '{{ U_WEBPUSH_GET_NOTIFICATION }}'; + + let formData = new FormData(); + formData.append('item_id', itemId.toString(10)); + formData.append('type_id', typeId.toString(10)); + + fetch(getNotificationUrl, { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest' + }, + body: formData + }) + .then((response) => response.json()) + .then((response) => { + const responseBody = response.title + "\n" + response.text; + const options = { + body: responseBody, + data: response, + icon: response.avatar.src + // foo: '' + //icon: image + } + self.registration.showNotification( + response.heading, + options + ); + } + ); +}); + +/** + * Event listener for notification click + */ +self.addEventListener('notificationclick', event => { + event.notification.close(); + if (typeof event.notification.data !== 'undefined') { + event.waitUntil(self.clients.openWindow(event.notification.data.url)); + } +}); From 166f0deae90939f8fdd3187b7166586af71fb800 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 28 Oct 2022 21:46:34 +0200 Subject: [PATCH 25/63] [ticket/17010] Make get_form_tokens() parameters optional PHPBB3-17010 --- phpBB/phpbb/form/form_helper.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phpBB/phpbb/form/form_helper.php b/phpBB/phpbb/form/form_helper.php index ca4ca14b01..7a266f2339 100644 --- a/phpBB/phpbb/form/form_helper.php +++ b/phpBB/phpbb/form/form_helper.php @@ -46,13 +46,13 @@ class form_helper * Get form tokens for form * * @param string $form_name Name of form - * @param int $now Token generation time - * @param string $token_sid SID used for form token - * @param string $token Generated token + * @param int|null $now Token generation time + * @param string|null $token_sid SID used for form token + * @param string|null $token Generated token * * @return array Array containing form_token and creation_time of form token */ - public function get_form_tokens(string $form_name, int &$now = 0, string &$token_sid = '', string &$token = ''): array + public function get_form_tokens(string $form_name, ?int &$now = 0, ?string &$token_sid = '', ?string &$token = ''): array { $now = time(); $token_sid = ($this->user->data['user_id'] == ANONYMOUS && !empty($this->config['form_token_sid_guests'])) ? $this->user->session_id : ''; From 98e0559a1d6205804486ad3c7c46a0f07f0e980c Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 28 Oct 2022 22:08:29 +0200 Subject: [PATCH 26/63] [ticket/17010] Remove wrong references to emails PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 6e6cc7a1eb..ae6317a4cd 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -149,14 +149,14 @@ class webpush extends \phpbb\notification\method\messenger_base return; } - // Load all users we want to notify (we need their email address) + // Load all users we want to notify $user_ids = []; foreach ($this->queue as $notification) { $user_ids[] = $notification->user_id; } - // We do not send emails to banned users + // Do not send push notifications to banned users if (!function_exists('phpbb_get_banned_user_ids')) { include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext); @@ -196,7 +196,7 @@ class webpush extends \phpbb\notification\method\messenger_base $web_push = new \Minishlink\WebPush\WebPush($auth); $number_of_notifications = 0; - // Time to go through the queue and send emails + // Time to go through the queue and send notifications /** @var type_interface $notification */ foreach ($this->queue as $notification) { From ac6512da140cc27c88ab50e23d4cf7c77e570489 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 28 Oct 2022 23:31:49 +0200 Subject: [PATCH 27/63] [ticket/17010] Add missing copyright header PHPBB3-17010 --- phpBB/phpbb/ucp/controller/webpush.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/phpBB/phpbb/ucp/controller/webpush.php b/phpBB/phpbb/ucp/controller/webpush.php index 9181ad0e83..f1f5c197be 100644 --- a/phpBB/phpbb/ucp/controller/webpush.php +++ b/phpBB/phpbb/ucp/controller/webpush.php @@ -1,4 +1,15 @@ + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ namespace phpbb\ucp\controller; From b3777894cb26326d75ed71072dc1c0396aa2a718 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 30 Oct 2022 11:44:59 +0100 Subject: [PATCH 28/63] [ticket/17010] Fix stylelint issues PHPBB3-17010 --- phpBB/styles/prosilver/theme/buttons.css | 6 +++--- phpBB/styles/prosilver/theme/colours.css | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/phpBB/styles/prosilver/theme/buttons.css b/phpBB/styles/prosilver/theme/buttons.css index f4671ff58c..e8f8fbe961 100644 --- a/phpBB/styles/prosilver/theme/buttons.css +++ b/phpBB/styles/prosilver/theme/buttons.css @@ -36,9 +36,9 @@ .button[disabled]:hover, .button.disabled, .button.disabled:hover { - background: #eee; - color: #aaa; - border-color: #aaa; + background: #eeeeee; + border-color: #aaaaaa; + color: #aaaaaa; cursor: default; } diff --git a/phpBB/styles/prosilver/theme/colours.css b/phpBB/styles/prosilver/theme/colours.css index ae39635d65..fe5e4d7c85 100644 --- a/phpBB/styles/prosilver/theme/colours.css +++ b/phpBB/styles/prosilver/theme/colours.css @@ -702,8 +702,8 @@ dd.profile-warnings { .button.disabled, .button.disabled:hover { background: #e0e0e0; - color: #9e9e9e; border-color: #9e9e9e; + color: #9e9e9e; } .button .icon, From e97313839ee19c0ca0271ac6b4b5f7934ef07691 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Mon, 31 Oct 2022 17:03:04 +0100 Subject: [PATCH 29/63] [ticket/17010] Add missing words & loading indicator, fix invalid twig code PHPBB3-17010 --- phpBB/language/en/ucp.php | 5 +- phpBB/styles/all/js/webpush.js.twig | 51 +++++++++++-------- .../template/ucp_notifications_options.html | 4 +- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/phpBB/language/en/ucp.php b/phpBB/language/en/ucp.php index 09bf7824e5..19d3a2c06e 100644 --- a/phpBB/language/en/ucp.php +++ b/phpBB/language/en/ucp.php @@ -356,9 +356,10 @@ $lang = array_merge($lang, array( 'NOTIFY_METHOD_EXPLAIN' => 'Method for sending messages sent via this board.', 'NOTIFY_METHOD_IM' => 'Jabber only', 'NOTIFY_ON_PM' => 'Notify me on new private messages', - 'NOTIFY_WEBPUSH_ACTIVATE' => 'Activate push notifications', 'NOTIFY_WEBPUSH_ENABLE' => 'Enable receiving webpush notifications', - 'NOTIFY_WEBPUSH_ENABLE_EXPLAIN' => 'Enable receiving browser-based push notifications.
The notifications can be turned off at any time in your browser settings or by disabling the push notifications below.', + 'NOTIFY_WEBPUSH_ENABLE_EXPLAIN' => 'Enable receiving browser-based push notifications.
The notifications can be turned off at any time in your browser settings, by unsubscribing, or by disabling the push notifications below.', + 'NOTIFY_WEBPUSH_SUBSCRIBE' => 'Subscribe', + 'NOTIFY_WEBPUSH_UNSUBSCRIBE' => 'Unsubscribe', 'NOT_ADDED_FRIENDS_ANONYMOUS' => 'You cannot add the anonymous user to your friends list.', 'NOT_ADDED_FRIENDS_BOTS' => 'You cannot add bots to your friends list.', 'NOT_ADDED_FRIENDS_FOES' => 'You cannot add users to your friends list who are on your foes list.', diff --git a/phpBB/styles/all/js/webpush.js.twig b/phpBB/styles/all/js/webpush.js.twig index 1287f133d9..e634f4539c 100644 --- a/phpBB/styles/all/js/webpush.js.twig +++ b/phpBB/styles/all/js/webpush.js.twig @@ -12,7 +12,7 @@ function PhpbbWebpush() { /** @type {string} URL to unsubscribe from push */ const unsubscribeUrl = '{{ U_WEBPUSH_UNSUBSCRIBE }}'; - /** @type {{creationTime: number, formToken: string}} Form tokens */ + /** @type { {creationTime: number, formToken: string} } Form tokens */ this.formTokens = { creationTime: {{ FORM_TOKENS.creation_time }}, formToken: '{{ FORM_TOKENS.form_token }}' @@ -30,7 +30,6 @@ function PhpbbWebpush() { this.init = function() { subscribeButton = document.querySelector('#subscribe_webpush'); unsubscribeButton = document.querySelector('#unsubscribe_webpush'); - let serviceWorkerRegistered = false; // Service workers are only supported in secure context if (window.isSecureContext !== true) { @@ -41,20 +40,17 @@ function PhpbbWebpush() { if ('serviceWorker' in navigator && 'PushManager' in window) { navigator.serviceWorker.register(serviceWorkerUrl) .then(() => { - serviceWorkerRegistered = true; + subscribeButton.addEventListener('click', subscribeButtonHandler); + unsubscribeButton.addEventListener('click', unsubscribeButtonHandler); + + updateButtonState(); }) .catch(error => { console.info(error); + // Service worker could not be registered + subscribeButton.disabled = true; }); - } - - if (serviceWorkerRegistered) { - subscribeButton.addEventListener('click', subscribeButtonHandler); - unsubscribeButton.addEventListener('click', unsubscribeButtonHandler); - - updateButtonState(); } else { - // Service worker could not be registered subscribeButton.disabled = true; } }; @@ -127,6 +123,7 @@ function PhpbbWebpush() { applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY) }); + const loadingIndicator = phpbb.loadingIndicator(); fetch(subscribeUrl, { method: 'POST', headers: { @@ -134,10 +131,14 @@ function PhpbbWebpush() { }, body: getFormData(newSubscription) }) - .then((response) => response.json()) + .then((response) => { + loadingIndicator.fadeOut(phpbb.alertTime); + return response.json(); + }) .then(handleSubscribe) .catch((error) => { - phpbb.alert({{ lang('AJAX_ERROR_TITLE') }}, error); + loadingIndicator.fadeOut(phpbb.alertTime); + phpbb.alert('{{ lang('AJAX_ERROR_TITLE') }}', error); }); } @@ -156,19 +157,27 @@ function PhpbbWebpush() { } const subscription = await registration.pushManager.getSubscription(); + const loadingIndicator = phpbb.loadingIndicator(); fetch(unsubscribeUrl, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: getFormData({endpoint: subscription.endpoint}) - }).then(() => { - return subscription.unsubscribe(); - }).then((unsubscribed) => { - if (unsubscribed) { - setSubscriptionState(false); - } - }); + }) + .then(() => { + loadingIndicator.fadeOut(phpbb.alertTime); + return subscription.unsubscribe(); + }) + .then((unsubscribed) => { + if (unsubscribed) { + setSubscriptionState(false); + } + }) + .catch((error) => { + loadingIndicator.fadeOut(phpbb.alertTime); + phpbb.alert('{{ lang('AJAX_ERROR_TITLE') }}', error); + }); } /** @@ -194,7 +203,7 @@ function PhpbbWebpush() { function getFormData(data) { let formData = new FormData(); formData.append('form_token', phpbb.webpush.formTokens.formToken); - formData.append('creation_time', phpbb.webpush.formTokens.creationTime); + formData.append('creation_time', phpbb.webpush.formTokens.creationTime.toString()); formData.append('data', JSON.stringify(data)); return formData; diff --git a/phpBB/styles/prosilver/template/ucp_notifications_options.html b/phpBB/styles/prosilver/template/ucp_notifications_options.html index fc85b98af2..205e8d4394 100644 --- a/phpBB/styles/prosilver/template/ucp_notifications_options.html +++ b/phpBB/styles/prosilver/template/ucp_notifications_options.html @@ -12,8 +12,8 @@

{{ lang('NOTIFY_WEBPUSH_ENABLE_EXPLAIN') }}
- - + +
From edaff6cd2d8f1b30c7ef66e69acc0277d357a4d2 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 1 Nov 2022 12:44:22 +0100 Subject: [PATCH 30/63] [ticket/17010] Remove undeliverable subscriptions PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index ae6317a4cd..994ccc35ac 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -13,6 +13,7 @@ namespace phpbb\notification\method; +use Minishlink\WebPush\Subscription; use phpbb\config\config; use phpbb\db\driver\driver_interface; use phpbb\notification\type\type_interface; @@ -24,7 +25,7 @@ use phpbb\user_loader; * This class handles sending push messages for notifications */ -class webpush extends \phpbb\notification\method\messenger_base +class webpush extends messenger_base { /** @var config */ protected $config; @@ -169,7 +170,7 @@ class webpush extends \phpbb\notification\method\messenger_base // Get subscriptions for users $user_subscription_map = []; - $sql = 'SELECT user_id, endpoint, p256dh, auth + $sql = 'SELECT subscription_id, user_id, endpoint, p256dh, auth FROM ' . $this->push_subscriptions_table . ' WHERE ' . $this->db->sql_in_set('user_id', $notify_users); $result = $this->db->sql_query($sql); @@ -196,6 +197,8 @@ class webpush extends \phpbb\notification\method\messenger_base $web_push = new \Minishlink\WebPush\WebPush($auth); $number_of_notifications = 0; + $remove_subscriptions = []; + // Time to go through the queue and send notifications /** @var type_interface $notification */ foreach ($this->queue as $notification) @@ -221,7 +224,7 @@ class webpush extends \phpbb\notification\method\messenger_base { try { - $push_subscription = \Minishlink\WebPush\Subscription::create([ + $push_subscription = Subscription::create([ 'endpoint' => $subscription['endpoint'], 'keys' => [ 'p256dh' => $subscription['p256dh'], @@ -233,13 +236,19 @@ class webpush extends \phpbb\notification\method\messenger_base } catch (\ErrorException $exception) { - // @todo: decide whether we want to remove invalid subscriptions directly? - // Might need too many resources ... + $remove_subscriptions[] = $subscription['subscription_id']; } } } - // @todo: Try offloading to after request + // Remove any subscriptions that couldn't be queued, i.e. that have invalid data + if (count($remove_subscriptions)) + { + $sql = 'DELETE FROM ' . $this->push_subscriptions_table . ' + WHERE ' . $this->db->sql_in_set('subscription_id', $remove_subscriptions); + $this->db->sql_query($sql); + } + try { foreach ($web_push->flush($number_of_notifications) as $report) From ad196d1dba96cf8222a2e71438578094f7d95de2 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 29 Jan 2023 09:48:23 +0100 Subject: [PATCH 31/63] [ticket/17010] Move web push testing to slow test group PHPBB3-17010 --- tests/notification/notification_method_webpush_test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/notification/notification_method_webpush_test.php b/tests/notification/notification_method_webpush_test.php index f0e1f1cb50..ce79f57838 100644 --- a/tests/notification/notification_method_webpush_test.php +++ b/tests/notification/notification_method_webpush_test.php @@ -19,7 +19,7 @@ use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; require_once __DIR__ . '/base.php'; /** - * @group + * @group slow */ class notification_method_webpush_test extends phpbb_tests_notification_base { From a8dc08a21903b48fb2e43f60b4d71acea3f3ae32 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 29 Jan 2023 09:54:01 +0100 Subject: [PATCH 32/63] [ticket/17010] Add prune notification support to webpush PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 994ccc35ac..d11959319c 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -292,6 +292,19 @@ class webpush extends messenger_base $this->db->sql_query($sql); } + /** + * {@inheritDoc} + */ + public function prune_notifications($timestamp, $only_read = true): void + { + $sql = 'DELETE FROM ' . $this->notification_webpush_table . ' + WHERE notification_time < ' . (int) $timestamp . + (($only_read) ? ' AND notification_read = 1' : ''); + $this->db->sql_query($sql); + + $this->config->set('read_notification_last_gc', (string) time(), false); + } + /** * Clean data to contain only what we need for webpush notifications table * From 974b7a9184ff5764eca2531b8c34a465a00208da Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Mon, 30 Jan 2023 21:39:49 +0100 Subject: [PATCH 33/63] [ticket/17010] Resolve use of undefined variable and possibly wrong type PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index d11959319c..ea71e1cab8 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -176,7 +176,7 @@ class webpush extends messenger_base $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) { - if (isset($user_subscriptions[$row['user_id']])) + if (isset($user_subscription_map[$row['user_id']])) { $user_subscription_map[$row['user_id']] += $row; } @@ -274,7 +274,7 @@ class webpush extends messenger_base public function mark_notifications($notification_type_id, $item_id, $user_id, $time = false, $mark_read = true) { $sql = 'DELETE FROM ' . $this->notification_webpush_table . ' - WHERE ' . ($notification_type_id !== false ? $this->db->sql_in_set('notification_type_id', $notification_type_id) : '1=1') . + WHERE ' . ($notification_type_id !== false ? $this->db->sql_in_set('notification_type_id', is_array($notification_type_id) ? $notification_type_id : [$notification_type_id]) : '1=1') . ($user_id !== false ? ' AND ' . $this->db->sql_in_set('user_id', $user_id) : '') . ($item_id !== false ? ' AND ' . $this->db->sql_in_set('item_id', $item_id) : ''); $this->db->sql_query($sql); @@ -286,7 +286,7 @@ class webpush extends messenger_base public function mark_notifications_by_parent($notification_type_id, $item_parent_id, $user_id, $time = false, $mark_read = true) { $sql = 'DELETE FROM ' . $this->notification_webpush_table . ' - WHERE ' . ($notification_type_id !== false ? $this->db->sql_in_set('notification_type_id', $notification_type_id) : '1=1') . + WHERE ' . ($notification_type_id !== false ? $this->db->sql_in_set('notification_type_id', is_array($notification_type_id) ? $notification_type_id : [$notification_type_id]) : '1=1') . ($user_id !== false ? ' AND ' . $this->db->sql_in_set('user_id', $user_id) : '') . ($item_parent_id !== false ? ' AND ' . $this->db->sql_in_set('item_parent_id', $item_parent_id, false, true) : ''); $this->db->sql_query($sql); From 3513c85ee61bdb88adffc3b732ac3e8cb9309adc Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Mon, 30 Jan 2023 23:09:57 +0100 Subject: [PATCH 34/63] [ticket/17010] Stop skipping node dependency setup on slow tests PHPBB3-17010 --- .github/workflows/tests.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 761d8e3e87..1c4de83e50 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -218,12 +218,10 @@ jobs: .github/setup-ldap.sh - name: Setup node - if: ${{ matrix.SLOWTESTS != 1 }} uses: actions/setup-node@v3 with: node-version: 16 - name: Setup node dependencies - if: ${{ matrix.SLOWTESTS != 1 }} run: npm ci - name: Setup SPHINX From 7410c3be6f31d566451caca8650d8f4adea67273 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 23 Jun 2023 17:44:21 +0200 Subject: [PATCH 35/63] [ticket/17010] Generate VAPID keys with javascript in ACP PHPBB3-17010 --- phpBB/adm/style/ajax.js | 62 +++++++++++++++++++++++++++++ phpBB/includes/acp/acp_board.php | 68 +++++++++++++++++++++----------- phpBB/language/en/acp/board.php | 1 + 3 files changed, 109 insertions(+), 22 deletions(-) diff --git a/phpBB/adm/style/ajax.js b/phpBB/adm/style/ajax.js index 5949c73920..25a6527481 100644 --- a/phpBB/adm/style/ajax.js +++ b/phpBB/adm/style/ajax.js @@ -157,6 +157,68 @@ phpbb.addAjaxCallback('row_delete', function(res) { } }); +/** + * This callback generates the VAPID keys for the web push notification service. + */ +phpbb.addAjaxCallback('generate_vapid_keys', (res) => { + function rawKeyToBase64(rawKey) { + const keyBuffer = new Uint8Array(rawKey); + let keyText = ''; + const keyLength = keyBuffer.byteLength; + for (let i = 0; i < keyLength; i++) { + keyText += String.fromCharCode(keyBuffer[i]); + } + + keyText = window.btoa(keyText); + return keyText; + } + + function base64SafeEncode(base64String) { + const base64URL = base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); + return base64URL; + } + + async function generateVAPIDKeys() { + try { + // Generate a new key pair using the Subtle Crypto API + const keyPair = await crypto.subtle.generateKey( + { + name: 'ECDH', + namedCurve: 'P-256', + }, + true, + ['deriveKey', 'deriveBits'] + ); + + // Export the private key as a JWK (JSON Web Key) object + const privateKeyJwk = await crypto.subtle.exportKey('jwk', keyPair.privateKey); + console.log(privateKeyJwk.d); + + const privateKeyString = privateKeyJwk.d; + + // Export the public key as a JWK object + const publicKeyBuffer = await crypto.subtle.exportKey('raw', keyPair.publicKey); + const publicKeyString = base64SafeEncode(rawKeyToBase64(publicKeyBuffer)); + console.log(publicKeyString); + + return { + privateKey: privateKeyString, + publicKey: publicKeyString + }; + } catch (error) { + console.error('Error generating private key:', error); + return null; + } + } + + generateVAPIDKeys().then(keyPair => { + const publicKeyInput = document.querySelector('#webpush_vapid_public'); + const privateKeyInput = document.querySelector('#webpush_vapid_private'); + publicKeyInput.value = keyPair.publicKey; + privateKeyInput.value = keyPair.privateKey; + }) +}) + /** * Handler for submitting permissions form in chunks * This call will submit permissions forms in chunks of 5 fieldsets. diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index ceb7f47d58..d0ced6788f 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -491,7 +491,7 @@ class acp_board 'title' => 'ACP_WEBPUSH_SETTINGS', 'vars' => [ 'legend1' => 'GENERAL_SETTINGS', - 'webpush_enable' => ['lang' => 'WEBPUSH_ENABLE', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true], + 'webpush_enable' => ['lang' => 'WEBPUSH_ENABLE', 'validate' => 'bool', 'type' => 'custom', 'method' => 'webpush_enable', 'explain' => true], 'webpush_vapid_public' => ['lang' => 'WEBPUSH_VAPID_PUBLIC', 'validate' => 'string', 'type' => 'text:25:255', 'explain' => true], 'webpush_vapid_private' => ['lang' => 'WEBPUSH_VAPID_PRIVATE', 'validate' => 'string', 'type' => 'password:25:255', 'explain' => true], @@ -539,27 +539,6 @@ class acp_board } } - if ($mode == 'webpush') - { - // Create VAPID keys if keys are empty and web push is enabled - if ($submit && $cfg_array['webpush_enable'] && $cfg_array['webpush_enable'] != $config['webpush_enable'] - && empty($cfg_array['webpush_vapid_public']) && empty($cfg_array['webpush_vapid_private']) - && empty($config['webpush_vapid_public']) && empty($config['webpush_vapid_private'])) - { - try - { - $vapid_keys = VAPID::createVapidKeys(); - $cfg_array['webpush_vapid_public'] = $vapid_keys['publicKey']; - $cfg_array['webpush_vapid_private'] = $vapid_keys['privateKey']; - } - catch (\ErrorException $exception) - { - // Nothing we can do about this, user will have to follow the - // documentation and manually create these. - } - } - } - // We validate the complete config if wished validate_config_vars($display_vars['vars'], $cfg_array, $error); @@ -1383,4 +1362,49 @@ class acp_board return ' '; } + + /** + * Generate form data for web push enable + * + * @param string $value Webpush enable value + * @param string $key Webpush enable config key + * + * @return array[] Form data + */ + public function webpush_enable($value, $key): array + { + return [ + [ + 'tag' => 'radio', + 'buttons' => [ + [ + 'name' => "config[$key]", + 'label' => $this->language->lang('YES'), + 'type' => 'radio', + 'class' => 'radio', + 'value' => 1, + 'checked' => $value, + ], + [ + 'name' => "config[$key]", + 'label' => $this->language->lang('NO'), + 'type' => 'radio', + 'class' => 'radio', + 'value' => 0, + 'checked' => !$value, + ], + ], + ], + [ + 'tag' => 'input', + 'class' => 'button2', + 'name' => "config[$key]", + 'type' => 'button', + 'value' => $this->language->lang('WEBPUSH_GENERATE_VAPID_KEYS'), + 'data' => [ + 'ajax' => 'generate_vapid_keys', + ] + ], + ]; + } } diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index 8fe9f832f2..d2905d94e8 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -603,6 +603,7 @@ $lang = array_merge($lang, [ 'ACP_WEBPUSH_SETTINGS_EXPLAIN' => 'Here you can enable and control the use of Webpush for board notifications. Webpush is a simple protocol for the delivery of real-time events to user agents, more commonly known as push messages. It is supported by most modern browsers on desktop and mobile devices.', 'WEBPUSH_ENABLE' => 'Enable Webpush', 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow receiving notifications via Webpush.
Note: If VAPID keys have not been set, phpBB will try to automatically create them when enabling Webpush.', + 'WEBPUSH_GENERATE_VAPID_KEYS' => 'Generate VAPID keys', 'WEBPUSH_VAPID_PUBLIC' => 'VAPID public key', 'WEBPUSH_VAPID_PUBLIC_EXPLAIN' => 'The VAPID public key will be shared to authenticate push messages sent by your site.
Warning: Changing the VAPID public key will automatically invalidate all webpush subscriptions.', 'WEBPUSH_VAPID_PRIVATE' => 'VAPID private key', From 3cbe14eb4a3fc2b4c69216cda41a508f1e405398 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 24 Jun 2023 09:40:35 +0200 Subject: [PATCH 36/63] [ticket/17010] Clean up code and move some functions to core js PHPBB3-17010 --- phpBB/adm/style/ajax.js | 35 +++++++++++---------------------- phpBB/assets/javascript/core.js | 27 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/phpBB/adm/style/ajax.js b/phpBB/adm/style/ajax.js index 25a6527481..67956e946a 100644 --- a/phpBB/adm/style/ajax.js +++ b/phpBB/adm/style/ajax.js @@ -160,24 +160,13 @@ phpbb.addAjaxCallback('row_delete', function(res) { /** * This callback generates the VAPID keys for the web push notification service. */ -phpbb.addAjaxCallback('generate_vapid_keys', (res) => { - function rawKeyToBase64(rawKey) { - const keyBuffer = new Uint8Array(rawKey); - let keyText = ''; - const keyLength = keyBuffer.byteLength; - for (let i = 0; i < keyLength; i++) { - keyText += String.fromCharCode(keyBuffer[i]); - } - - keyText = window.btoa(keyText); - return keyText; - } - - function base64SafeEncode(base64String) { - const base64URL = base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); - return base64URL; - } +phpbb.addAjaxCallback('generate_vapid_keys', () => { + /** + * Generate VAPID keypair with public and private key string + * + * @returns {Promise<{privateKey: string, publicKey: string}|null>} + */ async function generateVAPIDKeys() { try { // Generate a new key pair using the Subtle Crypto API @@ -190,28 +179,26 @@ phpbb.addAjaxCallback('generate_vapid_keys', (res) => { ['deriveKey', 'deriveBits'] ); - // Export the private key as a JWK (JSON Web Key) object const privateKeyJwk = await crypto.subtle.exportKey('jwk', keyPair.privateKey); - console.log(privateKeyJwk.d); - const privateKeyString = privateKeyJwk.d; - // Export the public key as a JWK object const publicKeyBuffer = await crypto.subtle.exportKey('raw', keyPair.publicKey); - const publicKeyString = base64SafeEncode(rawKeyToBase64(publicKeyBuffer)); - console.log(publicKeyString); + const publicKeyString = phpbb.base64UrlEncode(phpbb.rawKeyToBase64(publicKeyBuffer)); return { privateKey: privateKeyString, publicKey: publicKeyString }; } catch (error) { - console.error('Error generating private key:', error); + console.error('Error generating keys with SubtleCrypto:', error); return null; } } generateVAPIDKeys().then(keyPair => { + if (!keyPair) { + return; + } const publicKeyInput = document.querySelector('#webpush_vapid_public'); const privateKeyInput = document.querySelector('#webpush_vapid_private'); publicKeyInput.value = keyPair.publicKey; diff --git a/phpBB/assets/javascript/core.js b/phpBB/assets/javascript/core.js index d301cc8da8..c0b225214d 100644 --- a/phpBB/assets/javascript/core.js +++ b/phpBB/assets/javascript/core.js @@ -1677,6 +1677,33 @@ phpbb.getFunctionByName = function (functionName) { return context[func]; }; +/** + * Convert raw key ArrayBuffer to base64 string. + * + * @param {ArrayBuffer} rawKey Raw key array buffer as exported by SubtleCrypto exportKey() + * @returns {string} Base64 encoded raw key string + */ +phpbb.rawKeyToBase64 = (rawKey) => { + const keyBuffer = new Uint8Array(rawKey); + let keyText = ''; + const keyLength = keyBuffer.byteLength; + for (let i = 0; i < keyLength; i++) { + keyText += String.fromCharCode(keyBuffer[i]); + } + + return window.btoa(keyText); +}; + +/** + * Base64URL encode base64 encoded string + * + * @param {string} base64String Base64 encoded string + * @returns {string} Base64URL encoded string + */ +phpbb.base64UrlEncode = (base64String) => { + return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); +}; + /** * Register page dropdowns. */ From 21ccb804d3765d1fe2afbe9d06ee6bd875d35c44 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 24 Jun 2023 09:42:30 +0200 Subject: [PATCH 37/63] [ticket/17010] Update explain string in ACP for webpush enable PHPBB3-17010 --- phpBB/language/en/acp/board.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index d2905d94e8..1c65cccbc4 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -602,7 +602,7 @@ $lang = array_merge($lang, array( $lang = array_merge($lang, [ 'ACP_WEBPUSH_SETTINGS_EXPLAIN' => 'Here you can enable and control the use of Webpush for board notifications. Webpush is a simple protocol for the delivery of real-time events to user agents, more commonly known as push messages. It is supported by most modern browsers on desktop and mobile devices.', 'WEBPUSH_ENABLE' => 'Enable Webpush', - 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow receiving notifications via Webpush.
Note: If VAPID keys have not been set, phpBB will try to automatically create them when enabling Webpush.', + 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow receiving notifications via Webpush.', 'WEBPUSH_GENERATE_VAPID_KEYS' => 'Generate VAPID keys', 'WEBPUSH_VAPID_PUBLIC' => 'VAPID public key', 'WEBPUSH_VAPID_PUBLIC_EXPLAIN' => 'The VAPID public key will be shared to authenticate push messages sent by your site.
Warning: Changing the VAPID public key will automatically invalidate all webpush subscriptions.', From 6cc8df5d95ec54394f72c06862ed17d018aa4894 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 25 Jun 2023 09:26:06 +0200 Subject: [PATCH 38/63] [ticket/17010] Adjust wording on ACP page for webpush PHPBB3-17010 --- phpBB/language/en/acp/board.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index 1c65cccbc4..7a9b81ef1e 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -602,12 +602,12 @@ $lang = array_merge($lang, array( $lang = array_merge($lang, [ 'ACP_WEBPUSH_SETTINGS_EXPLAIN' => 'Here you can enable and control the use of Webpush for board notifications. Webpush is a simple protocol for the delivery of real-time events to user agents, more commonly known as push messages. It is supported by most modern browsers on desktop and mobile devices.', 'WEBPUSH_ENABLE' => 'Enable Webpush', - 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow receiving notifications via Webpush.', - 'WEBPUSH_GENERATE_VAPID_KEYS' => 'Generate VAPID keys', - 'WEBPUSH_VAPID_PUBLIC' => 'VAPID public key', - 'WEBPUSH_VAPID_PUBLIC_EXPLAIN' => 'The VAPID public key will be shared to authenticate push messages sent by your site.
Warning: Changing the VAPID public key will automatically invalidate all webpush subscriptions.', - 'WEBPUSH_VAPID_PRIVATE' => 'VAPID private key', - 'WEBPUSH_VAPID_PRIVATE_EXPLAIN' => 'The VAPID private key will be used to create authenticated push messages sent by your site. The VAPID private key must be a valid public-private key pair with the VAPID public key.
Warning: Changing the VAPID private key will automatically invalidate all webpush subscriptions.', + 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow receiving notifications via Webpush. It is required to enter or generate valid VAPID identification keys to be able to use Webpush.', + 'WEBPUSH_GENERATE_VAPID_KEYS' => 'Generate Identification keys', + 'WEBPUSH_VAPID_PUBLIC' => 'Server identification public key', + 'WEBPUSH_VAPID_PUBLIC_EXPLAIN' => 'The Voluntary Application Server Identification (VAPID) public key will be shared to authenticate push messages sent by your site.
Warning: Changing the VAPID public key will automatically invalidate all Webpush subscriptions.', + 'WEBPUSH_VAPID_PRIVATE' => 'Server identification private key', + 'WEBPUSH_VAPID_PRIVATE_EXPLAIN' => 'The Voluntary Application Server Identification (VAPID) private key will be used to create authenticated push messages sent by your site. The VAPID private key must be a valid public-private key pair with the VAPID public key.
Warning: Changing the VAPID private key will automatically invalidate all Webpush subscriptions.', ]); // Jabber settings From c35e9c2438e5978ef8589906422e189853641807 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 25 Jun 2023 16:51:29 +0200 Subject: [PATCH 39/63] [ticket/17010] Correctly fill subscription map for multiple subscriptions PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index ea71e1cab8..94e2e95c69 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -176,14 +176,7 @@ class webpush extends messenger_base $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) { - if (isset($user_subscription_map[$row['user_id']])) - { - $user_subscription_map[$row['user_id']] += $row; - } - else - { - $user_subscription_map[$row['user_id']] = [$row]; - } + $user_subscription_map[$row['user_id']][] = $row; } $auth = [ From b779ce5910b88cd995a97c4efac97797d52eee61 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 25 Jun 2023 21:03:08 +0200 Subject: [PATCH 40/63] [ticket/17010] Remove optional column check for non-existent column PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 94e2e95c69..5ec857af05 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -291,8 +291,7 @@ class webpush extends messenger_base public function prune_notifications($timestamp, $only_read = true): void { $sql = 'DELETE FROM ' . $this->notification_webpush_table . ' - WHERE notification_time < ' . (int) $timestamp . - (($only_read) ? ' AND notification_read = 1' : ''); + WHERE notification_time < ' . (int) $timestamp; $this->db->sql_query($sql); $this->config->set('read_notification_last_gc', (string) time(), false); From 79ff21fdf583d2d71c78007170172ff2cbc0b9bb Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Mon, 26 Jun 2023 08:53:26 +0200 Subject: [PATCH 41/63] [ticket/17010] Move get subscription map to separate function and extend tests Unit tests will also now ensure there are no special surprises with more than one subscription for users. PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 35 ++++++++++++++----- .../notification_method_webpush_test.php | 13 +++++-- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 5ec857af05..5091c2823d 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -169,15 +169,7 @@ class webpush extends messenger_base $this->user_loader->load_users($notify_users, array(USER_IGNORE)); // Get subscriptions for users - $user_subscription_map = []; - $sql = 'SELECT subscription_id, user_id, endpoint, p256dh, auth - FROM ' . $this->push_subscriptions_table . ' - WHERE ' . $this->db->sql_in_set('user_id', $notify_users); - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - $user_subscription_map[$row['user_id']][] = $row; - } + $user_subscription_map = $this->get_user_subscription_map($notify_users); $auth = [ 'VAPID' => [ @@ -316,4 +308,29 @@ class webpush extends messenger_base return array_intersect_key($data, $row); } + + /** + * Get subscriptions for notify users + * + * @param array $notify_users Users to notify + * + * @return array Subscription map + */ + protected function get_user_subscription_map(array $notify_users): array + { + // Get subscriptions for users + $user_subscription_map = []; + + $sql = 'SELECT subscription_id, user_id, endpoint, p256dh, auth + FROM ' . $this->push_subscriptions_table . ' + WHERE ' . $this->db->sql_in_set('user_id', $notify_users); + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + $user_subscription_map[$row['user_id']][] = $row; + } + $this->db->sql_freeresult($result); + + return $user_subscription_map; + } } diff --git a/tests/notification/notification_method_webpush_test.php b/tests/notification/notification_method_webpush_test.php index ce79f57838..888ad11a3b 100644 --- a/tests/notification/notification_method_webpush_test.php +++ b/tests/notification/notification_method_webpush_test.php @@ -338,7 +338,14 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $subscription_info = []; foreach ($expected_users as $user_id => $user_data) { - $subscription_info[$user_id] = $this->create_subscription_for_user($user_id); + $subscription_info[$user_id][] = $this->create_subscription_for_user($user_id); + } + + // Create second subscription for first user ID passed + if (count($expected_users)) + { + $first_user_id = array_key_first($expected_users); + $subscription_info[$first_user_id][] = $this->create_subscription_for_user($first_user_id); } $post_data = array_merge([ @@ -361,7 +368,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base foreach ($expected_users as $user_id => $data) { - $messages = $this->get_messages_for_subscription($subscription_info[$user_id]['clientHash']); + $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); $this->assertEmpty($messages); } @@ -372,7 +379,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base foreach ($expected_users as $user_id => $data) { - $messages = $this->get_messages_for_subscription($subscription_info[$user_id]['clientHash']); + $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); $this->assertNotEmpty($messages); } } From 5098f315fd6968ced4eadbcb3cdc14f2caa70b91 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 2 Jul 2023 17:27:50 +0200 Subject: [PATCH 42/63] [ticket/17010] Use special chars decode to have valid URL with amp PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 5091c2823d..3298ee2e3c 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -122,7 +122,7 @@ class webpush extends messenger_base 'heading' => $this->config['sitename'], 'title' => strip_tags($notification->get_title()), 'text' => strip_tags($notification->get_reference()), - 'url' => $notification->get_url(), + 'url' => htmlspecialchars_decode($notification->get_url()), 'avatar' => $notification->get_avatar(), ]), 'notification_time' => time(), From 6b00e9fe09c464b55a23076da684b744607dbd8a Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Mon, 3 Jul 2023 14:38:01 +0200 Subject: [PATCH 43/63] [ticket/17010] Handle already existing subscriptions PHPBB3-17010 --- .../db/migration/data/v400/add_webpush.php | 1 - phpBB/phpbb/ucp/controller/webpush.php | 26 +++++++++-- phpBB/styles/all/js/webpush.js.twig | 45 ++++++++++++++++--- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/phpBB/phpbb/db/migration/data/v400/add_webpush.php b/phpBB/phpbb/db/migration/data/v400/add_webpush.php index 248be387bd..351e02a361 100644 --- a/phpBB/phpbb/db/migration/data/v400/add_webpush.php +++ b/phpBB/phpbb/db/migration/data/v400/add_webpush.php @@ -48,7 +48,6 @@ class add_webpush extends migration 'COLUMNS' => [ 'subscription_id' => ['ULINT', null, 'auto_increment'], 'user_id' => ['ULINT', 0], -// 'device_name' => ['VCHAR:64', ''], 'endpoint' => ['TEXT', ''], 'expiration_time' => ['TIMESTAMP', 0], 'p256dh' => ['VCHAR', ''], diff --git a/phpBB/phpbb/ucp/controller/webpush.php b/phpBB/phpbb/ucp/controller/webpush.php index f1f5c197be..e292f35fac 100644 --- a/phpBB/phpbb/ucp/controller/webpush.php +++ b/phpBB/phpbb/ucp/controller/webpush.php @@ -35,9 +35,6 @@ class webpush /** @var string UCP form token name */ private const FORM_TOKEN_UCP = 'ucp_webpush'; - /** @var string Push worker form token name */ - private const FORM_TOKEN_WORKER = 'webpush_worker'; - /** @var config */ protected $config; @@ -206,6 +203,7 @@ class webpush $template_data += [ 'VAPID_PUBLIC_KEY' => $this->config['webpush_vapid_public'], 'U_WEBPUSH_WORKER_URL' => $this->controller_helper->route('phpbb_ucp_push_worker_controller'), + 'SUBSCRIPTIONS' => $this->get_subscriptions(), ]; $content = $this->template->render('webpush.js.twig', $template_data); @@ -273,4 +271,26 @@ class webpush 'form_tokens' => $this->form_helper->get_form_tokens(self::FORM_TOKEN_UCP), ]); } + + /** + * Get subscriptions for current user + * + * @return array Subscriptions for user + */ + protected function get_subscriptions(): array + { + $subscriptions = []; + + $sql = 'SELECT endpoint, expiration_time + FROM ' . $this->push_subscriptions_table . ' + WHERE user_id = ' . $this->user->id(); + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + $subscriptions[] = $row; + } + $this->db->sql_freeresult($result); + + return $subscriptions; + } } diff --git a/phpBB/styles/all/js/webpush.js.twig b/phpBB/styles/all/js/webpush.js.twig index e634f4539c..b9c776d549 100644 --- a/phpBB/styles/all/js/webpush.js.twig +++ b/phpBB/styles/all/js/webpush.js.twig @@ -18,6 +18,13 @@ function PhpbbWebpush() { formToken: '{{ FORM_TOKENS.form_token }}' }; + /** @type [{endpoint: string, expiration: string}[]] Subscriptions */ + let subscriptions = [ + {% for sub in SUBSCRIPTIONS %} + {endpoint: '{{ sub.endpoint }}', expiration: '{{ sub.expiration }}' }, + {% endfor %} + ]; + /** @type {string} VAPID public key */ const VAPID_PUBLIC_KEY = '{{ VAPID_PUBLIC_KEY }}'; @@ -70,7 +77,7 @@ function PhpbbWebpush() { registration.pushManager.getSubscription() .then((subscribed) => { - if (subscribed) { + if (isValidSubscription(subscribed)) { setSubscriptionState(true); } }) @@ -78,6 +85,31 @@ function PhpbbWebpush() { } } + /** + * Check whether subscription is valid + * + * @param {PushSubscription} subscription + * @returns {boolean} + */ + const isValidSubscription = (subscription) => { + if (!subscription) { + return false; + } + + if (subscription.expirationTime && subscription.expirationTime <= Date.now()) { + return false; + } + + for (const curSubscription of subscriptions) { + if (subscription.endpoint === curSubscription.endpoint) { + return true; + } + } + + // Subscription is not in valid subscription list for user + return false; + }; + /** * Set subscription state for buttons * @@ -111,16 +143,19 @@ function PhpbbWebpush() { } const registration = await navigator.serviceWorker.getRegistration(serviceWorkerUrl); + + // We might already have a subscription that is unknown to this instance of phpBB. + // Unsubscribe before trying to subscribe again. if (typeof registration !== 'undefined') { const subscribed = await registration.pushManager.getSubscription(); if (subscribed) { - setSubscriptionState(true); - return; + await subscribed.unsubscribe(); } } - const newSubscription = await registration.pushManager.subscribe({ + + let newSubscription = await registration.pushManager.subscribe({ userVisibleOnly: true, - applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY) + applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY), }); const loadingIndicator = phpbb.loadingIndicator(); From f3eb774abdb181f2dafc985186ba3b12590754b3 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 30 Jul 2023 09:02:19 +0200 Subject: [PATCH 44/63] [ticket/17010] Add missing language variable PHPBB3-17010 --- phpBB/language/en/acp/common.php | 1 + 1 file changed, 1 insertion(+) diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php index 84c047af54..85418652da 100644 --- a/phpBB/language/en/acp/common.php +++ b/phpBB/language/en/acp/common.php @@ -593,6 +593,7 @@ $lang = array_merge($lang, array( 'LOG_CONFIG_SETTINGS' => 'Altered board settings', 'LOG_CONFIG_SIGNATURE' => 'Altered signature settings', 'LOG_CONFIG_VISUAL' => 'Altered anti-spambot settings', + 'LOG_CONFIG_WEBPUSH' => 'Altered webpush settings', 'LOG_APPROVE_TOPIC' => 'Approved topic
» %s', 'LOG_BUMP_TOPIC' => 'User bumped topic
» %s', From 479e54db932d09e748eef07d08386e1613ba546c Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 15 Aug 2023 08:12:33 +0200 Subject: [PATCH 45/63] [ticket/17010] Add logging to webpush notifications PHPBB3-17010 --- .../container/services_notification.yml | 5 ++-- phpBB/language/en/acp/common.php | 3 +++ phpBB/phpbb/notification/method/webpush.php | 23 ++++++++++++++----- tests/notification/base.php | 1 + .../notification_method_email_test.php | 1 + tests/notification/submit_post_base.php | 1 + 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/phpBB/config/default/container/services_notification.yml b/phpBB/config/default/container/services_notification.yml index acb136641c..92592850f9 100644 --- a/phpBB/config/default/container/services_notification.yml +++ b/phpBB/config/default/container/services_notification.yml @@ -248,10 +248,11 @@ services: class: phpbb\notification\method\webpush shared: false arguments: - - '@user_loader' - - '@user' - '@config' - '@dbal.conn' + - '@log' + - '@user_loader' + - '@user' - '%core.root_path%' - '%core.php_ext%' - '%tables.notification_push%' diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php index 85418652da..b9e2416341 100644 --- a/phpBB/language/en/acp/common.php +++ b/phpBB/language/en/acp/common.php @@ -814,6 +814,9 @@ $lang = array_merge($lang, array( ), 'LOG_WARNINGS_DELETED_ALL' => 'Deleted all user warnings
» %s', + 'LOG_WEBPUSH_MESSAGE_FAIL' => 'WebPush message could not be sent: %s', + 'LOG_WEBPUSH_SUBSCRIPTION_REMOVED' => 'Removed WebPush subscription:» %s', + 'LOG_WORD_ADD' => 'Added word censor
» %s', 'LOG_WORD_DELETE' => 'Deleted word censor
» %s', 'LOG_WORD_EDIT' => 'Edited word censor
» %s', diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 3298ee2e3c..c4d44946ed 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -16,6 +16,7 @@ namespace phpbb\notification\method; use Minishlink\WebPush\Subscription; use phpbb\config\config; use phpbb\db\driver\driver_interface; +use phpbb\log\log_interface; use phpbb\notification\type\type_interface; use phpbb\user; use phpbb\user_loader; @@ -33,6 +34,9 @@ class webpush extends messenger_base /** @var driver_interface */ protected $db; + /** @var log_interface */ + protected $log; + /** @var user */ protected $user; @@ -45,23 +49,25 @@ class webpush extends messenger_base /** * Notification Method web push constructor * - * @param user_loader $user_loader - * @param user $user * @param config $config * @param driver_interface $db + * @param log_interface $log + * @param user_loader $user_loader + * @param user $user * @param string $phpbb_root_path * @param string $php_ext * @param string $notification_webpush_table * @param string $push_subscriptions_table */ - public function __construct(user_loader $user_loader, user $user, config $config, driver_interface $db, string $phpbb_root_path, + public function __construct(config $config, driver_interface $db, log_interface $log, user_loader $user_loader, user $user, string $phpbb_root_path, string $php_ext, string $notification_webpush_table, string $push_subscriptions_table) { parent::__construct($user_loader, $phpbb_root_path, $php_ext); - $this->user = $user; $this->config = $config; $this->db = $db; + $this->log = $log; + $this->user = $user; $this->notification_webpush_table = $notification_webpush_table; $this->push_subscriptions_table = $push_subscriptions_table; } @@ -222,6 +228,10 @@ class webpush extends messenger_base catch (\ErrorException $exception) { $remove_subscriptions[] = $subscription['subscription_id']; + $this->log->add('user', $user['user_id'], $user['user_ip'] ?? '', 'LOG_WEBPUSH_SUBSCRIPTION_REMOVED', false, [ + 'reportee_id' => $user['user_id'], + $user['username'], + ]); } } } @@ -240,13 +250,14 @@ class webpush extends messenger_base { if (!$report->isSuccess()) { - // @todo: log errors / remove subscription + $report_data = \phpbb\json\sanitizer::sanitize($report->jsonSerialize()); + $this->log->add('admin', ANONYMOUS, '', 'LOG_WEBPUSH_MESSAGE_FAIL', false, [$report_data['reason']]); } } } catch (\ErrorException $exception) { - // @todo: write to log + $this->log->add('critical', ANONYMOUS, '', 'LOG_WEBPUSH_MESSAGE_FAIL', false, [$exception->getMessage()]); } // We're done, empty the queue diff --git a/tests/notification/base.php b/tests/notification/base.php index 37718a85a9..8e6f67c219 100644 --- a/tests/notification/base.php +++ b/tests/notification/base.php @@ -106,6 +106,7 @@ abstract class phpbb_tests_notification_base extends phpbb_database_test_case $phpbb_container->set('auth', $auth); $phpbb_container->set('cache.driver', $cache_driver); $phpbb_container->set('cache', $cache); + $phpbb_container->set('log', new \phpbb\log\dummy()); $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils()); $phpbb_container->set( 'text_formatter.s9e.mention_helper', diff --git a/tests/notification/notification_method_email_test.php b/tests/notification/notification_method_email_test.php index 77db505c04..6b464b1c42 100644 --- a/tests/notification/notification_method_email_test.php +++ b/tests/notification/notification_method_email_test.php @@ -83,6 +83,7 @@ class notification_method_email_test extends phpbb_tests_notification_base $phpbb_container->set('auth', $auth); $phpbb_container->set('cache.driver', $cache_driver); $phpbb_container->set('cache', $cache); + $phpbb_container->set('log', new \phpbb\log\dummy()); $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils()); $phpbb_container->set('event_dispatcher', $this->phpbb_dispatcher); $phpbb_container->setParameter('core.root_path', $phpbb_root_path); diff --git a/tests/notification/submit_post_base.php b/tests/notification/submit_post_base.php index 3219c85e27..124ac4d1b4 100644 --- a/tests/notification/submit_post_base.php +++ b/tests/notification/submit_post_base.php @@ -135,6 +135,7 @@ abstract class phpbb_notification_submit_post_base extends phpbb_database_test_c $phpbb_container->set('auth', $auth); $phpbb_container->set('cache.driver', $cache_driver); $phpbb_container->set('cache', $cache); + $phpbb_container->set('log', new \phpbb\log\dummy()); $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils()); $phpbb_container->set( 'text_formatter.s9e.mention_helper', From 2da2211898e8931ac68977438f0d11b24881a531 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 15 Aug 2023 09:47:11 +0200 Subject: [PATCH 46/63] [ticket/17010] Update webpush test PHPBB3-17010 --- tests/notification/notification_method_webpush_test.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/notification/notification_method_webpush_test.php b/tests/notification/notification_method_webpush_test.php index 888ad11a3b..9cf374da1a 100644 --- a/tests/notification/notification_method_webpush_test.php +++ b/tests/notification/notification_method_webpush_test.php @@ -125,6 +125,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $phpbb_container->set('auth', $auth); $phpbb_container->set('cache.driver', $cache_driver); $phpbb_container->set('cache', $cache); + $phpbb_container->set('log', new \phpbb\log\dummy()); $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils()); $phpbb_container->set('dispatcher', $this->phpbb_dispatcher); $phpbb_container->setParameter('core.root_path', $phpbb_root_path); @@ -147,10 +148,11 @@ class notification_method_webpush_test extends phpbb_tests_notification_base ); $this->notification_method_webpush = new \phpbb\notification\method\webpush( - $phpbb_container->get('user_loader'), - $phpbb_container->get('user'), $phpbb_container->get('config'), $phpbb_container->get('dbal.conn'), + $phpbb_container->get('log'), + $phpbb_container->get('user_loader'), + $phpbb_container->get('user'), $phpbb_root_path, $phpEx, $phpbb_container->getParameter('tables.notification_push'), From d85da61c9a9b9203d0058bee10bf1312f926fa63 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 5 Nov 2023 12:55:07 +0100 Subject: [PATCH 47/63] [ticket/17010] Move webpush js to assets folder PHPBB3-17010 --- .../all/js/webpush.js.twig => assets/javascript/webpush.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename phpBB/{styles/all/js/webpush.js.twig => assets/javascript/webpush.js} (100%) diff --git a/phpBB/styles/all/js/webpush.js.twig b/phpBB/assets/javascript/webpush.js similarity index 100% rename from phpBB/styles/all/js/webpush.js.twig rename to phpBB/assets/javascript/webpush.js From fa91bf791f125e63ef467dac760c34c778cbd881 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 5 Nov 2023 12:56:01 +0100 Subject: [PATCH 48/63] [ticket/17010] Make webpush js not depend on twig compilation PHPBB3-17010 --- phpBB/assets/javascript/webpush.js | 93 +++++++++++-------- .../template/ucp_notifications_options.html | 4 +- .../template/ucp_notifications_webpush.html | 21 +++++ 3 files changed, 77 insertions(+), 41 deletions(-) create mode 100644 phpBB/styles/prosilver/template/ucp_notifications_webpush.html diff --git a/phpBB/assets/javascript/webpush.js b/phpBB/assets/javascript/webpush.js index b9c776d549..c326fe1cff 100644 --- a/phpBB/assets/javascript/webpush.js +++ b/phpBB/assets/javascript/webpush.js @@ -4,37 +4,48 @@ function PhpbbWebpush() { /** @type {string} URL to service worker */ - const serviceWorkerUrl = '{{ U_WEBPUSH_WORKER_URL }}'; + let serviceWorkerUrl = ''; /** @type {string} URL to subscribe to push */ - const subscribeUrl = '{{ U_WEBPUSH_SUBSCRIBE }}'; + let subscribeUrl = ''; /** @type {string} URL to unsubscribe from push */ - const unsubscribeUrl = '{{ U_WEBPUSH_UNSUBSCRIBE }}'; + let unsubscribeUrl = ''; /** @type { {creationTime: number, formToken: string} } Form tokens */ this.formTokens = { - creationTime: {{ FORM_TOKENS.creation_time }}, - formToken: '{{ FORM_TOKENS.form_token }}' + creationTime: 0, + formToken: '', }; - /** @type [{endpoint: string, expiration: string}[]] Subscriptions */ - let subscriptions = [ - {% for sub in SUBSCRIPTIONS %} - {endpoint: '{{ sub.endpoint }}', expiration: '{{ sub.expiration }}' }, - {% endfor %} - ]; + /** @type {{endpoint: string, expiration: string}[]} Subscriptions */ + let subscriptions; + + /** @type {string} Title of error message */ + let ajaxErrorTitle = ''; /** @type {string} VAPID public key */ - const VAPID_PUBLIC_KEY = '{{ VAPID_PUBLIC_KEY }}'; + let vapidPublicKey = ''; - let subscribeButton, - unsubscribeButton; + /** @type {HTMLElement} Subscribe button */ + let subscribeButton; + + /** @type {HTMLElement} Unsubscribe button */ + let unsubscribeButton; /** - * Init function for phpBB webpush + * Init function for phpBB Web Push + * @type {array} options */ - this.init = function() { + this.init = function(options) { + serviceWorkerUrl = options.serviceWorkerUrl; + subscribeUrl = options.subscribeUrl; + unsubscribeUrl = options.unsubscribeUrl; + this.formTokens = options.formTokens; + subscriptions = options.subscriptions; + ajaxErrorTitle = options.ajaxErrorTitle; + vapidPublicKey = options.vapidPublicKey; + subscribeButton = document.querySelector('#subscribe_webpush'); unsubscribeButton = document.querySelector('#unsubscribe_webpush'); @@ -70,17 +81,17 @@ function PhpbbWebpush() { function updateButtonState() { if (Notification.permission === 'granted') { navigator.serviceWorker.getRegistration(serviceWorkerUrl) - .then((registration) => { + .then(registration => { if (typeof registration === 'undefined') { return; } registration.pushManager.getSubscription() - .then((subscribed) => { + .then(subscribed => { if (isValidSubscription(subscribed)) { setSubscriptionState(true); } - }) + }); }); } } @@ -91,7 +102,7 @@ function PhpbbWebpush() { * @param {PushSubscription} subscription * @returns {boolean} */ - const isValidSubscription = (subscription) => { + const isValidSubscription = subscription => { if (!subscription) { return false; } @@ -153,27 +164,27 @@ function PhpbbWebpush() { } } - let newSubscription = await registration.pushManager.subscribe({ + const newSubscription = await registration.pushManager.subscribe({ userVisibleOnly: true, - applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY), + applicationServerKey: urlB64ToUint8Array(vapidPublicKey), }); const loadingIndicator = phpbb.loadingIndicator(); fetch(subscribeUrl, { - method: 'POST', - headers: { - 'X-Requested-With': 'XMLHttpRequest' - }, - body: getFormData(newSubscription) - }) - .then((response) => { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + body: getFormData(newSubscription), + }) + .then(response => { loadingIndicator.fadeOut(phpbb.alertTime); return response.json(); }) .then(handleSubscribe) - .catch((error) => { + .catch(error => { loadingIndicator.fadeOut(phpbb.alertTime); - phpbb.alert('{{ lang('AJAX_ERROR_TITLE') }}', error); + phpbb.alert(ajaxErrorTitle, error); }); } @@ -196,22 +207,22 @@ function PhpbbWebpush() { fetch(unsubscribeUrl, { method: 'POST', headers: { - 'X-Requested-With': 'XMLHttpRequest' + 'X-Requested-With': 'XMLHttpRequest', }, - body: getFormData({endpoint: subscription.endpoint}) + body: getFormData({ endpoint: subscription.endpoint }), }) .then(() => { loadingIndicator.fadeOut(phpbb.alertTime); return subscription.unsubscribe(); }) - .then((unsubscribed) => { + .then(unsubscribed => { if (unsubscribed) { setSubscriptionState(false); } }) - .catch((error) => { + .catch(error => { loadingIndicator.fadeOut(phpbb.alertTime); - phpbb.alert('{{ lang('AJAX_ERROR_TITLE') }}', error); + phpbb.alert(ajaxErrorTitle, error); }); } @@ -223,7 +234,7 @@ function PhpbbWebpush() { function handleSubscribe(response) { if (response.success) { setSubscriptionState(true); - if (response.hasOwnProperty('form_tokens')) { + if ('form_tokens' in response) { updateFormTokens(response.form_tokens); } } @@ -236,7 +247,7 @@ function PhpbbWebpush() { * @returns {FormData} Form data */ function getFormData(data) { - let formData = new FormData(); + const formData = new FormData(); formData.append('form_token', phpbb.webpush.formTokens.formToken); formData.append('creation_time', phpbb.webpush.formTokens.creationTime.toString()); formData.append('data', JSON.stringify(data)); @@ -261,13 +272,14 @@ function PhpbbWebpush() { * @returns {Uint8Array} */ function urlB64ToUint8Array(base64String) { - const padding = '='.repeat((4 - base64String.length % 4) % 4); + const padding = '='.repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); const rawData = window.atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } + return outputArray; } } @@ -283,5 +295,6 @@ function domReady(callBack) { phpbb.webpush = new PhpbbWebpush(); domReady(() => { - phpbb.webpush.init(); + /* global phpbbWebpushOptions */ + phpbb.webpush.init(phpbbWebpushOptions); }); diff --git a/phpBB/styles/prosilver/template/ucp_notifications_options.html b/phpBB/styles/prosilver/template/ucp_notifications_options.html index 205e8d4394..a0d9caad12 100644 --- a/phpBB/styles/prosilver/template/ucp_notifications_options.html +++ b/phpBB/styles/prosilver/template/ucp_notifications_options.html @@ -1,6 +1,8 @@ {% include('ucp_header.html') %} -{% INCLUDEJS(T_WEBPUSH_JS_PATH) %} +{% if NOTIFICATIONS_WEBPUSH_ENABLE %} + {% include('ucp_notifications_webpush.html') %} +{% endif %} diff --git a/phpBB/styles/prosilver/template/ucp_notifications_webpush.html b/phpBB/styles/prosilver/template/ucp_notifications_webpush.html new file mode 100644 index 0000000000..31c4790b33 --- /dev/null +++ b/phpBB/styles/prosilver/template/ucp_notifications_webpush.html @@ -0,0 +1,21 @@ + + +{% INCLUDEJS(T_ASSETS_PATH ~ '/javascript/webpush.js') %} + From 8d9a7aa62c3002c0545b856691f85677769aee15 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 5 Nov 2023 12:56:39 +0100 Subject: [PATCH 49/63] [ticket/17010] Add new interface and create template data in type PHPBB3-17010 --- phpBB/includes/ucp/ucp_notifications.php | 59 +++++++++---------- .../method/extended_method_interface.php | 29 +++++++++ phpBB/phpbb/notification/method/webpush.php | 33 ++++++++++- phpBB/phpbb/ucp/controller/webpush.php | 2 +- 4 files changed, 89 insertions(+), 34 deletions(-) create mode 100644 phpBB/phpbb/notification/method/extended_method_interface.php diff --git a/phpBB/includes/ucp/ucp_notifications.php b/phpBB/includes/ucp/ucp_notifications.php index 9e03e68509..62d7fbd112 100644 --- a/phpBB/includes/ucp/ucp_notifications.php +++ b/phpBB/includes/ucp/ucp_notifications.php @@ -14,6 +14,11 @@ /** * @ignore */ + +use phpbb\controller\helper; +use phpbb\form\form_helper; +use phpbb\notification\method\extended_method_interface; + if (!defined('IN_PHPBB')) { exit; @@ -23,17 +28,29 @@ class ucp_notifications { public $u_action; + private const FORM_TOKEN_NAME = 'ucp_notification'; + + /** @var helper */ + private helper $controller_helper; + + /** @var form_helper */ + private form_helper $form_helper; + public function main($id, $mode) { global $config, $template, $user, $request, $phpbb_container, $phpbb_dispatcher; global $phpbb_root_path, $phpEx; - add_form_key('ucp_notification'); + add_form_key(self::FORM_TOKEN_NAME); $start = $request->variable('start', 0); $form_time = $request->variable('form_time', 0); $form_time = ($form_time <= 0 || $form_time > time()) ? time() : $form_time; + + $this->controller_helper = $phpbb_container->get('controller.helper'); + $this->form_helper = $phpbb_container->get('form_helper'); + /* @var $phpbb_notifications \phpbb\notification\manager */ $phpbb_notifications = $phpbb_container->get('notification_manager'); @@ -48,7 +65,7 @@ class ucp_notifications // Add/remove subscriptions if ($request->is_set_post('submit')) { - if (!check_form_key('ucp_notification')) + if (!check_form_key(self::FORM_TOKEN_NAME)) { trigger_error('FORM_INVALID'); } @@ -103,15 +120,12 @@ class ucp_notifications trigger_error($message); } - $this->output_notification_methods($phpbb_notifications, $template, $user, 'notification_methods'); + $this->output_notification_methods($phpbb_notifications, $template, $user); $this->output_notification_types($subscriptions, $phpbb_notifications, $template, $user, $phpbb_dispatcher, 'notification_types'); - /** @var \phpbb\controller\helper $controller_helper */ - $controller_helper = $phpbb_container->get('controller.helper'); - $template->assign_vars([ - 'T_WEBPUSH_JS_PATH' => $controller_helper->route('phpbb_ucp_push_js_controller'), + 'FORM_TOKENS' => $this->form_helper->get_form_tokens(self::FORM_TOKEN_NAME), ]); $this->tpl_name = 'ucp_notifications_options'; @@ -145,7 +159,7 @@ class ucp_notifications // Mark specific notifications read if ($request->is_set_post('submit')) { - if (!check_form_key('ucp_notification')) + if (!check_form_key(self::FORM_TOKEN_NAME)) { trigger_error('FORM_INVALID'); } @@ -273,35 +287,18 @@ class ucp_notifications { $notification_methods = $phpbb_notifications->get_subscription_methods(); - if (isset($notification_methods['notification.method.webpush'])) + foreach ($notification_methods as $method_data) { - $this->output_webpush_data($template); - } + if ($method_data['method'] instanceof extended_method_interface) + { + $ucp_template_data = $method_data['method']->get_ucp_template_data($this->controller_helper, $this->form_helper); + $template->assign_vars($ucp_template_data); + } - foreach ($notification_methods as $method => $method_data) - { $template->assign_block_vars($block, array( 'METHOD' => $method_data['id'], - 'NAME' => $user->lang($method_data['lang']), )); } } - - /** - * Output data for webpush - * - * @param \phpbb\template\template $template - * - * @return void - */ - protected function output_webpush_data(\phpbb\template\template $template): void - { - global $config; - - $template->assign_vars([ - 'NOTIFICATIONS_WEBPUSH_ENABLE' => true, // already checked, otherwise we wouldn't be here - 'NOTIFICATIONS_WEBPUSH_VAPID_PUBLIC' => $config['webpush_vapid_public'], - ]); - } } diff --git a/phpBB/phpbb/notification/method/extended_method_interface.php b/phpBB/phpbb/notification/method/extended_method_interface.php new file mode 100644 index 0000000000..866288ec3e --- /dev/null +++ b/phpBB/phpbb/notification/method/extended_method_interface.php @@ -0,0 +1,29 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\notification\method; + +use phpbb\controller\helper; +use phpbb\form\form_helper; + +interface extended_method_interface extends method_interface +{ + /** + * Get UCP template data for type + * + * @param helper $controller_helper + * @param form_helper $form_helper + * @return array Template data + */ + public function get_ucp_template_data(helper $controller_helper, form_helper $form_helper): array; +} diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index c4d44946ed..273ddcddd5 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -15,7 +15,9 @@ namespace phpbb\notification\method; use Minishlink\WebPush\Subscription; use phpbb\config\config; +use phpbb\controller\helper; use phpbb\db\driver\driver_interface; +use phpbb\form\form_helper; use phpbb\log\log_interface; use phpbb\notification\type\type_interface; use phpbb\user; @@ -26,7 +28,7 @@ use phpbb\user_loader; * This class handles sending push messages for notifications */ -class webpush extends messenger_base +class webpush extends messenger_base implements extended_method_interface { /** @var config */ protected $config; @@ -320,6 +322,33 @@ class webpush extends messenger_base return array_intersect_key($data, $row); } + public function get_ucp_template_data(helper $controller_helper, form_helper $form_helper): array + { + $subscription_map = $this->get_user_subscription_map([$this->user->id()]); + $subscriptions = []; + + if (isset($subscription_map[$this->user->id()])) + { + foreach ($subscription_map[$this->user->id()] as $subscription) + { + $subscriptions[] = [ + 'endpoint' => $subscription['endpoint'], + 'expirationTime' => $subscription['expiration_time'], + ]; + } + } + + return [ + 'NOTIFICATIONS_WEBPUSH_ENABLE' => true, + 'U_WEBPUSH_SUBSCRIBE' => $controller_helper->route('phpbb_ucp_push_subscribe_controller'), + 'U_WEBPUSH_UNSUBSCRIBE' => $controller_helper->route('phpbb_ucp_push_unsubscribe_controller'), + 'VAPID_PUBLIC_KEY' => $this->config['webpush_vapid_public'], + 'U_WEBPUSH_WORKER_URL' => $controller_helper->route('phpbb_ucp_push_worker_controller'), + 'SUBSCRIPTIONS' => $subscriptions, + 'WEBPUSH_FORM_TOKENS' => $form_helper->get_form_tokens(\phpbb\ucp\controller\webpush::FORM_TOKEN_UCP), + ]; + } + /** * Get subscriptions for notify users * @@ -332,7 +361,7 @@ class webpush extends messenger_base // Get subscriptions for users $user_subscription_map = []; - $sql = 'SELECT subscription_id, user_id, endpoint, p256dh, auth + $sql = 'SELECT subscription_id, user_id, endpoint, p256dh, auth, expiration_time FROM ' . $this->push_subscriptions_table . ' WHERE ' . $this->db->sql_in_set('user_id', $notify_users); $result = $this->db->sql_query($sql); diff --git a/phpBB/phpbb/ucp/controller/webpush.php b/phpBB/phpbb/ucp/controller/webpush.php index e292f35fac..a9b5e133cf 100644 --- a/phpBB/phpbb/ucp/controller/webpush.php +++ b/phpBB/phpbb/ucp/controller/webpush.php @@ -33,7 +33,7 @@ use Twig\Error\SyntaxError; class webpush { /** @var string UCP form token name */ - private const FORM_TOKEN_UCP = 'ucp_webpush'; + public const FORM_TOKEN_UCP = 'ucp_webpush'; /** @var config */ protected $config; From bfc3a969eeabc410074ee82eec1f74ae63bc08ef Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 5 Nov 2023 14:38:52 +0100 Subject: [PATCH 50/63] [ticket/17010] Remove no longer used route & handler PHPBB3-17010 --- phpBB/config/default/routing/ucp.yml | 4 ---- phpBB/phpbb/ucp/controller/webpush.php | 33 -------------------------- 2 files changed, 37 deletions(-) diff --git a/phpBB/config/default/routing/ucp.yml b/phpBB/config/default/routing/ucp.yml index dac6461ac5..772910bfe1 100644 --- a/phpBB/config/default/routing/ucp.yml +++ b/phpBB/config/default/routing/ucp.yml @@ -21,7 +21,3 @@ phpbb_ucp_push_subscribe_controller: phpbb_ucp_push_unsubscribe_controller: path: /push/unsubscribe defaults: { _controller: phpbb.ucp.controller.webpush:unsubscribe } - -phpbb_ucp_push_js_controller: - path: /push/js - defaults: { _controller: phpbb.ucp.controller.webpush:js } diff --git a/phpBB/phpbb/ucp/controller/webpush.php b/phpBB/phpbb/ucp/controller/webpush.php index a9b5e133cf..79f6b110cc 100644 --- a/phpBB/phpbb/ucp/controller/webpush.php +++ b/phpBB/phpbb/ucp/controller/webpush.php @@ -187,39 +187,6 @@ class webpush } } - /** - * Handle request to web push javascript - * - * @return Response - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - public function js(): Response - { - // @todo: return forbidden for guest & bot - - $template_data = $this->get_subscribe_vars(); - $template_data += [ - 'VAPID_PUBLIC_KEY' => $this->config['webpush_vapid_public'], - 'U_WEBPUSH_WORKER_URL' => $this->controller_helper->route('phpbb_ucp_push_worker_controller'), - 'SUBSCRIPTIONS' => $this->get_subscriptions(), - ]; - - $content = $this->template->render('webpush.js.twig', $template_data); - - $response = new Response($content); - $response->headers->set('Content-Type', 'text/javascript; charset=UTF-8'); - - if (!empty($this->user->data['is_bot'])) - { - // Let reverse proxies know we detected a bot. - $response->headers->set('X-PHPBB-IS-BOT', 'yes'); - } - - return $response; - } - /** * Handle subscribe requests * From d79e10e032a474e072a540e719dcbf57d2141039 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 5 Nov 2023 14:39:12 +0100 Subject: [PATCH 51/63] [ticket/17010] Unify naming of Web Push PHPBB3-17010 --- phpBB/language/en/acp/board.php | 6 +++--- phpBB/language/en/acp/common.php | 8 ++++---- phpBB/language/en/ucp.php | 4 ++-- phpBB/phpbb/notification/method/webpush.php | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index 7a9b81ef1e..58a256bb2c 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -600,9 +600,9 @@ $lang = array_merge($lang, array( )); $lang = array_merge($lang, [ - 'ACP_WEBPUSH_SETTINGS_EXPLAIN' => 'Here you can enable and control the use of Webpush for board notifications. Webpush is a simple protocol for the delivery of real-time events to user agents, more commonly known as push messages. It is supported by most modern browsers on desktop and mobile devices.', - 'WEBPUSH_ENABLE' => 'Enable Webpush', - 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow receiving notifications via Webpush. It is required to enter or generate valid VAPID identification keys to be able to use Webpush.', + 'ACP_WEBPUSH_SETTINGS_EXPLAIN' => 'Here you can enable and control the use of Web Push for board notifications. Web Push is a simple protocol for the delivery of real-time events to user agents, more commonly known as push messages. It is supported by most modern browsers on desktop and mobile devices.', + 'WEBPUSH_ENABLE' => 'Enable Web Push', + 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow receiving notifications via Web Push. It is required to enter or generate valid VAPID identification keys to be able to use Web Push.', 'WEBPUSH_GENERATE_VAPID_KEYS' => 'Generate Identification keys', 'WEBPUSH_VAPID_PUBLIC' => 'Server identification public key', 'WEBPUSH_VAPID_PUBLIC_EXPLAIN' => 'The Voluntary Application Server Identification (VAPID) public key will be shared to authenticate push messages sent by your site.
Warning: Changing the VAPID public key will automatically invalidate all Webpush subscriptions.', diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php index b9e2416341..ae2b54f62f 100644 --- a/phpBB/language/en/acp/common.php +++ b/phpBB/language/en/acp/common.php @@ -219,7 +219,7 @@ $lang = array_merge($lang, array( 'ACP_VIEW_GLOBAL_MOD_PERMISSIONS' => 'View global moderation permissions', 'ACP_VIEW_USER_PERMISSIONS' => 'View user-based permissions', - 'ACP_WEBPUSH_SETTINGS' => 'Webpush settings', + 'ACP_WEBPUSH_SETTINGS' => 'Web Push settings', 'ACP_WORDS' => 'Word censoring', 'ACTION' => 'Action', @@ -593,7 +593,7 @@ $lang = array_merge($lang, array( 'LOG_CONFIG_SETTINGS' => 'Altered board settings', 'LOG_CONFIG_SIGNATURE' => 'Altered signature settings', 'LOG_CONFIG_VISUAL' => 'Altered anti-spambot settings', - 'LOG_CONFIG_WEBPUSH' => 'Altered webpush settings', + 'LOG_CONFIG_WEBPUSH' => 'Altered Web Push settings', 'LOG_APPROVE_TOPIC' => 'Approved topic
» %s', 'LOG_BUMP_TOPIC' => 'User bumped topic
» %s', @@ -814,8 +814,8 @@ $lang = array_merge($lang, array( ), 'LOG_WARNINGS_DELETED_ALL' => 'Deleted all user warnings
» %s', - 'LOG_WEBPUSH_MESSAGE_FAIL' => 'WebPush message could not be sent: %s', - 'LOG_WEBPUSH_SUBSCRIPTION_REMOVED' => 'Removed WebPush subscription:» %s', + 'LOG_WEBPUSH_MESSAGE_FAIL' => 'Web Push message could not be sent: %s', + 'LOG_WEBPUSH_SUBSCRIPTION_REMOVED' => 'Removed Web Push subscription:» %s', 'LOG_WORD_ADD' => 'Added word censor
» %s', 'LOG_WORD_DELETE' => 'Deleted word censor
» %s', diff --git a/phpBB/language/en/ucp.php b/phpBB/language/en/ucp.php index 19d3a2c06e..3d90f15742 100644 --- a/phpBB/language/en/ucp.php +++ b/phpBB/language/en/ucp.php @@ -332,7 +332,7 @@ $lang = array_merge($lang, array( 'NOTIFICATION_METHOD_BOARD' => 'Notifications', 'NOTIFICATION_METHOD_EMAIL' => 'Email', 'NOTIFICATION_METHOD_JABBER' => 'Jabber', - 'NOTIFICATION_METHOD_WEBPUSH' => 'Web push', + 'NOTIFICATION_METHOD_WEBPUSH' => 'Web Push', 'NOTIFICATION_TYPE' => 'Notification type', 'NOTIFICATION_TYPE_BOOKMARK' => 'Someone replies to a topic you have bookmarked', 'NOTIFICATION_TYPE_GROUP_REQUEST' => 'Someone requests to join a group you lead', @@ -356,7 +356,7 @@ $lang = array_merge($lang, array( 'NOTIFY_METHOD_EXPLAIN' => 'Method for sending messages sent via this board.', 'NOTIFY_METHOD_IM' => 'Jabber only', 'NOTIFY_ON_PM' => 'Notify me on new private messages', - 'NOTIFY_WEBPUSH_ENABLE' => 'Enable receiving webpush notifications', + 'NOTIFY_WEBPUSH_ENABLE' => 'Enable receiving Web Push notifications', 'NOTIFY_WEBPUSH_ENABLE_EXPLAIN' => 'Enable receiving browser-based push notifications.
The notifications can be turned off at any time in your browser settings, by unsubscribing, or by disabling the push notifications below.', 'NOTIFY_WEBPUSH_SUBSCRIBE' => 'Subscribe', 'NOTIFY_WEBPUSH_UNSUBSCRIBE' => 'Unsubscribe', diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 273ddcddd5..386dfd2525 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -24,7 +24,7 @@ use phpbb\user; use phpbb\user_loader; /** -* Web push notification method class +* Web Push notification method class * This class handles sending push messages for notifications */ @@ -42,14 +42,14 @@ class webpush extends messenger_base implements extended_method_interface /** @var user */ protected $user; - /** @var string Notification web push table */ + /** @var string Notification Web Push table */ protected $notification_webpush_table; /** @var string Notification push subscriptions table */ protected $push_subscriptions_table; /** - * Notification Method web push constructor + * Notification Method Web Push constructor * * @param config $config * @param driver_interface $db @@ -147,7 +147,7 @@ class webpush extends messenger_base implements extended_method_interface } /** - * Notify using web push + * Notify using Web Push * * @return void */ @@ -206,7 +206,7 @@ class webpush extends messenger_base implements extended_method_interface continue; } - // add actual web push data + // Add actual Web Push data $data = [ 'item_id' => $notification->item_id, 'type_id' => $notification->notification_type_id, From a86d222ab04459746843698bf1431c127ae377c4 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 5 Nov 2023 14:59:10 +0100 Subject: [PATCH 52/63] [ticket/17010] Remove not needed empty line PHPBB3-17010 --- phpBB/includes/ucp/ucp_notifications.php | 1 - 1 file changed, 1 deletion(-) diff --git a/phpBB/includes/ucp/ucp_notifications.php b/phpBB/includes/ucp/ucp_notifications.php index 62d7fbd112..94a1586f9f 100644 --- a/phpBB/includes/ucp/ucp_notifications.php +++ b/phpBB/includes/ucp/ucp_notifications.php @@ -47,7 +47,6 @@ class ucp_notifications $form_time = $request->variable('form_time', 0); $form_time = ($form_time <= 0 || $form_time > time()) ? time() : $form_time; - $this->controller_helper = $phpbb_container->get('controller.helper'); $this->form_helper = $phpbb_container->get('form_helper'); From adf639e871275da79265b18ba2bc29af187695dc Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 5 Nov 2023 15:15:11 +0100 Subject: [PATCH 53/63] [ticket/17010] Clean up code and remove unused method PHPBB3-17010 --- phpBB/phpbb/ucp/controller/webpush.php | 32 ++++---------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/phpBB/phpbb/ucp/controller/webpush.php b/phpBB/phpbb/ucp/controller/webpush.php index 79f6b110cc..f1cd513df4 100644 --- a/phpBB/phpbb/ucp/controller/webpush.php +++ b/phpBB/phpbb/ucp/controller/webpush.php @@ -113,9 +113,9 @@ class webpush $sql = 'SELECT push_data FROM ' . $this->notification_webpush_table . ' - WHERE user_id = ' . $this->user->id() . ' - AND notification_type_id = ' . $type_id . ' - AND item_id = ' . $item_id; + WHERE user_id = ' . (int) $this->user->id() . ' + AND notification_type_id = ' . (int) $type_id . ' + AND item_id = ' . (int) $item_id; $result = $this->db->sql_query($sql); $notification_data = $this->db->sql_fetchfield('push_data'); $this->db->sql_freeresult($result); @@ -229,8 +229,8 @@ class webpush $endpoint = $data['endpoint']; $sql = 'DELETE FROM ' . $this->push_subscriptions_table . ' - WHERE user_id = ' . $this->user->id() . " - AND endpoint = '" . $this->db->sql_escape($endpoint) . "'"; + WHERE user_id = ' . (int) $this->user->id() . " + AND endpoint = '" . (int) $this->db->sql_escape($endpoint) . "'"; $this->db->sql_query($sql); return new JsonResponse([ @@ -238,26 +238,4 @@ class webpush 'form_tokens' => $this->form_helper->get_form_tokens(self::FORM_TOKEN_UCP), ]); } - - /** - * Get subscriptions for current user - * - * @return array Subscriptions for user - */ - protected function get_subscriptions(): array - { - $subscriptions = []; - - $sql = 'SELECT endpoint, expiration_time - FROM ' . $this->push_subscriptions_table . ' - WHERE user_id = ' . $this->user->id(); - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - $subscriptions[] = $row; - } - $this->db->sql_freeresult($result); - - return $subscriptions; - } } From 4e9fb6ed4f71b9f84e52524a2ddfaffc9bf06f9e Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 5 Nov 2023 15:35:04 +0100 Subject: [PATCH 54/63] [ticket/17010] Clean up push worker PHPBB3-17010 --- phpBB/styles/all/js/push_worker.js.twig | 44 +++++++++++-------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/phpBB/styles/all/js/push_worker.js.twig b/phpBB/styles/all/js/push_worker.js.twig index 91dbf6f4ad..8d6ec3c6af 100644 --- a/phpBB/styles/all/js/push_worker.js.twig +++ b/phpBB/styles/all/js/push_worker.js.twig @@ -6,46 +6,40 @@ self.addEventListener('push', event => { return; } - let itemId = 0, - typeId = 0; + let itemId = 0; + let typeId = 0; try { const notificationData = event.data.json(); - itemId = notificationData['item_id']; - typeId = notificationData['type_id']; - } catch (e) { + itemId = notificationData.item_id; + typeId = notificationData.type_id; + } catch { self.registration.showNotification(event.data.text()); return; } const getNotificationUrl = '{{ U_WEBPUSH_GET_NOTIFICATION }}'; - let formData = new FormData(); + const formData = new FormData(); formData.append('item_id', itemId.toString(10)); formData.append('type_id', typeId.toString(10)); fetch(getNotificationUrl, { - method: 'POST', - headers: { - 'X-Requested-With': 'XMLHttpRequest' - }, - body: formData - }) - .then((response) => response.json()) - .then((response) => { - const responseBody = response.title + "\n" + response.text; + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + body: formData, + }) + .then(response => response.json()) + .then(response => { + const responseBody = response.title + '\n' + response.text; const options = { body: responseBody, data: response, - icon: response.avatar.src - // foo: '' - //icon: image - } - self.registration.showNotification( - response.heading, - options - ); - } - ); + icon: response.avatar.src, + }; + self.registration.showNotification(response.heading, options); + }); }); /** From 5ada5728ee40814840a96d4d5a3e1d5673888f2a Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sun, 5 Nov 2023 15:41:17 +0100 Subject: [PATCH 55/63] [ticket/17010] Also check .js.twig files with eslint PHPBB3-17010 --- .github/check-js.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/check-js.sh b/.github/check-js.sh index 702ec03cbf..03d040d25e 100755 --- a/.github/check-js.sh +++ b/.github/check-js.sh @@ -15,4 +15,5 @@ sudo npm install -g > /dev/null npm ci > /dev/null set -x node_modules/eslint/bin/eslint.js "phpBB/**/*.js" +node_modules/eslint/bin/eslint.js "phpBB/**/*.js.twig" node_modules/eslint/bin/eslint.js "gulpfile.js" From 44861c1f45cf1d69e040cae1517062f3fb471c7a Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 24 Feb 2024 17:49:44 +0100 Subject: [PATCH 56/63] [ticket/17010] Add ban manager to webpush test PHPBB3-17010 --- .../webpush_notification.type.post.xml | 2 -- .../notification_method_webpush_test.php | 24 +++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/notification/fixtures/webpush_notification.type.post.xml b/tests/notification/fixtures/webpush_notification.type.post.xml index 9d3d72d531..7654ed30aa 100644 --- a/tests/notification/fixtures/webpush_notification.type.post.xml +++ b/tests/notification/fixtures/webpush_notification.type.post.xml @@ -1,7 +1,5 @@ - -
forum_iduser_id diff --git a/tests/notification/notification_method_webpush_test.php b/tests/notification/notification_method_webpush_test.php index 9cf374da1a..0faff80576 100644 --- a/tests/notification/notification_method_webpush_test.php +++ b/tests/notification/notification_method_webpush_test.php @@ -97,8 +97,8 @@ class notification_method_webpush_test extends phpbb_tests_notification_base 'webpush_vapid_private' => self::VAPID_KEYS['privateKey'], ]); $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); - $lang = new \phpbb\language\language($lang_loader); - $user = new \phpbb\user($lang, '\phpbb\datetime'); + $language = new \phpbb\language\language($lang_loader); + $user = new \phpbb\user($language, '\phpbb\datetime'); $this->user = $user; $this->user_loader = new \phpbb\user_loader($avatar_helper, $this->db, $phpbb_root_path, $phpEx, 'phpbb_users'); $auth = $this->auth = new phpbb_mock_notifications_auth(); @@ -113,19 +113,20 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $phpbb_root_path, $phpEx ); + $phpbb_log = new \phpbb\log\dummy(); $phpbb_container = $this->container = new ContainerBuilder(); $loader = new YamlFileLoader($phpbb_container, new FileLocator(__DIR__ . '/fixtures')); $loader->load('services_notification.yml'); $phpbb_container->set('user_loader', $this->user_loader); $phpbb_container->set('user', $user); - $phpbb_container->set('language', $lang); + $phpbb_container->set('language', $language); $phpbb_container->set('config', $this->config); $phpbb_container->set('dbal.conn', $this->db); $phpbb_container->set('auth', $auth); $phpbb_container->set('cache.driver', $cache_driver); $phpbb_container->set('cache', $cache); - $phpbb_container->set('log', new \phpbb\log\dummy()); + $phpbb_container->set('log', $phpbb_log); $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils()); $phpbb_container->set('dispatcher', $this->phpbb_dispatcher); $phpbb_container->setParameter('core.root_path', $phpbb_root_path); @@ -147,6 +148,19 @@ class notification_method_webpush_test extends phpbb_tests_notification_base ) ); + $ban_type_email = new \phpbb\ban\type\email($this->db, 'phpbb_bans', 'phpbb_users', 'phpbb_sessions', 'phpbb_sessions_keys'); + $ban_type_user = new \phpbb\ban\type\user($this->db, 'phpbb_bans', 'phpbb_users', 'phpbb_sessions', 'phpbb_sessions_keys'); + $ban_type_ip = new \phpbb\ban\type\ip($this->db, 'phpbb_bans', 'phpbb_users', 'phpbb_sessions', 'phpbb_sessions_keys'); + $phpbb_container->set('ban.type.email', $ban_type_email); + $phpbb_container->set('ban.type.user', $ban_type_user); + $phpbb_container->set('ban.type.ip', $ban_type_ip); + $collection = new \phpbb\di\service_collection($phpbb_container); + $collection->add('ban.type.email'); + $collection->add('ban.type.user'); + $collection->add('ban.type.ip'); + $ban_manager = new \phpbb\ban\manager($collection, new \phpbb\cache\driver\dummy(), $this->db, $language, $phpbb_log, $user, 'phpbb_bans', 'phpbb_users'); + $phpbb_container->set('ban.manager', $ban_manager); + $this->notification_method_webpush = new \phpbb\notification\method\webpush( $phpbb_container->get('config'), $phpbb_container->get('dbal.conn'), @@ -169,7 +183,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $this->phpbb_dispatcher, $this->db, $this->cache, - $lang, + $language, $this->user, 'phpbb_notification_types', 'phpbb_user_notifications' From e993d82bc57f613b9555ac67738a818f93fd283e Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 24 Feb 2024 20:53:03 +0100 Subject: [PATCH 57/63] [ticket/17010] Update language keys based on review recommendations PHPBB3-17010 --- phpBB/language/en/acp/board.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index 58a256bb2c..8dde2c7252 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -600,14 +600,14 @@ $lang = array_merge($lang, array( )); $lang = array_merge($lang, [ - 'ACP_WEBPUSH_SETTINGS_EXPLAIN' => 'Here you can enable and control the use of Web Push for board notifications. Web Push is a simple protocol for the delivery of real-time events to user agents, more commonly known as push messages. It is supported by most modern browsers on desktop and mobile devices.', + 'ACP_WEBPUSH_SETTINGS_EXPLAIN' => 'Here you can enable Web Push for board notifications. Web Push is a protocol for the real-time delivery of events to user agents, commonly referred to as push messages. It is compatible with the majority of modern browsers on both desktop and mobile devices. Users can opt to receive Web Push alerts in their browser by subscribing and enabling their preferred notifications in the UCP.', 'WEBPUSH_ENABLE' => 'Enable Web Push', - 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow receiving notifications via Web Push. It is required to enter or generate valid VAPID identification keys to be able to use Web Push.', + 'WEBPUSH_ENABLE_EXPLAIN' => 'Allow users to receive notifications in their browser or device via Web Push. To utilize Web Push, you must input or generate valid VAPID identification keys.', 'WEBPUSH_GENERATE_VAPID_KEYS' => 'Generate Identification keys', 'WEBPUSH_VAPID_PUBLIC' => 'Server identification public key', - 'WEBPUSH_VAPID_PUBLIC_EXPLAIN' => 'The Voluntary Application Server Identification (VAPID) public key will be shared to authenticate push messages sent by your site.
Warning: Changing the VAPID public key will automatically invalidate all Webpush subscriptions.', + 'WEBPUSH_VAPID_PUBLIC_EXPLAIN' => 'The Voluntary Application Server Identification (VAPID) public key is shared to authenticate push messages from your site.
Caution: Modifying the VAPID public key will automatically render all Web Push subscriptions invalid.', 'WEBPUSH_VAPID_PRIVATE' => 'Server identification private key', - 'WEBPUSH_VAPID_PRIVATE_EXPLAIN' => 'The Voluntary Application Server Identification (VAPID) private key will be used to create authenticated push messages sent by your site. The VAPID private key must be a valid public-private key pair with the VAPID public key.
Warning: Changing the VAPID private key will automatically invalidate all Webpush subscriptions.', + 'WEBPUSH_VAPID_PRIVATE_EXPLAIN' => 'The Voluntary Application Server Identification (VAPID) private key is used to generate authenticated push messages dispatched from your site. The VAPID private key must form a valid public-private key pair alongside the VAPID public key.
Caution: Modifying the VAPID private key will automatically render all Web Push subscriptions invalid.', ]); // Jabber settings From a3f656356836e3b6436f6b136102d2a3dc22301b Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Sat, 24 Feb 2024 22:29:48 +0100 Subject: [PATCH 58/63] [ticket/17010] Remove duplicated css for buttons PHPBB3-17010 --- phpBB/styles/prosilver/theme/colours.css | 9 --------- 1 file changed, 9 deletions(-) diff --git a/phpBB/styles/prosilver/theme/colours.css b/phpBB/styles/prosilver/theme/colours.css index fe5e4d7c85..7bdd5f4b87 100644 --- a/phpBB/styles/prosilver/theme/colours.css +++ b/phpBB/styles/prosilver/theme/colours.css @@ -697,15 +697,6 @@ dd.profile-warnings { color: #d41142; } -.button[disabled], -.button[disabled]:hover, -.button.disabled, -.button.disabled:hover { - background: #e0e0e0; - border-color: #9e9e9e; - color: #9e9e9e; -} - .button .icon, .button-secondary, .c-button-icon { From f36542c661a498dca9733cbb7a2189f95eff1e8f Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 27 Feb 2024 17:30:46 +0100 Subject: [PATCH 59/63] [ticket/17010] Update web-push-testing to 1.1.0 PHPBB3-17010 --- package-lock.json | 55 ++++++++++++++++------------------------------- package.json | 2 +- 2 files changed, 20 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5801393ff3..4f0ec060d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "postcss-sorting": "^7.0.1", "stylelint": "^14.7.0", "stylelint-order": "^5.0.0", - "web-push-testing": "^1.0.0" + "web-push-testing": "^1.1.0" } }, "node_modules/@babel/code-frame": { @@ -4208,9 +4208,9 @@ "dev": true }, "node_modules/jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "dev": true, "dependencies": { "jws": "^3.2.2", @@ -4222,20 +4222,11 @@ "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", - "semver": "^5.6.0" + "semver": "^7.5.4" }, "engines": { - "node": ">=4", - "npm": ">=1.4.28" - } - }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" + "node": ">=12", + "npm": ">=6" } }, "node_modules/just-debounce": { @@ -8750,15 +8741,15 @@ } }, "node_modules/web-push-testing": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.0.0.tgz", - "integrity": "sha512-p14iWiQSPY4kjpbaz59QfczaxEyD4uWg1aBjUS+Y9LAzQPU4Z95Jnq3FiOIazrvrfY0/1uDBbQkjXVxHq6wI/Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.1.0.tgz", + "integrity": "sha512-VcQK/2u79/OKFgXtg8nxwC6CZCpS6eRyPF3TSy3mWIMmySv7s7yB+ugTGWiKOe+oKsR7ZdvpM+Nvbt/PhrTt5A==", "dev": true, "dependencies": { "arg": "^5.0.1", "express": "^4.17.2", "http_ece": "^1.1.0", - "jsonwebtoken": "^8.5.1", + "jsonwebtoken": "^9.0.0", "node-persist": "^2.1.0", "ps-node": "^0.1.6", "urlsafe-base64": "^1.0.0" @@ -12402,9 +12393,9 @@ "dev": true }, "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "dev": true, "requires": { "jws": "^3.2.2", @@ -12416,15 +12407,7 @@ "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "semver": "^7.5.4" } }, "just-debounce": { @@ -15963,15 +15946,15 @@ } }, "web-push-testing": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.0.0.tgz", - "integrity": "sha512-p14iWiQSPY4kjpbaz59QfczaxEyD4uWg1aBjUS+Y9LAzQPU4Z95Jnq3FiOIazrvrfY0/1uDBbQkjXVxHq6wI/Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.1.0.tgz", + "integrity": "sha512-VcQK/2u79/OKFgXtg8nxwC6CZCpS6eRyPF3TSy3mWIMmySv7s7yB+ugTGWiKOe+oKsR7ZdvpM+Nvbt/PhrTt5A==", "dev": true, "requires": { "arg": "^5.0.1", "express": "^4.17.2", "http_ece": "^1.1.0", - "jsonwebtoken": "^8.5.1", + "jsonwebtoken": "^9.0.0", "node-persist": "^2.1.0", "ps-node": "^0.1.6", "urlsafe-base64": "^1.0.0" diff --git a/package.json b/package.json index 22103522f3..ecaa5d3c4f 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,6 @@ "postcss-sorting": "^7.0.1", "stylelint": "^14.7.0", "stylelint-order": "^5.0.0", - "web-push-testing": "^1.0.0" + "web-push-testing": "^1.1.0" } } From fc14c5fd0b19c344717e3c01898224dbe70bd507 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 27 Feb 2024 20:43:27 +0100 Subject: [PATCH 60/63] [ticket/17010] Increase test coverage PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 69 +++++++- .../webpush_notification.type.post.xml | 6 + .../notification_method_webpush_test.php | 147 ++++++++++++++++-- 3 files changed, 204 insertions(+), 18 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index 386dfd2525..c71256a29d 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -239,12 +239,10 @@ class webpush extends messenger_base implements extended_method_interface } // Remove any subscriptions that couldn't be queued, i.e. that have invalid data - if (count($remove_subscriptions)) - { - $sql = 'DELETE FROM ' . $this->push_subscriptions_table . ' - WHERE ' . $this->db->sql_in_set('subscription_id', $remove_subscriptions); - $this->db->sql_query($sql); - } + $this->remove_subscriptions($remove_subscriptions); + + // List to fill with expired subscriptions based on return + $expired_endpoints = []; try { @@ -252,8 +250,16 @@ class webpush extends messenger_base implements extended_method_interface { if (!$report->isSuccess()) { - $report_data = \phpbb\json\sanitizer::sanitize($report->jsonSerialize()); - $this->log->add('admin', ANONYMOUS, '', 'LOG_WEBPUSH_MESSAGE_FAIL', false, [$report_data['reason']]); + // Fill array of endpoints to remove if subscription has expired + if ($report->isSubscriptionExpired()) + { + $expired_endpoints = $report->getEndpoint(); + } + else + { + $report_data = \phpbb\json\sanitizer::sanitize($report->jsonSerialize()); + $this->log->add('admin', ANONYMOUS, '', 'LOG_WEBPUSH_MESSAGE_FAIL', false, [$report_data['reason']]); + } } } } @@ -262,6 +268,8 @@ class webpush extends messenger_base implements extended_method_interface $this->log->add('critical', ANONYMOUS, '', 'LOG_WEBPUSH_MESSAGE_FAIL', false, [$exception->getMessage()]); } + $this->clean_expired_subscriptions($user_subscription_map, $expired_endpoints); + // We're done, empty the queue $this->empty_queue(); } @@ -373,4 +381,49 @@ class webpush extends messenger_base implements extended_method_interface return $user_subscription_map; } + + /** + * Remove subscriptions + * + * @param array $subscription_ids Subscription ids to remove + * @return void + */ + public function remove_subscriptions(array $subscription_ids): void + { + if (count($subscription_ids)) + { + $sql = 'DELETE FROM ' . $this->push_subscriptions_table . ' + WHERE ' . $this->db->sql_in_set('subscription_id', $subscription_ids); + $this->db->sql_query($sql); + } + } + + /** + * Clean expired subscriptions from the database + * + * @param array $user_subscription_map User subscription map + * @param array $expired_endpoints Expired endpoints + * @return void + */ + protected function clean_expired_subscriptions(array $user_subscription_map, array $expired_endpoints): void + { + if (!count($expired_endpoints)) + { + return; + } + + $remove_subscriptions = []; + foreach ($expired_endpoints as $endpoint) + { + foreach ($user_subscription_map as $user_id => $subscriptions) + { + if (isset($subscriptions['endpoint']) && $subscriptions['endpoint'] == $endpoint) + { + $remove_subscriptions[] = $subscriptions[$endpoint]['subscription_id']; + } + } + } + + $this->remove_subscriptions($remove_subscriptions); + } } diff --git a/tests/notification/fixtures/webpush_notification.type.post.xml b/tests/notification/fixtures/webpush_notification.type.post.xml index 7654ed30aa..896df4e1c1 100644 --- a/tests/notification/fixtures/webpush_notification.type.post.xml +++ b/tests/notification/fixtures/webpush_notification.type.post.xml @@ -133,6 +133,12 @@ username_clean user_permissions user_sig + + 1 + Anonymous + + + 2 poster diff --git a/tests/notification/notification_method_webpush_test.php b/tests/notification/notification_method_webpush_test.php index 0faff80576..3a0a48064c 100644 --- a/tests/notification/notification_method_webpush_test.php +++ b/tests/notification/notification_method_webpush_test.php @@ -32,6 +32,12 @@ class notification_method_webpush_test extends phpbb_tests_notification_base /** @var webpush */ protected $notification_method_webpush; + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\log\log_interface */ + protected $log; + public function getDataSet() { return $this->createXMLDataSet(__DIR__ . '/fixtures/webpush_notification.type.post.xml'); @@ -97,9 +103,11 @@ class notification_method_webpush_test extends phpbb_tests_notification_base 'webpush_vapid_private' => self::VAPID_KEYS['privateKey'], ]); $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); - $language = new \phpbb\language\language($lang_loader); - $user = new \phpbb\user($language, '\phpbb\datetime'); + $this->language = new \phpbb\language\language($lang_loader); + $this->language->add_lang('acp/common'); + $user = new \phpbb\user($this->language, '\phpbb\datetime'); $this->user = $user; + $this->user->data['user_options'] = 230271; $this->user_loader = new \phpbb\user_loader($avatar_helper, $this->db, $phpbb_root_path, $phpEx, 'phpbb_users'); $auth = $this->auth = new phpbb_mock_notifications_auth(); $this->phpbb_dispatcher = new phpbb_mock_event_dispatcher(); @@ -113,20 +121,22 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $phpbb_root_path, $phpEx ); - $phpbb_log = new \phpbb\log\dummy(); + + $log_table = 'phpbb_log'; + $this->log = new \phpbb\log\log($this->db, $user, $auth, $this->phpbb_dispatcher, $phpbb_root_path, 'adm/', $phpEx, $log_table); $phpbb_container = $this->container = new ContainerBuilder(); $loader = new YamlFileLoader($phpbb_container, new FileLocator(__DIR__ . '/fixtures')); $loader->load('services_notification.yml'); $phpbb_container->set('user_loader', $this->user_loader); $phpbb_container->set('user', $user); - $phpbb_container->set('language', $language); + $phpbb_container->set('language', $this->language); $phpbb_container->set('config', $this->config); $phpbb_container->set('dbal.conn', $this->db); $phpbb_container->set('auth', $auth); $phpbb_container->set('cache.driver', $cache_driver); $phpbb_container->set('cache', $cache); - $phpbb_container->set('log', $phpbb_log); + $phpbb_container->set('log', $this->log); $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils()); $phpbb_container->set('dispatcher', $this->phpbb_dispatcher); $phpbb_container->setParameter('core.root_path', $phpbb_root_path); @@ -158,7 +168,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $collection->add('ban.type.email'); $collection->add('ban.type.user'); $collection->add('ban.type.ip'); - $ban_manager = new \phpbb\ban\manager($collection, new \phpbb\cache\driver\dummy(), $this->db, $language, $phpbb_log, $user, 'phpbb_bans', 'phpbb_users'); + $ban_manager = new \phpbb\ban\manager($collection, new \phpbb\cache\driver\dummy(), $this->db, $this->language, $this->log, $user, 'phpbb_bans', 'phpbb_users'); $phpbb_container->set('ban.manager', $ban_manager); $this->notification_method_webpush = new \phpbb\notification\method\webpush( @@ -183,7 +193,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $this->phpbb_dispatcher, $this->db, $this->cache, - $language, + $this->language, $this->user, 'phpbb_notification_types', 'phpbb_user_notifications' @@ -400,7 +410,122 @@ class notification_method_webpush_test extends phpbb_tests_notification_base } } - protected function create_subscription_for_user($user_id): array + /** + * @dataProvider data_notification_webpush + */ + public function test_notify_empty_queue($notification_type, $post_data, $expected_users): void + { + foreach ($expected_users as $user_id => $user_data) + { + $this->create_subscription_for_user($user_id); + } + + $post_data = array_merge([ + 'post_time' => 1349413322, + 'poster_id' => 1, + 'topic_title' => '', + 'post_subject' => '', + 'post_username' => '', + 'forum_name' => '', + ], + + $post_data); + $notification_options = [ + 'item_id' => $post_data['post_id'], + 'item_parent_id' => $post_data['topic_id'], + ]; + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet'); + + $this->notifications->add_notifications($notification_type, $post_data); + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified'); + + $post_data['post_id']++; + $notification_options['item_id'] = $post_data['post_id']; + $post_data['post_time'] = 1349413323; + + $this->notifications->add_notifications($notification_type, $post_data); + + $notified_users2 = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals($expected_users, $notified_users2, 'Assert that expected users stay the same after replying to same topic'); + } + + /** + * @dataProvider data_notification_webpush + */ + public function test_notify_invalid_endpoint($notification_type, $post_data, $expected_users): void + { + $subscription_info = []; + foreach ($expected_users as $user_id => $user_data) + { + $subscription_info[$user_id][] = $this->create_subscription_for_user($user_id); + } + + // Create second subscription for first user ID passed + if (count($expected_users)) + { + $first_user_id = array_key_first($expected_users); + $first_user_sub = $this->create_subscription_for_user($first_user_id, true); + $subscription_info[$first_user_id][] = $first_user_sub; + } + + $post_data = array_merge([ + 'post_time' => 1349413322, + 'poster_id' => 1, + 'topic_title' => '', + 'post_subject' => '', + 'post_username' => '', + 'forum_name' => '', + ], + + $post_data); + $notification_options = [ + 'item_id' => $post_data['post_id'], + 'item_parent_id' => $post_data['topic_id'], + ]; + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet'); + + foreach ($expected_users as $user_id => $data) + { + $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); + $this->assertEmpty($messages); + } + + $this->notifications->add_notifications($notification_type, $post_data); + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified'); + + foreach ($expected_users as $user_id => $data) + { + $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); + $this->assertNotEmpty($messages); + } + + if (isset($first_user_sub)) + { + $admin_logs = $this->log->get_logs('admin'); + $this->db->sql_query('DELETE FROM phpbb_log'); // Clear logs + $this->assertCount(1, $admin_logs, 'Assert that an admin log was created for invalid endpoint'); + + $log_entry = $admin_logs[0]; + + $this->assertStringStartsWith('Web Push message could not be sent:', $log_entry['action']); + $this->assertStringContainsString('400', $log_entry['action']); + } + } + + public function test_get_type(): void + { + $this->assertEquals('notification.method.webpush', $this->notification_method_webpush->get_type()); + } + + protected function create_subscription_for_user($user_id, bool $invalidate_endpoint = false): array { $client = new \GuzzleHttp\Client(); try @@ -420,8 +545,10 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $this->assertStringStartsWith('http://localhost:9012/notify/', $subscription_data['endpoint']); $this->assertIsArray($subscription_data['keys']); - // Add subscription data to admin user (user id 2) - + if ($invalidate_endpoint) + { + $subscription_data['endpoint'] .= 'invalid'; + } $push_subscriptions_table = $this->container->getParameter('tables.push_subscriptions'); From 93a6b8d87a46f3248d710b90b483da83bd13a57b Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Thu, 29 Feb 2024 22:16:35 +0100 Subject: [PATCH 61/63] [ticket/17010] Update web-push-testing to 1.1.1 PHPBB3-17010 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4f0ec060d5..1bcd449701 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "postcss-sorting": "^7.0.1", "stylelint": "^14.7.0", "stylelint-order": "^5.0.0", - "web-push-testing": "^1.1.0" + "web-push-testing": "^1.1.1" } }, "node_modules/@babel/code-frame": { @@ -8741,9 +8741,9 @@ } }, "node_modules/web-push-testing": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.1.0.tgz", - "integrity": "sha512-VcQK/2u79/OKFgXtg8nxwC6CZCpS6eRyPF3TSy3mWIMmySv7s7yB+ugTGWiKOe+oKsR7ZdvpM+Nvbt/PhrTt5A==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.1.1.tgz", + "integrity": "sha512-q+FcdmPeePLI7kwxIngl9jzOfqnbp8t5DD17tQ8j1VxdEtsYb8HAbaieZH5q7uudQn+LFniAn0V9xsOnFctV7Q==", "dev": true, "dependencies": { "arg": "^5.0.1", @@ -15946,9 +15946,9 @@ } }, "web-push-testing": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.1.0.tgz", - "integrity": "sha512-VcQK/2u79/OKFgXtg8nxwC6CZCpS6eRyPF3TSy3mWIMmySv7s7yB+ugTGWiKOe+oKsR7ZdvpM+Nvbt/PhrTt5A==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/web-push-testing/-/web-push-testing-1.1.1.tgz", + "integrity": "sha512-q+FcdmPeePLI7kwxIngl9jzOfqnbp8t5DD17tQ8j1VxdEtsYb8HAbaieZH5q7uudQn+LFniAn0V9xsOnFctV7Q==", "dev": true, "requires": { "arg": "^5.0.1", diff --git a/package.json b/package.json index ecaa5d3c4f..58e6fe6ae7 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,6 @@ "postcss-sorting": "^7.0.1", "stylelint": "^14.7.0", "stylelint-order": "^5.0.0", - "web-push-testing": "^1.1.0" + "web-push-testing": "^1.1.1" } } From fcfed793858d44473cc242d73d0ba044cf0786ad Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Fri, 1 Mar 2024 19:40:26 +0100 Subject: [PATCH 62/63] [ticket/17010] Properly handle expired subscriptions and extend tests PHPBB3-17010 --- phpBB/phpbb/notification/method/webpush.php | 11 +- .../webpush_notification.type.post.xml | 2 + .../notification_method_webpush_test.php | 165 +++++++++++++++++- 3 files changed, 169 insertions(+), 9 deletions(-) diff --git a/phpBB/phpbb/notification/method/webpush.php b/phpBB/phpbb/notification/method/webpush.php index c71256a29d..e5b8910a07 100644 --- a/phpBB/phpbb/notification/method/webpush.php +++ b/phpBB/phpbb/notification/method/webpush.php @@ -253,7 +253,7 @@ class webpush extends messenger_base implements extended_method_interface // Fill array of endpoints to remove if subscription has expired if ($report->isSubscriptionExpired()) { - $expired_endpoints = $report->getEndpoint(); + $expired_endpoints[] = $report->getEndpoint(); } else { @@ -415,11 +415,14 @@ class webpush extends messenger_base implements extended_method_interface $remove_subscriptions = []; foreach ($expired_endpoints as $endpoint) { - foreach ($user_subscription_map as $user_id => $subscriptions) + foreach ($user_subscription_map as $subscriptions) { - if (isset($subscriptions['endpoint']) && $subscriptions['endpoint'] == $endpoint) + foreach ($subscriptions as $subscription) { - $remove_subscriptions[] = $subscriptions[$endpoint]['subscription_id']; + if (isset($subscription['endpoint']) && $subscription['endpoint'] == $endpoint) + { + $remove_subscriptions[] = $subscription['subscription_id']; + } } } } diff --git a/tests/notification/fixtures/webpush_notification.type.post.xml b/tests/notification/fixtures/webpush_notification.type.post.xml index 896df4e1c1..d59d5b92c4 100644 --- a/tests/notification/fixtures/webpush_notification.type.post.xml +++ b/tests/notification/fixtures/webpush_notification.type.post.xml @@ -1,5 +1,7 @@ +
+
forum_iduser_id diff --git a/tests/notification/notification_method_webpush_test.php b/tests/notification/notification_method_webpush_test.php index 3a0a48064c..ff95c74b09 100644 --- a/tests/notification/notification_method_webpush_test.php +++ b/tests/notification/notification_method_webpush_test.php @@ -406,7 +406,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base foreach ($expected_users as $user_id => $data) { $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); - $this->assertNotEmpty($messages); + $this->assertNotEmpty($messages, 'Failed asserting that user ' . $user_id . ' has received messages.'); } } @@ -438,10 +438,10 @@ class notification_method_webpush_test extends phpbb_tests_notification_base $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet'); - $this->notifications->add_notifications($notification_type, $post_data); + $this->notification_method_webpush->notify(); // should have no effect $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); - $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified'); + $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet'); $post_data['post_id']++; $notification_options['item_id'] = $post_data['post_id']; @@ -504,7 +504,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base foreach ($expected_users as $user_id => $data) { $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); - $this->assertNotEmpty($messages); + $this->assertNotEmpty($messages, 'Failed asserting that user ' . $user_id . ' has received messages.'); } if (isset($first_user_sub)) @@ -520,11 +520,139 @@ class notification_method_webpush_test extends phpbb_tests_notification_base } } + /** + * @dataProvider data_notification_webpush + */ + public function test_notify_expired($notification_type, $post_data, $expected_users) + { + $subscription_info = []; + foreach ($expected_users as $user_id => $user_data) + { + $subscription_info[$user_id][] = $this->create_subscription_for_user($user_id); + } + + $expected_delivered_users = $expected_users; + + // Expire subscriptions for first user + if (count($expected_users)) + { + + $first_user_id = array_key_first($expected_users); + $first_user_subs = $subscription_info[$first_user_id]; + unset($expected_delivered_users[$first_user_id]); + $this->expire_subscription($first_user_subs[0]['clientHash']); + } + + $post_data = array_merge([ + 'post_time' => 1349413322, + 'poster_id' => 1, + 'topic_title' => '', + 'post_subject' => '', + 'post_username' => '', + 'forum_name' => '', + ], + + $post_data); + $notification_options = [ + 'item_id' => $post_data['post_id'], + 'item_parent_id' => $post_data['topic_id'], + ]; + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet'); + + foreach ($expected_delivered_users as $user_id => $data) + { + $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); + $this->assertEmpty($messages); + } + + $this->notifications->add_notifications($notification_type, $post_data); + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified'); + + foreach ($expected_delivered_users as $user_id => $data) + { + $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); + $this->assertNotEmpty($messages, 'Failed asserting that user ' . $user_id . ' has received messages.'); + } + } + public function test_get_type(): void { $this->assertEquals('notification.method.webpush', $this->notification_method_webpush->get_type()); } + /** + * @dataProvider data_notification_webpush + */ + public function test_prune_notifications($notification_type, $post_data, $expected_users): void + { + $subscription_info = []; + foreach ($expected_users as $user_id => $user_data) + { + $subscription_info[$user_id][] = $this->create_subscription_for_user($user_id); + } + + // Create second subscription for first user ID passed + if (count($expected_users)) + { + $first_user_id = array_key_first($expected_users); + $subscription_info[$first_user_id][] = $this->create_subscription_for_user($first_user_id); + } + + $post_data = array_merge([ + 'post_time' => 1349413322, + 'poster_id' => 1, + 'topic_title' => '', + 'post_subject' => '', + 'post_username' => '', + 'forum_name' => '', + ], + + $post_data); + $notification_options = [ + 'item_id' => $post_data['post_id'], + 'item_parent_id' => $post_data['topic_id'], + ]; + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet'); + + foreach ($expected_users as $user_id => $data) + { + $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); + $this->assertEmpty($messages); + } + + $this->notifications->add_notifications($notification_type, $post_data); + + $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options); + $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified'); + + foreach ($expected_users as $user_id => $data) + { + $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']); + $this->assertNotEmpty($messages, 'Failed asserting that user ' . $user_id . ' has received messages.'); + } + + // Prune notifications with 0 time, shouldn't change anything + $prune_time = time(); + $this->notification_method_webpush->prune_notifications(0); + $this->assertGreaterThanOrEqual($prune_time, $this->config->offsetGet('read_notification_last_gc'), 'Assert that prune time was set'); + + $cur_notifications = $this->get_notifications(); + $this->assertSameSize($cur_notifications, $expected_users, 'Assert that no notifications have been pruned'); + + // Prune only read not supported, will prune all + $this->notification_method_webpush->prune_notifications($prune_time); + $this->assertGreaterThanOrEqual($prune_time, $this->config->offsetGet('read_notification_last_gc'), 'Assert that prune time was set'); + + $cur_notifications = $this->get_notifications(); + $this->assertCount(0, $cur_notifications, 'Assert that no notifications have been pruned'); + } + protected function create_subscription_for_user($user_id, bool $invalidate_endpoint = false): array { $client = new \GuzzleHttp\Client(); @@ -563,6 +691,22 @@ class notification_method_webpush_test extends phpbb_tests_notification_base return $subscription_data; } + protected function expire_subscription(string $client_hash): void + { + $client = new \GuzzleHttp\Client(); + try + { + $response = $client->request('POST', 'http://localhost:9012/expire-subscription/' . $client_hash); + } + catch (\GuzzleHttp\Exception\GuzzleException $exception) + { + $this->fail('Failed expiring subscription with web-push-testing client: ' . $exception->getMessage()); + } + + $subscription_return = \phpbb\json\sanitizer::decode((string) $response->getBody()); + $this->assertEquals(200, $response->getStatusCode(), 'Expected response status to be 200'); + } + protected function get_messages_for_subscription($client_hash): array { $client = new \GuzzleHttp\Client(); @@ -574,7 +718,7 @@ class notification_method_webpush_test extends phpbb_tests_notification_base } catch (\GuzzleHttp\Exception\GuzzleException $exception) { - $this->fail('Failed getting messages from web-push-testing client'); + $this->fail('Failed getting messages from web-push-testing client: ' . $exception->getMessage()); } $response_data = json_decode($response->getBody()->getContents(), true); @@ -584,4 +728,15 @@ class notification_method_webpush_test extends phpbb_tests_notification_base return $response_data['data']['messages']; } + + protected function get_notifications(): array + { + $webpush_table = $this->container->getParameter('tables.notification_push'); + $sql = 'SELECT * FROM ' . $webpush_table; + $result = $this->db->sql_query($sql); + $sql_ary = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); + + return $sql_ary; + } } From 7c36c0c5bdf429feebe2cf3571661fa3179af5f4 Mon Sep 17 00:00:00 2001 From: Marc Alexander Date: Tue, 2 Apr 2024 21:26:15 +0200 Subject: [PATCH 63/63] [ticket/17010] Remove invalid int cast PHPBB3-17010 --- phpBB/phpbb/ucp/controller/webpush.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpBB/phpbb/ucp/controller/webpush.php b/phpBB/phpbb/ucp/controller/webpush.php index f1cd513df4..a7ebbffae8 100644 --- a/phpBB/phpbb/ucp/controller/webpush.php +++ b/phpBB/phpbb/ucp/controller/webpush.php @@ -230,7 +230,7 @@ class webpush $sql = 'DELETE FROM ' . $this->push_subscriptions_table . ' WHERE user_id = ' . (int) $this->user->id() . " - AND endpoint = '" . (int) $this->db->sql_escape($endpoint) . "'"; + AND endpoint = '" . $this->db->sql_escape($endpoint) . "'"; $this->db->sql_query($sql); return new JsonResponse([