PluginProbe
ActivityPub / 3.2.5
ActivityPub v3.2.5
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
← All changes | includes/rest/class-server.php +65 -18 1.3.03.2.5 View file →
@@ -4,9 +4,9 @@
4 4 use stdClass;
5 5 use WP_Error;
6 6 use WP_REST_Response;
7 7 use Activitypub\Signature;
8 -use Activitypub\Model\Application_User;
8 +use Activitypub\Model\Application;
9 9
10 10 /**
11 11 * ActivityPub Server REST-Class
12 12 *
@@ -20,8 +20,9 @@
20 20 */
21 21 public static function init() {
22 22 self::register_routes();
23 23
24 + \add_filter( 'rest_request_before_callbacks', array( self::class, 'validate_activitypub_requests' ), 9, 3 );
24 25 \add_filter( 'rest_request_before_callbacks', array( self::class, 'authorize_activitypub_requests' ), 10, 3 );
25 26 }
26 27
27 28 /**
@@ -46,14 +47,10 @@
46 47 *
47 48 * @return WP_REST_Response The JSON profile of the Application Actor.
48 49 */
49 50 public static function application_actor() {
50 - $user = new Application_User();
51 + $user = new Application();
51 52
52 - $user->set_context(
53 - \Activitypub\Activity\Activity::CONTEXT
54 - );
55 -
56 53 $json = $user->to_array();
57 54
58 55 $rest_response = new WP_REST_Response( $json, 200 );
59 56 $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
@@ -65,8 +62,11 @@
65 62 * Callback function to authorize each api requests
66 63 *
67 64 * @see WP_REST_Request
68 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 69 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
70 70 * Usually a WP_REST_Response or WP_Error.
71 71 * @param array $handler Route handler used for the request.
72 72 * @param WP_REST_Request $request Request used to generate the response.
@@ -77,8 +77,12 @@
77 77 if ( 'HEAD' === $request->get_method() ) {
78 78 return $response;
79 79 }
80 80
81 + if ( \is_wp_error( $response ) ) {
82 + return $response;
83 + }
84 +
81 85 $route = $request->get_route();
82 86
83 87 // check if it is an activitypub request and exclude webfinger and nodeinfo endpoints
84 88 if (
@@ -83,9 +87,10 @@
83 87 // check if it is an activitypub request and exclude webfinger and nodeinfo endpoints
84 88 if (
85 89 ! \str_starts_with( $route, '/' . ACTIVITYPUB_REST_NAMESPACE ) ||
86 90 \str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'webfinger' ) ||
87 - \str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'nodeinfo' )
91 + \str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'nodeinfo' ) ||
92 + \str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'application' )
88 93 ) {
89 94 return $response;
90 95 }
91 96
@@ -105,10 +110,14 @@
105 110 if ( $defer ) {
106 111 return $response;
107 112 }
108 113
109 - // POST-Requets are always signed
110 - if ( 'GET' !== $request->get_method() ) {
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 + ) {
111 120 $verified_request = Signature::verify_http_signature( $request );
112 121 if ( \is_wp_error( $verified_request ) ) {
113 122 return new WP_Error(
114 123 'activitypub_signature_verification',
@@ -115,17 +124,55 @@
115 124 $verified_request->get_error_message(),
116 125 array( 'status' => 401 )
117 126 );
118 127 }
119 - } elseif ( 'GET' === $request->get_method() && ACTIVITYPUB_AUTHORIZED_FETCH ) { // GET-Requests are only signed in secure mode
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 + );
128 175 }
129 176
130 177 return $response;
131 178 }