From ae8dabbd215bd1aabb68997c3d04222a4286cccf Mon Sep 17 00:00:00 2001 From: Lai Power Date: Wed, 18 Jan 2023 16:39:54 +0000 Subject: [PATCH] updated plugin `ActivityPub` version 0.15.0 --- wp-content/plugins/activitypub/Dockerfile | 27 ++++++ .../plugins/activitypub/activitypub.php | 2 +- .../activitypub/docker-compose-test.yml | 17 ++++ .../includes/class-activitypub.php | 29 ++++++ .../activitypub/includes/model/class-post.php | 12 ++- .../includes/rest/class-followers.php | 3 + .../includes/rest/class-following.php | 3 + .../activitypub/includes/rest/class-inbox.php | 95 +++++++++++++++++-- .../includes/rest/class-outbox.php | 3 + .../includes/rest/class-webfinger.php | 2 +- wp-content/plugins/activitypub/readme.txt | 8 +- .../activitypub/templates/blog-json.php | 2 +- 12 files changed, 186 insertions(+), 17 deletions(-) create mode 100644 wp-content/plugins/activitypub/Dockerfile create mode 100644 wp-content/plugins/activitypub/docker-compose-test.yml diff --git a/wp-content/plugins/activitypub/Dockerfile b/wp-content/plugins/activitypub/Dockerfile new file mode 100644 index 00000000..3b056fd9 --- /dev/null +++ b/wp-content/plugins/activitypub/Dockerfile @@ -0,0 +1,27 @@ +FROM php:7.4-alpine3.13 + +RUN mkdir /app + +WORKDIR /app + +# Install Git, NPM & needed libraries +RUN apk update \ + && apk add bash git nodejs npm gettext subversion mysql mysql-client zip \ + && rm -f /var/cache/apk/* + +RUN docker-php-ext-install mysqli + +# Install Composer +RUN EXPECTED_CHECKSUM=$(curl -s https://composer.github.io/installer.sig) \ + && curl https://getcomposer.org/installer -o composer-setup.php \ + && ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" \ + && if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then >&2 echo 'ERROR: Invalid installer checksum'; rm composer-setup.php; exit 1; fi \ + && php composer-setup.php --quiet \ + && php -r "unlink('composer-setup.php');" \ + && mv composer.phar /usr/local/bin/composer + +RUN curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \ + chmod +x wp-cli.phar && \ + mv wp-cli.phar /usr/local/bin/wp + +RUN chmod +x -R ./ diff --git a/wp-content/plugins/activitypub/activitypub.php b/wp-content/plugins/activitypub/activitypub.php index fe7809e2..770a2778 100644 --- a/wp-content/plugins/activitypub/activitypub.php +++ b/wp-content/plugins/activitypub/activitypub.php @@ -3,7 +3,7 @@ * Plugin Name: ActivityPub * Plugin URI: https://github.com/pfefferle/wordpress-activitypub/ * Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format. - * Version: 0.14.3 + * Version: 0.15.0 * Author: Matthias Pfefferle * Author URI: https://notiz.blog/ * License: MIT diff --git a/wp-content/plugins/activitypub/docker-compose-test.yml b/wp-content/plugins/activitypub/docker-compose-test.yml new file mode 100644 index 00000000..a2938237 --- /dev/null +++ b/wp-content/plugins/activitypub/docker-compose-test.yml @@ -0,0 +1,17 @@ +version: '2' +services: + test-db: + image: mysql:5.7 + environment: + MYSQL_DATABASE: activitypub-test + MYSQL_ROOT_PASSWORD: activitypub-test + + test-php: + build: + context: . + dockerfile: Dockerfile + links: + - test-db + volumes: + - .:/app + command: ["composer", "run-script", "test"] diff --git a/wp-content/plugins/activitypub/includes/class-activitypub.php b/wp-content/plugins/activitypub/includes/class-activitypub.php index c2a068da..658f72c5 100644 --- a/wp-content/plugins/activitypub/includes/class-activitypub.php +++ b/wp-content/plugins/activitypub/includes/class-activitypub.php @@ -24,6 +24,8 @@ class Activitypub { } \add_action( 'transition_post_status', array( '\Activitypub\Activitypub', 'schedule_post_activity' ), 10, 3 ); + \add_action( 'wp_trash_post', array( '\Activitypub\Activitypub', 'trash_post' ), 1 ); + \add_action( 'untrash_post', array( '\Activitypub\Activitypub', 'untrash_post' ), 1 ); } /** @@ -38,6 +40,11 @@ class Activitypub { return $template; } + // check if user can publish posts + if ( \is_author() && ! user_can( \get_the_author_meta( 'ID' ), 'publish_posts' ) ) { + return $template; + } + if ( \is_author() ) { $json_template = \dirname( __FILE__ ) . '/../templates/author-json.php'; } elseif ( \is_singular() ) { @@ -180,4 +187,26 @@ class Activitypub { } return \get_comment_meta( $comment->comment_ID, 'avatar_url', true ); } + + /** + * Store permalink in meta, to send delete Activity + * + * @param string $post_id The Post ID + * + * @return void + */ + public static function trash_post( $post_id ) { + \add_post_meta( $post_id, 'activitypub_canonical_url', \get_permalink( $post_id ), true ); + } + + /** + * Delete permalink from meta + * + * @param string $post_id The Post ID + * + * @return void + */ + public static function untrash_post( $post_id ) { + \delete_post_meta( $post_id, 'activitypub_canonical_url' ); + } } diff --git a/wp-content/plugins/activitypub/includes/model/class-post.php b/wp-content/plugins/activitypub/includes/model/class-post.php index e8851991..cd8f119e 100644 --- a/wp-content/plugins/activitypub/includes/model/class-post.php +++ b/wp-content/plugins/activitypub/includes/model/class-post.php @@ -68,11 +68,15 @@ class Post { } public function generate_id() { - $post = $this->post; - $permalink = \get_permalink( $post ); + $post = $this->post; - // replace 'trashed' for delete activity - return \str_replace( '__trashed', '', $permalink ); + if ( 'trash' === get_post_status( $post ) ) { + $permalink = \get_post_meta( $post->ID, 'activitypub_canonical_url', true ); + } else { + $permalink = \get_permalink( $post ); + } + + return $permalink; } public function generate_attachments() { diff --git a/wp-content/plugins/activitypub/includes/rest/class-followers.php b/wp-content/plugins/activitypub/includes/rest/class-followers.php index 34392ce0..2734c780 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-followers.php +++ b/wp-content/plugins/activitypub/includes/rest/class-followers.php @@ -101,6 +101,9 @@ class Followers { $params['user_id'] = array( 'required' => true, 'type' => 'integer', + 'validate_callback' => function( $param, $request, $key ) { + return user_can( $param, 'publish_posts' ); + }, ); return $params; diff --git a/wp-content/plugins/activitypub/includes/rest/class-following.php b/wp-content/plugins/activitypub/includes/rest/class-following.php index 06d3f0b3..d7caff44 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-following.php +++ b/wp-content/plugins/activitypub/includes/rest/class-following.php @@ -99,6 +99,9 @@ class Following { $params['user_id'] = array( 'required' => true, 'type' => 'integer', + 'validate_callback' => function( $param, $request, $key ) { + return user_can( $param, 'publish_posts' ); + }, ); return $params; diff --git a/wp-content/plugins/activitypub/includes/rest/class-inbox.php b/wp-content/plugins/activitypub/includes/rest/class-inbox.php index 3408950a..5c21f43c 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-inbox.php +++ b/wp-content/plugins/activitypub/includes/rest/class-inbox.php @@ -33,7 +33,7 @@ class Inbox { array( 'methods' => \WP_REST_Server::EDITABLE, 'callback' => array( '\Activitypub\Rest\Inbox', 'shared_inbox_post' ), - 'args' => self::shared_inbox_request_parameters(), + 'args' => self::shared_inbox_post_parameters(), 'permission_callback' => '__return_true', ), ) @@ -46,12 +46,13 @@ class Inbox { array( 'methods' => \WP_REST_Server::EDITABLE, 'callback' => array( '\Activitypub\Rest\Inbox', 'user_inbox_post' ), - 'args' => self::user_inbox_request_parameters(), + 'args' => self::user_inbox_post_parameters(), 'permission_callback' => '__return_true', ), array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( '\Activitypub\Rest\Inbox', 'user_inbox_get' ), + 'args' => self::user_inbox_get_parameters(), 'permission_callback' => '__return_true', ), ) @@ -195,7 +196,7 @@ class Inbox { * * @return array list of parameters */ - public static function user_inbox_request_parameters() { + public static function user_inbox_get_parameters() { $params = array(); $params['page'] = array( @@ -205,6 +206,32 @@ class Inbox { $params['user_id'] = array( 'required' => true, 'type' => 'integer', + 'validate_callback' => function( $param, $request, $key ) { + return user_can( $param, 'publish_posts' ); + }, + ); + + return $params; + } + + /** + * The supported parameters + * + * @return array list of parameters + */ + public static function user_inbox_post_parameters() { + $params = array(); + + $params['page'] = array( + 'type' => 'integer', + ); + + $params['user_id'] = array( + 'required' => true, + 'type' => 'integer', + 'validate_callback' => function( $param, $request, $key ) { + return user_can( $param, 'publish_posts' ); + }, ); $params['id'] = array( @@ -243,7 +270,7 @@ class Inbox { * * @return array list of parameters */ - public static function shared_inbox_request_parameters() { + public static function shared_inbox_post_parameters() { $params = array(); $params['page'] = array( @@ -410,6 +437,12 @@ class Inbox { return; } + // check if Activity is public or not + if ( ! self::is_activity_public( $object ) ) { + // @todo maybe send email + return; + } + $comment_post_id = \url_to_postid( $object['object']['inReplyTo'] ); // save only replys and reactions @@ -446,21 +479,53 @@ class Inbox { \add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 ); } + /** + * Extract recipient URLs from Activity object + * + * @param array $data + * + * @return array The list of user URLs + */ public static function extract_recipients( $data ) { - $recipients = array(); - $users = array(); + $recipient_items = array(); foreach ( array( 'to', 'bto', 'cc', 'bcc', 'audience' ) as $i ) { if ( array_key_exists( $i, $data ) ) { - $recipients = array_merge( $recipients, $data[ $i ] ); + $recipient_items = array_merge( $recipient_items, $data[ $i ] ); } if ( array_key_exists( $i, $data['object'] ) ) { - $recipients = array_merge( $recipients, $data[ $i ] ); + $recipient_items = array_merge( $recipient_items, $data[ $i ] ); } } - $recipients = array_unique( $recipients ); + $recipients = array(); + + // flatten array + foreach ( $recipient_items as $recipient ) { + if ( is_array( $recipient ) ) { + // check if recipient is an object + if ( array_key_exists( 'id', $recipient ) ) { + $recipients[] = $recipient['id']; + } + } else { + $recipients[] = $recipient; + } + } + + return array_unique( $recipients ); + } + + /** + * Get local user recipients + * + * @param array $data + * + * @return array The list of local users + */ + public static function get_recipients( $data ) { + $recipients = self::extract_recipients( $data ); + $users = array(); foreach ( $recipients as $recipient ) { $user_id = \Activitypub\url_to_authorid( $recipient ); @@ -474,4 +539,16 @@ class Inbox { return $users; } + + /** + * Check if passed Activity is Public + * + * @param array $data + * @return boolean + */ + public static function is_activity_public( $data ) { + $recipients = self::extract_recipients( $data ); + + return in_array( 'https://www.w3.org/ns/activitystreams#Public', $recipients, true ); + } } diff --git a/wp-content/plugins/activitypub/includes/rest/class-outbox.php b/wp-content/plugins/activitypub/includes/rest/class-outbox.php index e43ab712..7eec5ac2 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-outbox.php +++ b/wp-content/plugins/activitypub/includes/rest/class-outbox.php @@ -138,6 +138,9 @@ class Outbox { $params['user_id'] = array( 'required' => true, 'type' => 'integer', + 'validate_callback' => function( $param, $request, $key ) { + return user_can( $param, 'publish_posts' ); + }, ); return $params; diff --git a/wp-content/plugins/activitypub/includes/rest/class-webfinger.php b/wp-content/plugins/activitypub/includes/rest/class-webfinger.php index dc84da41..0c6d5f13 100644 --- a/wp-content/plugins/activitypub/includes/rest/class-webfinger.php +++ b/wp-content/plugins/activitypub/includes/rest/class-webfinger.php @@ -59,7 +59,7 @@ class Webfinger { $user = \get_user_by( 'login', \esc_sql( $resource_identifier ) ); - if ( ! $user ) { + if ( ! $user || ! user_can( $user, 'publish_posts' ) ) { return new \WP_Error( 'activitypub_user_not_found', \__( 'User not found', 'activitypub' ), array( 'status' => 404 ) ); } diff --git a/wp-content/plugins/activitypub/readme.txt b/wp-content/plugins/activitypub/readme.txt index e4b5e135..4a9d2dc7 100644 --- a/wp-content/plugins/activitypub/readme.txt +++ b/wp-content/plugins/activitypub/readme.txt @@ -4,7 +4,7 @@ Donate link: https://notiz.blog/donate/ Tags: OStatus, fediverse, activitypub, activitystream Requires at least: 4.7 Tested up to: 6.1 -Stable tag: 0.14.3 +Stable tag: 0.15.0 Requires PHP: 5.6 License: MIT License URI: http://opensource.org/licenses/MIT @@ -88,6 +88,12 @@ Where 'blog' is the path to the subdirectory at which your blog resides. Project maintained on GitHub at [pfefferle/wordpress-activitypub](https://github.com/pfefferle/wordpress-activitypub). += 0.15.0 = + +* Enable ActivityPub only for users that can `publish_posts` +* Persist only public Activities +* Fix remote-delete + = 0.14.3 = * Better error handling. props [@akirk](https://github.com/akirk) diff --git a/wp-content/plugins/activitypub/templates/blog-json.php b/wp-content/plugins/activitypub/templates/blog-json.php index 18e65afc..4a1efc84 100644 --- a/wp-content/plugins/activitypub/templates/blog-json.php +++ b/wp-content/plugins/activitypub/templates/blog-json.php @@ -38,7 +38,7 @@ $json->manuallyApprovesFollowers = \apply_filters( 'activitypub_json_manually_ap $json->publicKey = array( 'id' => \get_home_url( '/' ) . '#main-key', 'owner' => \get_home_url( '/' ), - 'publicKeyPem' => \trim(), + 'publicKeyPem' => '', ); $json->tag = array();