From f011f2bce90e5c053224097f6c6ed3c80060229b Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Mon, 20 Nov 2023 12:17:52 +1100 Subject: [PATCH] hide instance actor from users The Instance Actor is required for signing http GET requests but is not a "user" and should not be otherwise interacted with. - hides instance actor profile page, returning a 404 - excludes instance actor from search results and suggestions including in Getting Started - replaces link to user profile in user admin page with a brief message box - replaces panel in user admin page that allows for user to be suspended or removed with a message explaining why that is a very bad idea fixes #3119 --- bookwyrm/activitypub/base_activity.py | 2 +- bookwyrm/suggested_users.py | 13 +- .../templates/settings/users/user_info.html | 11 ++ .../users/user_moderation_actions.html | 140 ++++++++++-------- bookwyrm/templatetags/utilities.py | 7 + bookwyrm/views/get_started.py | 2 + bookwyrm/views/search.py | 3 +- bookwyrm/views/user.py | 6 +- 8 files changed, 115 insertions(+), 69 deletions(-) diff --git a/bookwyrm/activitypub/base_activity.py b/bookwyrm/activitypub/base_activity.py index 05e7d8a05..aa4b5b687 100644 --- a/bookwyrm/activitypub/base_activity.py +++ b/bookwyrm/activitypub/base_activity.py @@ -396,7 +396,7 @@ def resolve_remote_id( def get_representative(): """Get or create an actor representing the instance - to sign requests to 'secure mastodon' servers""" + to sign outgoing HTTP GET requests""" username = f"{INSTANCE_ACTOR_USERNAME}@{DOMAIN}" email = "bookwyrm@localhost" try: diff --git a/bookwyrm/suggested_users.py b/bookwyrm/suggested_users.py index 3e9bef9c4..a13ee97fd 100644 --- a/bookwyrm/suggested_users.py +++ b/bookwyrm/suggested_users.py @@ -8,6 +8,7 @@ from opentelemetry import trace from bookwyrm import models from bookwyrm.redis_store import RedisStore, r +from bookwyrm.settings import INSTANCE_ACTOR_USERNAME from bookwyrm.tasks import app, SUGGESTED_USERS from bookwyrm.telemetry import open_telemetry @@ -98,9 +99,15 @@ class SuggestedUsers(RedisStore): for (pk, score) in values ] # annotate users with mutuals and shared book counts - users = models.User.objects.filter( - is_active=True, bookwyrm_user=True, id__in=[pk for (pk, _) in values] - ).annotate(mutuals=Case(*annotations, output_field=IntegerField(), default=0)) + users = ( + models.User.objects.filter( + is_active=True, bookwyrm_user=True, id__in=[pk for (pk, _) in values] + ) + .annotate( + mutuals=Case(*annotations, output_field=IntegerField(), default=0) + ) + .exclude(localname=INSTANCE_ACTOR_USERNAME) + ) if local: users = users.filter(local=True) return users.order_by("-mutuals")[:5] diff --git a/bookwyrm/templates/settings/users/user_info.html b/bookwyrm/templates/settings/users/user_info.html index f35c60db9..e07a7e439 100644 --- a/bookwyrm/templates/settings/users/user_info.html +++ b/bookwyrm/templates/settings/users/user_info.html @@ -1,6 +1,7 @@ {% load i18n %} {% load markdown %} {% load humanize %} +{% load utilities %}
@@ -13,7 +14,17 @@
{% endif %} + {% if user.localname|is_instance_admin %} +
+
+ {% trans "This account is the instance actor for signing HTTP requests." %} +
+
+ {% else %}

{% trans "View user profile" %}

+ {% endif %} + + {% url 'settings-user' user.id as url %} {% if not request.path == url %}

{% trans "Go to user admin" %}

diff --git a/bookwyrm/templates/settings/users/user_moderation_actions.html b/bookwyrm/templates/settings/users/user_moderation_actions.html index 4a624a5e4..fd3e66aa8 100644 --- a/bookwyrm/templates/settings/users/user_moderation_actions.html +++ b/bookwyrm/templates/settings/users/user_moderation_actions.html @@ -1,4 +1,5 @@ {% load i18n %} +{% load utilities %}
{% if not user.is_active and user.deactivation_reason == "self_deletion" or user.deactivation_reason == "moderator_deletion" %}
@@ -7,77 +8,90 @@ {% else %}

{% trans "User Actions" %}

-
-
- {% if user.is_active %} -

- {% trans "Send direct message" %} -

- {% endif %} + {% if user.localname|is_instance_admin %} +
+
+
+

{% trans "This is the instance admin actor" %}

+
+
+

{% trans "You must not delete or disable this account as it is critical to the functioning of your server. This actor signs outgoing GET requests to smooth interaction with secure ActivityPub servers." %}

+

{% trans "This account is not discoverable by ordinary users and does not have a profile page." %}

+
+
+
+ {% else %} +
+
+ {% if user.is_active %} +

+ {% trans "Send direct message" %} +

