From bcf29e1caf12327a0748886cedfd157424135eef Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Tue, 31 Oct 2023 17:16:49 -0500 Subject: [PATCH 01/40] Profiles: update followers when profile fields change --- includes/class-activity-dispatcher.php | 55 ++++++++++++++++------ includes/class-scheduler.php | 65 ++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 13 deletions(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 462a75afa..2ba3e5d5f 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -25,6 +25,7 @@ class Activity_Dispatcher { public static function init() { \add_action( 'activitypub_send_activity', array( self::class, 'send_activity' ), 10, 2 ); \add_action( 'activitypub_send_activity', array( self::class, 'send_activity_or_announce' ), 10, 2 ); + \add_action( 'activitypub_send_update_profile_activity', array( self::class, 'send_profile_update' ), 10, 1 ); } /** @@ -71,17 +72,7 @@ public static function send_activity( WP_Post $wp_post, $type ) { $activity->set_type( $type ); $activity->set_object( $object ); - $follower_inboxes = Followers::get_inboxes( $wp_post->post_author ); - $mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() ); - - $inboxes = array_merge( $follower_inboxes, $mentioned_inboxes ); - $inboxes = array_unique( $inboxes ); - - $json = $activity->to_json(); - - foreach ( $inboxes as $inbox ) { - safe_remote_post( $inbox, $json, $wp_post->post_author ); - } + self::send_activity_to_inboxes( $activity, $wp_post->post_author ); } /** @@ -110,7 +101,45 @@ public static function send_announce( WP_Post $wp_post, $type ) { // send only the id $activity->set_object( $object->get_id() ); - $follower_inboxes = Followers::get_inboxes( $wp_post->post_author ); + self::send_activity_to_inboxes( $activity, $wp_post->post_author ); + } + + /** + * Send a "Update" Activity when a user updates their profile. + * + * @param int $user_id The user ID to send an update for. + * + */ + public static function send_profile_update( $user_id ) { + $user = Users::get_by_various( $user_id ); + + // bail if that's not a good user + if ( is_wp_error( $user ) ) { + return; + } + + // build the update + $activity = new Activity(); + $activity->set_id( $user->get_url() . '#update' ); + $activity->set_type( 'Update' ); + $activity->set_actor( $user->get_url() ); + $activity->set_object( $user->get_url() ); + $activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' ); + + // send the update + self::send_activity_to_inboxes( $activity, $user_id ); + } + + /** + * Send an Activity to all followers and mentioned users. + * + * @param Activity $activity The ActivityPub Activity. + * @param int $user_id The user ID. + * + * @return void + */ + private static function send_activity_to_inboxes( $activity, $user_id ) { + $follower_inboxes = Followers::get_inboxes( $user_id ); $mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() ); $inboxes = array_merge( $follower_inboxes, $mentioned_inboxes ); @@ -119,7 +148,7 @@ public static function send_announce( WP_Post $wp_post, $type ) { $json = $activity->to_json(); foreach ( $inboxes as $inbox ) { - safe_remote_post( $inbox, $json, $wp_post->post_author ); + safe_remote_post( $inbox, $json, $user_id ); } } } diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 63f927325..dfeff2121 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -6,12 +6,15 @@ use Activitypub\Collection\Followers; use Activitypub\Transformer\Post; +use function Activitypub\is_user_type_disabled; + /** * ActivityPub Scheduler Class * * @author Matthias Pfefferle */ class Scheduler { + /** * Initialize the class, registering WordPress hooks */ @@ -22,6 +25,21 @@ public static function init() { \add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) ); \add_action( 'admin_init', array( self::class, 'schedule_migration' ) ); + + // profile updates for blog options + if ( ! is_user_type_disabled( 'blog' ) ) { + \add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) ); + \add_action( 'update_option_blogdescription', array( self::class, 'blog_user_update' ) ); + \add_action( 'update_option_blogname', array( self::class, 'blog_user_update' ) ); + \add_filter( 'pre_set_theme_mod_custom_logo', array( self::class, 'blog_user_update' ) ); + \add_filter( 'pre_set_theme_mod_header_image', array( self::class, 'blog_user_update' ) ); + } + + // profile updates for user options + if ( ! is_user_type_disabled( 'user' ) ) { + \add_action( 'updated_user_meta', array( self::class, 'user_update' ), 10, 3 ); + // @todo figure out a feasible way of updating the header image since it's not unique to any user. + } } /** @@ -166,4 +184,51 @@ public static function schedule_migration() { \wp_schedule_single_event( \time(), 'activitypub_schedule_migration' ); } } + + /** + * Send a profile update when relevant user meta is updated. + * + * @param int $meta_id Meta ID being updated. + * @param int $user_id User ID being updated. + * @param string $meta_key Meta key being updated. + * @return void + */ + public static function user_update( $meta_id, $user_id, $meta_key ) { + // don't bother if the user can't publish + if ( ! \user_can( $user_id, 'publish_posts' ) ) { + return; + } + // the user meta fields that affect a profile. + $fields = array( + 'activitypub_user_description', + 'description', + 'user_url', + 'display_name', + ); + if ( in_array( $meta_key, $fields, true ) ) { + self::schedule_profile_update( $user_id ); + } + } + + /** + * Theme mods only have a dynamic filter so we fudge it like this. + * @param mixed $value + * @return mixed + */ + public static function blog_user_update( $value = null ) { + self::schedule_profile_update( 0 ); + return $value; + } + + /** + * Send a profile update to all followers. Gets hooked into all relevant options/meta etc. + * @param int $user_id The user ID to update (Could be 0 for Blog-User). + */ + public function schedule_profile_update( $user_id ) { + \wp_schedule_single_event( + \time(), + 'activitypub_send_update_profile_activity', + array( $user_id ) + ); + } } From e3a78fbadc170d32b38aa1d35b2b024754a4d28e Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Tue, 31 Oct 2023 17:35:08 -0500 Subject: [PATCH 02/40] use static --- includes/class-scheduler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index dfeff2121..7cf62eb24 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -224,7 +224,7 @@ public static function blog_user_update( $value = null ) { * Send a profile update to all followers. Gets hooked into all relevant options/meta etc. * @param int $user_id The user ID to update (Could be 0 for Blog-User). */ - public function schedule_profile_update( $user_id ) { + public static function schedule_profile_update( $user_id ) { \wp_schedule_single_event( \time(), 'activitypub_send_update_profile_activity', From af3e61ca832a8d8c9b2d744747f1345576baad21 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Wed, 1 Nov 2023 16:51:52 -0500 Subject: [PATCH 03/40] only try to merge mention inboxes when valid --- includes/class-activity-dispatcher.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 2ba3e5d5f..fe31d37a3 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -140,11 +140,20 @@ public static function send_profile_update( $user_id ) { */ private static function send_activity_to_inboxes( $activity, $user_id ) { $follower_inboxes = Followers::get_inboxes( $user_id ); - $mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() ); + + $mentioned_inboxes = array(); + $cc = $activity->get_cc(); + if ( $cc ) { + $mentioned_inboxes = Mention::get_inboxes( $cc ); + } $inboxes = array_merge( $follower_inboxes, $mentioned_inboxes ); $inboxes = array_unique( $inboxes ); + if ( empty( $inboxes ) ) { + return; + } + $json = $activity->to_json(); foreach ( $inboxes as $inbox ) { From 3785cee6f2cf4ad7d4693db5b1840f26c9052a84 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 Nov 2023 12:36:03 -0700 Subject: [PATCH 04/40] Add Server Class, known_inboxes method --- includes/class-server.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 includes/class-server.php diff --git a/includes/class-server.php b/includes/class-server.php new file mode 100644 index 000000000..11d017de4 --- /dev/null +++ b/includes/class-server.php @@ -0,0 +1,24 @@ + 'publish_posts' + ) ); + $follower_inboxes_all = []; + foreach ( $authors as $user ) { + $follower_inboxes = Followers::get_inboxes( $user->ID ); + $follower_inboxes_all = array_merge( $follower_inboxes, $follower_inboxes_all ); + } + return array_unique( array_filter( $follower_inboxes_all ) ); + } + +} From d64ed7323a189f4e2eaec7dc8f12a3371b8ad5d6 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 Nov 2023 12:37:35 -0700 Subject: [PATCH 05/40] Add a server dispatch activity --- includes/class-activity-dispatcher.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index fe31d37a3..6c2cad443 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -26,6 +26,7 @@ public static function init() { \add_action( 'activitypub_send_activity', array( self::class, 'send_activity' ), 10, 2 ); \add_action( 'activitypub_send_activity', array( self::class, 'send_activity_or_announce' ), 10, 2 ); \add_action( 'activitypub_send_update_profile_activity', array( self::class, 'send_profile_update' ), 10, 1 ); + \add_action( 'activitypub_send_server_activity', array( self::class, 'send_server_activity' ), 10, 1 ); } /** @@ -160,4 +161,19 @@ private static function send_activity_to_inboxes( $activity, $user_id ) { safe_remote_post( $inbox, $json, $user_id ); } } + + /** + * Send an Activity to all known (shared_)inboxes. + * + * @param Activity $activity The ActivityPub Activity. + * + * @return void + */ + private static function send_server_activity( $activity ) { + $json = $activity->to_json(); + $inboxes = Server::known_inboxes(); + foreach ( $inboxes as $inbox ) { + safe_remote_post( $inbox, $json, -1 ); + } + } } From 565a6caa1b1aa07824ee156b986d18fe7a60b42a Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 Nov 2023 12:38:29 -0700 Subject: [PATCH 06/40] Add a delete wp user action --- includes/class-scheduler.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 7cf62eb24..bb7773749 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -5,6 +5,7 @@ use Activitypub\Collection\Users; use Activitypub\Collection\Followers; use Activitypub\Transformer\Post; +use Activitypub\Activity\Activity; use function Activitypub\is_user_type_disabled; @@ -39,6 +40,8 @@ public static function init() { if ( ! is_user_type_disabled( 'user' ) ) { \add_action( 'updated_user_meta', array( self::class, 'user_update' ), 10, 3 ); // @todo figure out a feasible way of updating the header image since it's not unique to any user. + + \add_action( 'delete_user', array( self::class, 'schedule_profile_delete' ), 10, 3 ); } } @@ -231,4 +234,24 @@ public static function schedule_profile_update( $user_id ) { array( $user_id ) ); } + + /** + * Send an Actor Delete activity. + * @param int $user_id The user ID to Delete. + */ + public static function schedule_profile_delete( $user_id ) { + $user = get_userdata( $user_id ); + if ( $user->has_cap( 'publish_posts' ) ) { + $author_url = \get_author_posts_url( $user->ID ); + + $activity = new Activity(); + $activity->set_id( $author_url . '#delete' ); + $activity->set_type( 'Delete' ); + $activity->set_actor( $author_url ); + $activity->set_object( $author_url ); + $activity->set_to( [ 'https://www.w3.org/ns/activitystreams#Public' ] ); + + \wp_schedule_single_event( \time(), 'activitypub_send_server_activity', array( $activity ) ); + } + } } From 9e8065ae5c38f90be46c4e678d5a863a569d92bb Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 Nov 2023 12:58:04 -0700 Subject: [PATCH 07/40] lint:fix --- includes/class-activity-dispatcher.php | 2 +- includes/class-server.php | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 6c2cad443..508c2023e 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -140,7 +140,7 @@ public static function send_profile_update( $user_id ) { * @return void */ private static function send_activity_to_inboxes( $activity, $user_id ) { - $follower_inboxes = Followers::get_inboxes( $user_id ); + $follower_inboxes = Followers::get_inboxes( $user_id ); $mentioned_inboxes = array(); $cc = $activity->get_cc(); diff --git a/includes/class-server.php b/includes/class-server.php index 11d017de4..06e8eecb9 100644 --- a/includes/class-server.php +++ b/includes/class-server.php @@ -9,10 +9,12 @@ */ class Server { - private static function known_inboxes () { - $authors = get_users( array( - 'capability' => 'publish_posts' - ) ); + private static function known_inboxes() { + $authors = get_users( + array( + 'capability' => 'publish_posts', + ) + ); $follower_inboxes_all = []; foreach ( $authors as $user ) { $follower_inboxes = Followers::get_inboxes( $user->ID ); @@ -20,5 +22,4 @@ private static function known_inboxes () { } return array_unique( array_filter( $follower_inboxes_all ) ); } - } From 7543884a17d4168ade43faaafa775980f397e320 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Fri, 10 Nov 2023 08:25:14 -0700 Subject: [PATCH 08/40] Add blog user followers to known inboxes --- includes/class-server.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/includes/class-server.php b/includes/class-server.php index 06e8eecb9..06566a84f 100644 --- a/includes/class-server.php +++ b/includes/class-server.php @@ -2,6 +2,11 @@ namespace Activitypub; +use Activitypub\Collection\Followers; +use Activitypub\Collection\Users; + +use function Activitypub\is_user_disabled; + /** * ActivityPub Server Class * @@ -20,6 +25,10 @@ private static function known_inboxes() { $follower_inboxes = Followers::get_inboxes( $user->ID ); $follower_inboxes_all = array_merge( $follower_inboxes, $follower_inboxes_all ); } + if ( ! is_user_disabled( Users::BLOG_USER_ID ) ) { + $follower_inboxes = Followers::get_inboxes( Users::BLOG_USER_ID ); + $follower_inboxes_all = array_merge( $follower_inboxes, $follower_inboxes_all ); + } return array_unique( array_filter( $follower_inboxes_all ) ); } } From 05b4ebf83070a9108960d8159d9549dadb804c35 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Fri, 10 Nov 2023 17:41:56 -0700 Subject: [PATCH 09/40] public function --- includes/class-server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-server.php b/includes/class-server.php index 06566a84f..c82dd423e 100644 --- a/includes/class-server.php +++ b/includes/class-server.php @@ -14,7 +14,7 @@ */ class Server { - private static function known_inboxes() { + public static function known_inboxes() { $authors = get_users( array( 'capability' => 'publish_posts', From 2bfb930e61141b7ae05bcea915bc5587979ea5d1 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Fri, 10 Nov 2023 17:43:25 -0700 Subject: [PATCH 10/40] update send_server_activity --- includes/class-activity-dispatcher.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 508c2023e..5ca80b075 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -6,6 +6,7 @@ use Activitypub\Collection\Users; use Activitypub\Collection\Followers; use Activitypub\Transformer\Post; +use Activitypub\Server; use function Activitypub\is_single_user; use function Activitypub\is_user_disabled; @@ -26,7 +27,7 @@ public static function init() { \add_action( 'activitypub_send_activity', array( self::class, 'send_activity' ), 10, 2 ); \add_action( 'activitypub_send_activity', array( self::class, 'send_activity_or_announce' ), 10, 2 ); \add_action( 'activitypub_send_update_profile_activity', array( self::class, 'send_profile_update' ), 10, 1 ); - \add_action( 'activitypub_send_server_activity', array( self::class, 'send_server_activity' ), 10, 1 ); + \add_action( 'activitypub_send_server_activity', array( self::class, 'send_server_activity' ), 10, 2 ); } /** @@ -169,11 +170,11 @@ private static function send_activity_to_inboxes( $activity, $user_id ) { * * @return void */ - private static function send_server_activity( $activity ) { + public static function send_server_activity( $activity, $user_id = Users::APPLICATION_USER_ID ) { $json = $activity->to_json(); $inboxes = Server::known_inboxes(); foreach ( $inboxes as $inbox ) { - safe_remote_post( $inbox, $json, -1 ); + safe_remote_post( $inbox, $json, $user_id ); } } } From 6c548bc1217d38f9c45b84a01b1ac49b9f361d6d Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Fri, 10 Nov 2023 17:44:06 -0700 Subject: [PATCH 11/40] update delete schedulers --- includes/class-scheduler.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index bb7773749..264a912ee 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -7,6 +7,7 @@ use Activitypub\Transformer\Post; use Activitypub\Activity\Activity; +use function Activitypub\get_private_key_for; use function Activitypub\is_user_type_disabled; /** @@ -42,6 +43,7 @@ public static function init() { // @todo figure out a feasible way of updating the header image since it's not unique to any user. \add_action( 'delete_user', array( self::class, 'schedule_profile_delete' ), 10, 3 ); + \add_action( 'deleted_user', array( self::class, 'schedule_user_delete' ), 10, 3 ); } } @@ -242,6 +244,9 @@ public static function schedule_profile_update( $user_id ) { public static function schedule_profile_delete( $user_id ) { $user = get_userdata( $user_id ); if ( $user->has_cap( 'publish_posts' ) ) { + $temp_private_key = get_private_key_for( $user->ID ); + add_option( 'activitypub_temp_sig_' . $user->ID, $temp_private_key ); + $author_url = \get_author_posts_url( $user->ID ); $activity = new Activity(); @@ -251,7 +256,19 @@ public static function schedule_profile_delete( $user_id ) { $activity->set_object( $author_url ); $activity->set_to( [ 'https://www.w3.org/ns/activitystreams#Public' ] ); - \wp_schedule_single_event( \time(), 'activitypub_send_server_activity', array( $activity ) ); + \wp_schedule_single_event( \time(), 'activitypub_send_server_activity', array( $activity, $user_id ) ); + } + } + + /** + * Delete actor related options. + * @param int $user_id The deleted user ID. + */ + public static function schedule_user_delete( $user_id ) { + $user = get_userdata( $user_id ); + error_log( 'schedule_user_delete: ' . print_r( $user, true ) ); + if ( $user->has_cap( 'publish_posts' ) ) { + delete_option( 'activitypub_temp_sig_' . $user_id ); } } } From 41f05ef54c0209f44e6f699367ebd5736244a44e Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Fri, 10 Nov 2023 17:52:48 -0700 Subject: [PATCH 12/40] get temporary key for deleted user --- includes/class-signature.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/class-signature.php b/includes/class-signature.php index d021cf0ed..7632139f3 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -186,7 +186,11 @@ protected static function check_legacy_key_pair_for( $user_id ) { */ public static function generate_signature( $user_id, $http_method, $url, $date, $digest = null ) { $user = Users::get_by_id( $user_id ); - $key = self::get_private_key_for( $user->get__id() ); + if ( ! is_wp_error( $user ) ) { + $key = self::get_private_key_for( $user->get__id() ); + } else { + $key = get_option( 'activitypub_temp_sig_' . $user_id, true ); + } $url_parts = \wp_parse_url( $url ); From 06a023e7deb09ea35898cc4bc3625b571625df9d Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Mon, 13 Nov 2023 11:50:20 -0700 Subject: [PATCH 13/40] Rename to Application class --- includes/class-activity-dispatcher.php | 4 ++-- includes/{class-server.php => class-application.php} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename includes/{class-server.php => class-application.php} (93%) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 5ca80b075..cd971fcec 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -6,7 +6,7 @@ use Activitypub\Collection\Users; use Activitypub\Collection\Followers; use Activitypub\Transformer\Post; -use Activitypub\Server; +use Activitypub\Application; use function Activitypub\is_single_user; use function Activitypub\is_user_disabled; @@ -172,7 +172,7 @@ private static function send_activity_to_inboxes( $activity, $user_id ) { */ public static function send_server_activity( $activity, $user_id = Users::APPLICATION_USER_ID ) { $json = $activity->to_json(); - $inboxes = Server::known_inboxes(); + $inboxes = Application::known_inboxes(); foreach ( $inboxes as $inbox ) { safe_remote_post( $inbox, $json, $user_id ); } diff --git a/includes/class-server.php b/includes/class-application.php similarity index 93% rename from includes/class-server.php rename to includes/class-application.php index c82dd423e..1e6b2b85f 100644 --- a/includes/class-server.php +++ b/includes/class-application.php @@ -8,11 +8,11 @@ use function Activitypub\is_user_disabled; /** - * ActivityPub Server Class + * ActivityPub Application Class * * @author Django Doucet */ -class Server { +class Application { public static function known_inboxes() { $authors = get_users( From fcbfa74e7c4c63148049c7b8588310215784ec9f Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Tue, 7 May 2024 10:26:12 -0500 Subject: [PATCH 14/40] Fix PHP errors and lint issues --- includes/class-activity-dispatcher.php | 29 -------------- includes/class-scheduler.php | 53 +------------------------- 2 files changed, 1 insertion(+), 81 deletions(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 67b1b6ae5..2dedbb71d 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -7,7 +7,6 @@ use Activitypub\Collection\Users; use Activitypub\Collection\Followers; use Activitypub\Transformer\Factory; -use Activitypub\Transformer\Post; use Activitypub\Application; use Activitypub\Transformer\Comment; @@ -141,32 +140,6 @@ public static function send_profile_update( $user_id ) { self::send_activity_to_followers( $activity, $user_id, $user ); } - /** - * Send a "Update" Activity when a user updates their profile. - * - * @param int $user_id The user ID to send an update for. - * - */ - public static function send_profile_update( $user_id ) { - $user = Users::get_by_various( $user_id ); - - // bail if that's not a good user - if ( is_wp_error( $user ) ) { - return; - } - - // build the update - $activity = new Activity(); - $activity->set_id( $user->get_url() . '#update' ); - $activity->set_type( 'Update' ); - $activity->set_actor( $user->get_url() ); - $activity->set_object( $user->get_url() ); - $activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' ); - - // send the update - self::send_activity_to_inboxes( $activity, $user_id ); - } - /** * Send an Activity to all followers and mentioned users. * @@ -217,8 +190,6 @@ public static function send_server_activity( $activity, $user_id = Users::APPLIC foreach ( $inboxes as $inbox ) { safe_remote_post( $inbox, $json, $user_id ); } - - set_wp_object_state( $wp_object, 'federated' ); } /** diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 7d8218027..97dfa28ac 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -5,13 +5,11 @@ use Activitypub\Transformer\Post; use Activitypub\Collection\Users; use Activitypub\Collection\Followers; -use Activitypub\Transformer\Post; use Activitypub\Activity\Activity; use function Activitypub\get_private_key_for; use function Activitypub\is_user_type_disabled; use function Activitypub\was_comment_sent; -use function Activitypub\is_user_type_disabled; use function Activitypub\should_comment_be_federated; use function Activitypub\get_remote_metadata_by_actor; @@ -69,7 +67,6 @@ function ( $comment_id ) { \add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) ); \add_action( 'admin_init', array( self::class, 'schedule_migration' ) ); - // profile updates for blog options if ( ! is_user_type_disabled( 'blog' ) ) { \add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) ); @@ -81,7 +78,7 @@ function ( $comment_id ) { // profile updates for user options if ( ! is_user_type_disabled( 'user' ) ) { - \add_action( 'delete_user', array( self::class, 'schedule_profile_delete' ), 10, 3 ); + \add_action( 'delete_user', array( self::class, 'schedule_profile_delete' ), 10, 3 ); \add_action( 'deleted_user', array( self::class, 'schedule_user_delete' ), 10, 3 ); \add_action( 'wp_update_user', array( self::class, 'user_update' ) ); \add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); @@ -339,53 +336,6 @@ public static function schedule_profile_update( $user_id ) { ); } - /** - * Send a profile update when relevant user meta is updated. - * - * @param int $meta_id Meta ID being updated. - * @param int $user_id User ID being updated. - * @param string $meta_key Meta key being updated. - * @return void - */ - public static function user_update( $meta_id, $user_id, $meta_key ) { - // don't bother if the user can't publish - if ( ! \user_can( $user_id, 'publish_posts' ) ) { - return; - } - // the user meta fields that affect a profile. - $fields = array( - 'activitypub_user_description', - 'description', - 'user_url', - 'display_name', - ); - if ( in_array( $meta_key, $fields, true ) ) { - self::schedule_profile_update( $user_id ); - } - } - - /** - * Theme mods only have a dynamic filter so we fudge it like this. - * @param mixed $value - * @return mixed - */ - public static function blog_user_update( $value = null ) { - self::schedule_profile_update( 0 ); - return $value; - } - - /** - * Send a profile update to all followers. Gets hooked into all relevant options/meta etc. - * @param int $user_id The user ID to update (Could be 0 for Blog-User). - */ - public static function schedule_profile_update( $user_id ) { - \wp_schedule_single_event( - \time(), - 'activitypub_send_update_profile_activity', - array( $user_id ) - ); - } - /** * Send an Actor Delete activity. * @param int $user_id The user ID to Delete. @@ -415,7 +365,6 @@ public static function schedule_profile_delete( $user_id ) { */ public static function schedule_user_delete( $user_id ) { $user = get_userdata( $user_id ); - error_log( 'schedule_user_delete: ' . print_r( $user, true ) ); if ( $user->has_cap( 'publish_posts' ) ) { delete_option( 'activitypub_temp_sig_' . $user_id ); } From ca3d1bc4688d6ed1c45e38f59763d587c7b98848 Mon Sep 17 00:00:00 2001 From: Django Date: Wed, 8 May 2024 09:08:45 -0600 Subject: [PATCH 15/40] Update includes/class-scheduler.php has_cap activitypub Co-authored-by: Matthias Pfefferle --- includes/class-scheduler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 97dfa28ac..13f1ce67d 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -365,7 +365,7 @@ public static function schedule_profile_delete( $user_id ) { */ public static function schedule_user_delete( $user_id ) { $user = get_userdata( $user_id ); - if ( $user->has_cap( 'publish_posts' ) ) { + if ( $user->has_cap( 'activitypub' ) ) { delete_option( 'activitypub_temp_sig_' . $user_id ); } } From e894bc6ba41988a4d4a4db8a561d12ba10861895 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 10:12:06 -0600 Subject: [PATCH 16/40] Rename action and function --- includes/class-activity-dispatcher.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 2dedbb71d..435c2f689 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -33,7 +33,7 @@ public static function init() { \add_action( 'activitypub_send_activity', array( self::class, 'send_activity' ), 10, 2 ); \add_action( 'activitypub_send_activity', array( self::class, 'send_activity_or_announce' ), 10, 2 ); \add_action( 'activitypub_send_update_profile_activity', array( self::class, 'send_profile_update' ), 10, 1 ); - \add_action( 'activitypub_send_server_activity', array( self::class, 'send_server_activity' ), 10, 2 ); + \add_action( 'activitypub_send_actor_delete_activity', array( self::class, 'send_actor_delete_activity' ), 10, 2 ); } /** @@ -184,7 +184,7 @@ private static function send_activity_to_followers( $activity, $user_id, $wp_obj * * @return void */ - public static function send_server_activity( $activity, $user_id = Users::APPLICATION_USER_ID ) { + public static function send_actor_delete_activity( $activity, $user_id = Users::APPLICATION_USER_ID ) { $json = $activity->to_json(); $inboxes = Application::known_inboxes(); foreach ( $inboxes as $inbox ) { From 1aa30edcf767fa19421f03a6dbf1ca6b9d4a217d Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 10:15:04 -0600 Subject: [PATCH 17/40] Use get_all_followers for inboxes --- includes/class-activity-dispatcher.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 435c2f689..946bab05e 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -7,7 +7,6 @@ use Activitypub\Collection\Users; use Activitypub\Collection\Followers; use Activitypub\Transformer\Factory; -use Activitypub\Application; use Activitypub\Transformer\Comment; use function Activitypub\is_single_user; @@ -186,7 +185,7 @@ private static function send_activity_to_followers( $activity, $user_id, $wp_obj */ public static function send_actor_delete_activity( $activity, $user_id = Users::APPLICATION_USER_ID ) { $json = $activity->to_json(); - $inboxes = Application::known_inboxes(); + $inboxes = Followers::get_all_followers(); foreach ( $inboxes as $inbox ) { safe_remote_post( $inbox, $json, $user_id ); } From fb0f679279ce6f7311e88a27af97521af65864c3 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 10:17:59 -0600 Subject: [PATCH 18/40] schedule_actor_delete --- includes/class-scheduler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 13f1ce67d..a49c97113 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -78,7 +78,7 @@ function ( $comment_id ) { // profile updates for user options if ( ! is_user_type_disabled( 'user' ) ) { - \add_action( 'delete_user', array( self::class, 'schedule_profile_delete' ), 10, 3 ); + \add_action( 'delete_user', array( self::class, 'schedule_actor_delete' ), 10, 3 ); \add_action( 'deleted_user', array( self::class, 'schedule_user_delete' ), 10, 3 ); \add_action( 'wp_update_user', array( self::class, 'user_update' ) ); \add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); @@ -340,7 +340,7 @@ public static function schedule_profile_update( $user_id ) { * Send an Actor Delete activity. * @param int $user_id The user ID to Delete. */ - public static function schedule_profile_delete( $user_id ) { + public static function schedule_actor_delete( $user_id ) { $user = get_userdata( $user_id ); if ( $user->has_cap( 'publish_posts' ) ) { $temp_private_key = get_private_key_for( $user->ID ); From 61951f7ffb76f0630340fc44c52df988bade58b1 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 10:18:51 -0600 Subject: [PATCH 19/40] has_cap activitypub --- includes/class-scheduler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index a49c97113..c751c0bdd 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -342,7 +342,7 @@ public static function schedule_profile_update( $user_id ) { */ public static function schedule_actor_delete( $user_id ) { $user = get_userdata( $user_id ); - if ( $user->has_cap( 'publish_posts' ) ) { + if ( $user->has_cap( 'activitypub' ) ) { $temp_private_key = get_private_key_for( $user->ID ); add_option( 'activitypub_temp_sig_' . $user->ID, $temp_private_key ); From 4cef50d95268ecd124b6a79fc9503dc88e857be1 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 10:19:36 -0600 Subject: [PATCH 20/40] activitypub_send_actor_delete_activity --- includes/class-scheduler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index c751c0bdd..ef8332cc0 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -355,7 +355,7 @@ public static function schedule_actor_delete( $user_id ) { $activity->set_object( $author_url ); $activity->set_to( [ 'https://www.w3.org/ns/activitystreams#Public' ] ); - \wp_schedule_single_event( \time(), 'activitypub_send_server_activity', array( $activity, $user_id ) ); + \wp_schedule_single_event( \time(), 'activitypub_send_actor_delete_activity', array( $activity, $user_id ) ); } } From 6c1fa000c487dc14b26259ee4866ba4702e2d1f6 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 10:22:04 -0600 Subject: [PATCH 21/40] remove Application class --- includes/class-application.php | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 includes/class-application.php diff --git a/includes/class-application.php b/includes/class-application.php deleted file mode 100644 index 1e6b2b85f..000000000 --- a/includes/class-application.php +++ /dev/null @@ -1,34 +0,0 @@ - 'publish_posts', - ) - ); - $follower_inboxes_all = []; - foreach ( $authors as $user ) { - $follower_inboxes = Followers::get_inboxes( $user->ID ); - $follower_inboxes_all = array_merge( $follower_inboxes, $follower_inboxes_all ); - } - if ( ! is_user_disabled( Users::BLOG_USER_ID ) ) { - $follower_inboxes = Followers::get_inboxes( Users::BLOG_USER_ID ); - $follower_inboxes_all = array_merge( $follower_inboxes, $follower_inboxes_all ); - } - return array_unique( array_filter( $follower_inboxes_all ) ); - } -} From 01b4d091a9b33710a4b9f31c3cefb9a02b860195 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 10:36:33 -0600 Subject: [PATCH 22/40] private key already stored --- includes/class-scheduler.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index ef8332cc0..9b3beece4 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -343,8 +343,6 @@ public static function schedule_profile_update( $user_id ) { public static function schedule_actor_delete( $user_id ) { $user = get_userdata( $user_id ); if ( $user->has_cap( 'activitypub' ) ) { - $temp_private_key = get_private_key_for( $user->ID ); - add_option( 'activitypub_temp_sig_' . $user->ID, $temp_private_key ); $author_url = \get_author_posts_url( $user->ID ); From 22a664e4e1d1edb63eeeb9f2ad7758cb42fe27aa Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 10:38:59 -0600 Subject: [PATCH 23/40] clean up user, delete signature options key --- includes/class-scheduler.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 9b3beece4..dfb54e445 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -7,7 +7,7 @@ use Activitypub\Collection\Followers; use Activitypub\Activity\Activity; -use function Activitypub\get_private_key_for; +use function Activitypub\get_signature_options_key_for; use function Activitypub\is_user_type_disabled; use function Activitypub\was_comment_sent; use function Activitypub\should_comment_be_federated; @@ -364,7 +364,8 @@ public static function schedule_actor_delete( $user_id ) { public static function schedule_user_delete( $user_id ) { $user = get_userdata( $user_id ); if ( $user->has_cap( 'activitypub' ) ) { - delete_option( 'activitypub_temp_sig_' . $user_id ); + $option_key = get_signature_options_key_for( $user_id ); + delete_option( $option_key ); } } } From aa448d3d97a9ea207cc12acebdeb79b7c1d222e6 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 19:51:08 -0600 Subject: [PATCH 24/40] get signing keys from options for actor delete activity --- includes/class-signature.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/includes/class-signature.php b/includes/class-signature.php index 5646373b0..37ea67f5f 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -188,8 +188,11 @@ public static function generate_signature( $user_id, $http_method, $url, $date, $user = Users::get_by_id( $user_id ); if ( ! is_wp_error( $user ) ) { $key = self::get_private_key_for( $user->get__id() ); + $key_id = $user->get_url() . '#main-key'; } else { - $key = get_option( 'activitypub_temp_sig_' . $user_id, true ); + $temp_sig_options = get_option( 'activitypub_temp_sig_' . $user_id ); + $key = $temp_sig_options['private_key']; + $key_id = $temp_sig_options['key_id']; } $url_parts = \wp_parse_url( $url ); @@ -219,8 +222,6 @@ public static function generate_signature( $user_id, $http_method, $url, $date, \openssl_sign( $signed_string, $signature, $key, \OPENSSL_ALGO_SHA256 ); $signature = \base64_encode( $signature ); // phpcs:ignore - $key_id = $user->get_url() . '#main-key'; - if ( ! empty( $digest ) ) { return \sprintf( 'keyId="%s",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="%s"', $key_id, $signature ); } else { From f9fee8c003eb18fb0c6b52e20c3c3bdcbc776ca8 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 19:52:07 -0600 Subject: [PATCH 25/40] Send Delete activity to shared_inboxes --- includes/class-activity-dispatcher.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 946bab05e..b66dd2e16 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -185,7 +185,12 @@ private static function send_activity_to_followers( $activity, $user_id, $wp_obj */ public static function send_actor_delete_activity( $activity, $user_id = Users::APPLICATION_USER_ID ) { $json = $activity->to_json(); - $inboxes = Followers::get_all_followers(); + $followers = Followers::get_all_followers(); + $known_inboxes = []; + foreach ( $followers as $follower ) { + $known_inboxes[] = $follower->get_shared_inbox(); + } + $inboxes = array_unique( $known_inboxes ); foreach ( $inboxes as $inbox ) { safe_remote_post( $inbox, $json, $user_id ); } From 2df226eb304ffaab7651bbada73c28e666214201 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 23:55:04 -0600 Subject: [PATCH 26/40] set temp signature option --- includes/class-scheduler.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index dfb54e445..e764e6d87 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -7,7 +7,7 @@ use Activitypub\Collection\Followers; use Activitypub\Activity\Activity; -use function Activitypub\get_signature_options_key_for; +use function Activitypub\get_private_key_for; use function Activitypub\is_user_type_disabled; use function Activitypub\was_comment_sent; use function Activitypub\should_comment_be_federated; @@ -343,8 +343,11 @@ public static function schedule_profile_update( $user_id ) { public static function schedule_actor_delete( $user_id ) { $user = get_userdata( $user_id ); if ( $user->has_cap( 'activitypub' ) ) { - $author_url = \get_author_posts_url( $user->ID ); + add_option( 'activitypub_temp_sig_' . $user_id, array( + 'key_id' => $author_url . '#main-key', + 'private_key' => Signature::get_private_key_for( $user_id ), + ) ); $activity = new Activity(); $activity->set_id( $author_url . '#delete' ); From d1e00747d4bc9c611a00afd5e5c35292edb78c3c Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Wed, 8 May 2024 23:57:42 -0600 Subject: [PATCH 27/40] cleanup --- includes/class-scheduler.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index e764e6d87..3f26d8122 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -65,7 +65,6 @@ function ( $comment_id ) { // Follower Cleanups \add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) ); \add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) ); - \add_action( 'admin_init', array( self::class, 'schedule_migration' ) ); // profile updates for blog options if ( ! is_user_type_disabled( 'blog' ) ) { @@ -79,7 +78,6 @@ function ( $comment_id ) { // profile updates for user options if ( ! is_user_type_disabled( 'user' ) ) { \add_action( 'delete_user', array( self::class, 'schedule_actor_delete' ), 10, 3 ); - \add_action( 'deleted_user', array( self::class, 'schedule_user_delete' ), 10, 3 ); \add_action( 'wp_update_user', array( self::class, 'user_update' ) ); \add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); // @todo figure out a feasible way of updating the header image since it's not unique to any user. @@ -360,15 +358,4 @@ public static function schedule_actor_delete( $user_id ) { } } - /** - * Delete actor related options. - * @param int $user_id The deleted user ID. - */ - public static function schedule_user_delete( $user_id ) { - $user = get_userdata( $user_id ); - if ( $user->has_cap( 'activitypub' ) ) { - $option_key = get_signature_options_key_for( $user_id ); - delete_option( $option_key ); - } - } } From bf50942f10002293953c4e55a325d8ba233fc140 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Thu, 9 May 2024 00:24:56 -0600 Subject: [PATCH 28/40] phpcs --- includes/class-scheduler.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 3f26d8122..271a38aee 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -342,10 +342,13 @@ public static function schedule_actor_delete( $user_id ) { $user = get_userdata( $user_id ); if ( $user->has_cap( 'activitypub' ) ) { $author_url = \get_author_posts_url( $user->ID ); - add_option( 'activitypub_temp_sig_' . $user_id, array( - 'key_id' => $author_url . '#main-key', - 'private_key' => Signature::get_private_key_for( $user_id ), - ) ); + add_option( + 'activitypub_temp_sig_' . $user_id, + array( + 'key_id' => $author_url . '#main-key', + 'private_key' => Signature::get_private_key_for( $user_id ), + ) + ); $activity = new Activity(); $activity->set_id( $author_url . '#delete' ); From c781662db51c2a7f4f86693d9394e371e46326c0 Mon Sep 17 00:00:00 2001 From: Django Doucet Date: Thu, 9 May 2024 00:32:20 -0600 Subject: [PATCH 29/40] phpcbf --- includes/class-scheduler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 271a38aee..041600dc3 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -360,5 +360,4 @@ public static function schedule_actor_delete( $user_id ) { \wp_schedule_single_event( \time(), 'activitypub_send_actor_delete_activity', array( $activity, $user_id ) ); } } - } From b84286e85a41e284bd203ceceb4f5e5ed4667558 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 25 Jun 2024 16:34:58 +0200 Subject: [PATCH 30/40] init cli --- activitypub.php | 13 +++++++++++ includes/class-cli.php | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 includes/class-cli.php diff --git a/activitypub.php b/activitypub.php index ace3ff443..86351a215 100644 --- a/activitypub.php +++ b/activitypub.php @@ -15,6 +15,8 @@ namespace Activitypub; +use WP_CLI; + use function Activitypub\is_blog_public; use function Activitypub\site_supports_blocks; @@ -234,3 +236,14 @@ function get_plugin_version() { return $meta['Version']; } + +// Check for CLI env, to add the CLI commands +if ( defined( 'WP_CLI' ) && WP_CLI ) { + WP_CLI::add_command( + 'activitypub', + '\Activitypub\Cli', + array( + 'shortdesc' => __( 'Some ActivityPub related commands.', 'activitypub' ), + ) + ); +} diff --git a/includes/class-cli.php b/includes/class-cli.php new file mode 100644 index 000000000..38c5c95a7 --- /dev/null +++ b/includes/class-cli.php @@ -0,0 +1,49 @@ + Date: Wed, 26 Jun 2024 14:42:59 +0200 Subject: [PATCH 31/40] add http gone class --- activitypub.php | 1 + includes/class-http.php | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/activitypub.php b/activitypub.php index 86351a215..ab83ec313 100644 --- a/activitypub.php +++ b/activitypub.php @@ -83,6 +83,7 @@ function plugin_init() { \add_action( 'init', array( __NAMESPACE__ . '\Health_Check', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Scheduler', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Comment', 'init' ) ); + \add_action( 'init', array( __NAMESPACE__ . '\Http_Gone', 'init' ) ); if ( site_supports_blocks() ) { \add_action( 'init', array( __NAMESPACE__ . '\Blocks', 'init' ) ); diff --git a/includes/class-http.php b/includes/class-http.php index 2a8ce7d0d..f7488897e 100644 --- a/includes/class-http.php +++ b/includes/class-http.php @@ -15,18 +15,18 @@ class Http { /** * Send a POST Request with the needed HTTP Headers * - * @param string $url The URL endpoint - * @param string $body The Post Body - * @param int $user_id The WordPress User-ID + * @param string $url The URL endpoint + * @param string $body The Post Body + * @param WP_User|int $user The WordPress User or ID * * @return array|WP_Error The POST Response or an WP_ERROR */ - public static function post( $url, $body, $user_id ) { - \do_action( 'activitypub_pre_http_post', $url, $body, $user_id ); + public static function post( $url, $body, $user ) { + \do_action( 'activitypub_pre_http_post', $url, $body, $user ); $date = \gmdate( 'D, d M Y H:i:s T' ); $digest = Signature::generate_digest( $body ); - $signature = Signature::generate_signature( $user_id, 'post', $url, $date, $digest ); + $signature = Signature::generate_signature( $user, 'post', $url, $date, $digest ); $wp_version = get_masked_wp_version(); @@ -58,7 +58,7 @@ public static function post( $url, $body, $user_id ) { $response = new WP_Error( $code, __( 'Failed HTTP Request', 'activitypub' ), array( 'status' => $code ) ); } - \do_action( 'activitypub_safe_remote_post_response', $response, $url, $body, $user_id ); + \do_action( 'activitypub_safe_remote_post_response', $response, $url, $body, $user ); return $response; } From 2f33377770bf7b035bcee0d08460327b31fe6829 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 26 Jun 2024 14:43:20 +0200 Subject: [PATCH 32/40] add http gone --- includes/class-http-gone.php | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 includes/class-http-gone.php diff --git a/includes/class-http-gone.php b/includes/class-http-gone.php new file mode 100644 index 000000000..d89a90d0c --- /dev/null +++ b/includes/class-http-gone.php @@ -0,0 +1,71 @@ +query['pagename'] ) ) { + $query = new WP_Query( + array( + 'pagename' => $wp_query->query['pagename'] . '__trashed', + 'post_status' => 'trash', + ) + ); + + if ( $query->get_posts() ) { + $is_410 = true; + } + } elseif ( ! empty( $wp_query->query['name'] ) ) { + $query = new WP_Query( + array( + 'name' => $wp_query->query['name'] . '__trashed', + 'post_status' => 'trash', + ) + ); + + if ( $query->get_posts() ) { + $is_410 = true; + } + } elseif ( ! empty( $wp_query->query['author_name'] ) ) { + // Check if author is deleted + $is_410 = true; + } else { + return; + } + + // Return 410 if trashed post exists + if ( $is_410 ) { + status_header( 410 ); + // check if theme has a 410.php template + $template_410 = get_query_template( 410 ); + // return 410 template + if ( $template_410 ) { + load_template( $template_410 ); + exit; + } + } + } +} From 39e6e71c5b6d8df07d8bffa683a26d1334b33917 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 26 Jun 2024 14:43:43 +0200 Subject: [PATCH 33/40] rewrite dispatcher a bit --- includes/class-activity-dispatcher.php | 25 +++--- includes/class-cli.php | 91 ++++++++++++++++++++- includes/class-scheduler.php | 35 ++++---- includes/class-signature.php | 103 +++++++----------------- includes/collection/class-followers.php | 52 ++++++------ includes/functions.php | 4 +- 6 files changed, 176 insertions(+), 134 deletions(-) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index a025ac955..4287e54da 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -187,20 +187,25 @@ private static function send_activity_to_followers( $activity, $user_id, $wp_obj /** * Send an Activity to all known (shared_)inboxes. * - * @param Activity $activity The ActivityPub Activity. + * @param WP_User $user The deleted WordPress User. * * @return void */ - public static function send_actor_delete_activity( $activity, $user_id = Users::APPLICATION_USER_ID ) { - $json = $activity->to_json(); - $followers = Followers::get_all_followers(); - $known_inboxes = []; - foreach ( $followers as $follower ) { - $known_inboxes[] = $follower->get_shared_inbox(); - } - $inboxes = array_unique( $known_inboxes ); + public static function send_actor_delete_activity( $user ) { + $url = \get_author_posts_url( null, $user->user_nicename ); + + $activity = new Activity(); + $activity->set_id( $url . '#delete' ); + $activity->set_type( 'Delete' ); + $activity->set_actor( $url ); + $activity->set_object( $url ); + $activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' ); + + $json = $activity->to_json(); + $inboxes = Followers::get_inboxes( Followers::ALL ); + foreach ( $inboxes as $inbox ) { - safe_remote_post( $inbox, $json, $user_id ); + safe_remote_post( $inbox, $json, $user ); } } diff --git a/includes/class-cli.php b/includes/class-cli.php index 38c5c95a7..ec13532e1 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -10,6 +10,75 @@ * @package Activitypub */ class Cli extends WP_CLI_Command { + /** + * See the Plugin Meta-Informations + * + * ## OPTIONS + * + * [--Name] + * The Plugin Name + * + * [--PluginURI] + * The Plugin URI + * + * [--Version] + * The Plugin Version + * + * [--Description] + * The Plugin Description + * + * [--Author] + * The Plugin Author + * + * [--AuthorURI] + * The Plugin Author URI + * + * [--TextDomain] + * The Plugin Text Domain + * + * [--DomainPath] + * The Plugin Domain Path + * + * [--Network] + * The Plugin Network + * + * [--RequiresWP] + * The Plugin Requires at least + * + * [--RequiresPHP] + * The Plugin Requires PHP + * + * [--UpdateURI] + * The Plugin Update URI + * + * See: https://developer.wordpress.org/reference/functions/get_plugin_data/#return + * + * ## EXAMPLES + * + * $ wp webmention meta + * + * $ wp webmention meta --Version + * Version: 1.0.0 + * + * @param array|null $args The arguments. + * @param array|null $assoc_args The associative arguments. + * + * @return void + */ + public function meta( $args, $assoc_args ) { + $plugin_data = get_plugin_meta(); + + if ( $assoc_args ) { + $plugin_data = array_intersect_key( $plugin_data, $assoc_args ); + } else { + WP_CLI::line( __( "ActivityPub Plugin Meta:\n", 'activitypub' ) ); + } + + foreach ( $plugin_data as $key => $value ) { + WP_CLI::line( $key . ': ' . $value ); + } + } + /** * Remove the blog from the Fediverse. * @@ -28,15 +97,33 @@ public function self_destruct( $args, $assoc_args ) { $question = __( 'We are in the process of deleting your blog from the Fediverse. This action could be irreversible, so are you sure you want to continue?', 'activitypub' ); WP_CLI::confirm( WP_CLI::colorize( "%r{$question}%n" ), $assoc_args = array() ); - WP_CLI::success( __( 'Deleting your blog from the Fediverse...', 'activitypub' ) ); + WP_CLI::success( __( 'Deleting your Blog from the Fediverse...', 'activitypub' ) ); + + // Deactivate the ActivityPub Plugin after the deletion. + WP_CLI::runcommand( 'plugin deactivate activitypub' ); } /** * Delete or Update a User. * + * ## OPTIONS + * + * + * : The action to perform. Either `delete` or `update`. + * --- + * options: + * - delete + * - update + * --- + * + * + * : The id of the registered WordPress user. + * * ## EXAMPLES * - * $ wp activitypub user + * $ wp activitypub user delete 1 + * + * @synopsis * * @param array|null $args The arguments. * @param array|null $assoc_args The associative arguments. diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 041600dc3..c09608c54 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -336,28 +336,21 @@ public static function schedule_profile_update( $user_id ) { /** * Send an Actor Delete activity. - * @param int $user_id The user ID to Delete. + * + * @param int $id ID of the user to delete. + * @param int|null $reassign ID of the user to reassign posts and links to. + * Default null, for no reassignment. + * @param WP_User $user WP_User object of the user to delete. */ - public static function schedule_actor_delete( $user_id ) { - $user = get_userdata( $user_id ); - if ( $user->has_cap( 'activitypub' ) ) { - $author_url = \get_author_posts_url( $user->ID ); - add_option( - 'activitypub_temp_sig_' . $user_id, - array( - 'key_id' => $author_url . '#main-key', - 'private_key' => Signature::get_private_key_for( $user_id ), - ) - ); - - $activity = new Activity(); - $activity->set_id( $author_url . '#delete' ); - $activity->set_type( 'Delete' ); - $activity->set_actor( $author_url ); - $activity->set_object( $author_url ); - $activity->set_to( [ 'https://www.w3.org/ns/activitystreams#Public' ] ); - - \wp_schedule_single_event( \time(), 'activitypub_send_actor_delete_activity', array( $activity, $user_id ) ); + public static function schedule_actor_delete( $user_id, $reassign, $deleted_user ) { + $caps = $deleted_user->allcaps; + + // Check if 'activitypub' capability is set. + if ( isset( $caps['activitypub'] ) ) { + // Do not send activities if user is not allowed to publish. + unset( $deleted_user->data->user_pass ); + unset( $deleted_user->allcaps ); + \wp_schedule_single_event( \time(), 'activitypub_send_actor_delete_activity', array( $deleted_user ) ); } } } diff --git a/includes/class-signature.php b/includes/class-signature.php index 37ea67f5f..74c889e5c 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -36,17 +36,17 @@ public static function get_public_key_for( $user_id, $force = false ) { /** * Return the private key for a given user. * - * @param int $user_id The WordPress User ID. - * @param bool $force Force the generation of a new key pair. + * @param WP_User|int $user The WordPress User ID. + * @param bool $force Force the generation of a new key pair. * * @return mixed The private key. */ - public static function get_private_key_for( $user_id, $force = false ) { + public static function get_private_key_for( $user, $force = false ) { if ( $force ) { - self::generate_key_pair_for( $user_id ); + self::generate_key_pair_for( $user ); } - $key_pair = self::get_keypair_for( $user_id ); + $key_pair = self::get_keypair_for( $user ); return $key_pair['private_key']; } @@ -54,16 +54,16 @@ public static function get_private_key_for( $user_id, $force = false ) { /** * Return the key pair for a given user. * - * @param int $user_id The WordPress User ID. + * @param WP_User|int $user The WordPress User ID. * * @return array The key pair. */ - public static function get_keypair_for( $user_id ) { - $option_key = self::get_signature_options_key_for( $user_id ); + public static function get_keypair_for( $user ) { + $option_key = self::get_signature_options_key_for( $user ); $key_pair = \get_option( $option_key ); if ( ! $key_pair ) { - $key_pair = self::generate_key_pair_for( $user_id ); + $key_pair = self::generate_key_pair_for( $user ); } return $key_pair; @@ -72,19 +72,13 @@ public static function get_keypair_for( $user_id ) { /** * Generates the pair keys * - * @param int $user_id The WordPress User ID. + * @param WP_User|int $user The WordPress User ID. * * @return array The key pair. */ - protected static function generate_key_pair_for( $user_id ) { - $option_key = self::get_signature_options_key_for( $user_id ); - $key_pair = self::check_legacy_key_pair_for( $user_id ); - - if ( $key_pair ) { - \add_option( $option_key, $key_pair ); - - return $key_pair; - } + protected static function generate_key_pair_for( $user ) { + $option_key = self::get_signature_options_key_for( $user ); + $key_pair = self::check_legacy_key_pair_for( $user ); $config = array( 'digest_alg' => 'sha512', @@ -124,76 +118,35 @@ protected static function generate_key_pair_for( $user_id ) { /** * Return the option key for a given user. * - * @param int $user_id The WordPress User ID. + * @param WP_User|int $user The WordPress User ID. * * @return string The option key. */ - protected static function get_signature_options_key_for( $user_id ) { - $id = $user_id; - - if ( $user_id > 0 ) { - $user = \get_userdata( $user_id ); - // sanatize username because it could include spaces and special chars - $id = sanitize_title( $user->user_login ); - } - - return 'activitypub_keypair_for_' . $id; - } - - /** - * Check if there is a legacy key pair - * - * @param int $user_id The WordPress User ID. - * - * @return array|bool The key pair or false. - */ - protected static function check_legacy_key_pair_for( $user_id ) { - switch ( $user_id ) { - case 0: - $public_key = \get_option( 'activitypub_blog_user_public_key' ); - $private_key = \get_option( 'activitypub_blog_user_private_key' ); - break; - case -1: - $public_key = \get_option( 'activitypub_application_user_public_key' ); - $private_key = \get_option( 'activitypub_application_user_private_key' ); - break; - default: - $public_key = \get_user_meta( $user_id, 'magic_sig_public_key', true ); - $private_key = \get_user_meta( $user_id, 'magic_sig_private_key', true ); - break; + protected static function get_signature_options_key_for( $user ) { + if ( ! 'WP_User' instanceof $user ) { + $user = \get_userdata( $user ); } - if ( ! empty( $public_key ) && is_string( $public_key ) && ! empty( $private_key ) && is_string( $private_key ) ) { - return array( - 'private_key' => $private_key, - 'public_key' => $public_key, - ); - } + // sanatize username because it could include spaces and special chars + $id = sanitize_title( $user->user_login ); - return false; + return 'activitypub_keypair_for_' . $id; } /** * Generates the Signature for a HTTP Request * - * @param int $user_id The WordPress User ID. - * @param string $http_method The HTTP method. - * @param string $url The URL to send the request to. - * @param string $date The date the request is sent. - * @param string $digest The digest of the request body. + * @param int|WP_User $user The WordPress User or ID. + * @param string $http_method The HTTP method. + * @param string $url The URL to send the request to. + * @param string $date The date the request is sent. + * @param string $digest The digest of the request body. * * @return string The signature. */ - public static function generate_signature( $user_id, $http_method, $url, $date, $digest = null ) { - $user = Users::get_by_id( $user_id ); - if ( ! is_wp_error( $user ) ) { - $key = self::get_private_key_for( $user->get__id() ); - $key_id = $user->get_url() . '#main-key'; - } else { - $temp_sig_options = get_option( 'activitypub_temp_sig_' . $user_id ); - $key = $temp_sig_options['private_key']; - $key_id = $temp_sig_options['key_id']; - } + public static function generate_signature( $user, $http_method, $url, $date, $digest = null ) { + $key = self::get_private_key_for( $user ); + $key_id = $user->get_url() . '#main-key'; $url_parts = \wp_parse_url( $url ); diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index b51224fd0..6edd09e36 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -19,6 +19,7 @@ class Followers { const POST_TYPE = 'ap_follower'; const CACHE_KEY_INBOXES = 'follower_inboxes_%s'; + const ALL = 'all'; /** * Add new Follower @@ -253,7 +254,7 @@ public static function count_followers( $user_id ) { /** * Returns all Inboxes fo a Users Followers * - * @param int $user_id The ID of the WordPress User + * @param int|string $user_id The ID of the WordPress User or 'all' for all Inboxes * * @return array The list of Inboxes */ @@ -265,32 +266,35 @@ public static function get_inboxes( $user_id ) { return $inboxes; } - // get all Followers of a ID of the WordPress User - $posts = new WP_Query( - array( - 'nopaging' => true, - 'post_type' => self::POST_TYPE, - 'fields' => 'ids', - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query - 'meta_query' => array( - 'relation' => 'AND', - array( - 'key' => 'activitypub_inbox', - 'compare' => 'EXISTS', - ), - array( - 'key' => 'activitypub_user_id', - 'value' => $user_id, - ), - array( - 'key' => 'activitypub_inbox', - 'value' => '', - 'compare' => '!=', - ), + $args = array( + 'nopaging' => true, + 'post_type' => self::POST_TYPE, + 'fields' => 'ids', + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => 'activitypub_inbox', + 'compare' => 'EXISTS', ), - ) + array( + 'key' => 'activitypub_inbox', + 'value' => '', + 'compare' => '!=', + ), + ), ); + // Add User-ID to the query + if ( self::ALL !== $user_id ) { + $args['meta_query'][] = array( + 'key' => 'activitypub_user_id', + 'value' => $user_id, + ); + } + + // get all Followers of a ID of the WordPress User + $posts = new WP_Query( $args ); $posts = $posts->get_posts(); if ( ! $posts ) { diff --git a/includes/functions.php b/includes/functions.php index d33a2a4f9..b1a525b3d 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -20,8 +20,8 @@ function get_context() { return \apply_filters( 'activitypub_json_context', $context ); } -function safe_remote_post( $url, $body, $user_id ) { - return Http::post( $url, $body, $user_id ); +function safe_remote_post( $url, $body, $user ) { + return Http::post( $url, $body, $user ); } function safe_remote_get( $url ) { From 520c3fbf413d15bb7a6765551cc1d1de2331a5b2 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 26 Jun 2024 14:52:41 +0200 Subject: [PATCH 34/40] mark as federated --- includes/class-activity-dispatcher.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 4287e54da..9fb7c5b73 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -182,6 +182,8 @@ private static function send_activity_to_followers( $activity, $user_id, $wp_obj foreach ( $inboxes as $inbox ) { safe_remote_post( $inbox, $json, $user_id ); } + + set_wp_object_state( $wp_object, 'federated' ); } /** From 962e6dd4147237d3b0c0cae3492c6a9abbf24503 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 26 Jun 2024 15:47:25 +0200 Subject: [PATCH 35/40] cleanups --- includes/class-cli.php | 2 +- includes/class-signature.php | 64 +++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index ec13532e1..b6d17b2e8 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -131,6 +131,6 @@ public function self_destruct( $args, $assoc_args ) { * @return void */ public function user( $args, $assoc_args ) { - + // @todo add code } } diff --git a/includes/class-signature.php b/includes/class-signature.php index 74c889e5c..a37b8e48d 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -18,17 +18,17 @@ class Signature { /** * Return the public key for a given user. * - * @param int $user_id The WordPress User ID. - * @param bool $force Force the generation of a new key pair. + * @param WP_User|int $user_id The WordPress User ID. + * @param bool $force Force the generation of a new key pair. * * @return mixed The public key. */ - public static function get_public_key_for( $user_id, $force = false ) { + public static function get_public_key_for( $user, $force = false ) { if ( $force ) { - self::generate_key_pair_for( $user_id ); + self::generate_key_pair_for( $user ); } - $key_pair = self::get_keypair_for( $user_id ); + $key_pair = self::get_keypair_for( $user ); return $key_pair['public_key']; } @@ -218,7 +218,11 @@ public static function verify_http_signature( $request ) { } if ( ! isset( $headers['signature'] ) ) { - return new WP_Error( 'activitypub_signature', __( 'Request not signed', 'activitypub' ), array( 'status' => 401 ) ); + return new WP_Error( + 'activitypub_signature', + __( 'Request not signed', 'activitypub' ), + array( 'status' => 401 ) + ); } if ( array_key_exists( 'signature', $headers ) ) { @@ -228,7 +232,14 @@ public static function verify_http_signature( $request ) { } if ( ! isset( $signature_block ) || ! $signature_block ) { - return new WP_Error( 'activitypub_signature', __( 'Incompatible request signature. keyId and signature are required', 'activitypub' ), array( 'status' => 401 ) ); + return new WP_Error( + 'activitypub_signature', + __( + 'Incompatible request signature. keyId and signature are required', + 'activitypub' + ), + array( 'status' => 401 ) + ); } $signed_headers = $signature_block['headers']; @@ -238,12 +249,26 @@ public static function verify_http_signature( $request ) { $signed_data = self::get_signed_data( $signed_headers, $signature_block, $headers ); if ( ! $signed_data ) { - return new WP_Error( 'activitypub_signature', __( 'Signed request date outside acceptable time window', 'activitypub' ), array( 'status' => 401 ) ); + return new WP_Error( + 'activitypub_signature', + __( + 'Signed request date outside acceptable time window', + 'activitypub' + ), + array( 'status' => 401 ) + ); } $algorithm = self::get_signature_algorithm( $signature_block ); if ( ! $algorithm ) { - return new WP_Error( 'activitypub_signature', __( 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)', 'activitypub' ), array( 'status' => 401 ) ); + return new WP_Error( + 'activitypub_signature', + __( + 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)', + 'activitypub' + ), + array( 'status' => 401 ) + ); } if ( \in_array( 'digest', $signed_headers, true ) && isset( $body ) ) { @@ -260,7 +285,14 @@ public static function verify_http_signature( $request ) { } if ( \base64_encode( \hash( $hashalg, $body, true ) ) !== $digest[1] ) { // phpcs:ignore - return new WP_Error( 'activitypub_signature', __( 'Invalid Digest header', 'activitypub' ), array( 'status' => 401 ) ); + return new WP_Error( + 'activitypub_signature', + __( + 'Invalid Digest header', + 'activitypub' + ), + array( 'status' => 401 ) + ); } } @@ -273,7 +305,14 @@ public static function verify_http_signature( $request ) { $verified = \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0; if ( ! $verified ) { - return new WP_Error( 'activitypub_signature', __( 'Invalid signature', 'activitypub' ), array( 'status' => 401 ) ); + return new WP_Error( + 'activitypub_signature', + __( + 'Invalid signature', + 'activitypub' + ), + array( 'status' => 401 ) + ); } return $verified; } @@ -315,7 +354,8 @@ public static function get_signature_algorithm( $signature_block ) { if ( $signature_block['algorithm'] ) { switch ( $signature_block['algorithm'] ) { case 'rsa-sha-512': - return 'sha512'; //hs2019 https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12 + //hs2019 https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12 + return 'sha512'; default: return 'sha256'; } From 273090798d24ec3ddc5430c916ca7a676a423558 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 26 Jun 2024 15:54:12 +0200 Subject: [PATCH 36/40] remove lagacy code --- includes/class-signature.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/class-signature.php b/includes/class-signature.php index a37b8e48d..e9f05881b 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -78,9 +78,7 @@ public static function get_keypair_for( $user ) { */ protected static function generate_key_pair_for( $user ) { $option_key = self::get_signature_options_key_for( $user ); - $key_pair = self::check_legacy_key_pair_for( $user ); - - $config = array( + $config = array( 'digest_alg' => 'sha512', 'private_key_bits' => 2048, 'private_key_type' => \OPENSSL_KEYTYPE_RSA, From 295d5bc5b87ff6c3c9c13ea285adb5457c8fb992 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 26 Jun 2024 15:59:11 +0200 Subject: [PATCH 37/40] fix instance of check --- includes/class-signature.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-signature.php b/includes/class-signature.php index e9f05881b..de9fd1975 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -121,7 +121,7 @@ protected static function generate_key_pair_for( $user ) { * @return string The option key. */ protected static function get_signature_options_key_for( $user ) { - if ( ! 'WP_User' instanceof $user ) { + if ( ! $user instanceof WP_User ) { $user = \get_userdata( $user ); } @@ -144,7 +144,7 @@ protected static function get_signature_options_key_for( $user ) { */ public static function generate_signature( $user, $http_method, $url, $date, $digest = null ) { $key = self::get_private_key_for( $user ); - $key_id = $user->get_url() . '#main-key'; + $key_id = '#main-key'; //$user->get_url() . '#main-key'; $url_parts = \wp_parse_url( $url ); From 4fcf1519b2ecf0c9233dde2df648d06d4624779c Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 27 Jun 2024 14:23:21 +0200 Subject: [PATCH 38/40] better $user handling --- includes/class-signature.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/includes/class-signature.php b/includes/class-signature.php index de9fd1975..5621a5635 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -121,12 +121,15 @@ protected static function generate_key_pair_for( $user ) { * @return string The option key. */ protected static function get_signature_options_key_for( $user ) { - if ( ! $user instanceof WP_User ) { - $user = \get_userdata( $user ); + if ( $user instanceof WP_User || (int) $user > 0 ) { + $user = \get_userdata( $user ); + $user_login = $user->user_login; + } else { + $user_login = $user; } // sanatize username because it could include spaces and special chars - $id = sanitize_title( $user->user_login ); + $id = sanitize_title( $user_login ); return 'activitypub_keypair_for_' . $id; } From f319cac3cb3f6a5a83f03c4ee8f9c7cc7e63a17d Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 27 Jun 2024 14:25:42 +0200 Subject: [PATCH 39/40] better support of system users --- includes/class-signature.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/class-signature.php b/includes/class-signature.php index 5621a5635..2d76b0309 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -121,7 +121,9 @@ protected static function generate_key_pair_for( $user ) { * @return string The option key. */ protected static function get_signature_options_key_for( $user ) { - if ( $user instanceof WP_User || (int) $user > 0 ) { + if ( $user instanceof WP_User ) { + $user_login = $user->user_login; + } elseif ( (int) $user > 0 ) { $user = \get_userdata( $user ); $user_login = $user->user_login; } else { From 62714a59665062971f24c81c9ba2499a07fa6531 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 9 Jul 2024 10:58:45 +0200 Subject: [PATCH 40/40] better description --- activitypub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.php b/activitypub.php index ab83ec313..0c298fe25 100644 --- a/activitypub.php +++ b/activitypub.php @@ -244,7 +244,7 @@ function get_plugin_version() { 'activitypub', '\Activitypub\Cli', array( - 'shortdesc' => __( 'Some ActivityPub related commands.', 'activitypub' ), + 'shortdesc' => __( 'ActivityPub related commands: Meta-Infos and Self-Destruct.', 'activitypub' ), ) ); }