Skip to content

Commit

Permalink
Merge pull request #3360 from hughrun/move-fix
Browse files Browse the repository at this point in the history
refactor Move for more redundancy
  • Loading branch information
Minnozz committed Apr 24, 2024
2 parents 637f19b + f24fdf7 commit 3d183a3
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 4 deletions.
2 changes: 1 addition & 1 deletion bookwyrm/models/move.py
Expand Up @@ -10,7 +10,7 @@


class Move(ActivityMixin, BookWyrmModel):
"""migrating an activitypub user account"""
"""migrating an activitypub object"""

user = fields.ForeignKey(
"User", on_delete=models.PROTECT, activitypub_field="actor"
Expand Down
40 changes: 40 additions & 0 deletions bookwyrm/tests/data/ap_user_move.json
@@ -0,0 +1,40 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value"
}
],
"id": "https://example.com/user/mouse",
"type": "Person",
"preferredUsername": "mouse",
"name": "MOUSE?? MOUSE!!",
"inbox": "https://example.com/user/mouse/inbox",
"outbox": "https://example.com/user/mouse/outbox",
"followers": "https://example.com/user/mouse/followers",
"following": "https://example.com/user/mouse/following",
"summary": "",
"publicKey": {
"id": "https://example.com/user/mouse/#main-key",
"owner": "https://example.com/user/mouse",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6QisDrjOQvkRo/MqNmSYPwqtt\nCxg/8rCW+9jKbFUKvqjTeKVotEE85122v/DCvobCCdfQuYIFdVMk+dB1xJ0iPGPg\nyU79QHY22NdV9mFKA2qtXVVxb5cxpA4PlwOHM6PM/k8B+H09OUrop2aPUAYwy+vg\n+MXyz8bAXrIS1kq6fQIDAQAB\n-----END PUBLIC KEY-----"
},
"endpoints": {
"sharedInbox": "https://example.com/inbox"
},
"bookwyrmUser": true,
"manuallyApprovesFollowers": false,
"discoverable": false,
"alsoKnownAs": ["https://your.domain.here/user/rat"],
"devices": "https://friend.camp/users/tripofmice/collections/devices",
"tag": [],
"icon": {
"type": "Image",
"mediaType": "image/png",
"url": "https://example.com/images/avatars/AL-2-crop-50.png"
}
}
60 changes: 60 additions & 0 deletions bookwyrm/tests/models/test_move.py
@@ -0,0 +1,60 @@
""" testing move models """
from unittest.mock import patch
from django.core.exceptions import PermissionDenied
from django.test import TestCase

from bookwyrm import models


class MoveUser(TestCase):
"""move your account to another identity"""

@classmethod
def setUpTestData(cls):
"""we need some users for this"""
with patch("bookwyrm.models.user.set_remote_server.delay"):
cls.target_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)

with (
patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"),
patch("bookwyrm.activitystreams.populate_stream_task.delay"),
patch("bookwyrm.lists_stream.populate_lists_task.delay"),
):
cls.origin_user = models.User.objects.create_user(
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
)
cls.origin_user.remote_id = "http://local.com/user/mouse"
cls.origin_user.save(broadcast=False, update_fields=["remote_id"])

def test_user_move_unauthorized(self):
"""attempt a user move without alsoKnownAs set"""

with self.assertRaises(PermissionDenied):
models.MoveUser.objects.create(
user=self.origin_user,
object=self.origin_user.remote_id,
target=self.target_user,
)

@patch("bookwyrm.suggested_users.remove_user_task.delay")
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async")
def test_user_move(self, *_):
"""move user"""

self.target_user.also_known_as.add(self.origin_user.id)
self.target_user.save(broadcast=False)

models.MoveUser.objects.create(
user=self.origin_user,
object=self.origin_user.remote_id,
target=self.target_user,
)
self.assertEqual(self.origin_user.moved_to, self.target_user.remote_id)
116 changes: 116 additions & 0 deletions bookwyrm/tests/views/preferences/test_move.py
@@ -0,0 +1,116 @@
""" test move functionality """
import json
from unittest.mock import patch
import pathlib
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase
from django.test.client import RequestFactory
import responses

from bookwyrm import forms, models, views


@patch("bookwyrm.activitystreams.add_status_task.delay")
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
@patch("bookwyrm.suggested_users.rerank_user_task.delay")
class ViewsHelpers(TestCase):
"""viewing and creating statuses"""

