Skip to content

Commit

Permalink
Update webfinger endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
obenland committed Jan 10, 2025
1 parent 2d25a19 commit 641b75c
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 42 deletions.
2 changes: 1 addition & 1 deletion activitypub.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ function rest_init() {
Rest\Inbox::init();
Rest\Followers::init();
Rest\Following::init();
Rest\Webfinger::init();
Rest\Comment::init();
Rest\Server::init();
Rest\Collection::init();
Rest\Interaction::init();
Rest\Post::init();
( new Rest\Webfinger_Controller() )->register_routes();

// Load NodeInfo endpoints only if blog is public.
if ( is_blog_public() ) {
Expand Down
139 changes: 98 additions & 41 deletions includes/rest/class-webfinger-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,70 @@

namespace Activitypub\Rest;

use WP_REST_Response;

/**
* ActivityPub WebFinger REST-Class.
*
* @author Matthias Pfefferle
*
* @see https://webfinger.net/
*/
class Webfinger {
class Webfinger_Controller extends \WP_REST_Controller {
/**
* Initialize the class, registering WordPress hooks.
* The namespace of this controller's route.
*
* @var string
*/
public static function init() {
self::register_routes();
}
protected $namespace = ACTIVITYPUB_REST_NAMESPACE;

/**
* The base of this controller's route.
*
* @var string
*/
protected $rest_base = 'webfinger';

/**
* Register routes.
*/
public static function register_routes() {
public function register_routes() {
\register_rest_route(
ACTIVITYPUB_REST_NAMESPACE,
'/webfinger',
$this->namespace,
'/' . $this->rest_base,
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array( self::class, 'webfinger' ),
'args' => self::request_parameters(),
'callback' => array( $this, 'get_item' ),
'permission_callback' => '__return_true',
'args' => array(
'resource' => array(
'description' => 'The WebFinger resource.',
'type' => 'string',
'required' => true,
'pattern' => '^(acct:)|^(https?://)(.+)$',
),
),
),
'schema' => array( $this, 'get_item_schema' ),
)
);
}

/**
* WebFinger endpoint.
* Retrieves the WebFinger profile.
*
* @param \WP_REST_Request $request The request object.
*
* @return WP_REST_Response The response object.
* @return \WP_REST_Response Response object.
*/
public static function webfinger( $request ) {
public function get_item( $request ) {
/**
* Action triggered prior to the ActivityPub profile being created and sent to the client.
*/
\do_action( 'activitypub_rest_webfinger_pre' );

$code = 200;

$resource = $request->get_param( 'resource' );
$response = self::get_profile( $resource );
$response = $this->get_profile( $resource );
$code = 200;

if ( \is_wp_error( $response ) ) {
$code = 400;
Expand All @@ -69,48 +81,93 @@ public static function webfinger( $request ) {
}
}

return new WP_REST_Response(
return new \WP_REST_Response(
$response,
$code,
array(
'Access-Control-Allow-Origin' => '*',
'Content-Type' => 'application/jrd+json; charset=' . get_option( 'blog_charset' ),
'Content-Type' => 'application/jrd+json; charset=' . \get_option( 'blog_charset' ),
)
);
}

/**
* The supported parameters.
*
* @return array list of parameters
*/
public static function request_parameters() {
$params = array();

$params['resource'] = array(
'required' => true,
'type' => 'string',
'pattern' => '^(acct:)|^(https?://)(.+)$',
'sanitize_callback' => 'sanitize_text_field',
);

return $params;
}

/**
* Get the WebFinger profile.
*
* @param string $webfinger the WebFinger resource.
* @param string $webfinger The WebFinger resource.
*
* @return array|\WP_Error The WebFinger profile or WP_Error if not found.
*/
public static function get_profile( $webfinger ) {
public function get_profile( $webfinger ) {
/**
* Filter the WebFinger data.
*
* @param array $data The WebFinger data.
* @param string $webfinger The WebFinger resource.
*/
return apply_filters( 'webfinger_data', array(), $webfinger );
return \apply_filters( 'webfinger_data', array(), $webfinger );
}

/**
* Retrieves the schema for the WebFinger endpoint.
*
* @return array Schema data.
*/
public function get_item_schema() {
if ( $this->schema ) {
return $this->add_additional_fields_schema( $this->schema );
}

$this->schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'webfinger',
'type' => 'object',
'required' => array( 'subject', 'links' ),
'properties' => array(
'subject' => array(
'description' => 'The subject of this WebFinger record.',
'type' => 'string',
'format' => 'uri',
),
'aliases' => array(
'description' => 'Alternative identifiers for the subject.',
'type' => 'array',
'items' => array(
'type' => 'string',
'format' => 'uri',
),
),
'links' => array(
'description' => 'Links associated with the subject.',
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'rel' => array(
'description' => 'The relation type of the link.',
'type' => 'string',
'required' => true,
),
'type' => array(
'description' => 'The content type of the link.',
'type' => 'string',
),
'href' => array(
'description' => 'The target URL of the link.',
'type' => 'string',
'format' => 'uri',
),
'template' => array(
'description' => 'A URI template for the link.',
'type' => 'string',
'format' => 'uri',
),
),
),
),
),
);

return $this->add_additional_fields_schema( $this->schema );
}
}
1 change: 1 addition & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,6 @@ function http_disable_request( $response, $args, $url ) {
// Start up the WP testing environment.
require $_tests_dir . '/includes/bootstrap.php';
require __DIR__ . '/class-activitypub-testcase-cache-http.php';
require __DIR__ . '/class-test-rest-controller-testcase.php';

\Activitypub\Migration::add_default_settings();
79 changes: 79 additions & 0 deletions tests/class-test-rest-controller-testcase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
/**
* REST Controller Testcase file.
*
* @package Activitypub
*/


/**
* REST Controller Testcase.
*/
abstract class Test_REST_Controller_Testcase extends \WP_Test_REST_TestCase {

/**
* The REST server.
*
* @var \WP_REST_Server
*/
protected $server;

/**
* Set up the test.
*/
public function set_up() {
parent::set_up();
add_filter( 'rest_url', array( $this, 'filter_rest_url_for_leading_slash' ), 10, 2 );

global $wp_rest_server;
$wp_rest_server = new \Spy_REST_Server();
do_action( 'rest_api_init', $wp_rest_server );
}

/**
* Tear down the test.
*/
public function tear_down() {
remove_filter( 'rest_url', array( $this, 'test_rest_url_for_leading_slash' ) );

global $wp_rest_server;
$wp_rest_server = null;

parent::tear_down();
}

/**
* Test get_item.
*/
abstract public function test_get_item();

/**
* Test register_routes.
*/
abstract public function test_get_item_schema();

/**
* Filter REST URL for leading slash.
*
* @param string $url URL.
* @param string $path Path.
* @return string
*/
public function filter_rest_url_for_leading_slash( $url, $path ) {
if ( is_multisite() || get_option( 'permalink_structure' ) ) {
return $url;
}

// Make sure path for rest_url has a leading slash for proper resolution.
if ( 0 !== strpos( $path, '/' ) ) {
$this->fail(
sprintf(
'REST API URL "%s" should have a leading slash.',
$path
)
);
}

return $url;
}
}
Loading

0 comments on commit 641b75c

Please sign in to comment.