Add endpoint to remove web push subscription (#32626)

This commit is contained in:
David Roetzel 2024-10-23 10:02:31 +02:00 committed by GitHub
parent d1b20ea8f7
commit 05f23df3b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 76 additions and 5 deletions

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::Web::PushSubscriptionsController < Api::Web::BaseController class Api::Web::PushSubscriptionsController < Api::Web::BaseController
before_action :require_user! before_action :require_user!, except: :destroy
before_action :set_push_subscription, only: :update before_action :set_push_subscription, only: :update
before_action :destroy_previous_subscriptions, only: :create, if: :prior_subscriptions? before_action :destroy_previous_subscriptions, only: :create, if: :prior_subscriptions?
after_action :update_session_with_subscription, only: :create after_action :update_session_with_subscription, only: :create
@ -17,6 +17,13 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
end end
def destroy
push_subscription = ::Web::PushSubscription.find_by_token_for(:unsubscribe, params[:id])
push_subscription&.destroy
head 200
end
private private
def active_session def active_session

View file

@ -29,6 +29,8 @@ class Web::PushSubscription < ApplicationRecord
delegate :locale, to: :associated_user delegate :locale, to: :associated_user
generates_token_for :unsubscribe, expires_in: Web::PushNotificationWorker::TTL
def pushable?(notification) def pushable?(notification)
policy_allows_notification?(notification) && alert_enabled_for_notification_type?(notification) policy_allows_notification?(notification) && alert_enabled_for_notification_type?(notification)
end end

View file

@ -2,10 +2,11 @@
class Web::PushNotificationWorker class Web::PushNotificationWorker
include Sidekiq::Worker include Sidekiq::Worker
include RoutingHelper
sidekiq_options queue: 'push', retry: 5 sidekiq_options queue: 'push', retry: 5
TTL = 48.hours.to_s TTL = 48.hours
URGENCY = 'normal' URGENCY = 'normal'
def perform(subscription_id, notification_id) def perform(subscription_id, notification_id)
@ -23,12 +24,13 @@ class Web::PushNotificationWorker
request.add_headers( request.add_headers(
'Content-Type' => 'application/octet-stream', 'Content-Type' => 'application/octet-stream',
'Ttl' => TTL, 'Ttl' => TTL.to_s,
'Urgency' => URGENCY, 'Urgency' => URGENCY,
'Content-Encoding' => 'aesgcm', 'Content-Encoding' => 'aesgcm',
'Encryption' => "salt=#{Webpush.encode64(payload.fetch(:salt)).delete('=')}", 'Encryption' => "salt=#{Webpush.encode64(payload.fetch(:salt)).delete('=')}",
'Crypto-Key' => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{web_push_request.crypto_key_header}", 'Crypto-Key' => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{web_push_request.crypto_key_header}",
'Authorization' => web_push_request.authorization_header 'Authorization' => web_push_request.authorization_header,
'Unsubscribe-URL' => subscription_url
) )
request.perform do |response| request.perform do |response|
@ -72,4 +74,8 @@ class Web::PushNotificationWorker
def request_pool def request_pool
RequestPool.current RequestPool.current
end end
def subscription_url
api_web_push_subscription_url(id: @subscription.generate_token_for(:unsubscribe))
end
end end

View file

@ -346,7 +346,7 @@ namespace :api, format: false do
namespace :web do namespace :web do
resource :settings, only: [:update] resource :settings, only: [:update]
resources :embeds, only: [:show] resources :embeds, only: [:show]
resources :push_subscriptions, only: [:create] do resources :push_subscriptions, only: [:create, :destroy] do
member do member do
put :update put :update
end end

View file

@ -0,0 +1,55 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'API Web Push Subscriptions' do
describe 'DELETE /api/web/push_subscriptions/:id' do
subject { delete api_web_push_subscription_path(token) }
context 'when the subscription exists' do
let!(:web_push_subscription) do
Fabricate(:web_push_subscription)
end
let(:token) do
web_push_subscription.generate_token_for(:unsubscribe)
end
it 'deletes the subscription' do
expect { subject }
.to change(Web::PushSubscription, :count).by(-1)
expect(response).to have_http_status(200)
end
end
context 'when the subscription does not exist' do
let(:web_push_subscription) do
Fabricate(:web_push_subscription)
end
let(:token) do
web_push_subscription.generate_token_for(:unsubscribe)
end
before do
token # memoize before destroying the record
web_push_subscription.destroy!
end
it 'does nothing' do
subject
expect(response).to have_http_status(200)
end
end
context 'when the token is invalid' do
let(:token) { 'invalid--invalid' }
it 'does nothing' do
subject
expect(response).to have_http_status(200)
end
end
end
end

View file

@ -61,6 +61,7 @@ RSpec.describe Web::PushNotificationWorker do
'Ttl' => '172800', 'Ttl' => '172800',
'Urgency' => 'normal', 'Urgency' => 'normal',
'Authorization' => 'WebPush jwt.encoded.payload', 'Authorization' => 'WebPush jwt.encoded.payload',
'Unsubscribe-URL' => %r{/api/web/push_subscriptions/},
}, },
body: "+\xB8\xDBT}\u0013\xB6\xDD.\xF9\xB0\xA7\xC8Ҁ\xFD\x99#\xF7\xAC\x83\xA4\xDB,\u001F\xB5\xB9w\x85>\xF7\xADr" body: "+\xB8\xDBT}\u0013\xB6\xDD.\xF9\xB0\xA7\xC8Ҁ\xFD\x99#\xF7\xAC\x83\xA4\xDB,\u001F\xB5\xB9w\x85>\xF7\xADr"
) )