@classmethod
def setUpTestData(cls):
"""we need basic test data and mocks"""

with (
patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"),
patch("bookwyrm.activitystreams.populate_stream_task.delay"),
patch("bookwyrm.lists_stream.populate_lists_task.delay"),
patch("bookwyrm.suggested_users.rerank_user_task.delay"),
):

cls.local_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=True,
discoverable=True,
localname="rat",
remote_id="https://your.domain.here/user/rat",
)

with (
patch("bookwyrm.models.user.set_remote_server.delay"),
patch("bookwyrm.suggested_users.rerank_user_task.delay"),
):
cls.remote_user = models.User.objects.create_user(
"mouse@example.com",
"mouse@mouse.com",
"mouseword",
local=False,
remote_id="https://example.com/user/mouse",
)

def setUp(self):
"""individual test setup"""
self.factory = RequestFactory()
datafile = pathlib.Path(__file__).parent.joinpath(
"../../data/ap_user_move.json"
)
self.userdata = json.loads(datafile.read_bytes())
del self.userdata["icon"]

@responses.activate
@patch("bookwyrm.models.user.set_remote_server.delay")
@patch("bookwyrm.suggested_users.remove_user_task.delay")
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async")
def test_move_user_view(self, *_):
"""move user"""

self.assertEqual(self.remote_user.remote_id, "https://example.com/user/mouse")
self.assertIsNone(self.local_user.moved_to)
self.assertIsNone(self.remote_user.moved_to)
self.assertIsNone(self.local_user.also_known_as.first())
self.assertIsNone(self.remote_user.also_known_as.first())

username = "mouse@example.com"
wellknown = {
"subject": "acct:mouse@example.com",
"links": [
{
"rel": "self",
"type": "application/activity+json",
"href": "https://example.com/user/mouse",
}
],
}
responses.add(
responses.GET,
f"https://example.com/.well-known/webfinger?resource=acct:{username}",
json=wellknown,
status=200,
)
responses.add(
responses.GET,
"https://example.com/user/mouse",
json=self.userdata,
status=200,
)

view = views.MoveUser.as_view()
form = forms.MoveUserForm()
form.data["target"] = "mouse@example.com"
form.data["password"] = "ratword"

request = self.factory.post("", form.data)
request.user = self.local_user
middleware = SessionMiddleware()
middleware.process_request(request)
request.session.save()

with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
view(request)
self.local_user.refresh_from_db()

self.assertEqual(self.local_user.also_known_as.first(), self.remote_user)
self.assertEqual(self.remote_user.also_known_as.first(), self.local_user)
self.assertEqual(self.local_user.moved_to, "https://example.com/user/mouse")
9 changes: 7 additions & 2 deletions bookwyrm/views/helpers.py
Expand Up @@ -61,7 +61,7 @@ def is_bookwyrm_request(request):
return True


def handle_remote_webfinger(query, unknown_only=False):
def handle_remote_webfinger(query, unknown_only=False, refresh=False):
"""webfingerin' other servers"""
user = None

Expand All @@ -76,6 +76,11 @@ def handle_remote_webfinger(query, unknown_only=False):
return None

try:

if refresh:
# Always fetch the remote info - don't even bother checking the DB
raise models.User.DoesNotExist("remote_only is set to True")

user = models.User.objects.get(username__iexact=query)

if unknown_only:
Expand All @@ -93,7 +98,7 @@ def handle_remote_webfinger(query, unknown_only=False):
if link.get("rel") == "self":
try:
user = activitypub.resolve_remote_id(
link["href"], model=models.User
link["href"], model=models.User, refresh=refresh
)
except (KeyError, activitypub.ActivitySerializerError):
return None
Expand Down
3 changes: 2 additions & 1 deletion bookwyrm/views/preferences/move_user.py
Expand Up @@ -32,7 +32,7 @@ def post(self, request):

if form.is_valid() and user.check_password(form.cleaned_data["password"]):
username = form.cleaned_data["target"]
target = handle_remote_webfinger(username)
target = handle_remote_webfinger(username, refresh=True)

try:
models.MoveUser.objects.create(
Expand Down Expand Up @@ -81,6 +81,7 @@ def post(self, request):
return TemplateResponse(request, "preferences/alias_user.html", data)

user.also_known_as.add(remote_user.id)
user.save(broadcast=True) # broadcast the alias

return redirect("prefs-alias")

Expand Down

0 comments on commit 3d183a3

Please sign in to comment.