Add Service actor for signing get requests
This commit is contained in:
parent
3a0fef27e0
commit
27636b62d5
4 changed files with 80 additions and 29 deletions
|
@ -60,9 +60,9 @@ class Http {
|
||||||
*
|
*
|
||||||
* @return array|WP_Error The GET Response or an WP_ERROR
|
* @return array|WP_Error The GET Response or an WP_ERROR
|
||||||
*/
|
*/
|
||||||
public static function get( $url, $user_id ) {
|
public static function get( $url ) {
|
||||||
$date = \gmdate( 'D, d M Y H:i:s T' );
|
$date = \gmdate( 'D, d M Y H:i:s T' );
|
||||||
$signature = Signature::generate_signature( $user_id, 'get', $url, $date );
|
$signature = Signature::generate_signature( -1, 'get', $url, $date );
|
||||||
|
|
||||||
$wp_version = \get_bloginfo( 'version' );
|
$wp_version = \get_bloginfo( 'version' );
|
||||||
$user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
|
$user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
|
||||||
|
|
|
@ -35,14 +35,15 @@ class Signature {
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public static function get_private_key( $user_id, $force = false ) {
|
public static function get_private_key( $user_id, $force = false ) {
|
||||||
$key = \get_user_meta( $user_id, 'magic_sig_private_key' );
|
if ( $force ) {
|
||||||
|
self::generate_key_pair( $user_id );
|
||||||
if ( $key && ! $force ) {
|
|
||||||
return $key[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self::generate_key_pair( $user_id );
|
if ( -1 === $user_id ) {
|
||||||
$key = \get_user_meta( $user_id, 'magic_sig_private_key' );
|
$key = \get_option('activitypub_magic_sig_private_key' );
|
||||||
|
} else {
|
||||||
|
$key = \get_user_meta( $user_id, 'magic_sig_private_key' );
|
||||||
|
}
|
||||||
|
|
||||||
return $key[0];
|
return $key[0];
|
||||||
}
|
}
|
||||||
|
@ -63,14 +64,22 @@ class Signature {
|
||||||
$priv_key = null;
|
$priv_key = null;
|
||||||
|
|
||||||
\openssl_pkey_export( $key, $priv_key );
|
\openssl_pkey_export( $key, $priv_key );
|
||||||
|
|
||||||
// private key
|
|
||||||
\update_user_meta( $user_id, 'magic_sig_private_key', $priv_key );
|
|
||||||
|
|
||||||
$detail = \openssl_pkey_get_details( $key );
|
$detail = \openssl_pkey_get_details( $key );
|
||||||
|
|
||||||
// public key
|
if ( -1 === $user_id ) {
|
||||||
\update_user_meta( $user_id, 'magic_sig_public_key', $detail['key'] );
|
// private key
|
||||||
|
\add_option('activitypub_magic_sig_private_key', $priv_key );
|
||||||
|
|
||||||
|
// public key
|
||||||
|
\add_option('activitypub_magic_sig_public_key', $detail['key'] );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// private key
|
||||||
|
\update_user_meta( $user_id, 'magic_sig_private_key', $priv_key );
|
||||||
|
|
||||||
|
// public key
|
||||||
|
\update_user_meta( $user_id, 'magic_sig_public_key', $detail['key'] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function generate_signature( $user_id, $http_method, $url, $date, $digest = null ) {
|
public static function generate_signature( $user_id, $http_method, $url, $date, $digest = null ) {
|
||||||
|
@ -101,7 +110,11 @@ class Signature {
|
||||||
\openssl_sign( $signed_string, $signature, $key, \OPENSSL_ALGO_SHA256 );
|
\openssl_sign( $signed_string, $signature, $key, \OPENSSL_ALGO_SHA256 );
|
||||||
$signature = \base64_encode( $signature ); // phpcs:ignore
|
$signature = \base64_encode( $signature ); // phpcs:ignore
|
||||||
|
|
||||||
$key_id = \get_author_posts_url( $user_id ) . '#main-key';
|
if ( -1 === $user_id ) {
|
||||||
|
$key_id = \get_rest_url( null, 'activitypub/1.0/service#main-key' );
|
||||||
|
} else {
|
||||||
|
$key_id = \get_author_posts_url( $user_id ) . '#main-key';
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! empty( $digest ) ) {
|
if ( ! empty( $digest ) ) {
|
||||||
return \sprintf( 'keyId="%s",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="%s"', $key_id, $signature );
|
return \sprintf( 'keyId="%s",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="%s"', $key_id, $signature );
|
||||||
|
|
|
@ -36,8 +36,8 @@ function safe_remote_post( $url, $body, $user_id ) {
|
||||||
return \Activitypub\Http::post( $url, $body, $user_id );
|
return \Activitypub\Http::post( $url, $body, $user_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
function safe_remote_get( $url, $user_id ) {
|
function safe_remote_get( $url ) {
|
||||||
return \Activitypub\Http::get( $url, $user_id );
|
return \Activitypub\Http::get( $url );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,21 +88,11 @@ function get_remote_metadata_by_actor( $actor ) {
|
||||||
return $metadata;
|
return $metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = \get_users(
|
|
||||||
array(
|
|
||||||
'number' => 1,
|
|
||||||
'capability__in' => array( 'publish_posts' ),
|
|
||||||
'fields' => 'ID',
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// we just need any user to generate a request signature
|
|
||||||
$user_id = \reset( $user );
|
|
||||||
$short_timeout = function() {
|
$short_timeout = function() {
|
||||||
return 3;
|
return 3;
|
||||||
};
|
};
|
||||||
add_filter( 'activitypub_remote_get_timeout', $short_timeout );
|
add_filter( 'activitypub_remote_get_timeout', $short_timeout );
|
||||||
$response = Http::get( $actor, $user_id );
|
$response = Http::get( $actor );
|
||||||
remove_filter( 'activitypub_remote_get_timeout', $short_timeout );
|
remove_filter( 'activitypub_remote_get_timeout', $short_timeout );
|
||||||
if ( \is_wp_error( $response ) ) {
|
if ( \is_wp_error( $response ) ) {
|
||||||
\set_transient( $transient_key, $response, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
|
\set_transient( $transient_key, $response, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Activitypub\Rest;
|
namespace Activitypub\Rest;
|
||||||
|
|
||||||
|
use WP_REST_Response;
|
||||||
use Activitypub\Signature;
|
use Activitypub\Signature;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +17,54 @@ class Server {
|
||||||
* Initialize the class, registering WordPress hooks
|
* Initialize the class, registering WordPress hooks
|
||||||
*/
|
*/
|
||||||
public static function init() {
|
public static function init() {
|
||||||
\add_filter( 'rest_request_before_callbacks', array( '\Activitypub\Rest\Server', 'authorize_activitypub_requests' ), 10, 3 );
|
\add_filter( 'rest_request_before_callbacks', array( self::class, 'authorize_activitypub_requests' ), 10, 3 );
|
||||||
|
\add_action( 'rest_api_init', array( self::class, 'register_routes' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register routes
|
||||||
|
*/
|
||||||
|
public static function register_routes() {
|
||||||
|
\register_rest_route(
|
||||||
|
'activitypub/1.0',
|
||||||
|
'/service',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => \WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( self::class, 'service_actor' ),
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render Service actor profile
|
||||||
|
*
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public static function service_actor() {
|
||||||
|
$json = new \stdClass();
|
||||||
|
|
||||||
|
$json->{'@context'} = \Activitypub\get_context();
|
||||||
|
$json->id = \get_rest_url( null, 'activitypub/1.0/service' );
|
||||||
|
$json->type = 'Application';
|
||||||
|
$json->preferredUsername = parse_url( get_site_url(), PHP_URL_HOST );
|
||||||
|
$json->name = get_bloginfo( 'name' );
|
||||||
|
$json->summary = "ActivityPub service actor";
|
||||||
|
$json->manuallyApprovesFollowers = TRUE;
|
||||||
|
$json->icon = [ get_site_icon_url() ];
|
||||||
|
$json->publicKey = (object) array(
|
||||||
|
'id' => \get_rest_url( null, 'activitypub/1.0/service#main-key' ),
|
||||||
|
'owner' => \get_rest_url( null, 'activitypub/1.0/service' ),
|
||||||
|
'publicKeyPem' => Signature::get_public_key( -1 ),
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = new WP_REST_Response( $json, 200 );
|
||||||
|
|
||||||
|
$response->header( 'Content-Type', 'application/activity+json' );
|
||||||
|
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue