Revoke Twitter connection when the token is revoked

This commit is contained in:
Karina Kwiatek 2023-01-01 23:46:10 +01:00
parent 9b5470cfd3
commit 9d1e77aeba
9 changed files with 56 additions and 5 deletions

View File

@ -8,6 +8,7 @@
&__user, &__user,
&__text { &__text {
margin-bottom: 0; margin-bottom: 0;
white-space: normal;
} }
&__icon { &__icon {
@ -36,7 +37,7 @@
&-dropdown { &-dropdown {
min-width: 400px; min-width: 400px;
& .dropdown-item:hover, & .dropdown-item:hover,
& .dropdown-item:active { & .dropdown-item:active {
background: transparent; background: transparent;
color: RGB(var(--body-text)); color: RGB(var(--body-text));
@ -55,4 +56,4 @@
margin-bottom: 0; margin-bottom: 0;
} }
} }
} }

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
class Notification::ServiceTokenExpired < Notification
end

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
class Notification::TwitterTokenExpired < Notification::ServiceTokenExpired
end

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
# stub model for notifying about expired service connections
class User::ExpiredServiceConnection < User
end

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
class User::ExpiredTwitterServiceConnection < User::ExpiredServiceConnection
end

View File

@ -0,0 +1,8 @@
.media.notification
.notification__icon
%i.fa.fa-2x.fa-fw.fa-twitter
.media-body
%h6.media-heading.notification__user
= t(".heading")
.notification__text
= t(".text_html", settings_sharing: link_to(t(".settings_services"), services_path))

View File

@ -9,8 +9,7 @@ class ShareWorker
# @param answer_id [Integer] the user id # @param answer_id [Integer] the user id
# @param service [String] the service to post to # @param service [String] the service to post to
def perform(user_id, answer_id, service) def perform(user_id, answer_id, service)
service_type = "Services::#{service.camelize}" user_service = find_service(user_id, service)
user_service = User.find(user_id).services.find_by(type: service_type)
user_service.post(Answer.find(answer_id)) user_service.post(Answer.find(answer_id))
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
@ -23,11 +22,21 @@ class ShareWorker
logger.info "Tried to post answer ##{answer_id} from user ##{user_id} to Twitter but the account is suspended." logger.info "Tried to post answer ##{answer_id} from user ##{user_id} to Twitter but the account is suspended."
rescue Twitter::Error::Unauthorized rescue Twitter::Error::Unauthorized
# User's Twitter token has expired or been revoked # User's Twitter token has expired or been revoked
# TODO: Notify user if this happens (https://github.com/Retrospring/retrospring/issues/123)
logger.info "Tried to post answer ##{answer_id} from user ##{user_id} to Twitter but the token has exired or been revoked." logger.info "Tried to post answer ##{answer_id} from user ##{user_id} to Twitter but the token has exired or been revoked."
user_service = find_service(user_id, service)
user_service.destroy
Object.const_get("Notification::ServiceTokenExpired").create(
target_id: user_id,
target_type: "User::Expired#{service.camelize}ServiceConnection",
recipient_id: user_id,
new: true
)
rescue => e rescue => e
logger.info "failed to post answer #{answer_id} to #{service} for user #{user_id}: #{e.message}" logger.info "failed to post answer #{answer_id} to #{service} for user #{user_id}: #{e.message}"
Sentry.capture_exception(e) Sentry.capture_exception(e)
raise raise
end end
def find_service(user_id, service) = User.find(user_id).services.find_by(type: "Services::#{service.camelize}")
end end

View File

@ -352,6 +352,10 @@ en:
link_text: "your comment" link_text: "your comment"
follow: follow:
heading_html: "followed you %{time} ago" heading_html: "followed you %{time} ago"
expiredtwitterserviceconnection:
heading: "Twitter connection expired"
text_html: "If you would like to continue automatically sharing your answers to Twitter, head to %{settings_sharing} and re-connect your account."
settings_services: "Sharing Settings"
services: services:
index: index:
title: "Service Settings" title: "Service Settings"

View File

@ -59,6 +59,18 @@ describe ShareWorker do
expect(Sidekiq.logger).to have_received(:info).with("Tried to post answer ##{answer.id} from user ##{user.id} to Twitter but the token has exired or been revoked.") expect(Sidekiq.logger).to have_received(:info).with("Tried to post answer ##{answer.id} from user ##{user.id} to Twitter but the token has exired or been revoked.")
end end
it "revokes the service connection when Twitter::Error::Unauthorized is raised" do
allow_any_instance_of(Services::Twitter).to receive(:post).with(answer).and_raise(Twitter::Error::Unauthorized)
subject
expect { ShareWorker.drain }.to change { Services::Twitter.count }.by(-1)
end
it "sends the user a notification when Twitter::Error::Unauthorized is raised" do
allow_any_instance_of(Services::Twitter).to receive(:post).with(answer).and_raise(Twitter::Error::Unauthorized)
subject
expect { ShareWorker.drain }.to change { Notification::ServiceTokenExpired.count }.by(1)
end
it "handles Twitter::Error::Forbidden" do it "handles Twitter::Error::Forbidden" do
allow_any_instance_of(Services::Twitter).to receive(:post).with(answer).and_raise(Twitter::Error::Forbidden) allow_any_instance_of(Services::Twitter).to receive(:post).with(answer).and_raise(Twitter::Error::Forbidden)
subject subject