Fix race condition in `POST /api/v1/push/subscription` (#30166)

main
Claire 2024-05-06 14:41:14 +02:00 committed by GitHub
parent dbaa4ed891
commit 8e4fea77e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 23 additions and 12 deletions

View File

@ -1,9 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Push::SubscriptionsController < Api::BaseController class Api::V1::Push::SubscriptionsController < Api::BaseController
include Redisable
include Lockable
before_action -> { doorkeeper_authorize! :push } before_action -> { doorkeeper_authorize! :push }
before_action :require_user! before_action :require_user!
before_action :set_push_subscription before_action :set_push_subscription, only: [:show, :update]
before_action :check_push_subscription, only: [:show, :update] before_action :check_push_subscription, only: [:show, :update]
def show def show
@ -11,7 +14,8 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
end end
def create def create
@push_subscription&.destroy! with_redis_lock("push_subscription:#{current_user.id}") do
destroy_web_push_subscriptions!
@push_subscription = Web::PushSubscription.create!( @push_subscription = Web::PushSubscription.create!(
endpoint: subscription_params[:endpoint], endpoint: subscription_params[:endpoint],
@ -21,6 +25,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
user_id: current_user.id, user_id: current_user.id,
access_token_id: doorkeeper_token.id access_token_id: doorkeeper_token.id
) )
end
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
end end
@ -31,14 +36,18 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
end end
def destroy def destroy
@push_subscription&.destroy! destroy_web_push_subscriptions!
render_empty render_empty
end end
private private
def destroy_web_push_subscriptions!
doorkeeper_token.web_push_subscriptions.destroy_all
end
def set_push_subscription def set_push_subscription
@push_subscription = Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id) @push_subscription = doorkeeper_token.web_push_subscriptions.first
end end
def check_push_subscription def check_push_subscription

View File

@ -6,6 +6,8 @@ module AccessTokenExtension
included do included do
include Redisable include Redisable
has_many :web_push_subscriptions, class_name: 'Web::PushSubscription', inverse_of: :access_token
after_commit :push_to_streaming_api after_commit :push_to_streaming_api
end end