19declare(strict_types=1);
60 $this->
logger = $DIC->logger()->root();
61 $this->
sub = $DIC->settings()->get(
'admin_email');
63 $settings =
new ilSetting(
'notifications');
64 $this->public_key = $settings->get(
'application_server_key');
65 $this->private_key = file_get_contents($settings->get(
'private_key_path'));
66 $this->is_enabled = $settings->get(
'enable_push') ===
'1';
77 if (!$this->is_enabled) {
78 $this->
logger->debug(
'Notifications are globaly disabled.');
84 $id = $notification->handlerParams[
'setting'][
'user_pref'];
86 $this->
logger->debug(
'Notification for ' .
$id .
' not send due to user preferences.');
92 $subscriptions = $this->subscription_repo->getUserSubscriptions($notification->user->getId());
93 if ($subscriptions === []) {
94 $this->
logger->debug(
'User ' . $notification->user->getId() .
' has no active Subscriptions');
99 $salt = (
new Randomizer())->getBytes(16);
102 $ttl = $notification->handlerParams[
'setting'][
'ttl'] ?? 60;
103 foreach ($subscriptions as $subscription) {
104 $url_parts = parse_url($subscription->getEndpoint());
107 'aud' => $url_parts[
'scheme'] .
'://' . $url_parts[
'host'],
108 'exp' => time() + (
int) $ttl
112 $pre_key = $this->
hash(
113 openssl_pkey_derive($this->
publicVapidToPEM($user_key), $this->private_key, 256),
116 $key = $this->
hash($this->
hash($user_key . $local_key, $pre_key, 32,
'WebPush: info'), $salt);
117 $encryptedText = openssl_encrypt(
120 $this->
hash(
'', $key, 16,
'Content-Encoding: aes128gcm'),
122 $this->
hash(
'', $key, 12,
'Content-Encoding: nonce'),
125 $post = $salt . $this->
padKey($local_key) . $encryptedText . $tag;
129 $curl->setOpt(CURLOPT_HTTPHEADER, [
130 'Content-Encoding: aes128gcm',
131 'Authorization: vapid t=' . JWT::encode(
$data, $this->private_key,
'ES256') .
', k=' . $this->public_key,
134 $curl->setOpt(CURLOPT_POST, 1);
135 $curl->setOpt(CURLOPT_RETURNTRANSFER, 1);
136 $curl->setOpt(CURLOPT_POSTFIELDS,
$post);
140 $this->
logger->error(
'Push notification [' . $subscription->getAuth() .
'] request failed.');
153 return base64_decode(str_replace([
'-',
'_'], [
'+',
'/'],
$data));
158 $der_key = pack(
'H*',
'3059301306072a8648ce3d020106082a8648ce3d030107034200') . $key;
159 return "-----BEGIN PUBLIC KEY-----\n" .
160 chunk_split(base64_encode($der_key), 64,
"\n") .
161 "-----END PUBLIC KEY-----\n";
167 private function hash(
string $data,
string $key,
int $length = PHP_INT_MAX, ?
string $preffix =
null): string
169 if ($preffix !==
null) {
172 return mb_substr(hash_hmac(
'sha256',
$data, $key,
true), 0, $length,
'8bit');
179 private function padKey(
string $key): string
181 return pack(
'N*', 4096) . pack(
'C*', mb_strlen($key,
'8bit')) . $key;
191 foreach ($notification->links as $link) {
193 'title' => $link->getTitle(),
194 'action' => $link->getUrl(),
198 return base64_encode(json_encode([
199 $notification->title,
201 'data' => [
'action' => $notification->action ??
'/'],
203 'body' => $notification->shortDescription,
204 'actions' => $actions
206 ], JSON_THROW_ON_ERROR)) . \chr(2);
212 $this->
logger->info(
"Push notification [$auth] response: $response");
214 switch ($http_code) {
217 $this->
logger->debug(
"Push notification [$auth] successful.");
222 $this->
logger->error(
"Push notification [$auth] request was invalid.");
226 $this->subscription_repo->deleteSubscription($auth);
227 $this->
logger->debug(
"Push notification [$auth] endpoint outdated. Subscription removed.");
231 $this->
logger->debug(
"Push notification [$auth] endpoint blocked due to heavy usage or spam.");
234 $this->
logger->info(
"Push notification [$auth] went into unkown/browser-specific handling.");
235 return PushQueueResult::UNKNOWN;
241 $this->last_queue_result =
null;
247 $this->last_queue_result ===
null ||
251 $this->last_queue_result = $result;
257 return \in_array(
$id, json_decode($user->
getPref(
'push_notification_provider') ??
'[]'),
true);
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
This is the lowest common denominator of all popular browsers.
hash(string $data, string $key, int $length=PHP_INT_MAX, ?string $preffix=null)
PushProviderInterface $provider
PushRepository $subscription_repo
padKey(string $key)
The key needs to be preffixed for the request to fit the endpoints requirement.
base64UrlDecode(string $data)
handleResponse(int $http_code, string $response, string $auth)
setLastQueueResult(PushQueueResult $result)
buildContent(ilNotificationObject $notification)
publicVapidToPEM(string $key)
PushQueueResult $last_queue_result
validateForUser(ilObjUser $user, string $id)
__construct(PushProviderInterface $provider)
notify(ilNotificationObject $notification)
Component logger with individual log levels by component id.
getPref(string $a_keyword)
Util class various functions, usage as namespace.
static getImagePath(string $image_name, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
const HTTP_REQUEST_ENTITY_TOO_LARGE
const HTTP_TOO_MANY_REQUESTS