You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

328 lines
13 KiB

<?php
use WordfenceLS\Controller_Settings;
require_once(dirname(__FILE__) . '/wfRESTBaseController.php');
class wfRESTConfigController extends wfRESTBaseController {
public static function disconnectConfig($adminEmail = null) {
global $wpdb;
delete_transient('wordfenceCentralJWT' . wfConfig::get('wordfenceCentralSiteID'));
if (is_null($adminEmail)) {
$adminEmail = wfConfig::get('wordfenceCentralConnectEmail');
}
$result = $wpdb->query('DELETE FROM ' . wfDB::networkTable('wfConfig') . " WHERE name LIKE 'wordfenceCentral%'");
wfConfig::set('wordfenceCentralDisconnected', true);
wfConfig::set('wordfenceCentralDisconnectTime', time());
wfConfig::set('wordfenceCentralDisconnectEmail', $adminEmail);
return !!$result;
}
public function registerRoutes() {
register_rest_route('wordfence/v1', '/config', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getConfig'),
'permission_callback' => array($this, 'verifyToken'),
'fields' => array(
'description' => __('Specific config options to return.', 'wordfence'),
'type' => 'array',
'required' => false,
),
));
register_rest_route('wordfence/v1', '/config', array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'setConfig'),
'permission_callback' => array($this, 'verifyToken'),
'fields' => array(
'description' => __('Specific config options to set.', 'wordfence'),
'type' => 'array',
'required' => true,
),
));
register_rest_route('wordfence/v1', '/disconnect', array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'disconnect'),
'permission_callback' => array($this, 'verifyToken'),
));
register_rest_route('wordfence/v1', '/premium-connect', array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'premiumConnect'),
'permission_callback' => array($this, 'verifyTokenPremium'),
));
}
/**
* @param WP_REST_Request $request
* @return mixed|WP_REST_Response
*/
public function getConfig($request) {
$fields = (array) $request['fields'];
$config = array();
$firewall = new wfFirewall();
$wafFields = array(
'autoPrepend' => $firewall->protectionMode() === wfFirewall::PROTECTION_MODE_EXTENDED,
'avoid_php_input' => wfWAF::getInstance()->getStorageEngine()->getConfig('avoid_php_input', false) ? 1 : 0,
'disabledRules' => array_keys((array) wfWAF::getInstance()->getStorageEngine()->getConfig('disabledRules')),
'ruleCount' => count((array) wfWAF::getInstance()->getRules()),
'disableWAFBlacklistBlocking' => wfWAF::getInstance()->getStorageEngine()->getConfig('disableWAFBlacklistBlocking'),
'enabled' => $firewall->wafStatus() !== wfFirewall::FIREWALL_MODE_DISABLED,
'firewallMode' => $firewall->firewallMode(),
'learningModeGracePeriod' => wfWAF::getInstance()->getStorageEngine()->getConfig('learningModeGracePeriod'),
'learningModeGracePeriodEnabled' => wfWAF::getInstance()->getStorageEngine()->getConfig('learningModeGracePeriodEnabled'),
'subdirectoryInstall' => $firewall->isSubDirectoryInstallation(),
'wafStatus' => $firewall->wafStatus(),
);
$lsFields = array(
Controller_Settings::OPTION_XMLRPC_ENABLED => Controller_Settings::shared()->get(Controller_Settings::OPTION_XMLRPC_ENABLED),
Controller_Settings::OPTION_2FA_WHITELISTED => Controller_Settings::shared()->get(Controller_Settings::OPTION_2FA_WHITELISTED),
Controller_Settings::OPTION_IP_SOURCE => Controller_Settings::shared()->get(Controller_Settings::OPTION_IP_SOURCE),
Controller_Settings::OPTION_IP_TRUSTED_PROXIES => Controller_Settings::shared()->get(Controller_Settings::OPTION_IP_TRUSTED_PROXIES),
Controller_Settings::OPTION_REQUIRE_2FA_ADMIN => Controller_Settings::shared()->get(Controller_Settings::OPTION_REQUIRE_2FA_ADMIN),
Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED => Controller_Settings::shared()->get(Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED),
Controller_Settings::OPTION_GLOBAL_NOTICES => Controller_Settings::shared()->get(Controller_Settings::OPTION_GLOBAL_NOTICES),
Controller_Settings::OPTION_REMEMBER_DEVICE_ENABLED => Controller_Settings::shared()->get(Controller_Settings::OPTION_REMEMBER_DEVICE_ENABLED),
Controller_Settings::OPTION_REMEMBER_DEVICE_DURATION => Controller_Settings::shared()->get(Controller_Settings::OPTION_REMEMBER_DEVICE_DURATION),
Controller_Settings::OPTION_ALLOW_XML_RPC => Controller_Settings::shared()->get(Controller_Settings::OPTION_ALLOW_XML_RPC),
Controller_Settings::OPTION_ENABLE_AUTH_CAPTCHA => Controller_Settings::shared()->get(Controller_Settings::OPTION_ENABLE_AUTH_CAPTCHA),
Controller_Settings::OPTION_RECAPTCHA_THRESHOLD => Controller_Settings::shared()->get(Controller_Settings::OPTION_RECAPTCHA_THRESHOLD),
Controller_Settings::OPTION_LAST_SECRET_REFRESH => Controller_Settings::shared()->get(Controller_Settings::OPTION_LAST_SECRET_REFRESH),
);
// Convert the database strings to typed values.
foreach ($lsFields as $lsField => $value) {
$lsFields[$lsField] = Controller_Settings::shared()->clean($lsField, $value);
}
if (!$fields) {
foreach (wfConfig::$defaultConfig as $group => $groupOptions) {
foreach ($groupOptions as $field => $values) {
$fields[] = $field;
}
}
foreach ($wafFields as $wafField => $value) {
$fields[] = 'waf.' . $wafField;
}
foreach ($lsFields as $lsField => $value) {
$fields[] = 'wfls_settings_' . $lsField;
}
}
foreach ($fields as $field) {
if (strpos($field, 'waf.') === 0) {
$wafField = substr($field, 4);
if (array_key_exists($wafField, $wafFields)) {
$config['waf'][$wafField] = $wafFields[$wafField];
}
continue;
}
if (strpos($field, 'wfls_settings_') === 0) {
$lsField = substr($field, 14);
if (array_key_exists($lsField, $lsFields)) {
$config['wfls_settings_' . $lsField] = $lsFields[$lsField];
}
continue;
}
if (array_key_exists($field, wfConfig::$defaultConfig['checkboxes'])) {
$config[$field] = (bool) wfConfig::get($field);
} else if (array_key_exists($field, wfConfig::$defaultConfig['otherParams']) ||
array_key_exists($field, wfConfig::$defaultConfig['defaultsOnly'])) {
$configConfig = !empty(wfConfig::$defaultConfig['otherParams'][$field]) ?
wfConfig::$defaultConfig['otherParams'][$field] : wfConfig::$defaultConfig['defaultsOnly'][$field];
if (!empty($configConfig['validation']['type'])) {
switch ($configConfig['validation']['type']) {
case wfConfig::TYPE_INT:
$config[$field] = wfConfig::getInt($field);
break;
case wfConfig::TYPE_DOUBLE:
case wfConfig::TYPE_FLOAT:
$config[$field] = floatval(wfConfig::get($field));
break;
case wfConfig::TYPE_BOOL:
$config[$field] = (bool) wfConfig::get($field);
break;
case wfConfig::TYPE_ARRAY:
$config[$field] = wfConfig::get_ser($field);
break;
case wfConfig::TYPE_STRING:
default:
$config[$field] = wfConfig::get($field);
break;
}
} else {
$config[$field] = wfConfig::get($field);
}
} else if (in_array($field, wfConfig::$serializedOptions)) {
$config[$field] = wfConfig::get_ser($field);
}
}
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
parse_str($api->makeAPIQueryString(), $qs);
$systemInfo = json_decode(wfUtils::base64url_decode($qs['s']), true);
$systemInfo['output_buffering'] = ini_get('output_buffering');
$systemInfo['ip'] = wfUtils::getIPAndServerVariable();
$systemInfo['detected_ips'] = wfUtils::getAllServerVariableIPs();
$systemInfo['admin_url'] = network_admin_url();
$response = rest_ensure_response(array(
'config' => $config,
'info' => $systemInfo,
));
return $response;
}
/**
* @param WP_REST_Request $request
* @return mixed|WP_REST_Response
*/
public function setConfig($request) {
wfCentral::preventConfigurationSync();
$fields = $request['fields'];
if (is_array($fields) && $fields) {
$loginSecurityConfig = array();
foreach ($fields as $key => $value) {
if (strpos($key, 'wfls_settings_') === 0) {
$lsField = substr($key, 14);
$loginSecurityConfig[$lsField] = $value;
}
}
if ($loginSecurityConfig) {
$errors = Controller_Settings::shared()->validate_multiple($loginSecurityConfig);
if ($errors !== true) {
if (count($errors) == 1) {
return new WP_Error('rest_set_config_error',
sprintf(__('An error occurred while saving the configuration: %s', 'wordfence'), $errors[0]['error']),
array('status' => 422));
} else if (count($errors) > 1) {
$compoundMessage = array();
foreach ($errors as $e) {
$compoundMessage[] = $e['error'];
}
return new WP_Error('rest_set_config_error',
sprintf(__('Errors occurred while saving the configuration: %s', 'wordfence'), implode(', ', $compoundMessage)),
array('status' => 422));
}
return new WP_Error('rest_set_config_error',
__('Errors occurred while saving the configuration.', 'wordfence'),
array('status' => 422));
}
try {
Controller_Settings::shared()->set_multiple($loginSecurityConfig);
foreach ($fields as $key => $value) {
if (strpos($key, 'wfls_settings_') === 0) {
unset($fields[$key]);
}
}
} catch (Exception $e) {
return new WP_Error('rest_save_config_error',
sprintf(__('A server error occurred while saving the configuration: %s', 'wordfence'), $e->getMessage()),
array('status' => 500));
}
}
$errors = wfConfig::validate($fields);
if ($errors !== true) {
if (count($errors) == 1) {
return new WP_Error('rest_set_config_error',
sprintf(__('An error occurred while saving the configuration: %s', 'wordfence'), $errors[0]['error']),
array('status' => 422));
} else if (count($errors) > 1) {
$compoundMessage = array();
foreach ($errors as $e) {
$compoundMessage[] = $e['error'];
}
return new WP_Error('rest_set_config_error',
sprintf(__('Errors occurred while saving the configuration: %s', 'wordfence'), implode(', ', $compoundMessage)),
array('status' => 422));
}
return new WP_Error('rest_set_config_error',
__('Errors occurred while saving the configuration.', 'wordfence'),
array('status' => 422));
}
try {
wfConfig::save($fields);
return rest_ensure_response(array(
'success' => true,
));
} catch (Exception $e) {
return new WP_Error('rest_save_config_error',
sprintf(__('A server error occurred while saving the configuration: %s', 'wordfence'), $e->getMessage()),
array('status' => 500));
}
}
return new WP_Error('rest_save_config_error',
__("Validation error: 'fields' parameter is empty or not an array.", 'wordfence'),
array('status' => 422));
}
/**
* @param WP_REST_Request $request
* @return mixed|WP_REST_Response
*/
public function disconnect($request) {
self::disconnectConfig();
return rest_ensure_response(array(
'success' => true,
));
}
/**
* @param WP_REST_Request $request
* @return mixed|WP_REST_Response
*/
public function premiumConnect($request) {
require_once(WORDFENCE_PATH . '/crypto/vendor/paragonie/sodium_compat/autoload-fast.php');
// Store values sent by Central.
$wordfenceCentralPK = $request['public-key'];
$wordfenceCentralSiteData = $request['site-data'];
$wordfenceCentralSiteID = $request['site-id'];
$keypair = ParagonIE_Sodium_Compat::crypto_sign_keypair();
$publicKey = ParagonIE_Sodium_Compat::crypto_sign_publickey($keypair);
$secretKey = ParagonIE_Sodium_Compat::crypto_sign_secretkey($keypair);
wfConfig::set('wordfenceCentralSecretKey', $secretKey);
wfConfig::set('wordfenceCentralConnected', 1);
wfConfig::set('wordfenceCentralCurrentStep', 6);
wfConfig::set('wordfenceCentralPK', pack("H*", $wordfenceCentralPK));
wfConfig::set('wordfenceCentralSiteData', json_encode($wordfenceCentralSiteData));
wfConfig::set('wordfenceCentralSiteID', $wordfenceCentralSiteID);
wfConfig::set('wordfenceCentralConnectTime', time());
wfConfig::set('wordfenceCentralConnectEmail', !empty($this->tokenData['adminEmail']) ? $this->tokenData['adminEmail'] : null);
// Return values created by Wordfence.
return rest_ensure_response(array(
'success' => true,
'public-key' => ParagonIE_Sodium_Compat::bin2hex($publicKey),
));
}
}