1
0
Fork 0
* update User model to allow for moved_to and also_known_as values
* allow users to add aliases (also_known_as) in UI
* allow users to move account to another one (moved_to)
* redirect webfinger to the new account after a move
* present notification to followers inviting to follow at new account

Note: unlike Mastodon we're not running any unfollow/autofollow action here: users can decide for themselves
This makes undoing moves easier.

TODO

There is still a bug with incoming Moves, at least from Mastodon.
This seems to be something to do with Update activities (rather than Move, strictly).
This commit is contained in:
Hugh Rundle 2023-09-18 21:21:04 +10:00
parent e7ba6a3141
commit 5b051631ec
No known key found for this signature in database
GPG key ID: A7E35779918253F9
24 changed files with 521 additions and 138 deletions

View file

@ -37,6 +37,7 @@ from .admin.user_admin import UserAdmin, UserAdminList, ActivateUserAdmin
from .preferences.change_password import ChangePassword
from .preferences.edit_user import EditUser
from .preferences.export import Export
from .preferences.move_user import MoveUser, AliasUser, remove_alias
from .preferences.delete_user import DeleteUser, DeactivateUser, ReactivateUser
from .preferences.block import Block, unblock
from .preferences.two_factor_auth import (

View file

@ -0,0 +1,98 @@
""" move your account somewhere else """
from django.core.exceptions import PermissionDenied
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.http import require_POST
from bookwyrm import forms, models
from bookwyrm.views.helpers import handle_remote_webfinger
# pylint: disable=no-self-use
@method_decorator(login_required, name="dispatch")
class MoveUser(View):
"""move user view"""
def get(self, request):
"""move page for a user"""
data = {
"form": forms.MoveUserForm(),
"user": request.user,
}
return TemplateResponse(request, "preferences/move_user.html", data)
def post(self, request):
"""Packing your stuff and moving house"""
form = forms.MoveUserForm(request.POST, instance=request.user)
user = models.User.objects.get(id=request.user.id)
if form.is_valid() and user.check_password(form.cleaned_data["password"]):
username = form.cleaned_data["target"]
target = handle_remote_webfinger(username)
try:
models.MoveUser.objects.create(
user=request.user, object=request.user.remote_id, target=target
)
return redirect("/")
except (PermissionDenied):
form.errors["target"] = [
"You must set this server's user as an alias on the user you wish to move to before moving"
]
data = {"form": form, "user": request.user}
return TemplateResponse(request, "preferences/move_user.html", data)
form.errors["password"] = ["Invalid password"]
data = {"form": form, "user": request.user}
return TemplateResponse(request, "preferences/move_user.html", data)
# pylint: disable=no-self-use
@method_decorator(login_required, name="dispatch")
class AliasUser(View):
"""alias user view"""
def get(self, request):
"""move page for a user"""
data = {
"form": forms.AliasUserForm(),
"user": request.user,
}
return TemplateResponse(request, "preferences/alias_user.html", data)
def post(self, request):
"""Creating a nom de plume"""
form = forms.AliasUserForm(request.POST, instance=request.user)
user = models.User.objects.get(id=request.user.id)
if form.is_valid() and user.check_password(form.cleaned_data["password"]):
username = form.cleaned_data["username"]
remote_user = handle_remote_webfinger(username)
if remote_user is None:
form.errors["username"] = ["Username does not exist"]
data = {"form": form, "user": request.user}
return TemplateResponse(request, "preferences/alias_user.html", data)
user.also_known_as.add(remote_user.id)
return redirect("prefs-alias")
form.errors["password"] = ["Invalid password"]
data = {"form": form, "user": request.user}
return TemplateResponse(request, "preferences/alias_user.html", data)
@login_required
@require_POST
def remove_alias(request):
"""remove an alias from the user profile"""
request.user.also_known_as.remove(request.POST["alias"])
return redirect("prefs-alias")

View file

@ -21,6 +21,7 @@ def webfinger(request):
username = resource.replace("acct:", "")
user = get_object_or_404(models.User, username__iexact=username)
href = user.moved_to if user.moved_to else user.remote_id
return JsonResponse(
{
@ -29,7 +30,7 @@ def webfinger(request):
{
"rel": "self",
"type": "application/activity+json",
"href": user.remote_id,
"href": href,
},
{
"rel": "http://ostatus.org/schema/1.0/subscribe",