Fix some signature and application user issues (#410)

* Fix some signature and application user issues

* it seems that firefish needs at least an inbox also for application users

* prepare domain change

* use https

* fix PHPDoc

* remove image check

---------

Co-authored-by: Matt Wiebe <wiebe@automattic.com>
This commit is contained in:
Matthias Pfefferle 2023-09-01 18:32:56 +02:00 committed by GitHub
parent 26ad8975d7
commit 2705172b77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 47 deletions

View file

@ -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',
),
);

View file

@ -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)
*

View file

@ -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;
}

View file

@ -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<url>
*/
protected $resource;
public static function from_wp_user( $user_id ) {
if ( is_user_disabled( $user_id ) ) {
return new WP_Error(

View file

@ -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 );

View file

@ -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 ) ) {

View file

@ -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 );