77 * @author Matthias Pfefferle
88 */
99class Health_Check {
10+
11+ /**
12+ * Initialize health checks
13+ *
14+ * @return void
15+ */
1016 public static function init () {
1117 \add_filter ( 'site_status_tests ' , array ( '\Activitypub\Health_Check ' , 'add_tests ' ) );
1218 }
1319
1420 public static function add_tests ( $ tests ) {
15- $ tests ['direct ' ]['activitypub_test_profile_url ' ] = array (
16- 'label ' => \__ ( 'Profile URL test ' , 'activitypub ' ),
17- 'test ' => array ( '\Activitypub\Health_Check ' , 'test_profile_url ' ),
21+ $ tests ['direct ' ]['activitypub_test_author_url ' ] = array (
22+ 'label ' => \__ ( 'Author URL test ' , 'activitypub ' ),
23+ 'test ' => array ( '\Activitypub\Health_Check ' , 'test_author_url ' ),
1824 );
1925
20- // $tests['direct']['activitypub_test_profile_url2 '] = array(
21- // 'label' => __( 'Profile URL Test', 'activitypub' ),
22- // 'test' => array( '\Activitypub\Health_Check', 'test_profile_url ' ),
23- // );
26+ $ tests ['direct ' ]['activitypub_test_webfinger ' ] = array (
27+ 'label ' => __ ( 'WebFinger Test ' , 'activitypub ' ),
28+ 'test ' => array ( '\Activitypub\Health_Check ' , 'test_webfinger ' ),
29+ );
2430
2531 return $ tests ;
2632 }
2733
28- public static function test_profile_url () {
34+ /**
35+ * Author URL tests
36+ *
37+ * @return void
38+ */
39+ public static function test_author_url () {
2940 $ result = array (
3041 'label ' => \__ ( 'Author URL accessible ' , 'activitypub ' ),
3142 'status ' => 'good ' ,
@@ -38,45 +49,208 @@ public static function test_profile_url() {
3849 \__ ( 'Your author URL is accessible and supports the required "Accept" header. ' , 'activitypub ' )
3950 ),
4051 'actions ' => '' ,
41- 'test ' => 'test_profile_url ' ,
52+ 'test ' => 'test_author_url ' ,
53+ );
54+
55+ $ check = self ::is_author_url_accessible ();
56+
57+ if ( true === $ check ) {
58+ return $ result ;
59+ }
60+
61+ $ result ['status ' ] = 'critical ' ;
62+ $ result ['label ' ] = \__ ( 'Author URL is not accessible ' , 'activitypub ' );
63+ $ result ['badge ' ]['color ' ] = 'red ' ;
64+ $ result ['description ' ] = \sprintf (
65+ '<p>%s</p> ' ,
66+ $ check ->get_error_message ()
4267 );
4368
44- $ enum = self ::is_profile_url_accessible ();
69+ return $ result ;
70+ }
4571
46- if ( true !== $ enum ) {
47- $ result ['status ' ] = 'critical ' ;
48- $ result ['label ' ] = \__ ( 'Profile URL is not accessible ' , 'activitypub ' );
49- $ result ['description ' ] = \sprintf (
72+ /**
73+ * WebFinger tests
74+ *
75+ * @return void
76+ */
77+ public static function test_webfinger () {
78+ $ result = array (
79+ 'label ' => \__ ( 'WebFinger endpoint ' , 'activitypub ' ),
80+ 'status ' => 'good ' ,
81+ 'badge ' => array (
82+ 'label ' => \__ ( 'ActivityPub ' , 'activitypub ' ),
83+ 'color ' => 'green ' ,
84+ ),
85+ 'description ' => \sprintf (
5086 '<p>%s</p> ' ,
51- \__ ( 'Your author URL is not accessible and/or does not return valid JSON. Please check if the author URL is accessible and does not redirect to another page (often done by SEO plugins). ' , 'activitypub ' )
52- );
87+ \__ ( 'Your WebFinger endpoint is accessible and returns the correct informations. ' , 'activitypub ' )
88+ ),
89+ 'actions ' => '' ,
90+ 'test ' => 'test_webfinger ' ,
91+ );
92+
93+ $ check = self ::is_webfinger_endpoint_accessible ();
94+
95+ if ( true === $ check ) {
96+ return $ result ;
5397 }
5498
99+ $ result ['status ' ] = 'critical ' ;
100+ $ result ['label ' ] = \__ ( 'WebFinger endpoint is not accessible ' , 'activitypub ' );
101+ $ result ['badge ' ]['color ' ] = 'red ' ;
102+ $ result ['description ' ] = \sprintf (
103+ '<p>%s</p> ' ,
104+ $ check ->get_error_message ()
105+ );
106+
55107 return $ result ;
56108 }
57109
58- public static function is_profile_url_accessible () {
110+ /**
111+ * Check if `author_posts_url` is accessible and that requerst returns correct JSON
112+ *
113+ * @return boolean|WP_Error
114+ */
115+ public static function is_author_url_accessible () {
59116 $ user = \wp_get_current_user ();
60117 $ author_url = \get_author_posts_url ( $ user ->ID );
61118 $ reference_author_url = self ::get_author_posts_url ( $ user ->ID , $ user ->user_nicename );
62119
63120 // check for "author" in URL
64121 if ( $ author_url !== $ reference_author_url ) {
65- return false ;
122+ return new \WP_Error (
123+ 'author_url_not_accessible ' ,
124+ \sprintf (
125+ // translators: %s: Author URL
126+ \__ (
127+ '<p>Your author URL <code>%s</code> was replaced, this is often done by plugins.</p> ' ,
128+ 'activitypub '
129+ ),
130+ $ author_url
131+ )
132+ );
66133 }
67134
68135 // try to access author URL
69- $ response = \wp_remote_get ( $ author_url , array ( 'headers ' => array ( 'Accept ' => 'application/activity+json ' ) ) );
136+ $ response = \wp_remote_get (
137+ $ author_url ,
138+ array (
139+ 'headers ' => array ( 'Accept ' => 'application/activity+json ' ),
140+ 'redirection ' => 0 ,
141+ )
142+ );
70143
71144 if ( \is_wp_error ( $ response ) ) {
72- return false ;
145+ return new \WP_Error (
146+ 'author_url_not_accessible ' ,
147+ \sprintf (
148+ // translators: %s: Author URL
149+ \__ (
150+ '<p>Your author URL <code>%s</code> is not accessible. Please check your WordPress setup or permalink structure. If the setup seems fine, maybe check if a plugin might restrict the access.</p> ' ,
151+ 'activitypub '
152+ ),
153+ $ author_url
154+ )
155+ );
156+ }
157+
158+ $ response_code = \wp_remote_retrieve_response_code ( $ response );
159+
160+ // check for redirects
161+ if ( \in_array ( $ response_code , array ( 301 , 302 , 307 , 308 ), true ) ) {
162+ return new \WP_Error (
163+ 'author_url_not_accessible ' ,
164+ \sprintf (
165+ // translators: %s: Author URL
166+ \__ (
167+ '<p>Your author URL <code>%s</code> is redirecting to another page, this is often done by SEO plugins like "Yoast SEO".</p> ' ,
168+ 'activitypub '
169+ ),
170+ $ author_url
171+ )
172+ );
73173 }
74174
75175 // check if response is JSON
76176 $ body = \wp_remote_retrieve_body ( $ response );
77177
78178 if ( ! \is_string ( $ body ) || ! \is_array ( \json_decode ( $ body , true ) ) ) {
79- return false ;
179+ return new \WP_Error (
180+ 'author_url_not_accessible ' ,
181+ \sprintf (
182+ // translators: %s: Author URL
183+ \__ (
184+ '<p>Your author URL <code>%s</code> does not return valid JSON for <code>application/activity+json</code>. Please check if your hosting supports alternate <code>Accept</code> headers.</p> ' ,
185+ 'activitypub '
186+ ),
187+ $ author_url
188+ )
189+ );
190+ }
191+
192+ return true ;
193+ }
194+
195+ /**
196+ * Check if WebFinger endoint is accessible and profile requerst returns correct JSON
197+ *
198+ * @return boolean|WP_Error
199+ */
200+ public static function is_webfinger_endpoint_accessible () {
201+ $ user = \wp_get_current_user ();
202+ $ webfinger = \Activitypub \get_webfinger_resource ( $ user ->ID );
203+
204+ $ url = \wp_parse_url ( \home_url (), \PHP_URL_SCHEME ) . ':// ' . \wp_parse_url ( \home_url (), \PHP_URL_HOST );
205+
206+ if ( \wp_parse_url ( \home_url (), \PHP_URL_PORT ) ) {
207+ $ url .= ': ' . \wp_parse_url ( \home_url (), \PHP_URL_PORT );
208+ }
209+
210+ $ url = \trailingslashit ( $ url ) . '.well-known/webfinger ' ;
211+
212+ $ url = \add_query_arg ( 'resource ' , 'acct: ' . $ webfinger , $ url );
213+
214+ // try to access author URL
215+ $ response = \wp_remote_get (
216+ $ url ,
217+ array (
218+ 'headers ' => array ( 'Accept ' => 'application/activity+json ' ),
219+ 'redirection ' => 0 ,
220+ )
221+ );
222+
223+ if ( \is_wp_error ( $ response ) ) {
224+ return new \WP_Error (
225+ 'webfinger_url_not_accessible ' ,
226+ \sprintf (
227+ // translators: %s: Author URL
228+ \__ (
229+ '<p>Your WebFinger endpoint <code>%s</code> is not accessible. Please check your WordPress setup or permalink structure.</p> ' ,
230+ 'activitypub '
231+ ),
232+ $ url
233+ )
234+ );
235+ }
236+
237+ $ response_code = \wp_remote_retrieve_response_code ( $ response );
238+
239+ // check if response is JSON
240+ $ body = \wp_remote_retrieve_body ( $ response );
241+
242+ if ( ! \is_string ( $ body ) || ! \is_array ( \json_decode ( $ body , true ) ) ) {
243+ return new \WP_Error (
244+ 'webfinger_url_not_accessible ' ,
245+ \sprintf (
246+ // translators: %s: Author URL
247+ \__ (
248+ '<p>Your WebFinger endpoint <code>%s</code> does not return valid JSON for <code>application/jrd+json</code>.</p> ' ,
249+ 'activitypub '
250+ ),
251+ $ url
252+ )
253+ );
80254 }
81255
82256 return true ;
0 commit comments