Merge pull request #127 from pfefferle/health-check-improvements

added health checks
This commit is contained in:
Matthias Pfefferle 2021-07-23 15:47:49 +02:00 committed by GitHub
commit 7cddec43b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 233 additions and 29 deletions

View file

@ -3,8 +3,8 @@
**Donate link:** https://notiz.blog/donate/ **Donate link:** https://notiz.blog/donate/
**Tags:** OStatus, fediverse, activitypub, activitystream **Tags:** OStatus, fediverse, activitypub, activitystream
**Requires at least:** 4.7 **Requires at least:** 4.7
**Tested up to:** 5.6 **Tested up to:** 5.8
**Stable tag:** 0.12.0 **Stable tag:** 0.13.0
**Requires PHP:** 5.6 **Requires PHP:** 5.6
**License:** MIT **License:** MIT
**License URI:** http://opensource.org/licenses/MIT **License URI:** http://opensource.org/licenses/MIT
@ -88,6 +88,11 @@ Where 'blog' is the path to the subdirectory at which your blog resides.
Project maintained on GitHub at [pfefferle/wordpress-activitypub](https://github.com/pfefferle/wordpress-activitypub). Project maintained on GitHub at [pfefferle/wordpress-activitypub](https://github.com/pfefferle/wordpress-activitypub).
### 0.13.0 ###
* add Autor URL and WebFinger health checks
* fix NodeInfo endpoint
### 0.12.0 ### ### 0.12.0 ###
* use "pre_option_require_name_email" filter instead of "check_comment_flood". props [@akirk](https://github.com/akirk) * use "pre_option_require_name_email" filter instead of "check_comment_flood". props [@akirk](https://github.com/akirk)

View file

@ -3,7 +3,7 @@
* Plugin Name: ActivityPub * Plugin Name: ActivityPub
* Plugin URI: https://github.com/pfefferle/wordpress-activitypub/ * Plugin URI: https://github.com/pfefferle/wordpress-activitypub/
* Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format. * Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format.
* Version: 0.12.0 * Version: 0.13.0
* Author: Matthias Pfefferle * Author: Matthias Pfefferle
* Author URI: https://notiz.blog/ * Author URI: https://notiz.blog/
* License: MIT * License: MIT
@ -75,6 +75,10 @@ function init() {
\add_filter( 'wp_rest_server_class', function() { \add_filter( 'wp_rest_server_class', function() {
return '\Activitypub\Rest\Server'; return '\Activitypub\Rest\Server';
} ); } );
if ( \WP_DEBUG ) {
require_once \dirname( __FILE__ ) . '/includes/debug.php';
}
} }
\add_action( 'plugins_loaded', '\Activitypub\init' ); \add_action( 'plugins_loaded', '\Activitypub\init' );

View file

@ -7,25 +7,36 @@ namespace Activitypub;
* @author Matthias Pfefferle * @author Matthias Pfefferle
*/ */
class Health_Check { class Health_Check {
/**
* Initialize health checks
*
* @return void
*/
public static function init() { public static function init() {
\add_filter( 'site_status_tests', array( '\Activitypub\Health_Check', 'add_tests' ) ); \add_filter( 'site_status_tests', array( '\Activitypub\Health_Check', 'add_tests' ) );
} }
public static function add_tests( $tests ) { public static function add_tests( $tests ) {
$tests['direct']['activitypub_test_profile_url'] = array( $tests['direct']['activitypub_test_author_url'] = array(
'label' => \__( 'Profile URL test', 'activitypub' ), 'label' => \__( 'Author URL test', 'activitypub' ),
'test' => array( '\Activitypub\Health_Check', 'test_profile_url' ), 'test' => array( '\Activitypub\Health_Check', 'test_author_url' ),
); );
//$tests['direct']['activitypub_test_profile_url2'] = array( $tests['direct']['activitypub_test_webfinger'] = array(
// 'label' => __( 'Profile URL Test', 'activitypub' ), 'label' => __( 'WebFinger Test', 'activitypub' ),
// 'test' => array( '\Activitypub\Health_Check', 'test_profile_url' ), 'test' => array( '\Activitypub\Health_Check', 'test_webfinger' ),
//); );
return $tests; return $tests;
} }
public static function test_profile_url() { /**
* Author URL tests
*
* @return void
*/
public static function test_author_url() {
$result = array( $result = array(
'label' => \__( 'Author URL accessible', 'activitypub' ), 'label' => \__( 'Author URL accessible', 'activitypub' ),
'status' => 'good', 'status' => 'good',
@ -38,45 +49,208 @@ class Health_Check {
\__( 'Your author URL is accessible and supports the required "Accept" header.', 'activitypub' ) \__( 'Your author URL is accessible and supports the required "Accept" header.', 'activitypub' )
), ),
'actions' => '', 'actions' => '',
'test' => 'test_profile_url', 'test' => 'test_author_url',
); );
$enum = self::is_profile_url_accessible(); $check = self::is_author_url_accessible();
if ( true !== $enum ) { if ( true === $check ) {
$result['status'] = 'critical'; return $result;
$result['label'] = \__( 'Profile URL is not accessible', 'activitypub' );
$result['description'] = \sprintf(
'<p>%s</p>',
\__( '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' )
);
} }
$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()
);
return $result; return $result;
} }
public static function is_profile_url_accessible() { /**
* WebFinger tests
*
* @return void
*/
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>',
\__( 'Your WebFinger endpoint is accessible and returns the correct informations.', 'activitypub' )
),
'actions' => '',
'test' => 'test_webfinger',
);
$check = self::is_webfinger_endpoint_accessible();
if ( true === $check ) {
return $result;
}
$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;
}
/**
* Check if `author_posts_url` is accessible and that requerst returns correct JSON
*
* @return boolean|WP_Error
*/
public static function is_author_url_accessible() {
$user = \wp_get_current_user(); $user = \wp_get_current_user();
$author_url = \get_author_posts_url( $user->ID ); $author_url = \get_author_posts_url( $user->ID );
$reference_author_url = self::get_author_posts_url( $user->ID, $user->user_nicename ); $reference_author_url = self::get_author_posts_url( $user->ID, $user->user_nicename );
// check for "author" in URL // check for "author" in URL
if ( $author_url !== $reference_author_url ) { if ( $author_url !== $reference_author_url ) {
return false; return new \WP_Error(
'author_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
'<p>Your author URL <code>%s</code> was replaced, this is often done by plugins.</p>',
'activitypub'
),
$author_url
)
);
} }
// try to access author URL // try to access author URL
$response = \wp_remote_get( $author_url, array( 'headers' => array( 'Accept' => 'application/activity+json' ) ) ); $response = \wp_remote_get(
$author_url,
array(
'headers' => array( 'Accept' => 'application/activity+json' ),
'redirection' => 0,
)
);
if ( \is_wp_error( $response ) ) { if ( \is_wp_error( $response ) ) {
return false; return new \WP_Error(
'author_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
'<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>',
'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(
'author_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
'<p>Your author URL <code>%s</code> is redirecting to another page, this is often done by SEO plugins like "Yoast SEO".</p>',
'activitypub'
),
$author_url
)
);
} }
// check if response is JSON // check if response is JSON
$body = \wp_remote_retrieve_body( $response ); $body = \wp_remote_retrieve_body( $response );
if ( ! \is_string( $body ) || ! \is_array( \json_decode( $body, true ) ) ) { if ( ! \is_string( $body ) || ! \is_array( \json_decode( $body, true ) ) ) {
return false; return new \WP_Error(
'author_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
'<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>',
'activitypub'
),
$author_url
)
);
}
return true;
}
/**
* Check if WebFinger endoint is accessible and profile requerst returns correct JSON
*
* @return boolean|WP_Error
*/
public static function is_webfinger_endpoint_accessible() {
$user = \wp_get_current_user();
$webfinger = \Activitypub\get_webfinger_resource( $user->ID );
$url = \wp_parse_url( \home_url(), \PHP_URL_SCHEME ) . '://' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
if ( \wp_parse_url( \home_url(), \PHP_URL_PORT ) ) {
$url .= ':' . \wp_parse_url( \home_url(), \PHP_URL_PORT );
}
$url = \trailingslashit( $url ) . '.well-known/webfinger';
$url = \add_query_arg( 'resource', 'acct:' . $webfinger, $url );
// try to access author URL
$response = \wp_remote_get(
$url,
array(
'headers' => array( 'Accept' => 'application/activity+json' ),
'redirection' => 0,
)
);
if ( \is_wp_error( $response ) ) {
return new \WP_Error(
'webfinger_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
'<p>Your WebFinger endpoint <code>%s</code> is not accessible. Please check your WordPress setup or permalink structure.</p>',
'activitypub'
),
$url
)
);
}
$response_code = \wp_remote_retrieve_response_code( $response );
// 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(
'webfinger_url_not_accessible',
\sprintf(
// translators: %s: Author URL
\__(
'<p>Your WebFinger endpoint <code>%s</code> does not return valid JSON for <code>application/jrd+json</code>.</p>',
'activitypub'
),
$url
)
);
} }
return true; return true;

