diff --git a/src/newsreader/accounts/models.py b/src/newsreader/accounts/models.py index a54c375..baf0b39 100644 --- a/src/newsreader/accounts/models.py +++ b/src/newsreader/accounts/models.py @@ -84,3 +84,7 @@ class User(AbstractUser): def delete(self, *args, **kwargs): self.task.delete() return super().delete(*args, **kwargs) + + @property + def has_twitter_auth(self): + return self.twitter_oauth_token and self.twitter_oauth_token_secret diff --git a/src/newsreader/accounts/templates/accounts/views/integrations.html b/src/newsreader/accounts/templates/accounts/views/integrations.html index c5ebea8..8bb0b2e 100644 --- a/src/newsreader/accounts/templates/accounts/views/integrations.html +++ b/src/newsreader/accounts/templates/accounts/views/integrations.html @@ -55,7 +55,7 @@ {% endif %} {% if twitter_revoke_url %} - + {% trans "Deauthorize account" %} {% else %} diff --git a/src/newsreader/accounts/urls.py b/src/newsreader/accounts/urls.py index f94f5ec..9f0f2e3 100644 --- a/src/newsreader/accounts/urls.py +++ b/src/newsreader/accounts/urls.py @@ -21,6 +21,7 @@ from newsreader.accounts.views import ( RegistrationView, SettingsView, TwitterAuthRedirectView, + TwitterRevokeRedirectView, TwitterTemplateView, ) @@ -96,6 +97,11 @@ urlpatterns = [ login_required(TwitterTemplateView.as_view()), name="twitter-template", ), + path( + "settings/integrations/twitter/revoke/", + login_required(TwitterRevokeRedirectView.as_view()), + name="twitter-revoke", + ), path( "settings/integrations", login_required(IntegrationsView.as_view()), diff --git a/src/newsreader/accounts/views/__init__.py b/src/newsreader/accounts/views/__init__.py index 7c0b946..81dd1fc 100644 --- a/src/newsreader/accounts/views/__init__.py +++ b/src/newsreader/accounts/views/__init__.py @@ -5,6 +5,7 @@ from newsreader.accounts.views.integrations import ( RedditTemplateView, RedditTokenRedirectView, TwitterAuthRedirectView, + TwitterRevokeRedirectView, TwitterTemplateView, ) from newsreader.accounts.views.password import ( diff --git a/src/newsreader/accounts/views/integrations.py b/src/newsreader/accounts/views/integrations.py index a8ec089..9a7c0be 100644 --- a/src/newsreader/accounts/views/integrations.py +++ b/src/newsreader/accounts/views/integrations.py @@ -23,6 +23,7 @@ from newsreader.news.collection.twitter import ( TWITTER_ACCESS_TOKEN_URL, TWITTER_AUTH_URL, TWITTER_REQUEST_TOKEN_URL, + TWITTER_REVOKE_URL, ) from newsreader.news.collection.utils import post @@ -69,11 +70,9 @@ class IntegrationsView(TemplateView): def get_twitter_context(self, **kwargs): twitter_revoke_url = None - user = self.request.user - # TODO add revoke url - if user.twitter_oauth_token and user.twitter_oauth_token_secret: - twitter_revoke_url = "https://foo/bar" + if self.request.user.has_twitter_auth: + twitter_revoke_url = reverse_lazy("accounts:twitter-revoke") return { "twitter_request_url": reverse_lazy("accounts:twitter-request"), @@ -176,6 +175,39 @@ class RedditRevokeRedirectView(RedirectView): return response +# TODO write tests +class TwitterRevokeRedirectView(RedirectView): + url = reverse_lazy("accounts:integrations") + + def get(self, request, *args, **kwargs): + if not request.user.has_twitter_auth: + messages.error(request, _("No twitter credentials found")) + return super().get(request, *args, **kwargs) + + oauth = OAuth( + settings.TWITTER_CONSUMER_ID, + client_secret=settings.TWITTER_CONSUMER_SECRET, + resource_owner_key=request.user.twitter_oauth_token, + resource_owner_secret=request.user.twitter_oauth_token_secret, + ) + + try: + post(TWITTER_REVOKE_URL, auth=oauth) + except StreamException: + logger.exception("Failed revoking Twitter account") + + messages.error(request, _("Unable revoke Twitter account")) + return super().get(request, *args, **kwargs) + + request.user.twitter_oauth_token = None + request.user.twitter_oauth_token_secret = None + request.user.save() + + messages.success(request, _("Twitter account revoked")) + return super().get(request, *args, **kwargs) + + +# TODO write tests class TwitterAuthRedirectView(RedirectView): url = reverse_lazy("accounts:integrations") @@ -210,6 +242,7 @@ class TwitterAuthRedirectView(RedirectView): # TODO remove cached tokens +# TODO write tests class TwitterTemplateView(TemplateView): template_name = "accounts/views/twitter.html" diff --git a/src/newsreader/news/collection/twitter.py b/src/newsreader/news/collection/twitter.py index 7f075d7..bfd4cbf 100644 --- a/src/newsreader/news/collection/twitter.py +++ b/src/newsreader/news/collection/twitter.py @@ -34,6 +34,7 @@ TWITTER_API_URL = "https://api.twitter.com/1.1" TWITTER_REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token" TWITTER_AUTH_URL = "https://api.twitter.com/oauth/authorize" TWITTER_ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token" +TWITTER_REVOKE_URL = f"{TWITTER_API_URL}/oauth/invalidate_token" class TwitterBuilder(PostBuilder):