wordpress-activitypub/includes/class-health-check.php

360 lines
9.6 KiB
PHP
Raw Normal View History

2019-09-27 10:12:59 +02:00
<?php
namespace Activitypub;
use WP_Error;
use Activitypub\Webfinger;
use function Activitypub\get_plugin_version;
use function Activitypub\get_webfinger_resource;
2019-09-27 10:12:59 +02:00
/**
* ActivityPub Health_Check Class
*
* @author Matthias Pfefferle
*/
class Health_Check {
2021-07-23 15:46:28 +02:00
/**
* Initialize health checks
*
* @return void
*/
2019-09-27 10:12:59 +02:00
public static function init() {
2023-04-20 15:22:11 +02:00
\add_filter( 'site_status_tests', array( self::class, 'add_tests' ) );
\add_filter( 'debug_information', array( self::class, 'debug_information' ) );
}
public static function add_tests( $tests ) {
2021-07-23 15:46:28 +02:00
$tests['direct']['activitypub_test_author_url'] = array(
'label' => \__( 'Author URL test', 'activitypub' ),
2023-04-20 15:22:11 +02:00
'test' => array( self::class, 'test_author_url' ),
);
2021-07-23 15:46:28 +02:00
$tests['direct']['activitypub_test_webfinger'] = array(
'label' => __( 'WebFinger Test', 'activitypub' ),
2023-04-20 15:22:11 +02:00
'test' => array( self::class, 'test_webfinger' ),
2021-07-23 15:46:28 +02:00
);
$tests['direct']['activitypub_test_system_cron'] = array(
'label' => __( 'System Cron Test', 'activitypub' ),
'test' => array( self::class, 'test_system_cron' ),
);
return $tests;
}
2021-07-23 15:46:28 +02:00
/**
* Author URL tests
*
2023-03-23 08:35:26 +01:00
* @return array
2021-07-23 15:46:28 +02:00
*/
public static function test_author_url() {
$result = array(
2021-01-13 23:22:17 +01:00
'label' => \__( 'Author URL accessible', 'activitypub' ),
'status' => 'good',
'badge' => array(
'label' => \__( 'ActivityPub', 'activitypub' ),
'color' => 'green',
),
'description' => \sprintf(
'<p>%s</p>',
2021-01-13 23:22:17 +01:00
\__( 'Your author URL is accessible and supports the required "Accept" header.', 'activitypub' )
),
'actions' => '',
2021-07-23 15:46:28 +02:00
'test' => 'test_author_url',
);
$check = self::is_author_url_accessible();
if ( true === $check ) {
return $result;
}
$result['status'] = 'critical';
$result['label'] = \__( 'Author URL is not accessible', 'activitypub' );
$result['badge']['color'] = 'red';
$result['description'] = \sprintf(
'<p>%s</p>',
$check->get_error_message()
);
2021-07-23 15:46:28 +02:00
return $result;
}
/**
* System Cron tests
*
* @return array
*/
public static function test_system_cron() {
$result = array(
'label' => \__( 'System Task Scheduler configured', 'activitypub' ),
'status' => 'good',
'badge' => array(
'label' => \__( 'ActivityPub', 'activitypub' ),
'color' => 'green',
),
'description' => \sprintf(
'<p>%s</p>',
\esc_html__( 'You seem to use the System Task Scheduler to process WP_Cron tasks.', 'activitypub' )
),
'actions' => '',
'test' => 'test_system_cron',
);
if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
return $result;
}
$result['status'] = 'recommended';
$result['label'] = \__( 'System Task Scheduler not configured', 'activitypub' );
$result['badge']['color'] = 'orange';
$result['description'] = \sprintf(
'<p>%s</p>',
2023-09-27 11:20:05 +02:00
\__( 'It is highly recommended to use your Systems Task Scheduler instead of the default <code>WP_Cron</code> setup. For further informations, check the "<a href="https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/" target="_blank">Hooking WP-Cron Into the System Task Scheduler</a>" guide from the Plugin Handbook.', 'activitypub' )
);
return $result;
}
2021-07-23 15:46:28 +02:00
/**
* WebFinger tests
*
2023-03-23 08:35:26 +01:00
* @return array
2021-07-23 15:46:28 +02:00
*/
public static function test_webfinger() {
$result = array(
'label' => \__( 'WebFinger endpoint', 'activitypub' ),
'status' => 'good',
'badge' => array(
'label' => \__( 'ActivityPub', 'activitypub' ),
'color' => 'green',
),
'description' => \sprintf(
'<p>%s</p>',
2023-03-23 08:35:26 +01:00
\__( 'Your WebFinger endpoint is accessible and returns the correct information.', 'activitypub' )
2021-07-23 15:46:28 +02:00
),
'actions' => '',
'test' => 'test_webfinger',
);
$check = self::is_webfinger_endpoint_accessible();
if ( true === $check ) {
return $result;
}
2021-07-23 15:46:28 +02:00
$result['status'] = 'critical';
$result['label'] = \__( 'WebFinger endpoint is not accessible', 'activitypub' );
$result['badge']['color'] = 'red';
$result['description'] = \sprintf(
'<p>%s</p>',
$check->get_error_message()
);
return $result;
}
2021-07-23 15:46:28 +02:00
/**
2023-03-23 08:35:26 +01:00
* Check if `author_posts_url` is accessible and that request returns correct JSON
2021-07-23 15:46:28 +02:00
*
* @return boolean|WP_Error
2021-07-23 15:46:28 +02:00
*/
public static function is_author_url_accessible() {
$user = \wp_get_current_user();
$author_url = \get_author_posts_url( $user->ID );
2021-01-13 23:22:17 +01:00
$reference_author_url = self::get_author_posts_url( $user->ID, $user->user_nicename );
// check for "author" in URL
2021-01-13 23:22:17 +01:00
if ( $author_url !== $reference_author_url ) {
return new WP_Error(
2021-07-23 15:46:28 +02:00
'author_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
2023-07-18 22:02:27 +02:00
'Your author URL <code>%s</code> was replaced, this is often done by plugins.',
2021-07-23 15:46:28 +02:00
'activitypub'
),
$author_url
)
);
}
// try to access author URL
2021-07-23 15:46:28 +02:00
$response = \wp_remote_get(
$author_url,
array(
'headers' => array( 'Accept' => 'application/activity+json' ),
'redirection' => 0,
)
);
if ( \is_wp_error( $response ) ) {
return new WP_Error(
2021-07-23 15:46:28 +02:00
'author_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
2023-07-18 22:02:27 +02:00
'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.',
2021-07-23 15:46:28 +02:00
'activitypub'
),
$author_url
)
);
}
$response_code = \wp_remote_retrieve_response_code( $response );
// check for redirects
if ( \in_array( $response_code, array( 301, 302, 307, 308 ), true ) ) {
return new WP_Error(
2021-07-23 15:46:28 +02:00
'author_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
2023-07-18 22:02:27 +02:00
'Your author URL <code>%s</code> is redirecting to another page, this is often done by SEO plugins like "Yoast SEO".',
2021-07-23 15:46:28 +02:00
'activitypub'
),
$author_url
)
);
}
// check if response is JSON
$body = \wp_remote_retrieve_body( $response );
if ( ! \is_string( $body ) || ! \is_array( \json_decode( $body, true ) ) ) {
return new WP_Error(
2021-07-23 15:46:28 +02:00
'author_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
2023-07-18 22:02:27 +02:00
'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.',
2021-07-23 15:46:28 +02:00
'activitypub'
),
$author_url
)
);
}
return true;
}
/**
2023-03-23 08:35:26 +01:00
* Check if WebFinger endpoint is accessible and profile request returns correct JSON
2021-07-23 15:46:28 +02:00
*
* @return boolean|WP_Error
2021-07-23 15:46:28 +02:00
*/
public static function is_webfinger_endpoint_accessible() {
2022-11-09 15:08:32 +01:00
$user = \wp_get_current_user();
$account = get_webfinger_resource( $user->ID );
2021-07-23 15:46:28 +02:00
$url = Webfinger::resolve( $account );
2022-11-09 15:08:32 +01:00
if ( \is_wp_error( $url ) ) {
2023-07-18 22:13:53 +02:00
$allowed = array( 'code' => array() );
$not_accessible = wp_kses(
// translators: %s: Author URL
\__(
'Your WebFinger endpoint <code>%s</code> is not accessible. Please check your WordPress setup or permalink structure.',
'activitypub'
),
$allowed
);
$invalid_response = wp_kses(
// translators: %s: Author URL
\__(
'Your WebFinger endpoint <code>%s</code> does not return valid JSON for <code>application/jrd+json</code>.',
'activitypub'
),
$allowed
);
2022-11-09 15:08:32 +01:00
$health_messages = array(
'webfinger_url_not_accessible' => \sprintf(
2023-07-18 22:13:53 +02:00
$not_accessible,
2022-11-09 15:08:32 +01:00
$url->get_error_data()
),
'webfinger_url_invalid_response' => \sprintf(
2021-07-23 15:46:28 +02:00
// translators: %s: Author URL
2023-07-18 22:13:53 +02:00
$invalid_response,
2022-11-09 15:08:32 +01:00
$url->get_error_data()
),
);
$message = null;
2022-11-09 15:27:50 +01:00
if ( isset( $health_messages[ $url->get_error_code() ] ) ) {
2022-11-09 15:08:32 +01:00
$message = $health_messages[ $url->get_error_code() ];
}
return new WP_Error(
2022-11-09 15:08:32 +01:00
$url->get_error_code(),
$message,
$url->get_error_data()
2021-07-23 15:46:28 +02:00
);
}
return true;
2019-09-27 10:12:59 +02:00
}
2021-01-13 23:22:17 +01:00
/**
* Retrieve the URL to the author page for the user with the ID provided.
*
* @global WP_Rewrite $wp_rewrite WordPress rewrite component.
*
* @param int $author_id Author ID.
* @param string $author_nicename Optional. The author's nicename (slug). Default empty.
*
* @return string The URL to the author's page.
*/
public static function get_author_posts_url( $author_id, $author_nicename = '' ) {
global $wp_rewrite;
$auth_id = (int) $author_id;
$link = $wp_rewrite->get_author_permastruct();
if ( empty( $link ) ) {
$file = home_url( '/' );
$link = $file . '?author=' . $auth_id;
} else {
if ( '' === $author_nicename ) {
$user = get_userdata( $author_id );
if ( ! empty( $user->user_nicename ) ) {
$author_nicename = $user->user_nicename;
}
}
$link = str_replace( '%author%', $author_nicename, $link );
$link = home_url( user_trailingslashit( $link ) );
}
return $link;
}
2022-11-22 00:05:17 +01:00
/**
* Static function for generating site debug data when required.
*
* @param array $info The debug information to be added to the core information page.
2023-03-23 08:35:26 +01:00
* @return array The filtered information
2022-11-22 00:05:17 +01:00
*/
public static function debug_information( $info ) {
$info['activitypub'] = array(
2022-12-06 17:38:32 +01:00
'label' => __( 'ActivityPub', 'activitypub' ),
2022-11-22 00:05:17 +01:00
'fields' => array(
'webfinger' => array(
2022-12-06 17:20:01 +01:00
'label' => __( 'WebFinger Resource', 'activitypub' ),
'value' => Webfinger::get_user_resource( wp_get_current_user()->ID ),
2022-11-22 00:05:17 +01:00
'private' => true,
),
'author_url' => array(
'label' => __( 'Author URL', 'activitypub' ),
'value' => get_author_posts_url( wp_get_current_user()->ID ),
'private' => true,
),
'plugin_version' => array(
'label' => __( 'Plugin Version', 'activitypub' ),
'value' => get_plugin_version(),
'private' => true,
),
2022-11-22 00:05:17 +01:00
),
);
return $info;
}
2019-09-27 10:12:59 +02:00
}