Skip to content

Commit dfc23ad

Browse files
authored
Webfinger: Use Rest Controller structure (#1146)
* Rename file to include controller * Update webfinger endpoint * Remove blank line * Fix tests
1 parent bca6fd7 commit dfc23ad

File tree

6 files changed

+452
-117
lines changed

6 files changed

+452
-117
lines changed

activitypub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ function rest_init() {
4444
Rest\Inbox::init();
4545
Rest\Followers::init();
4646
Rest\Following::init();
47-
Rest\Webfinger::init();
4847
Rest\Comment::init();
4948
Rest\Server::init();
5049
Rest\Collection::init();
5150
Rest\Interaction::init();
5251
Rest\Post::init();
52+
( new Rest\Webfinger_Controller() )->register_routes();
5353

5454
// Load NodeInfo endpoints only if blog is public.
5555
if ( is_blog_public() ) {
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
<?php
2+
/**
3+
* WebFinger REST-Class file.
4+
*
5+
* @package Activitypub
6+
*/
7+
8+
namespace Activitypub\Rest;
9+
10+
/**
11+
* ActivityPub WebFinger REST-Class.
12+
*
13+
* @author Matthias Pfefferle
14+
*
15+
* @see https://webfinger.net/
16+
*/
17+
class Webfinger_Controller extends \WP_REST_Controller {
18+
/**
19+
* The namespace of this controller's route.
20+
*
21+
* @var string
22+
*/
23+
protected $namespace = ACTIVITYPUB_REST_NAMESPACE;
24+
25+
/**
26+
* The base of this controller's route.
27+
*
28+
* @var string
29+
*/
30+
protected $rest_base = 'webfinger';
31+
32+
/**
33+
* Register routes.
34+
*/
35+
public function register_routes() {
36+
\register_rest_route(
37+
$this->namespace,
38+
'/' . $this->rest_base,
39+
array(
40+
array(
41+
'methods' => \WP_REST_Server::READABLE,
42+
'callback' => array( $this, 'get_item' ),
43+
'permission_callback' => '__return_true',
44+
'args' => array(
45+
'resource' => array(
46+
'description' => 'The WebFinger resource.',
47+
'type' => 'string',
48+
'required' => true,
49+
'pattern' => '^(acct:)|^(https?://)(.+)$',
50+
),
51+
),
52+
),
53+
'schema' => array( $this, 'get_item_schema' ),
54+
)
55+
);
56+
}
57+
58+
/**
59+
* Retrieves the WebFinger profile.
60+
*
61+
* @param \WP_REST_Request $request The request object.
62+
*
63+
* @return \WP_REST_Response Response object.
64+
*/
65+
public function get_item( $request ) {
66+
/**
67+
* Action triggered prior to the ActivityPub profile being created and sent to the client.
68+
*/
69+
\do_action( 'activitypub_rest_webfinger_pre' );
70+
71+
$resource = $request->get_param( 'resource' );
72+
$response = $this->get_profile( $resource );
73+
$code = 200;
74+
75+
if ( \is_wp_error( $response ) ) {
76+
$code = 400;
77+
$error_data = $response->get_error_data();
78+
79+
if ( isset( $error_data['status'] ) ) {
80+
$code = $error_data['status'];
81+
}
82+
}
83+
84+
return new \WP_REST_Response(
85+
$response,
86+
$code,
87+
array(
88+
'Access-Control-Allow-Origin' => '*',
89+
'Content-Type' => 'application/jrd+json; charset=' . \get_option( 'blog_charset' ),
90+
)
91+
);
92+
}
93+
94+
/**
95+
* Get the WebFinger profile.
96+
*
97+
* @param string $webfinger The WebFinger resource.
98+
*
99+
* @return array|\WP_Error The WebFinger profile or WP_Error if not found.
100+
*/
101+
public function get_profile( $webfinger ) {
102+
/**
103+
* Filter the WebFinger data.
104+
*
105+
* @param array $data The WebFinger data.
106+
* @param string $webfinger The WebFinger resource.
107+
*/
108+
return \apply_filters( 'webfinger_data', array(), $webfinger );
109+
}
110+
111+
/**
112+
* Retrieves the schema for the WebFinger endpoint.
113+
*
114+
* @return array Schema data.
115+
*/
116+
public function get_item_schema() {
117+
if ( $this->schema ) {
118+
return $this->add_additional_fields_schema( $this->schema );
119+
}
120+
121+
$this->schema = array(
122+
'$schema' => 'http://json-schema.org/draft-04/schema#',
123+
'title' => 'webfinger',
124+
'type' => 'object',
125+
'required' => array( 'subject', 'links' ),
126+
'properties' => array(
127+
'subject' => array(
128+
'description' => 'The subject of this WebFinger record.',
129+
'type' => 'string',
130+
'format' => 'uri',
131+
),
132+
'aliases' => array(
133+
'description' => 'Alternative identifiers for the subject.',
134+
'type' => 'array',
135+
'items' => array(
136+
'type' => 'string',
137+
'format' => 'uri',
138+
),
139+
),
140+
'links' => array(
141+
'description' => 'Links associated with the subject.',
142+
'type' => 'array',
143+
'items' => array(
144+
'type' => 'object',
145+
'properties' => array(
146+
'rel' => array(
147+
'description' => 'The relation type of the link.',
148+
'type' => 'string',
149+
'required' => true,
150+
),
151+
'type' => array(
152+
'description' => 'The content type of the link.',
153+
'type' => 'string',
154+
),
155+
'href' => array(
156+
'description' => 'The target URL of the link.',
157+
'type' => 'string',
158+
'format' => 'uri',
159+
),
160+
'template' => array(
161+
'description' => 'A URI template for the link.',
162+
'type' => 'string',
163+
'format' => 'uri',
164+
),
165+
),
166+
),
167+
),
168+
),
169+
);
170+
171+
return $this->add_additional_fields_schema( $this->schema );
172+
}
173+
}

includes/rest/class-webfinger.php

Lines changed: 0 additions & 116 deletions
This file was deleted.

tests/bootstrap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,6 @@ function http_disable_request( $response, $args, $url ) {
9999
// Start up the WP testing environment.
100100
require $_tests_dir . '/includes/bootstrap.php';
101101
require __DIR__ . '/class-activitypub-testcase-cache-http.php';
102+
require __DIR__ . '/class-test-rest-controller-testcase.php';
102103

103104
\Activitypub\Migration::add_default_settings();
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
/**
3+
* REST Controller Testcase file.
4+
*
5+
* @package Activitypub
6+
*/
7+
8+
namespace Activitypub\Tests;
9+
10+
/**
11+
* REST Controller Testcase.
12+
*/
13+
abstract class Test_REST_Controller_Testcase extends \WP_Test_REST_TestCase {
14+
15+
/**
16+
* The REST server.
17+
*
18+
* @var \WP_REST_Server
19+
*/
20+
protected $server;
21+
22+
/**
23+
* Set up the test.
24+
*/
25+
public function set_up() {
26+
parent::set_up();
27+
add_filter( 'rest_url', array( $this, 'filter_rest_url_for_leading_slash' ), 10, 2 );
28+
29+
global $wp_rest_server;
30+
$wp_rest_server = new \Spy_REST_Server();
31+
do_action( 'rest_api_init', $wp_rest_server );
32+
}
33+
34+
/**
35+
* Tear down the test.
36+
*/
37+
public function tear_down() {
38+
remove_filter( 'rest_url', array( $this, 'test_rest_url_for_leading_slash' ) );
39+
40+
global $wp_rest_server;
41+
$wp_rest_server = null;
42+
43+
parent::tear_down();
44+
}
45+
46+
/**
47+
* Test get_item.
48+
*/
49+
abstract public function test_get_item();
50+
51+
/**
52+
* Test register_routes.
53+
*/
54+
abstract public function test_get_item_schema();
55+
56+
/**
57+
* Filter REST URL for leading slash.
58+
*
59+
* @param string $url URL.
60+
* @param string $path Path.
61+
* @return string
62+
*/
63+
public function filter_rest_url_for_leading_slash( $url, $path ) {
64+
if ( is_multisite() || get_option( 'permalink_structure' ) ) {
65+
return $url;
66+
}
67+
68+
// Make sure path for rest_url has a leading slash for proper resolution.
69+
if ( 0 !== strpos( $path, '/' ) ) {
70+
$this->fail(
71+
sprintf(
72+
'REST API URL "%s" should have a leading slash.',
73+
$path
74+
)
75+
);
76+
}
77+
78+
return $url;
79+
}
80+
}

0 commit comments

Comments
 (0)