diff --git a/src/newsreader/accounts/templates/accounts/views/integrations.html b/src/newsreader/accounts/templates/accounts/views/integrations.html index e84d733..81a1fa1 100644 --- a/src/newsreader/accounts/templates/accounts/views/integrations.html +++ b/src/newsreader/accounts/templates/accounts/views/integrations.html @@ -9,33 +9,52 @@
- {% trans "Return to integrations page" %} + {% trans "Return to integrations page" %}
diff --git a/src/newsreader/accounts/urls.py b/src/newsreader/accounts/urls.py index c250a0f..68f27a6 100644 --- a/src/newsreader/accounts/urls.py +++ b/src/newsreader/accounts/urls.py @@ -13,6 +13,7 @@ from newsreader.accounts.views import ( PasswordResetConfirmView, PasswordResetDoneView, PasswordResetView, + RedditRevokeRedirectView, RedditTemplateView, RedditTokenRedirectView, RegistrationClosedView, @@ -78,6 +79,11 @@ urlpatterns = [ login_required(RedditTokenRedirectView.as_view()), name="reddit-refresh", ), + path( + "settings/integrations/reddit/revoke/", + login_required(RedditRevokeRedirectView.as_view()), + name="reddit-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 296cb28..9325728 100644 --- a/src/newsreader/accounts/views/__init__.py +++ b/src/newsreader/accounts/views/__init__.py @@ -1,6 +1,7 @@ from newsreader.accounts.views.auth import LoginView, LogoutView from newsreader.accounts.views.integrations import ( IntegrationsView, + RedditRevokeRedirectView, RedditTemplateView, RedditTokenRedirectView, ) diff --git a/src/newsreader/accounts/views/integrations.py b/src/newsreader/accounts/views/integrations.py index a32c747..fa343a2 100644 --- a/src/newsreader/accounts/views/integrations.py +++ b/src/newsreader/accounts/views/integrations.py @@ -1,3 +1,5 @@ +import logging + from django.contrib import messages from django.core.cache import cache from django.urls import reverse_lazy @@ -8,10 +10,14 @@ from newsreader.news.collection.exceptions import StreamException from newsreader.news.collection.reddit import ( get_reddit_access_token, get_reddit_authorization_url, + revoke_reddit_token, ) from newsreader.news.collection.tasks import RedditTokenTask +logger = logging.getLogger(__name__) + + class IntegrationsView(TemplateView): template_name = "accounts/views/integrations.html" @@ -41,6 +47,11 @@ class IntegrationsView(TemplateView): return { "reddit_authorization_url": reddit_authorization_url, "reddit_refresh_url": reddit_refresh_url, + "reddit_revoke_url": ( + reverse_lazy("accounts:reddit-revoke") + if not reddit_authorization_url + else None + ), } @@ -89,7 +100,7 @@ class RedditTemplateView(TemplateView): class RedditTokenRedirectView(RedirectView): - url = reverse_lazy("accounts:settings") + url = reverse_lazy("accounts:integrations") def get(self, request, *args, **kwargs): response = super().get(request, *args, **kwargs) @@ -105,3 +116,35 @@ class RedditTokenRedirectView(RedirectView): messages.error(request, _("Unable to retrieve token")) return response + + +class RedditRevokeRedirectView(RedirectView): + url = reverse_lazy("accounts:integrations") + + def get(self, request, *args, **kwargs): + response = super().get(request, *args, **kwargs) + + user = request.user + + if not user.reddit_refresh_token: + messages.error(request, _("No reddit account is linked to this account")) + return response + + try: + is_revoked = revoke_reddit_token(user) + except StreamException: + logger.exception(f"Unable to revoke reddit token for {user.pk}") + + messages.error(request, _("Unable to revoke reddit token")) + return response + + if not is_revoked: + messages.error(request, _("Unable to revoke reddit token")) + return response + + user.reddit_access_token = None + user.reddit_refresh_token = None + user.save() + + messages.success(request, _("Reddit account deathorized")) + return response diff --git a/src/newsreader/news/collection/reddit.py b/src/newsreader/news/collection/reddit.py index 0122bb1..1cb9e29 100644 --- a/src/newsreader/news/collection/reddit.py +++ b/src/newsreader/news/collection/reddit.py @@ -98,6 +98,21 @@ def get_reddit_access_token(code, user): return response_data["access_token"], response_data["refresh_token"] +# Note that the API always returns 204's with correct basic auth headers +def revoke_reddit_token(user): + client_auth = requests.auth.HTTPBasicAuth( + settings.REDDIT_CLIENT_ID, settings.REDDIT_CLIENT_SECRET + ) + + response = post( + f"{REDDIT_URL}/api/v1/revoke_token", + data={"token": user.reddit_refresh_token, "token_type_hint": "refresh_token"}, + auth=client_auth, + ) + + return response.status_code == 204 + + class RedditBuilder(PostBuilder): rule_type = RuleTypeChoices.subreddit