Add revoking reddit account access
This commit is contained in:
parent
cdc7ca90da
commit
655e641213
6 changed files with 102 additions and 18 deletions
|
|
@ -9,33 +9,52 @@
|
||||||
<div class="integrations">
|
<div class="integrations">
|
||||||
<h3 class="integrations__title">Reddit</h3>
|
<h3 class="integrations__title">Reddit</h3>
|
||||||
<div class="integrations__controls">
|
<div class="integrations__controls">
|
||||||
<button class="link button button--reddit {% if not reddit_authorization_url %}button--disabled{% endif %}" href="{% if reddit_authorization_url %}{{ reddit_authorization_url }}{% else %}#{% endif %}"{% if not reddit_authorization_url %} disabled{% endif %}>
|
{% if reddit_authorization_url %}
|
||||||
{% trans "Authorize account" %}
|
<a class="link button button--reddit" href="{{ reddit_authorization_url }}">
|
||||||
</button>
|
{% trans "Authorize account" %}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<button class="button button--reddit button--disabled" disabled>
|
||||||
|
{% trans "Authorize account" %}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<button class="link button button--reddit {% if not reddit_refresh_url %}button--disabled{% endif %}" href="{% if reddit_refresh_url %}{{ reddit_refresh_url }}{% else %}#{% endif %}"{% if not reddit_refresh_token %} disabled {% endif %}>
|
{% if reddit_refresh_url %}
|
||||||
{% trans "Refresh credentials" %}
|
<a class="link button button--reddit" href="{{ reddit_refresh_url }}">
|
||||||
</button>
|
{% trans "Refresh token" %}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<button class="button button--reddit button--disabled" disabled>
|
||||||
|
{% trans "Refresh token" %}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<button class="link button button--reddit">
|
{% if reddit_revoke_url %}
|
||||||
{% trans "Deauthorize account" %}
|
<a class="link button button--reddit" href="{{ reddit_revoke_url }}">
|
||||||
</button>
|
{% trans "Deauthorize account" %}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<button class="button button--reddit button--disabled" disabled>
|
||||||
|
{% trans "Deauthorize account" %}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="integrations">
|
<div class="integrations">
|
||||||
<h3 class="integrations__title">Twitter</h3>
|
<h3 class="integrations__title">Twitter</h3>
|
||||||
<div class="integrations__controls">
|
<div class="integrations__controls">
|
||||||
<button class="link button button--twitter" href="#">
|
<a class="link button button--twitter" href="#">
|
||||||
{% trans "Authorize account" %}
|
{% trans "Authorize account" %}
|
||||||
</button>
|
</a>
|
||||||
|
|
||||||
<button class="link button button--twitter" href="#">
|
<a class="link button button--twitter" href="#">
|
||||||
{% trans "Refresh credentials" %}
|
{% trans "Refresh token" %}
|
||||||
</button>
|
</a>
|
||||||
|
|
||||||
<button class="link button button--twitter" href="#">
|
<a class="link button button--twitter" href="#">
|
||||||
{% trans "Deauthorize account" %}
|
{% trans "Deauthorize account" %}
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a class="link" href="{% url 'accounts:settings' %}">{% trans "Return to integrations page" %}</a>
|
<a class="link" href="{% url 'accounts:integrations' %}">{% trans "Return to integrations page" %}</a>
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from newsreader.accounts.views import (
|
||||||
PasswordResetConfirmView,
|
PasswordResetConfirmView,
|
||||||
PasswordResetDoneView,
|
PasswordResetDoneView,
|
||||||
PasswordResetView,
|
PasswordResetView,
|
||||||
|
RedditRevokeRedirectView,
|
||||||
RedditTemplateView,
|
RedditTemplateView,
|
||||||
RedditTokenRedirectView,
|
RedditTokenRedirectView,
|
||||||
RegistrationClosedView,
|
RegistrationClosedView,
|
||||||
|
|
@ -78,6 +79,11 @@ urlpatterns = [
|
||||||
login_required(RedditTokenRedirectView.as_view()),
|
login_required(RedditTokenRedirectView.as_view()),
|
||||||
name="reddit-refresh",
|
name="reddit-refresh",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"settings/integrations/reddit/revoke/",
|
||||||
|
login_required(RedditRevokeRedirectView.as_view()),
|
||||||
|
name="reddit-revoke",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"settings/integrations",
|
"settings/integrations",
|
||||||
login_required(IntegrationsView.as_view()),
|
login_required(IntegrationsView.as_view()),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from newsreader.accounts.views.auth import LoginView, LogoutView
|
from newsreader.accounts.views.auth import LoginView, LogoutView
|
||||||
from newsreader.accounts.views.integrations import (
|
from newsreader.accounts.views.integrations import (
|
||||||
IntegrationsView,
|
IntegrationsView,
|
||||||
|
RedditRevokeRedirectView,
|
||||||
RedditTemplateView,
|
RedditTemplateView,
|
||||||
RedditTokenRedirectView,
|
RedditTokenRedirectView,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
|
|
@ -8,10 +10,14 @@ from newsreader.news.collection.exceptions import StreamException
|
||||||
from newsreader.news.collection.reddit import (
|
from newsreader.news.collection.reddit import (
|
||||||
get_reddit_access_token,
|
get_reddit_access_token,
|
||||||
get_reddit_authorization_url,
|
get_reddit_authorization_url,
|
||||||
|
revoke_reddit_token,
|
||||||
)
|
)
|
||||||
from newsreader.news.collection.tasks import RedditTokenTask
|
from newsreader.news.collection.tasks import RedditTokenTask
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IntegrationsView(TemplateView):
|
class IntegrationsView(TemplateView):
|
||||||
template_name = "accounts/views/integrations.html"
|
template_name = "accounts/views/integrations.html"
|
||||||
|
|
||||||
|
|
@ -41,6 +47,11 @@ class IntegrationsView(TemplateView):
|
||||||
return {
|
return {
|
||||||
"reddit_authorization_url": reddit_authorization_url,
|
"reddit_authorization_url": reddit_authorization_url,
|
||||||
"reddit_refresh_url": reddit_refresh_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):
|
class RedditTokenRedirectView(RedirectView):
|
||||||
url = reverse_lazy("accounts:settings")
|
url = reverse_lazy("accounts:integrations")
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
response = super().get(request, *args, **kwargs)
|
response = super().get(request, *args, **kwargs)
|
||||||
|
|
@ -105,3 +116,35 @@ class RedditTokenRedirectView(RedirectView):
|
||||||
|
|
||||||
messages.error(request, _("Unable to retrieve token"))
|
messages.error(request, _("Unable to retrieve token"))
|
||||||
return response
|
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
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,21 @@ def get_reddit_access_token(code, user):
|
||||||
return response_data["access_token"], response_data["refresh_token"]
|
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):
|
class RedditBuilder(PostBuilder):
|
||||||
rule_type = RuleTypeChoices.subreddit
|
rule_type = RuleTypeChoices.subreddit
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue