Skip to content

Commit b61302a

Browse files
pfefferleobenland
andauthored
Store and retrieve webfinger acct for remote actors (#2575)
Co-authored-by: Konstantin Obenland <[email protected]>
1 parent dfb6489 commit b61302a

File tree

7 files changed

+232
-13
lines changed

7 files changed

+232
-13
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: added
3+
4+
Store and retrieve webfinger acct for remote actors to improve identification and reduce lookups

includes/collection/class-remote-actors.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,10 @@ public static function get_actor( $post ) {
451451
self::add_error( $post->ID, $actor );
452452
}
453453

454+
if ( ! $actor->get_webfinger() ) {
455+
$actor->set_webfinger( self::get_acct( $post->ID ) );
456+
}
457+
454458
return $actor;
455459
}
456460

@@ -482,6 +486,13 @@ private static function prepare_custom_post_type( $actor ) {
482486
);
483487
}
484488

489+
if ( $actor->get_webfinger() ) {
490+
$webfinger = Sanitize::webfinger( $actor->get_webfinger() );
491+
} else {
492+
$webfinger = Webfinger::uri_to_acct( $actor->get_id() );
493+
$webfinger = \is_wp_error( $webfinger ) ? Webfinger::guess( $actor ) : Sanitize::webfinger( $webfinger );
494+
}
495+
485496
/*
486497
* Temporarily remove mention/hashtag/link filters to prevent infinite recursion when
487498
* storing remote actors with mentions/hashtags in their bios.
@@ -517,6 +528,7 @@ private static function prepare_custom_post_type( $actor ) {
517528

518529
$meta_input = array(
519530
'_activitypub_inbox' => $inbox,
531+
'_activitypub_acct' => $webfinger,
520532
);
521533

522534
// Store avatar URL if available.
@@ -623,8 +635,12 @@ public static function get_acct( $id ) {
623635
$acct = Webfinger::uri_to_acct( $post->guid );
624636

625637
if ( \is_wp_error( $acct ) ) {
626-
$actor = self::get_actor( $post );
627-
$acct = Webfinger::guess( $actor );
638+
$actor = Actor::init_from_json( $post->post_content );
639+
if ( \is_wp_error( $actor ) ) {
640+
return '';
641+
}
642+
643+
$acct = Webfinger::guess( $actor );
628644
}
629645

630646
$acct = Sanitize::webfinger( $acct );

includes/wp-admin/table/class-blocked-actors.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ public function prepare_items() {
234234
'post_title' => $actor->get_name() ?: $actor->get_preferred_username(),
235235
'username' => $actor->get_preferred_username(),
236236
'url' => object_to_uri( $actor->get_url() ?: $actor->get_id() ),
237-
'webfinger' => Remote_Actors::get_acct( $blocked_actor_post->ID ),
237+
'webfinger' => $actor->get_webfinger(),
238238
'identifier' => $actor->get_id(),
239239
'modified' => $blocked_actor_post->post_modified_gmt,
240240
);

includes/wp-admin/table/class-followers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ public function prepare_items() {
286286
'post_title' => $actor->get_name() ?: $actor->get_preferred_username(),
287287
'username' => $actor->get_preferred_username(),
288288
'url' => object_to_uri( $actor->get_url() ?: $actor->get_id() ),
289-
'webfinger' => Remote_Actors::get_acct( $follower->ID ),
289+
'webfinger' => $actor->get_webfinger(),
290290
'identifier' => $actor->get_id(),
291291
'modified' => $follower->post_modified_gmt,
292292
);

includes/wp-admin/table/class-following.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ public function prepare_items() {
254254
'post_title' => $actor->get_name() ?: $actor->get_preferred_username(),
255255
'username' => $actor->get_preferred_username(),
256256
'url' => object_to_uri( $actor->get_url() ?: $actor->get_id() ),
257-
'webfinger' => Remote_Actors::get_acct( $following->ID ),
257+
'webfinger' => $actor->get_webfinger(),
258258
'status' => Following_Collection::check_status( $this->user_id, $following->ID ),
259259
'identifier' => $actor->get_id(),
260260
'modified' => $following->post_modified_gmt,

integration/class-enable-mastodon-apps.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -401,21 +401,14 @@ private static function get_account_for_actor( $actor_or_uri ) {
401401
private static function actor_to_account( $actor ) {
402402
$account = new Account();
403403

404-
$acct = Webfinger_Util::uri_to_acct( $actor->get_id() );
405-
if ( $acct && ! \is_wp_error( $acct ) ) {
406-
$acct = \str_replace( 'acct:', '', $acct );
407-
} else {
408-
$acct = $actor->get_id();
409-
}
410-
411404
$actor_id = $actor->get__id();
412405
if ( ! $actor_id ) {
413406
$actor_id = $actor->get_id();
414407
}
415408

416409
$account->id = \strval( $actor_id );
417410
$account->username = $actor->get_preferred_username();
418-
$account->acct = $acct;
411+
$account->acct = $actor->get_webfinger();
419412
$account->display_name = $actor->get_name();
420413
$account->url = $actor->get_url();
421414
$account->created_at = new \DateTime( 'now' );

tests/phpunit/tests/includes/collection/class-test-remote-actors.php

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,4 +1216,210 @@ public function test_get_avatar_url_empty() {
12161216

12171217
// Clean up.
12181218
}
1219+
1220+
/**
1221+
* Test that webfinger acct is stored when creating an actor.
1222+
*
1223+
* @covers ::create
1224+
*/
1225+
public function test_webfinger_acct_stored_on_create() {
1226+
// Create an actor with webfinger.
1227+
$actor = array(
1228+
'id' => 'https://example.com/users/webfinger-store',
1229+
'type' => 'Person',
1230+
'url' => 'https://example.com/users/webfinger-store',
1231+
'inbox' => 'https://example.com/users/webfinger-store/inbox',
1232+
'name' => 'Webfinger Store',
1233+
'preferredUsername' => 'webfinger',
1234+
);
1235+
1236+
// Mock webfinger resolution.
1237+
$webfinger_callback = function ( $preempt, $parsed_args, $url ) {
1238+
if ( strpos( $url, '.well-known/webfinger' ) !== false ) {
1239+
return array(
1240+
'response' => array( 'code' => 200 ),
1241+
'body' => wp_json_encode(
1242+
array(
1243+
'subject' => 'acct:[email protected]',
1244+
'links' => array(
1245+
array(
1246+
'rel' => 'self',
1247+
'type' => 'application/activity+json',
1248+
'href' => 'https://example.com/users/webfinger-store',
1249+
),
1250+
),
1251+
)
1252+
),
1253+
);
1254+
}
1255+
return $preempt;
1256+
};
1257+
\add_filter( 'pre_http_request', $webfinger_callback, 10, 3 );
1258+
1259+
$post_id = Remote_Actors::create( $actor );
1260+
$this->assertIsInt( $post_id );
1261+
1262+
// Verify acct was stored.
1263+
$stored_acct = \get_post_meta( $post_id, '_activitypub_acct', true );
1264+
$this->assertEquals( '[email protected]', $stored_acct );
1265+
1266+
\remove_filter( 'pre_http_request', $webfinger_callback );
1267+
}
1268+
1269+
/**
1270+
* Test that webfinger is populated when loading an actor from database.
1271+
*
1272+
* @covers ::get_actor
1273+
*/
1274+
public function test_webfinger_populated_on_load() {
1275+
// Create an actor.
1276+
$actor = array(
1277+
'id' => 'https://example.com/users/webfinger-load',
1278+
'type' => 'Person',
1279+
'url' => 'https://example.com/users/webfinger-load',
1280+
'inbox' => 'https://example.com/users/webfinger-load/inbox',
1281+
'name' => 'Webfinger Load',
1282+
'preferredUsername' => 'webfingerload',
1283+
);
1284+
1285+
$post_id = Remote_Actors::create( $actor );
1286+
$this->assertIsInt( $post_id );
1287+
1288+
// Store acct manually.
1289+
\update_post_meta( $post_id, '_activitypub_acct', '[email protected]' );
1290+
1291+
// Load the actor.
1292+
$actor_obj = Remote_Actors::get_actor( $post_id );
1293+
1294+
// Verify webfinger was populated.
1295+
$this->assertEquals( '[email protected]', $actor_obj->get_webfinger() );
1296+
}
1297+
1298+
/**
1299+
* Test that webfinger is generated from actor URL when not available.
1300+
*
1301+
* @covers ::get_actor
1302+
*/
1303+
public function test_webfinger_generated_from_url() {
1304+
// Create an actor without stored webfinger.
1305+
$actor = array(
1306+
'id' => 'https://example.com/users/generate-webfinger',
1307+
'type' => 'Person',
1308+
'url' => 'https://example.com/users/generate-webfinger',
1309+
'inbox' => 'https://example.com/users/generate-webfinger/inbox',
1310+
'name' => 'Generate Webfinger',
1311+
'preferredUsername' => 'generatewf',
1312+
);
1313+
1314+
$post_id = Remote_Actors::create( $actor );
1315+
$this->assertIsInt( $post_id );
1316+
1317+
// Don't store acct meta.
1318+
\delete_post_meta( $post_id, '_activitypub_acct' );
1319+
1320+
// Mock webfinger resolution failure (will fall back to guess).
1321+
$webfinger_callback = function ( $preempt, $parsed_args, $url ) {
1322+
if ( strpos( $url, '.well-known/webfinger' ) !== false ) {
1323+
return array(
1324+
'response' => array( 'code' => 404 ),
1325+
'body' => 'Not Found',
1326+
);
1327+
}
1328+
return $preempt;
1329+
};
1330+
\add_filter( 'pre_http_request', $webfinger_callback, 10, 3 );
1331+
1332+
// Load the actor.
1333+
$actor_obj = Remote_Actors::get_actor( $post_id );
1334+
1335+
// Verify webfinger was guessed from URL.
1336+
$expected = '[email protected]';
1337+
$this->assertEquals( $expected, $actor_obj->get_webfinger() );
1338+
1339+
\remove_filter( 'pre_http_request', $webfinger_callback );
1340+
}
1341+
1342+
/**
1343+
* Test that webfinger acct is updated when actor is updated.
1344+
*
1345+
* @covers ::update
1346+
*/
1347+
public function test_webfinger_acct_updated_on_update() {
1348+
// Create an actor.
1349+
$actor = array(
1350+
'id' => 'https://example.com/users/webfinger-update',
1351+
'type' => 'Person',
1352+
'url' => 'https://example.com/users/webfinger-update',
1353+
'inbox' => 'https://example.com/users/webfinger-update/inbox',
1354+
'name' => 'Webfinger Update',
1355+
'preferredUsername' => 'webfingerupdate',
1356+
);
1357+
1358+
// Mock webfinger resolution.
1359+
$webfinger_callback = function ( $preempt, $parsed_args, $url ) {
1360+
if ( strpos( $url, '.well-known/webfinger' ) !== false ) {
1361+
return array(
1362+
'response' => array( 'code' => 200 ),
1363+
'body' => wp_json_encode(
1364+
array(
1365+
'subject' => 'acct:[email protected]',
1366+
'links' => array(
1367+
array(
1368+
'rel' => 'self',
1369+
'type' => 'application/activity+json',
1370+
'href' => 'https://example.com/users/webfinger-update',
1371+
),
1372+
),
1373+
)
1374+
),
1375+
);
1376+
}
1377+
return $preempt;
1378+
};
1379+
\add_filter( 'pre_http_request', $webfinger_callback, 10, 3 );
1380+
1381+
$post_id = Remote_Actors::create( $actor );
1382+
$this->assertIsInt( $post_id );
1383+
1384+
// Verify initial acct.
1385+
$stored_acct = \get_post_meta( $post_id, '_activitypub_acct', true );
1386+
$this->assertEquals( '[email protected]', $stored_acct );
1387+
1388+
// Update the actor with modified name.
1389+
$updated_actor = $actor;
1390+
$updated_actor['name'] = 'Webfinger Updated Name';
1391+
1392+
Remote_Actors::update( $post_id, $updated_actor );
1393+
1394+
// Verify acct is still stored correctly after update.
1395+
$updated_acct = \get_post_meta( $post_id, '_activitypub_acct', true );
1396+
$this->assertEquals( '[email protected]', $updated_acct );
1397+
1398+
\remove_filter( 'pre_http_request', $webfinger_callback );
1399+
}
1400+
1401+
/**
1402+
* Test that webfinger acct is stored when provided in actor data.
1403+
*
1404+
* @covers ::create
1405+
*/
1406+
public function test_webfinger_from_actor_data() {
1407+
// Create an actor with webfinger in the data.
1408+
$actor = array(
1409+
'id' => 'https://example.com/users/actor-data-wf',
1410+
'type' => 'Person',
1411+
'url' => 'https://example.com/users/actor-data-wf',
1412+
'inbox' => 'https://example.com/users/actor-data-wf/inbox',
1413+
'name' => 'Actor Data Webfinger',
1414+
'preferredUsername' => 'actordatawf',
1415+
'webfinger' => '[email protected]',
1416+
);
1417+
1418+
$post_id = Remote_Actors::create( $actor );
1419+
$this->assertIsInt( $post_id );
1420+
1421+
// Verify custom webfinger was stored.
1422+
$stored_acct = \get_post_meta( $post_id, '_activitypub_acct', true );
1423+
$this->assertEquals( '[email protected]', $stored_acct );
1424+
}
12191425
}

0 commit comments

Comments
 (0)