mirror of
https://gh.wpcy.net/https://github.com/discourse/wp-discourse.git
synced 2026-05-23 03:20:46 +08:00
* Update and improve webhooks Changes: - Add email matching as an identification basis for Update Userdata webhook. - Standardise webhook data processing and responses. - Add get_discourse_webhook_data utility method. - Improve Webhook admin panel UX and descriptions. Misc: - Update phpcs usage * Fix php 5.6 and 7.0 syntax checks * Fix phpcs issues arising from update * Remove ineffective nbsp from admin page
143 lines
4.6 KiB
PHP
Vendored
143 lines
4.6 KiB
PHP
Vendored
<?php
|
|
/**
|
|
* Shared Webhook methods.
|
|
*
|
|
* @package WPDiscourse
|
|
*/
|
|
|
|
namespace WPDiscourse\Shared;
|
|
|
|
use WPDiscourse\Logs\Logger;
|
|
|
|
/**
|
|
* Trait WebhookUtilities
|
|
*/
|
|
trait WebhookUtilities {
|
|
/**
|
|
* Supported Webhook events.
|
|
*
|
|
* @access protected
|
|
* @var mixed|void
|
|
*/
|
|
protected $supported_events = null;
|
|
|
|
/**
|
|
* Get webhook data for WPDiscourse classes.
|
|
*
|
|
* @param \WP_REST_Request $request The WP_REST_Request data object.
|
|
*
|
|
* @return object|\WP_Error
|
|
*/
|
|
protected function get_webhook_data( $request ) {
|
|
if ( ! $this->webhook_enabled() ) {
|
|
|
|
return new \WP_Error( 'discourse_webhook_error', __( 'The webhook is not enabled.' ) );
|
|
}
|
|
|
|
return $this->get_discourse_webhook_data( $request, $this->supported_events, $this->logger_context );
|
|
}
|
|
|
|
/**
|
|
* Get data from discourse webhook request.
|
|
*
|
|
* @param \WP_REST_Request $request The WP_REST_Request data object.
|
|
* @param array $supported_events An optional array of supported events.
|
|
* @param string $logger_context An optional logger context.
|
|
*
|
|
* @return object|\WP_Error
|
|
*/
|
|
protected function get_discourse_webhook_data( $request, $supported_events = null, $logger_context = 'discourse_webhook' ) {
|
|
|
|
$request = $this->verify_discourse_webhook_request( $request );
|
|
|
|
if ( is_wp_error( $request ) ) {
|
|
$this->logger->error( "$logger_context.webhook_verification_error", array( 'message', $request->get_error_message() ) );
|
|
|
|
return new \WP_Error( 'discourse_webhook_error', __( 'The webhook was not verified.' ) );
|
|
}
|
|
|
|
$event_type = $request->get_header( 'x_discourse_event_type' );
|
|
$event = $request->get_header( 'x_discourse_event' );
|
|
|
|
if ( $supported_events && ! in_array( $event, $supported_events, true ) ) {
|
|
|
|
return new \WP_Error( 'discourse_webhook_error', __( 'The webhook event is not supported.' ) );
|
|
}
|
|
|
|
$json = $request->get_json_params();
|
|
|
|
if ( is_wp_error( $json ) ) {
|
|
$this->logger->error( "$logger_context.response_body_error" );
|
|
|
|
return new \WP_Error( 'discourse_webhook_error', __( 'The webhook sent an invalid body.' ) );
|
|
}
|
|
|
|
return (object) array(
|
|
'json' => $json,
|
|
'event_type' => $event_type,
|
|
'event' => $event,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Verify that the request originated from a Discourse webhook and the the secret keys match.
|
|
*
|
|
* @param \WP_REST_Request $data The WP_REST_Request object.
|
|
*
|
|
* @return \WP_Error|\WP_REST_Request
|
|
*/
|
|
protected function verify_discourse_webhook_request( $data ) {
|
|
$options = isset( $this->options ) ? $this->options : $this->get_options();
|
|
// The X-Discourse-Event-Signature consists of 'sha256=' . hamc of raw payload.
|
|
// It is generated by computing `hash_hmac( 'sha256', $payload, $secret )`.
|
|
$sig = substr( $data->get_header( 'X-Discourse-Event-Signature' ), 7 );
|
|
if ( $sig ) {
|
|
$payload = $data->get_body();
|
|
// Key used for verifying the request - a matching key needs to be set on the Discourse webhook.
|
|
$secret = ! empty( $options['webhook-secret'] ) ? $options['webhook-secret'] : '';
|
|
|
|
if ( ! $secret ) {
|
|
|
|
return new \WP_Error( 'discourse_webhook_configuration_error', 'The webhook secret key has not been set.' );
|
|
}
|
|
|
|
if ( hash_hmac( 'sha256', $payload, $secret ) === $sig ) {
|
|
|
|
return $data;
|
|
} else {
|
|
|
|
return new \WP_Error( 'discourse_webhook_authentication_error', 'Discourse Webhook Request Error: signatures did not match.' );
|
|
}
|
|
}
|
|
|
|
return new \WP_Error( 'discourse_webhook_authentication_error', 'Discourse Webhook Request Error: the X-Discourse-Event-Signature was not set for the request.' );
|
|
}
|
|
|
|
/**
|
|
* Failed response to webhook request.
|
|
*
|
|
* @param string $message Message to add to the response.
|
|
*/
|
|
protected function failed_response( $message ) {
|
|
return wp_json_encode(
|
|
array(
|
|
'success' => false,
|
|
'message' => $message,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Success response to webhook request.
|
|
*
|
|
* @param string $message Message to add to the response.
|
|
*/
|
|
protected function success_response( $message = '' ) {
|
|
return wp_json_encode(
|
|
array(
|
|
'success' => true,
|
|
'message' => $message,
|
|
)
|
|
);
|
|
}
|
|
}
|