PluginProbe
ActivityPub / 3.2.4
ActivityPub v3.2.4
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
activitypub / includes / rest / class-server.php

class-server.php in ActivityPub 3.2.4, at includes/rest/class-server.php

180 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Activitypub\Rest;
3
4 use stdClass;
5 use WP_Error;
6 use WP_REST_Response;
7 use Activitypub\Signature;
8 use Activitypub\Model\Application;
9
10 /**
11 * ActivityPub Server REST-Class
12 *
13 * @author Django Doucet
14 *
15 * @see https://www.w3.org/TR/activitypub/#security-verification
16 */
17 class Server {
18 /**
19 * Initialize the class, registering WordPress hooks
20 */
21 public static function init() {
22 self::register_routes();
23
24 \add_filter( 'rest_request_before_callbacks', array( self::class, 'validate_activitypub_requests' ), 9, 3 );
25 \add_filter( 'rest_request_before_callbacks', array( self::class, 'authorize_activitypub_requests' ), 10, 3 );
26 }
27
28 /**
29 * Register routes
30 */
31 public static function register_routes() {
32 \register_rest_route(
33 ACTIVITYPUB_REST_NAMESPACE,
34 '/application',
35 array(
36 array(
37 'methods' => \WP_REST_Server::READABLE,
38 'callback' => array( self::class, 'application_actor' ),
39 'permission_callback' => '__return_true',
40 ),
41 )
42 );
43 }
44
45 /**
46 * Render Application actor profile
47 *
48 * @return WP_REST_Response The JSON profile of the Application Actor.
49 */
50 public static function application_actor() {
51 $user = new Application();
52
53 $json = $user->to_array();
54
55 $rest_response = new WP_REST_Response( $json, 200 );
56 $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
57
58 return $rest_response;
59 }
60
61 /**
62 * Callback function to authorize each api requests
63 *
64 * @see WP_REST_Request
65 *
66 * @see https://www.w3.org/wiki/SocialCG/ActivityPub/Primer/Authentication_Authorization#Authorized_fetch
67 * @see https://swicg.github.io/activitypub-http-signature/#authorized-fetch
68 *
69 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
70 * Usually a WP_REST_Response or WP_Error.
71 * @param array $handler Route handler used for the request.
72 * @param WP_REST_Request $request Request used to generate the response.
73 *
74 * @return mixed|WP_Error The response, error, or modified response.
75 */
76 public static function authorize_activitypub_requests( $response, $handler, $request ) {
77 if ( 'HEAD' === $request->get_method() ) {
78 return $response;
79 }
80
81 if ( \is_wp_error( $response ) ) {
82 return $response;
83 }
84
85 $route = $request->get_route();
86
87 // check if it is an activitypub request and exclude webfinger and nodeinfo endpoints
88 if (
89 ! \str_starts_with( $route, '/' . ACTIVITYPUB_REST_NAMESPACE ) ||
90 \str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'webfinger' ) ||
91 \str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'nodeinfo' ) ||
92 \str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'application' )
93 ) {
94 return $response;
95 }
96
97 /**
98 * Filter to defer signature verification
99 *
100 * Skip signature verification for debugging purposes or to reduce load for
101 * certain Activity-Types, like "Delete".
102 *
103 * @param bool $defer Whether to defer signature verification.
104 * @param WP_REST_Request $request The request used to generate the response.
105 *
106 * @return bool Whether to defer signature verification.
107 */
108 $defer = \apply_filters( 'activitypub_defer_signature_verification', false, $request );
109
110 if ( $defer ) {
111 return $response;
112 }
113
114 if (
115 // POST-Requests are always signed
116 'GET' !== $request->get_method() ||
117 // GET-Requests only require a signature in secure mode
118 ( 'GET' === $request->get_method() && ACTIVITYPUB_AUTHORIZED_FETCH )
119 ) {
120 $verified_request = Signature::verify_http_signature( $request );
121 if ( \is_wp_error( $verified_request ) ) {
122 return new WP_Error(
123 'activitypub_signature_verification',
124 $verified_request->get_error_message(),
125 array( 'status' => 401 )
126 );
127 }
128 }
129
130 return $response;
131 }
132
133 /**
134 * Callback function to validate incoming ActivityPub requests
135 *
136 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
137 * Usually a WP_REST_Response or WP_Error.
138 * @param array $handler Route handler used for the request.
139 * @param WP_REST_Request $request Request used to generate the response.
140 *
141 * @return mixed|WP_Error The response, error, or modified response.
142 */
143 public static function validate_activitypub_requests( $response, $handler, $request ) {
144 if ( 'HEAD' === $request->get_method() ) {
145 return $response;
146 }
147
148 $route = $request->get_route();
149
150 if (
151 \is_wp_error( $response ) ||
152 ! \str_starts_with( $route, '/' . ACTIVITYPUB_REST_NAMESPACE )
153 ) {
154 return $response;
155 }
156
157 $params = $request->get_json_params();
158
159 // Type is required for ActivityPub requests, so it fail later in the process
160 if ( ! isset( $params['type'] ) ) {
161 return $response;
162 }
163
164 if (
165 ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS &&
166 in_array( $params['type'], array( 'Create', 'Like', 'Announce' ), true )
167 ) {
168 return new WP_Error(
169 'activitypub_server_does_not_accept_incoming_interactions',
170 \__( 'This server does not accept incoming interactions.', 'activitypub' ),
171 // We have to use a 2XX status code here, because otherwise the response will be
172 // treated as an error and Mastodon might block this WordPress instance.
173 array( 'status' => 202 )
174 );
175 }
176
177 return $response;
178 }
179 }
180