more descriptive remote follow errors

- distinguish between invalid username, user not found, and remote follow not supported
- make helpers DRYer
This commit is contained in:
Hugh Rundle 2021-12-06 09:29:51 +11:00
parent c77edab79c
commit 781fe69470
No known key found for this signature in database
GPG key ID: CD23D6039184286B
4 changed files with 83 additions and 38 deletions

View file

@ -2,7 +2,28 @@
{% block content %} {% block content %}
<div class="block"> <div class="block">
{% if not request.user.is_authenticated and not error == 'remote_subscribe' %} {% if error == 'invalid_username' %}
<div class="notification is-warning has-text-centered" role="status">
{% blocktrans %}
<p><strong>{{ account }}</strong> is not a valid username.</p>
<p>Check you have the correct username before trying again.</p>
{% endblocktrans %}
</div>
{% elif error == 'user_not_found' %}
<div class="notification is-warning has-text-centered" role="status">
{% blocktrans %}
<p><strong>{{ account }}</strong> could not be found or <code>{{ remote_domain }}</code> does not support identity discovery.</p>
<p>Check you have the correct username before trying again.</p>
{% endblocktrans %}
</div>
{% elif error == 'not_supported' %}
<div class="notification is-warning has-text-centered" role="status">
{% blocktrans %}
<p><strong>{{ account }}</strong> was found but <code>{{ remote_domain }}</code> does not support 'remote follow'.</p>
<p>Try searching for <strong>{{ user }}</strong> on <code>{{ remote_domain }}</code> instead.</p>
{% endblocktrans %}
</div>
{% elif not request.user.is_authenticated %}
<div class="navbar-item"> <div class="navbar-item">
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
@ -31,11 +52,6 @@
<p>{% blocktrans %}Something went wrong trying to follow <strong>{{ account }}</strong>{% endblocktrans %}</p> <p>{% blocktrans %}Something went wrong trying to follow <strong>{{ account }}</strong>{% endblocktrans %}</p>
<p>{% trans 'Check you have the correct username before trying again.' %}</p> <p>{% trans 'Check you have the correct username before trying again.' %}</p>
</div> </div>
{% elif error == 'remote_subscribe' %}
<div class="notification is-warning has-text-centered" role="status">
<p>{% blocktrans %}Something went wrong trying to follow from <strong>{{ account }}</strong>{% endblocktrans %}</p>
<p>{% trans 'Check you have the correct username before trying again.' %}</p>
</div>
{% elif error == 'is_blocked' %} {% elif error == 'is_blocked' %}
<div class="notification is-danger has-text-centered" role="status"> <div class="notification is-danger has-text-centered" role="status">
<p>{% blocktrans %}You have blocked <strong>{{ account }}</strong>{% endblocktrans %}</p> <p>{% blocktrans %}You have blocked <strong>{{ account }}</strong>{% endblocktrans %}</p>

View file

@ -14,10 +14,10 @@
{% endblock %} {% endblock %}
{% block heading %} {% block heading %}
{% if not request.user.is_authenticated and not error == 'remote_subscribe' %} {% if error %}
{% trans "Let's log in first..." %}
{% elif error %}
{% trans 'Uh oh...' %} {% trans 'Uh oh...' %}
{% elif not request.user.is_authenticated %}
{% trans "Let's log in first..." %}
{% else %} {% else %}
{% blocktrans with sitename=site.name %}Follow from {{ sitename }}{% endblocktrans %} {% blocktrans with sitename=site.name %}Follow from {{ sitename }}{% endblocktrans %}
{% endif %} {% endif %}

View file