+ {% endif %} - {% if not user.is_active and user.deactivation_reason == "pending" %} -
- {% csrf_token %} - -
- {% endif %} - {% if user.is_active or user.deactivation_reason == "pending" %} -
- {% csrf_token %} - -
- {% else %} -
- {% csrf_token %} - -
+ {% if not user.is_active and user.deactivation_reason == "pending" %} +
+ {% csrf_token %} + +
+ {% endif %} + {% if user.is_active or user.deactivation_reason == "pending" %} +
+ {% csrf_token %} + +
+ {% else %} +
+ {% csrf_token %} + +
+ {% endif %} + + {% if user.local %} +
+ {% trans "Permanently delete user" as button_text %} + {% include "snippets/toggle/open_button.html" with controls_text="delete_user" text=button_text class="is-danger is-light" %} +
+ {% endif %} +
+ + {% if user.local %} +
+ {% include "settings/users/delete_user_form.html" with controls_text="delete_user" class="mt-2 mb-2" %} +
{% endif %} {% if user.local %}
- {% trans "Permanently delete user" as button_text %} - {% include "snippets/toggle/open_button.html" with controls_text="delete_user" text=button_text class="is-danger is-light" %} +
+ {% csrf_token %} + + {% if group_form.non_field_errors %} + {{ group_form.non_field_errors }} + {% endif %} + {% with group=user.groups.first %} +
+ +
+ + {% include 'snippets/form_errors.html' with errors_list=group_form.groups.errors id="desc_user_group" %} + {% endwith %} + +
{% endif %}
- - {% if user.local %} -
- {% include "settings/users/delete_user_form.html" with controls_text="delete_user" class="mt-2 mb-2" %} -
{% endif %} - - {% if user.local %} -
-
- {% csrf_token %} - - {% if group_form.non_field_errors %} - {{ group_form.non_field_errors }} - {% endif %} - {% with group=user.groups.first %} -
- -
- - {% include 'snippets/form_errors.html' with errors_list=group_form.groups.errors id="desc_user_group" %} - {% endwith %} - -
-
- {% endif %} -
- {% endif %}
diff --git a/bookwyrm/templatetags/utilities.py b/bookwyrm/templatetags/utilities.py index 42e67990f..2fd99403f 100644 --- a/bookwyrm/templatetags/utilities.py +++ b/bookwyrm/templatetags/utilities.py @@ -9,6 +9,7 @@ from django.utils.translation import gettext_lazy as _ from django.templatetags.static import static from bookwyrm.models import User +from bookwyrm.settings import INSTANCE_ACTOR_USERNAME register = template.Library() @@ -125,3 +126,9 @@ def id_to_username(user_id): value = f"{name}@{domain}" return value + + +@register.filter(name="is_instance_admin") +def is_instance_admin(localname): + """Returns a boolean indicating whether the user is the instance admin account""" + return localname == INSTANCE_ACTOR_USERNAME diff --git a/bookwyrm/views/get_started.py b/bookwyrm/views/get_started.py index fdb8824fa..511a886ca 100644 --- a/bookwyrm/views/get_started.py +++ b/bookwyrm/views/get_started.py @@ -11,6 +11,7 @@ from django.utils.decorators import method_decorator from django.views import View from bookwyrm import book_search, forms, models +from bookwyrm.settings import INSTANCE_ACTOR_USERNAME from bookwyrm.suggested_users import suggested_users from .preferences.edit_user import save_user_form @@ -108,6 +109,7 @@ class GetStartedUsers(View): .exclude( id=request.user.id, ) + .exclude(localname=INSTANCE_ACTOR_USERNAME) .order_by("-similarity")[:5] ) data = {"no_results": not user_results} diff --git a/bookwyrm/views/search.py b/bookwyrm/views/search.py index 2b7303fd7..743f33f59 100644 --- a/bookwyrm/views/search.py +++ b/bookwyrm/views/search.py @@ -13,7 +13,7 @@ from csp.decorators import csp_update from bookwyrm import models from bookwyrm.connectors import connector_manager from bookwyrm.book_search import search, format_search_result -from bookwyrm.settings import PAGE_LENGTH +from bookwyrm.settings import PAGE_LENGTH, INSTANCE_ACTOR_USERNAME from bookwyrm.utils import regex from .helpers import is_api_request from .helpers import handle_remote_webfinger @@ -113,6 +113,7 @@ def user_search(request): .filter( similarity__gt=0.5, ) + .exclude(localname=INSTANCE_ACTOR_USERNAME) .order_by("-similarity") ) diff --git a/bookwyrm/views/user.py b/bookwyrm/views/user.py index e547925a5..ba224d671 100644 --- a/bookwyrm/views/user.py +++ b/bookwyrm/views/user.py @@ -11,7 +11,7 @@ from django.views.decorators.http import require_POST from bookwyrm import models from bookwyrm.activitypub import ActivitypubResponse -from bookwyrm.settings import PAGE_LENGTH +from bookwyrm.settings import PAGE_LENGTH, INSTANCE_ACTOR_USERNAME from .helpers import get_user_from_username, is_api_request @@ -31,6 +31,10 @@ class User(View): return ActivitypubResponse(user.to_activity()) # otherwise we're at a UI view + # if it's not an API request, never show the instance actor profile page + if user.localname == INSTANCE_ACTOR_USERNAME: + raise Http404() + shelf_preview = [] # only show shelves that should be visible