Skip to content
This repository has been archived by the owner on Oct 31, 2022. It is now read-only.

Commit

Permalink
Merge pull request #22 from jaredcobb/1.0.7
Browse files Browse the repository at this point in the history
Update to 1.0.7
  • Loading branch information
jaredcobb authored Dec 16, 2018
2 parents 46bed19 + 9543416 commit 4c47049
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 67 deletions.
11 changes: 9 additions & 2 deletions README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Contributors: jaredcobb
Tags: ccb, church, api, chms
Requires at least: 4.6.0
Tested up to: 4.9.6
Stable tag: 1.0.6
Tested up to: 5.0.1
Stable tag: 1.0.7
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html

Expand Down Expand Up @@ -61,6 +61,13 @@ You'll need to ensure your [group settings](https://churchcommunitybuilder.force

== Changelog ==

= 1.0.7 =
* Fix for a bug where auto-sync was failing when group images are enabled.
* PHP 7.2 and above compatibility improvements.
* Stronger encryption methods for PHP 7.2 and above.
* Performance improvements in how the plugin loads and runs.
* Compatible with Gutenberg, WordPress 5.0.

= 1.0.6 =
* Fixes a bug where admin libraries may not be loaded under some cron contexts.

Expand Down
4 changes: 2 additions & 2 deletions ccb-core.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Plugin Name: Church Community Builder Core API
* Plugin URI: https://www.wpccb.com
* Description: A plugin to provide a core integration of the Church Community Builder API into WordPress custom post types
* Version: 1.0.6
* Version: 1.0.7
* Author: Jared Cobb
* Author URI: https://www.jaredcobb.com/
* License: GPL-2.0+
Expand All @@ -27,7 +27,7 @@
define( 'CCB_CORE_PATH', plugin_dir_path( __FILE__ ) );
define( 'CCB_CORE_URL', plugin_dir_url( __FILE__ ) );
define( 'CCB_CORE_BASENAME', plugin_basename( __FILE__ ) );
define( 'CCB_CORE_VERSION', '1.0.6' );
define( 'CCB_CORE_VERSION', '1.0.7' );

