mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-25 11:01:12 +00:00
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
This commit is contained in:
parent
06568aab88
commit
f011f2bce9
8 changed files with 115 additions and 69 deletions
|
@ -396,7 +396,7 @@ def resolve_remote_id(
|
||||||
|
|
||||||
def get_representative():
|
def get_representative():
|
||||||
"""Get or create an actor representing the instance
|
"""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}"
|
username = f"{INSTANCE_ACTOR_USERNAME}@{DOMAIN}"
|
||||||
email = "bookwyrm@localhost"
|
email = "bookwyrm@localhost"
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -8,6 +8,7 @@ from opentelemetry import trace
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
from bookwyrm.redis_store import RedisStore, r
|
from bookwyrm.redis_store import RedisStore, r
|
||||||
|
from bookwyrm.settings import INSTANCE_ACTOR_USERNAME
|
||||||
from bookwyrm.tasks import app, SUGGESTED_USERS
|
from bookwyrm.tasks import app, SUGGESTED_USERS
|
||||||
from bookwyrm.telemetry import open_telemetry
|
from bookwyrm.telemetry import open_telemetry
|
||||||
|
|
||||||
|
@ -98,9 +99,15 @@ class SuggestedUsers(RedisStore):
|
||||||
for (pk, score) in values
|
for (pk, score) in values
|
||||||
]
|
]
|
||||||
# annotate users with mutuals and shared book counts
|
# annotate users with mutuals and shared book counts
|
||||||
users = models.User.objects.filter(
|
users = (
|
||||||
is_active=True, bookwyrm_user=True, id__in=[pk for (pk, _) in values]
|
models.User.objects.filter(
|
||||||
).annotate(mutuals=Case(*annotations, output_field=IntegerField(), default=0))
|
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:
|
if local:
|
||||||
users = users.filter(local=True)
|
users = users.filter(local=True)
|
||||||
return users.order_by("-mutuals")[:5]
|
return users.order_by("-mutuals")[:5]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load markdown %}
|
{% load markdown %}
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
|
{% load utilities %}
|
||||||
|
|
||||||
<div class="block columns">
|
<div class="block columns">
|
||||||
<div class="column is-flex is-flex-direction-column">
|
<div class="column is-flex is-flex-direction-column">
|
||||||
|
@ -13,7 +14,17 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if user.localname|is_instance_admin %}
|
||||||
|
<div class="message is-warning">
|
||||||
|
<div class="message-body">
|
||||||
|
{% trans "This account is the instance actor for signing HTTP requests." %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
<p class="mt-2"><a href="{{ user.local_path }}">{% trans "View user profile" %}</a></p>
|
<p class="mt-2"><a href="{{ user.local_path }}">{% trans "View user profile" %}</a></p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% url 'settings-user' user.id as url %}
|
{% url 'settings-user' user.id as url %}
|
||||||
{% if not request.path == url %}
|
{% if not request.path == url %}
|
||||||
<p class="mt-2"><a href="{{ url }}">{% trans "Go to user admin" %}</a></p>
|
<p class="mt-2"><a href="{{ url }}">{% trans "Go to user admin" %}</a></p>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load utilities %}
|
||||||
<div class="block content">
|
<div class="block content">
|
||||||
{% if not user.is_active and user.deactivation_reason == "self_deletion" or user.deactivation_reason == "moderator_deletion" %}
|
{% if not user.is_active and user.deactivation_reason == "self_deletion" or user.deactivation_reason == "moderator_deletion" %}
|
||||||
<div class="notification is-danger">
|
<div class="notification is-danger">
|
||||||
|
@ -7,77 +8,90 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<h3>{% trans "User Actions" %}</h3>
|
<h3>{% trans "User Actions" %}</h3>
|
||||||
|
|
||||||
<div class="box">
|
{% if user.localname|is_instance_admin %}
|
||||||
<div class="is-flex">
|
<div class="box">
|
||||||
{% if user.is_active %}
|
<div class="message is-warning">
|
||||||
<p class="mr-1">
|
<div class="message-header">
|
||||||
<a class="button" href="{% url 'direct-messages-user' user.username %}">{% trans "Send direct message" %}</a>
|
<p>{% trans "This is the instance admin actor" %}</p>
|
||||||
</p>
|
</div>
|
||||||
{% endif %}
|
<div class="message-body">
|
||||||
|
<p>{% 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." %}</p>
|
||||||
|
<p>{% trans "This account is not discoverable by ordinary users and does not have a profile page." %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="box">
|
||||||
|
<div class="is-flex">
|
||||||
|
{% if user.is_active %}
|
||||||
|
<p class="mr-1">
|
||||||
|
<a class="button" href="{% url 'direct-messages-user' user.username %}">{% trans "Send direct message" %}</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if not user.is_active and user.deactivation_reason == "pending" %}
|
{% if not user.is_active and user.deactivation_reason == "pending" %}
|
||||||
<form name="activate" method="post" action="{% url 'settings-activate-user' user.id %}" class="mr-1">
|
<form name="activate" method="post" action="{% url 'settings-activate-user' user.id %}" class="mr-1">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="button is-success is-light">{% trans "Activate user" %}</button>
|
<button type="submit" class="button is-success is-light">{% trans "Activate user" %}</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.is_active or user.deactivation_reason == "pending" %}
|
{% if user.is_active or user.deactivation_reason == "pending" %}
|
||||||
<form name="suspend" method="post" action="{% url 'settings-report-suspend' user.id report.id %}" class="mr-1">
|
<form name="suspend" method="post" action="{% url 'settings-report-suspend' user.id report.id %}" class="mr-1">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="button is-danger is-light">{% trans "Suspend user" %}</button>
|
<button type="submit" class="button is-danger is-light">{% trans "Suspend user" %}</button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<form name="unsuspend" method="post" action="{% url 'settings-report-unsuspend' user.id report.id %}" class="mr-1">
|
<form name="unsuspend" method="post" action="{% url 'settings-report-unsuspend' user.id report.id %}" class="mr-1">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button class="button">{% trans "Un-suspend user" %}</button>
|
<button class="button">{% trans "Un-suspend user" %}</button>
|
||||||
</form>
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if user.local %}
|
||||||
|
<div>
|
||||||
|
{% 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" %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if user.local %}
|
||||||
|
<div>
|
||||||
|
{% include "settings/users/delete_user_form.html" with controls_text="delete_user" class="mt-2 mb-2" %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if user.local %}
|
{% if user.local %}
|
||||||
<div>
|
<div>
|
||||||
{% trans "Permanently delete user" as button_text %}
|
<form name="permission" method="post" action="{% url 'settings-user' user.id report.id %}">
|
||||||
{% include "snippets/toggle/open_button.html" with controls_text="delete_user" text=button_text class="is-danger is-light" %}
|
{% csrf_token %}
|
||||||
|
<label class="label" for="id_user_group">{% trans "Access level:" %}</label>
|
||||||
|
{% if group_form.non_field_errors %}
|
||||||
|
{{ group_form.non_field_errors }}
|
||||||
|
{% endif %}
|
||||||
|
{% with group=user.groups.first %}
|
||||||
|
<div class="select">
|
||||||
|
<select name="groups" id="id_user_group" aria-describedby="desc_user_group">
|
||||||
|
{% for value, name in group_form.fields.groups.choices %}
|
||||||
|
<option value="{{ value }}" {% if name == group.name %}selected{% endif %}>
|
||||||
|
{{ name|title }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
<option value="" {% if not group %}selected{% endif %}>
|
||||||
|
User
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include 'snippets/form_errors.html' with errors_list=group_form.groups.errors id="desc_user_group" %}
|
||||||
|
{% endwith %}
|
||||||
|
<button class="button">
|
||||||
|
{% trans "Save" %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if user.local %}
|
|
||||||
<div>
|
|
||||||
{% include "settings/users/delete_user_form.html" with controls_text="delete_user" class="mt-2 mb-2" %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if user.local %}
|
|
||||||
<div>
|
|
||||||
<form name="permission" method="post" action="{% url 'settings-user' user.id report.id %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<label class="label" for="id_user_group">{% trans "Access level:" %}</label>
|
|
||||||
{% if group_form.non_field_errors %}
|
|
||||||
{{ group_form.non_field_errors }}
|
|
||||||
{% endif %}
|
|
||||||
{% with group=user.groups.first %}
|
|
||||||
<div class="select">
|
|
||||||
<select name="groups" id="id_user_group" aria-describedby="desc_user_group">
|
|
||||||
{% for value, name in group_form.fields.groups.choices %}
|
|
||||||
<option value="{{ value }}" {% if name == group.name %}selected{% endif %}>
|
|
||||||
{{ name|title }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
<option value="" {% if not group %}selected{% endif %}>
|
|
||||||
User
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% include 'snippets/form_errors.html' with errors_list=group_form.groups.errors id="desc_user_group" %}
|
|
||||||
{% endwith %}
|
|
||||||
<button class="button">
|
|
||||||
{% trans "Save" %}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
|
||||||
from bookwyrm.models import User
|
from bookwyrm.models import User
|
||||||
|
from bookwyrm.settings import INSTANCE_ACTOR_USERNAME
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
@ -125,3 +126,9 @@ def id_to_username(user_id):
|
||||||
value = f"{name}@{domain}"
|
value = f"{name}@{domain}"
|
||||||
|
|
||||||
return value
|
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
|
||||||
|
|
|
@ -11,6 +11,7 @@ from django.utils.decorators import method_decorator
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
|
||||||
from bookwyrm import book_search, forms, models
|
from bookwyrm import book_search, forms, models
|
||||||
|
from bookwyrm.settings import INSTANCE_ACTOR_USERNAME
|
||||||
from bookwyrm.suggested_users import suggested_users
|
from bookwyrm.suggested_users import suggested_users
|
||||||
from .preferences.edit_user import save_user_form
|
from .preferences.edit_user import save_user_form
|
||||||
|
|
||||||
|
@ -108,6 +109,7 @@ class GetStartedUsers(View):
|
||||||
.exclude(
|
.exclude(
|
||||||
id=request.user.id,
|
id=request.user.id,
|
||||||
)
|
)
|
||||||
|
.exclude(localname=INSTANCE_ACTOR_USERNAME)
|
||||||
.order_by("-similarity")[:5]
|
.order_by("-similarity")[:5]
|
||||||
)
|
)
|
||||||
data = {"no_results": not user_results}
|
data = {"no_results": not user_results}
|
||||||
|
|
|
@ -13,7 +13,7 @@ from csp.decorators import csp_update
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
from bookwyrm.connectors import connector_manager
|
from bookwyrm.connectors import connector_manager
|
||||||
from bookwyrm.book_search import search, format_search_result
|
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 bookwyrm.utils import regex
|
||||||
from .helpers import is_api_request
|
from .helpers import is_api_request
|
||||||
from .helpers import handle_remote_webfinger
|
from .helpers import handle_remote_webfinger
|
||||||
|
@ -113,6 +113,7 @@ def user_search(request):
|
||||||
.filter(
|
.filter(
|
||||||
similarity__gt=0.5,
|
similarity__gt=0.5,
|
||||||
)
|
)
|
||||||
|
.exclude(localname=INSTANCE_ACTOR_USERNAME)
|
||||||
.order_by("-similarity")
|
.order_by("-similarity")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
from bookwyrm.activitypub import ActivitypubResponse
|
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
|
from .helpers import get_user_from_username, is_api_request
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +31,10 @@ class User(View):
|
||||||
return ActivitypubResponse(user.to_activity())
|
return ActivitypubResponse(user.to_activity())
|
||||||
# otherwise we're at a UI view
|
# 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 = []
|
shelf_preview = []
|
||||||
|
|
||||||
# only show shelves that should be visible
|
# only show shelves that should be visible
|
||||||
|
|
Loading…
Reference in a new issue