File: /var/www/html/wp-content/plugins/memberpress/app/controllers/MeprAuthenticatorCtrl.php
<?php
if (!defined('ABSPATH')) {
die('You are not allowed to call this page directly.');
}
class MeprAuthenticatorCtrl extends MeprBaseCtrl
{
/**
* Load the hooks.
*
* @return void
*/
public function load_hooks()
{
if (!defined('MEPR_AUTH_SERVICE_DOMAIN')) {
define('MEPR_AUTH_SERVICE_DOMAIN', 'auth.caseproof.com');
}
define('MEPR_AUTH_SERVICE_URL', 'https://' . MEPR_AUTH_SERVICE_DOMAIN);
add_action('admin_init', [$this, 'clear_connection_data']);
add_action('init', [$this, 'process_connect']);
add_action('init', [$this, 'process_disconnect']);
}
/**
* Clear the connection data.
*
* @return void
*/
public function clear_connection_data()
{
if (!isset($_GET['mp-clear-connection-data'])) {
return;
}
// Admins only.
if (!current_user_can('manage_options')) {
return;
}
// If nonce is present and valid, perform the action.
if (isset($_GET['_wpnonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'mp-clear-connection-data')) {
delete_option('mepr_authenticator_site_uuid');
delete_option('mepr_authenticator_account_email');
delete_option('mepr_authenticator_secret_token');
wp_safe_redirect(admin_url());
exit;
}
// Show confirmation page.
$nonce_url = wp_nonce_url(admin_url('?mp-clear-connection-data=1'), 'mp-clear-connection-data');
wp_die(
'<h1>' . esc_html__('Clear Connection Data', 'memberpress') . '</h1>' .
'<p>' . esc_html__('Are you sure you want to clear your MemberPress connection data? This will remove your site UUID, account email, and secret token.', 'memberpress') . '</p>' .
'<p><a class="button button-primary" href="' . esc_url($nonce_url) . '">' . esc_html__('Yes, Clear Connection Data', 'memberpress') . '</a> ' .
'<a class="button" href="' . esc_url(admin_url()) . '">' . esc_html__('Cancel', 'memberpress') . '</a></p>',
esc_html__('Confirm Action', 'memberpress'),
['back_link' => false]
);
}
// phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.Missing
/**
* Process a Connect
*
* @return void
*/
public function process_connect()
{
// Make sure we've entered our Authenticator process.
if (! isset($_GET['mepr-connect']) || 'true' !== $_GET['mepr-connect']) {
return;
}
// Validate the nonce on the WP side of things.
if (! isset($_GET['nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), 'mepr-connect')) {
return;
}
// Make sure the user is authorized.
if (! MeprUtils::is_mepr_admin()) {
return;
}
$site_uuid = sanitize_text_field(wp_unslash($_GET['site_uuid'] ?? ''));
$auth_code = sanitize_text_field(wp_unslash($_GET['auth_code'] ?? ''));
// GET request to obtain token.
$response = wp_remote_get(MEPR_AUTH_SERVICE_URL . "/api/tokens/{$site_uuid}", [
'sslverify' => false,
'headers' => [
'accept' => 'application/json',
],
'body' => [
'auth_code' => $auth_code,
],
]);
$body = json_decode(wp_remote_retrieve_body($response), true);
if (isset($body['account_email']) && ! empty($body['account_email'])) {
$email_saved = update_option('mepr_authenticator_account_email', sanitize_text_field($body['account_email']));
}
if (isset($body['secret_token']) && ! empty($body['secret_token'])) {
$token_saved = update_option('mepr_authenticator_secret_token', sanitize_text_field($body['secret_token']));
}
if (isset($body['user_uuid']) && ! empty($body['user_uuid'])) {
$user_uuid_saved = update_option('mepr_authenticator_user_uuid', sanitize_text_field($body['user_uuid']));
}
if ($site_uuid) {
update_option('mepr_authenticator_site_uuid', $site_uuid);
}
if (isset($_GET['stripe_connect']) && 'true' === $_GET['stripe_connect'] && isset($_GET['method_id']) && ! empty($_GET['method_id'])) {
wp_redirect(MeprStripeGateway::get_stripe_connect_url(sanitize_text_field(wp_unslash($_GET['method_id'])))); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
exit;
}
if (isset($_GET['square_connect']) && 'true' === $_GET['square_connect']) {
$options = MeprOptions::fetch();
$payment_method_id = sanitize_text_field(wp_unslash($_GET['square_payment_method_id'] ?? ''));
$environment = sanitize_text_field(wp_unslash($_GET['square_environment'] ?? ''));
$environment = $environment === 'sandbox' ? 'sandbox' : 'production';
$pm = $options->payment_method($payment_method_id);
try {
if (!$pm instanceof MeprSquarePaymentsGateway) {
throw new Exception(__('Sorry, this only works with Square.', 'memberpress'));
}
wp_redirect($pm->connect_url($environment)); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
exit;
} catch (Exception $e) {
$args = [
'page' => 'memberpress-options',
'mepr-square-connect-status' => 'error',
'error' => $e->getMessage(),
];
wp_safe_redirect(add_query_arg(array_map('rawurlencode', $args), admin_url('admin.php')));
exit;
}
}
$redirect_url = remove_query_arg(['mepr-connect', 'nonce', 'site_uuid', 'user_uuid', 'auth_code', 'license_key']);
$license_key = isset($_GET['license_key']) ? sanitize_text_field(wp_unslash($_GET['license_key'])) : '';
if (! empty($license_key) && class_exists('MeprUpdateCtrl')) {
try {
MeprUpdateCtrl::activate_license($license_key);
} catch (Exception $e) {
$redirect_url = add_query_arg('license_error', urlencode($e->getMessage()), $redirect_url);
}
}
wp_safe_redirect($redirect_url);
exit;
}
// phpcs:enable Squiz.Commenting.FunctionCommentThrowTag.Missing
/**
* Process a Disconnect
*
* @return void
*/
public function process_disconnect()
{
// Make sure we've entered our Authenticator process.
if (! isset($_GET['mepr-disconnect']) || 'true' !== $_GET['mepr-disconnect']) {
return;
}
// Validate the nonce on the WP side of things.
if (! isset($_GET['nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), 'mepr-disconnect')) {
return;
}
// Make sure the user is authorized.
if (! MeprUtils::is_mepr_admin()) {
return;
}
$site_email = get_option('mepr_authenticator_account_email');
$site_uuid = get_option('mepr_authenticator_site_uuid');
MeprHooks::do_action('mepr_memberpress_com_pre_disconnect', $site_uuid, $site_email);
// Create token payload.
$payload = [
'email' => $site_email,
'site_uuid' => $site_uuid,
];
// Create JWT.
$jwt = self::generate_jwt($payload);
// DELETE request to obtain token.
$response = wp_remote_request(MEPR_AUTH_SERVICE_URL . '/api/disconnect/memberpress', [
'method' => 'DELETE',
'sslverify' => false,
'headers' => MeprUtils::jwt_header($jwt, MEPR_AUTH_SERVICE_DOMAIN),
]);
$body = json_decode(wp_remote_retrieve_body($response), true);
if (isset($body['disconnected']) && true === $body['disconnected']) {
delete_option('mepr_authenticator_account_email');
delete_option('mepr_authenticator_secret_token');
delete_option('mepr_authenticator_site_uuid', $site_uuid);
}
wp_safe_redirect(remove_query_arg(['mepr-disconnect', 'nonce']));
exit;
}
/**
* Generates a JWT, signed by the stored secret token
*
* @param array $payload Payload data.
* @param string $secret Used to sign the JWT.
*
* @return string
*/
public static function generate_jwt($payload, $secret = false)
{
if (false === $secret) {
$secret = get_option('mepr_authenticator_secret_token');
}
// Create token header.
$header = [
'typ' => 'JWT',
'alg' => 'HS256',
];
$header = json_encode($header);
$header = self::base64url_encode($header);
// Create token payload.
$payload = json_encode($payload);
$payload = self::base64url_encode($payload);
// Create Signature Hash.
$signature = hash_hmac('sha256', "{$header}.{$payload}", $secret);
$signature = json_encode($signature);
$signature = self::base64url_encode($signature);
// Create JWT.
$jwt = "{$header}.{$payload}.{$signature}";
return $jwt;
}
/**
* Ensure that the Base64 string is passed within URLs without any URL encoding
*
* @param string $value The value to encode.
*
* @return string
*/
public static function base64url_encode($value)
{
return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
}
/**
* Assembles a URL for connecting to our Authentication service
*
* @param boolean $stripe_connect Will add a query string that is used to redirect to Stripe Connect after returning from Auth service.
* @param string|null $payment_method_id The payment method ID.
* @param array $additional_params Additional parameters to add to the URL.
* @param string|null $return_url The return URL.
*
* @return string
*/
public static function get_auth_connect_url($stripe_connect = false, $payment_method_id = false, $additional_params = [], $return_url = null)
{
$return_url = is_null($return_url) ? admin_url('admin.php?page=memberpress-account-login', false) : $return_url;
$connect_params = [
'return_url' => urlencode(add_query_arg('mepr-connect', 'true', $return_url)),
'nonce' => wp_create_nonce('mepr-connect'),
];
$site_uuid = get_option('mepr_authenticator_site_uuid');
if ($site_uuid) {
$connect_params['site_uuid'] = $site_uuid;
}
if (true === $stripe_connect && ! empty($payment_method_id)) {
$connect_params['stripe_connect'] = 'true';
$connect_params['method_id'] = $payment_method_id;
}
if (! empty($additional_params)) {
$connect_params = array_merge($connect_params, $additional_params);
}
return add_query_arg($connect_params, MEPR_AUTH_SERVICE_URL . '/connect/memberpress');
}
}