16
includes/debug.php Normal file
View file

@ -0,0 +1,16 @@
<?php
namespace Activitypub;
/**
* Allow localhost URLs if WP_DEBUG is true.
*
* @param array $r Array of HTTP request args.
* @param string $url The request URL.
* @return array $args Array or string of HTTP request arguments.
*/
function allow_localhost( $r, $url ) {
$r['reject_unsafe_urls'] = false;
return $r;
}
add_filter( 'http_request_args', '\Activitypub\allow_localhost', 10, 2 );

View file

@ -20,11 +20,11 @@ function get_context() {
'Hashtag' => 'as:Hashtag', 'Hashtag' => 'as:Hashtag',
'featured' => array( 'featured' => array(
'@id' => 'toot:featured', '@id' => 'toot:featured',
'@type' => '@id' '@type' => '@id',
), ),
'featuredTags' => array( 'featuredTags' => array(
'@id' => 'toot:featuredTags', '@id' => 'toot:featuredTags',
'@type' => '@id' '@type' => '@id',
), ),
), ),
); );

View file

@ -3,8 +3,8 @@ Contributors: pfefferle, mediaformat
Donate link: https://notiz.blog/donate/ Donate link: https://notiz.blog/donate/
Tags: OStatus, fediverse, activitypub, activitystream Tags: OStatus, fediverse, activitypub, activitystream
Requires at least: 4.7 Requires at least: 4.7
Tested up to: 5.6 Tested up to: 5.8
Stable tag: 0.12.0 Stable tag: 0.13.0
Requires PHP: 5.6 Requires PHP: 5.6
License: MIT License: MIT
License URI: http://opensource.org/licenses/MIT License URI: http://opensource.org/licenses/MIT
@ -88,6 +88,11 @@ Where 'blog' is the path to the subdirectory at which your blog resides.
Project maintained on GitHub at [pfefferle/wordpress-activitypub](https://github.com/pfefferle/wordpress-activitypub). Project maintained on GitHub at [pfefferle/wordpress-activitypub](https://github.com/pfefferle/wordpress-activitypub).
= 0.13.0 =
* add Autor URL and WebFinger health checks
* fix NodeInfo endpoint
= 0.12.0 = = 0.12.0 =
* use "pre_option_require_name_email" filter instead of "check_comment_flood". props [@akirk](https://github.com/akirk) * use "pre_option_require_name_email" filter instead of "check_comment_flood". props [@akirk](https://github.com/akirk)