diff --git a/includes/activity/class-activity.php b/includes/activity/class-activity.php index 8799238..c4bebaa 100644 --- a/includes/activity/class-activity.php +++ b/includes/activity/class-activity.php @@ -26,6 +26,7 @@ class Activity extends Base_Object { 'schema' => 'http://schema.org#', 'pt' => 'https://joinpeertube.org/ns#', 'toot' => 'http://joinmastodon.org/ns#', + 'webfinger' => 'https://webfinger.net/#', 'litepub' => 'http://litepub.social/ns#', 'value' => 'schema:value', 'Hashtag' => 'as:Hashtag', @@ -37,8 +38,13 @@ class Activity extends Base_Object { '@id' => 'toot:featuredTags', '@type' => '@id', ), + 'alsoKnownAs' => array( + '@id' => 'as:alsoKnownAs', + '@type' => '@id', + ), 'discoverable' => 'toot:discoverable', 'sensitive' => 'as:sensitive', + 'resource' => 'webfinger:resource', ), ); diff --git a/includes/class-signature.php b/includes/class-signature.php index 5ae37a9..af676ed 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -162,12 +162,10 @@ class Signature { $route = '/' . rest_get_url_prefix() . '/' . ltrim( $request->get_route(), '/' ); } $headers = $request->get_headers(); - $actor = isset( json_decode( $request->get_body() )->actor ) ? json_decode( $request->get_body() )->actor : ''; $headers['(request-target)'][0] = strtolower( $request->get_method() ) . ' ' . $route; } else { $request = self::format_server_request( $request ); $headers = $request['headers']; // $_SERVER array - $actor = null; $headers['(request-target)'][0] = strtolower( $headers['request_method'][0] ) . ' ' . $headers['request_uri'][0]; } @@ -176,9 +174,9 @@ class Signature { } if ( array_key_exists( 'signature', $headers ) ) { - $signature_block = self::parse_signature_header( $headers['signature'] ); + $signature_block = self::parse_signature_header( $headers['signature'][0] ); } elseif ( array_key_exists( 'authorization', $headers ) ) { - $signature_block = self::parse_signature_header( $headers['authorization'] ); + $signature_block = self::parse_signature_header( $headers['authorization'][0] ); } if ( ! isset( $signature_block ) || ! $signature_block ) { @@ -217,11 +215,8 @@ class Signature { } } - if ( $actor ) { - $public_key = self::get_remote_key( $actor ); - } else { - $public_key = self::get_remote_key( $signature_block['keyId'] ); - } + $public_key = self::get_remote_key( $signature_block['keyId'] ); + if ( \is_wp_error( $public_key ) ) { return $public_key; } @@ -242,7 +237,7 @@ class Signature { * @return string The public key. */ public static function get_remote_key( $key_id ) { // phpcs:ignore - $actor = get_remote_metadata_by_actor( strtok( strip_fragment_from_url( $key_id ), '?' ) ); // phpcs:ignore + $actor = get_remote_metadata_by_actor( strip_fragment_from_url( $key_id ) ); // phpcs:ignore if ( \is_wp_error( $actor ) ) { return $actor; } @@ -274,32 +269,31 @@ class Signature { /** * Parses the Signature header * - * @param array $header The signature header. + * @param string $signature The signature header. * * @return array signature parts */ - public static function parse_signature_header( $header ) { - $parsed_header = array(); - $matches = array(); - $h_string = \implode( ',', (array) $header[0] ); + public static function parse_signature_header( $signature ) { + $parsed_header = array(); + $matches = array(); - if ( \preg_match( '/keyId="(.*?)"/ism', $h_string, $matches ) ) { - $parsed_header['keyId'] = $matches[1]; + if ( \preg_match( '/keyId="(.*?)"/ism', $signature, $matches ) ) { + $parsed_header['keyId'] = trim( $matches[1] ); } - if ( \preg_match( '/created=([0-9]*)/ism', $h_string, $matches ) ) { - $parsed_header['(created)'] = $matches[1]; + if ( \preg_match( '/created=([0-9]*)/ism', $signature, $matches ) ) { + $parsed_header['(created)'] = trim( $matches[1] ); } - if ( \preg_match( '/expires=([0-9]*)/ism', $h_string, $matches ) ) { - $parsed_header['(expires)'] = $matches[1]; + if ( \preg_match( '/expires=([0-9]*)/ism', $signature, $matches ) ) { + $parsed_header['(expires)'] = trim( $matches[1] ); } - if ( \preg_match( '/algorithm="(.*?)"/ism', $h_string, $matches ) ) { - $parsed_header['algorithm'] = $matches[1]; + if ( \preg_match( '/algorithm="(.*?)"/ism', $signature, $matches ) ) { + $parsed_header['algorithm'] = trim( $matches[1] ); } - if ( \preg_match( '/headers="(.*?)"/ism', $h_string, $matches ) ) { - $parsed_header['headers'] = \explode( ' ', $matches[1] ); + if ( \preg_match( '/headers="(.*?)"/ism', $signature, $matches ) ) { + $parsed_header['headers'] = \explode( ' ', trim( $matches[1] ) ); } - if ( \preg_match( '/signature="(.*?)"/ism', $h_string, $matches ) ) { - $parsed_header['signature'] = \base64_decode( preg_replace( '/\s+/', '', $matches[1] ) ); // phpcs:ignore + if ( \preg_match( '/signature="(.*?)"/ism', $signature, $matches ) ) { + $parsed_header['signature'] = \base64_decode( preg_replace( '/\s+/', '', trim( $matches[1] ) ) ); // phpcs:ignore } if ( ( $parsed_header['signature'] ) && ( $parsed_header['algorithm'] ) && ( ! $parsed_header['headers'] ) ) { @@ -312,7 +306,7 @@ class Signature { /** * Gets the header data from the included pseudo headers * - * @param array $signed_headers + * @param array $signed_headers The signed headers. * @param array $signature_block (pseudo-headers) * @param array $headers (http headers) * diff --git a/includes/model/class-application-user.php b/includes/model/class-application-user.php index 2affde5..5537612 100644 --- a/includes/model/class-application-user.php +++ b/includes/model/class-application-user.php @@ -22,6 +22,13 @@ class Application_User extends Blog_User { */ protected $type = 'Application'; + /** + * If the User is discoverable. + * + * @var boolean + */ + protected $discoverable = false; + /** * Get the User-Url. * @@ -35,7 +42,7 @@ class Application_User extends Blog_User { return 'application'; } - public function get_username() { + public function get_preferred_username() { return $this::get_name(); } @@ -79,14 +86,6 @@ class Application_User extends Blog_User { } } - public function get_inbox() { - return null; - } - - public function get_outbox() { - return null; - } - public function get_followers() { return null; } diff --git a/includes/model/class-user.php b/includes/model/class-user.php index f1c657d..0b9127d 100644 --- a/includes/model/class-user.php +++ b/includes/model/class-user.php @@ -36,13 +36,6 @@ class User extends Actor { */ protected $featured; - /** - * The Webfinger-style identifier. - * - * @var string - */ - protected $resource; - /** * The User-Type * @@ -50,6 +43,20 @@ class User extends Actor { */ protected $type = 'Person'; + /** + * If the User is discoverable. + * + * @var boolean + */ + protected $discoverable = true; + + /** + * The WebFinger Resource. + * + * @var string + */ + protected $resource; + public static function from_wp_user( $user_id ) { if ( is_user_disabled( $user_id ) ) { return new WP_Error( diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 5cbc95c..dc166fb 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -171,6 +171,12 @@ class Inbox { } foreach ( $users as $user ) { + $user = User_Collection::get_by_various( $user ); + + if ( is_wp_error( $user ) ) { + continue; + } + $type = \strtolower( $type ); \do_action( 'activitypub_inbox', $data, $user->ID, $type ); diff --git a/includes/rest/class-server.php b/includes/rest/class-server.php index 325b9ad..01d9a9a 100644 --- a/includes/rest/class-server.php +++ b/includes/rest/class-server.php @@ -46,6 +46,11 @@ class Server { */ public static function application_actor() { $user = new Application_User(); + + $user->set_context( + \Activitypub\Activity\Activity::CONTEXT + ); + $json = $user->to_array(); $response = new WP_REST_Response( $json, 200 ); @@ -80,12 +85,12 @@ class Server { } // POST-Requets are always signed - if ( 'POST' === $request->get_method() ) { + if ( 'post' === \strtolower( $request->get_method() ) ) { $verified_request = Signature::verify_http_signature( $request ); if ( \is_wp_error( $verified_request ) ) { return $verified_request; } - } elseif ( 'GET' === $request->get_method() ) { // GET-Requests are only signed in secure mode + } elseif ( 'get' === \strtolower( $request->get_method() ) ) { // GET-Requests are only signed in secure mode if ( ACTIVITYPUB_AUTHORIZED_FETCH ) { $verified_request = Signature::verify_http_signature( $request ); if ( \is_wp_error( $verified_request ) ) { diff --git a/tests/test-class-activitypub-rest-post-signature-verification.php b/tests/test-class-activitypub-rest-post-signature-verification.php index 97f78f9..fdca732 100644 --- a/tests/test-class-activitypub-rest-post-signature-verification.php +++ b/tests/test-class-activitypub-rest-post-signature-verification.php @@ -39,7 +39,7 @@ class Test_Activitypub_Signature_Verification extends WP_UnitTestCase { // Start verification // parse_signature_header, get_signed_data, get_public_key - $signature_block = Activitypub\Signature::parse_signature_header( $headers['signature'] ); + $signature_block = Activitypub\Signature::parse_signature_header( $headers['signature'][0] ); $signed_headers = $signature_block['headers']; $signed_data = Activitypub\Signature::get_signed_data( $signed_headers, $signature_block, $headers );