@ -12,6 +12,7 @@ from .helpers import (
get_user_from_username, get_user_from_username,
handle_remote_webfinger, handle_remote_webfinger,
subscribe_remote_webfinger, subscribe_remote_webfinger,
WebFingerError,
) )
@ -142,7 +143,7 @@ def ostatus_follow_success(request):
def remote_follow_page(request): def remote_follow_page(request):
"""Display remote follow page""" """display remote follow page"""
user = get_user_from_username(request.user, request.GET.get("user")) user = get_user_from_username(request.user, request.GET.get("user"))
data = {"user": user} data = {"user": user}
return TemplateResponse(request, "ostatus/remote_follow.html", data) return TemplateResponse(request, "ostatus/remote_follow.html", data)
@ -152,10 +153,33 @@ def remote_follow_page(request):
def remote_follow(request): def remote_follow(request):
"""direct user to follow from remote account using ostatus subscribe protocol""" """direct user to follow from remote account using ostatus subscribe protocol"""
remote_user = request.POST.get("remote_user") remote_user = request.POST.get("remote_user")
template = subscribe_remote_webfinger(remote_user) try:
if template is None: if remote_user[0] == "@":
data = {"account": remote_user, "user": None, "error": "remote_subscribe"} remote_user = remote_user[1:]
return TemplateResponse(request, "ostatus/subscribe.html", data) remote_domain = remote_user.split("@")[1]
except:
remote_domain = None
wf_response = subscribe_remote_webfinger(remote_user)
user = get_object_or_404(models.User, id=request.POST.get("user")) user = get_object_or_404(models.User, id=request.POST.get("user"))
url = template.replace("{uri}", urllib.parse.quote(user.remote_id))
if wf_response is None:
data = {
"account": remote_user,
"user": user,
"error": "not_supported",
"remote_domain": remote_domain,
}
return TemplateResponse(request, "ostatus/subscribe.html", data)
if type(wf_response) == WebFingerError:
data = {
"account": remote_user,
"user": user,
"error": str(wf_response),
"remote_domain": remote_domain,
}
return TemplateResponse(request, "ostatus/subscribe.html", data)
url = wf_response.replace("{uri}", urllib.parse.quote(user.remote_id))
return redirect(url) return redirect(url)

View file

@ -15,6 +15,27 @@ from bookwyrm.status import create_generated_note
from bookwyrm.utils import regex from bookwyrm.utils import regex
class WebFingerError(Exception):
"""error class for problems finding user information with webfinger"""
pass
def get_domain_from_username(username):
# usernames could be @user@domain or user@domain
if not username:
return None
if username[0] == "@":
username = username[1:]
try:
domain = username.split("@")[1]
return domain
except IndexError:
return None
def get_user_from_username(viewer, username): def get_user_from_username(viewer, username):
"""helper function to resolve a localname or a username to a user""" """helper function to resolve a localname or a username to a user"""
if viewer.is_authenticated and viewer.localname == username: if viewer.is_authenticated and viewer.localname == username:
@ -52,17 +73,8 @@ def is_bookwyrm_request(request):
def handle_remote_webfinger(query): def handle_remote_webfinger(query):
"""webfingerin' other servers""" """webfingerin' other servers"""
user = None user = None
domain = get_domain_from_username(query)
# usernames could be @user@domain or user@domain if domain is None:
if not query:
return None
if query[0] == "@":
query = query[1:]
try:
domain = query.split("@")[1]
except IndexError:
return None return None
try: try:
@ -88,24 +100,17 @@ def handle_remote_webfinger(query):
def subscribe_remote_webfinger(query): def subscribe_remote_webfinger(query):
"""get subscribe template from other servers""" """get subscribe template from other servers"""
template = None template = None
domain = get_domain_from_username(query)
# usernames could be @user@domain or user@domain if domain is None:
if not query: return WebFingerError("invalid_username")
return None
if query[0] == "@":
query = query[1:]
try:
domain = query.split("@")[1]
except IndexError:
return None
url = f"https://{domain}/.well-known/webfinger?resource=acct:{query}" url = f"https://{domain}/.well-known/webfinger?resource=acct:{query}"
try: try:
data = get_data(url) data = get_data(url)
except (ConnectorException, HTTPError): except (ConnectorException, HTTPError):
return None return WebFingerError("user_not_found")
for link in data.get("links"): for link in data.get("links"):
if link.get("rel") == "http://ostatus.org/schema/1.0/subscribe": if link.get("rel") == "http://ostatus.org/schema/1.0/subscribe":