// Check minimum requirements before proceeding.
require_once CCB_CORE_PATH . 'includes/class-ccb-core-requirements.php';
Expand Down
21 changes: 2 additions & 19 deletions includes/class-ccb-core-admin-ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,6 @@
*/
class CCB_Core_Admin_AJAX {

/**
* An instance of the CCB_Core_API class
*
* @var CCB_Core_API
*/
private $api;

/**
* An instance of the CCB_Core_Synchronizer class
*
* @var CCB_Core_Synchronizer
*/
private $synchronizer;

/**
* Initialize the class and register hooks
*
Expand All @@ -42,9 +28,6 @@ public function __construct() {
add_action( 'wp_ajax_poll_sync', [ $this, 'ajax_poll_sync' ] );
add_action( 'wp_ajax_get_latest_sync', [ $this, 'ajax_get_latest_sync' ] );
add_action( 'wp_ajax_test_credentials', [ $this, 'ajax_test_credentials' ] );

$this->api = CCB_Core_API::instance();
$this->synchronizer = CCB_Core_Synchronizer::instance();
}

/**
Expand All @@ -61,7 +44,7 @@ public function ajax_sync() {

// Tell the user to move along and go about their business...
CCB_Core_Helpers::instance()->send_non_blocking_json_success();
$result = $this->synchronizer->synchronize();
$result = CCB_Core_Synchronizer::instance()->synchronize();

}

Expand Down Expand Up @@ -201,7 +184,7 @@ public function ajax_get_latest_sync() {
*/
public function ajax_test_credentials() {
check_ajax_referer( 'ccb_core_nonce', 'nonce' );
$response = $this->api->get( 'api_status' );
$response = CCB_Core_API::instance()->get( 'api_status' );

if ( 'SUCCESS' === $response['status'] ) {
wp_send_json_success();
Expand Down
14 changes: 7 additions & 7 deletions includes/class-ccb-core-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,7 @@ public static function instance() {
* @return void
*/
private function setup() {
// Wait to initialize the API credentials until after WordPress
// has loaded pluggable.php because we are using some WordPress helper functions.
add_action( 'plugins_loaded', [ $this, 'initialize_credentials' ] );
$this->initialize_credentials();
}

/**
Expand Down Expand Up @@ -258,10 +256,12 @@ private function request( $method, $service, $data = [] ) {
// We successfully parsed the XML response, however the
// response may contain error messages from CCB.
if ( isset( $parsed_response->response->errors->error ) ) {
$result['error'] = esc_html( sprintf(
__( 'The CCB API replied with an error: %s', 'ccb-core' ),
$parsed_response->response->errors->error
) );
$result['error'] = esc_html(
sprintf(
__( 'The CCB API replied with an error: %s', 'ccb-core' ),
$parsed_response->response->errors->error
)
);
$result['status'] = 'ERROR';
} else {
$result['status'] = 'SUCCESS';
Expand Down
21 changes: 7 additions & 14 deletions includes/class-ccb-core-cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@
*/
class CCB_Core_Cron {

/**
* An instance of the CCB_Core_Synchronizer class
*
* @var CCB_Core_Synchronizer
*/
private $synchronizer;

/**
* Initialize the class and set its properties.
*
Expand All @@ -38,8 +31,6 @@ public function __construct() {
add_action( 'ccb_core_auto_sync_hook', [ $this, 'auto_sync_callback' ] );
// When the cron settings are changed, configure the events.
add_action( 'update_option_ccb_core_settings', [ $this, 'cron_settings_changed' ], 10, 2 );

$this->synchronizer = CCB_Core_Synchronizer::instance();
}

/**
Expand All @@ -54,10 +45,12 @@ public function custom_cron_schedule( $schedules ) {
if ( ! empty( $settings['auto_sync_timeout'] ) ) {
$schedules['ccb_core_schedule'] = [
'interval' => MINUTE_IN_SECONDS * absint( $settings['auto_sync_timeout'] ),
'display' => esc_html( sprintf(
__( 'Every %s Minutes' ),
absint( $settings['auto_sync_timeout'] )
) ),
'display' => esc_html(
sprintf(
__( 'Every %s Minutes' ),
absint( $settings['auto_sync_timeout'] )
)
),
];
}
return $schedules;
Expand Down Expand Up @@ -109,7 +102,7 @@ private function remove_existing_cron_events() {
* @return void
*/
public function auto_sync_callback() {
$this->synchronizer->synchronize();
CCB_Core_Synchronizer::instance()->synchronize();
}

}
Expand Down
114 changes: 108 additions & 6 deletions includes/class-ccb-core-helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,47 @@ public function refresh_options( $old_value, $new_value ) {
* @return string
*/
public function encrypt( $data ) {
if ( ! function_exists( 'sodium_crypto_secretbox' ) ) {
return $this->legacy_encrypt( $data );
}

$encrypted_value = false;
if ( ! empty( $data ) ) {
try {
// Create a one-time random nonce and salt.
$nonce = random_bytes( SODIUM_CRYPTO_SECRETBOX_NONCEBYTES );
$salt = random_bytes( SODIUM_CRYPTO_PWHASH_SALTBYTES );
// Create a unique key that is seeded from the site's AUTH_KEY constant.
$key = sodium_crypto_pwhash(
SODIUM_CRYPTO_SECRETBOX_KEYBYTES,
AUTH_KEY,
$salt,
SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
// Encrypt the data with the nonce and salt prepended so that we can
// use them to decrypt the value later.
$encrypted_value = base64_encode( $nonce . $salt . sodium_crypto_secretbox( $data, $nonce, $key ) );
} catch ( Exception $ex ) {
return new WP_Error( 'encrypt_failure', __( 'The string could not be encrypted via Sodium', 'ccb-core' ) );
}

}

return $encrypted_value;
}

/**
* Encrypts and base64_encodes a string safe for serialization in WordPress
* when the Sodium functions are not available (typically below PHP 7.2)
*
* @param string $data The data to be encrypted.
*
* @return string
*/
private function legacy_encrypt( $data ) {
$encrypted_value = false;
$key = wp_salt() . md5( 'ccb-core' );
$key = hash_hmac( 'sha512', AUTH_SALT, AUTH_KEY );

if ( ! empty( $data ) ) {
try {
Expand All @@ -135,9 +173,68 @@ public function encrypt( $data ) {
* @return string
*/
public function decrypt( $data ) {
if ( ! function_exists( 'sodium_crypto_secretbox_open' ) ) {
return $this->legacy_decrypt( $data );
}

$decrypted_value = false;
$key = wp_salt() . md5( 'ccb-core' );
if ( ! empty( $data ) ) {
try {
// Decode the stored value.
$decoded_data = base64_decode( $data );
// Get the stored nonce from the beginning of the multibyte string.
$nonce = mb_substr(
$decoded_data,
0,
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES,
'8bit'
);
// Get the stored salt from the middle of the multibyte string.
$salt = mb_substr(
$decoded_data,
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES,
SODIUM_CRYPTO_PWHASH_SALTBYTES,
'8bit'
);
// Get the encrypted data from the end of the multibyte string.
$cipher = mb_substr(
$decoded_data,
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_PWHASH_SALTBYTES,
null,
'8bit'
);
// Generate the known key from the stored salt and site's AUTH_KEY constant.
$key = sodium_crypto_pwhash(
SODIUM_CRYPTO_SECRETBOX_KEYBYTES,
AUTH_KEY,
$salt,
SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
// Decrypt the data using the stored nonce.
$decrypted_value = sodium_crypto_secretbox_open( $cipher, $nonce, $key );
} catch ( Exception $ex ) {
return new WP_Error( 'decrypt_failure', __( 'The string could not be decrypted', 'ccb-core' ) );
}

}

return $decrypted_value;
}

/**
* Decrypts and base64_decodes a string when the Sodium functions
* are not available (typically below PHP 7.2)
*
* @since 1.0.0
* @access public
* @param string $data The data to be decrypted.
* @return string
*/
public function legacy_decrypt( $data ) {

$decrypted_value = false;
$key = hash_hmac( 'sha512', AUTH_SALT, AUTH_KEY );

if ( ! empty( $data ) ) {
try {
Expand Down Expand Up @@ -169,10 +266,12 @@ public function send_non_blocking_json_success( $data = [] ) {
header( 'Content-Type: application/json' );
header( 'Content-Encoding: none' );

echo wp_json_encode( [
'success' => true,
'data' => $data,
] );
echo wp_json_encode(
[
'success' => true,
'data' => $data,
]
);

header( 'Connection: close' );
header( 'Content-Length: ' . ob_get_length() );
Expand Down Expand Up @@ -211,6 +310,9 @@ public function download_image( $image_url, $filename = '', $post_id = 0 ) {
if ( ! function_exists( 'download_url' ) ) {
include_once ABSPATH . 'wp-admin/includes/file.php';
}
if ( ! function_exists( 'wp_read_image_metadata' ) ) {
include_once ABSPATH . 'wp-admin/includes/image.php';
}
if ( ! function_exists( 'media_handle_sideload' ) ) {
include_once ABSPATH . 'wp-admin/includes/media.php';
}
Expand Down
53 changes: 53 additions & 0 deletions includes/class-ccb-core-requirements.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ class CCB_Core_Requirements {
*/
private $required_wordpress = '4.6.0';

/**
* Required global constants defined in wp-config.php
*
* @var array
*/
private $required_keys = [ 'AUTH_KEY', 'AUTH_SALT' ];

/**
* Any applicable error messages
*
Expand All @@ -51,6 +58,8 @@ class CCB_Core_Requirements {
*/
public function __construct() {
$this->validate_versions();
$this->validate_keys();
$this->validate_encryption_methods();
}

/**
Expand Down Expand Up @@ -109,4 +118,48 @@ private function validate_versions() {
$this->register_disable_plugin();
}

/**
* Ensure the site has configured the required keys.
*
* @return void
*/
private function validate_keys() {
foreach ( $this->required_keys as $key ) {
if ( ! defined( $key ) || 32 > strlen( constant( $key ) ) ) {
$this->requirements_met = false;
$this->error_message = sprintf(
'Church Community Builder Core API requires that you configure a random ' .
'value for the %s constant that is at least 32 characters long. See ' .
'https://codex.wordpress.org/Editing_wp-config.php#Security_Keys ' .
'for more information.',
$key
);
$this->register_disable_plugin();
return;
}
}
}

/**
* Ensure the site has an encryption module installed.
*
* @return void
*/
private function validate_encryption_methods() {
if (
! function_exists( 'sodium_crypto_secretbox' )
&& ! function_exists( 'sodium_crypto_secretbox_open' )
&& ! function_exists( 'mcrypt_encrypt' )
&& ! function_exists( 'mcrypt_decrypt' )
) {
$this->requirements_met = false;
$this->error_message = 'Church Community Builder Core API requires that you ' .
'have an encryption library installed on your system. By default, ' .
'you should have the mcrypt module installed for PHP versions less than 7.2 ' .
'or the sodium module installed for PHP versions 7.2 or later. ' .
'Please contact your hosting provider for more information.';
$this->register_disable_plugin();
return;
}
}
}
Loading

0 comments on commit 4c47049

Please sign in to comment.