| @@ -2,258 +2,835 @@ | ||
| 2 | 2 | /* |
| 3 | 3 | Plugin Name: Two Factor Authentication |
| 4 | 4 | Plugin URI: https://www.simbahosting.co.uk/s3/product/two-factor-authentication/ |
| 5 | 5 | Description: Secure your WordPress login forms with two factor authentication - including WooCommerce login forms |
| 6 | -Author: David Anderson, original plugin by Oskar Hane and enhanced by Dee Nutbourne | |
| 6 | +Author: David Nutbourne + David Anderson, original plugin by Oskar Hane | |
| 7 | 7 | Author URI: https://www.simbahosting.co.uk |
| 8 | -Version: 1.14.4 | |
| 9 | -Text Domain: two-factor-authentication | |
| 10 | -Domain Path: /languages | |
| 8 | +Version: 1.2.6 | |
| 11 | 9 | License: GPLv2 or later |
| 12 | 10 | */ |
| 13 | 11 | |
| 14 | -register_activation_hook(__FILE__, 'simba_two_factor_authentication_activation'); | |
| 12 | +define('SIMBA_TFA_TEXT_DOMAIN', 'two-factor-authentication'); | |
| 13 | +define('SIMBA_TFA_PLUGIN_DIR', dirname( __FILE__ )); | |
| 14 | +define('SIMBA_TFA_PLUGIN_URL', plugins_url('', __FILE__)); | |
| 15 | 15 | |
| 16 | -if (!function_exists('simba_two_factor_authentication_activation')) { | |
| 17 | - function simba_two_factor_authentication_activation() { | |
| 18 | - if (!empty($GLOBALS['simba_two_factor_authentication'])) { | |
| 19 | - $is_2fa_plugin_active = false; | |
| 20 | - $installed_plugins_slugs = array_keys(get_plugins()); | |
| 21 | - foreach ($installed_plugins_slugs as $installed_plugin_slug) { | |
| 22 | - if (is_plugin_active($installed_plugin_slug)) { | |
| 23 | - $temp_split_plugin_slug = explode('/', $installed_plugin_slug); | |
| 24 | - if (isset($temp_split_plugin_slug[1]) && 'two-factor-login.php' == $temp_split_plugin_slug[1]) { | |
| 25 | - $is_2fa_plugin_active = true; | |
| 26 | - break; | |
| 27 | - } | |
| 28 | - } | |
| 16 | +class Simba_Two_Factor_Authentication { | |
| 17 | + | |
| 18 | + public $version = '1.2.6'; | |
| 19 | + private $php_required = '5.3'; | |
| 20 | + | |
| 21 | + private $frontend; | |
| 22 | + | |
| 23 | + public function __construct() { | |
| 24 | + | |
| 25 | + if (version_compare(PHP_VERSION, $this->php_required, '<' )) { | |
| 26 | + add_action('all_admin_notices', array($this, 'admin_notice_insufficient_php')); | |
| 27 | + $abort = true; | |
| 28 | + } | |
| 29 | + | |
| 30 | + if (!function_exists('mcrypt_get_iv_size')) { | |
| 31 | + add_action('all_admin_notices', array($this, 'admin_notice_missing_mcrypt')); | |
| 32 | + $abort = true; | |
| 33 | + } | |
| 34 | + | |
| 35 | + if (!empty($abort)) return; | |
| 36 | + | |
| 37 | + if (file_exists(SIMBA_TFA_PLUGIN_DIR.'/premium.php')) include_once(SIMBA_TFA_PLUGIN_DIR.'/premium.php'); | |
| 38 | + | |
| 39 | + add_action('wp_ajax_nopriv_simbatfa-init-otp', array($this, 'tfaInitLogin')); | |
| 40 | + | |
| 41 | + add_action('wp_ajax_simbatfa_shared_ajax', array($this, 'shared_ajax')); | |
| 42 | + | |
| 43 | + add_action('woocommerce_before_customer_login_form', array($this, 'woocommerce_before_customer_login_form')); | |
| 44 | + // The login form on the checkout doesn't call the woocommerce_before_customer_login_form action | |
| 45 | + add_action('woocommerce_before_checkout_form', array($this, 'woocommerce_before_customer_login_form')); | |
| 46 | + | |
| 47 | + if (is_admin()) { | |
| 48 | + //Save settings | |
| 49 | + add_action('admin_init', array($this, 'check_possible_reset')); | |
| 50 | + | |
| 51 | + //Add to Settings menu on sites | |
| 52 | + add_action('admin_menu', array($this, 'menu_entry_for_admin')); | |
| 53 | + | |
| 54 | + //Add settings link in plugin list | |
| 55 | + $plugin = plugin_basename(__FILE__); | |
| 56 | + add_filter("plugin_action_links_".$plugin, array($this, 'addPluginSettingsLink' )); | |
| 57 | + add_filter('network_admin_plugin_action_links_'.$plugin, array($this, 'addPluginSettingsLink' )); | |
| 58 | + | |
| 59 | + // Entry that everybody gets | |
| 60 | + add_action('network_admin_menu', array($this, 'admin_menu')); | |
| 61 | + add_action('admin_menu', array($this, 'admin_menu')); | |
| 62 | + | |
| 63 | + } else { | |
| 64 | + add_action('init', array($this, 'check_possible_reset')); | |
| 65 | + } | |
| 66 | + | |
| 67 | + add_action('plugins_loaded', array($this, 'plugins_loaded')); | |
| 68 | + add_action('init', array($this, 'init')); | |
| 69 | + | |
| 70 | + //Show off sync message for hotp | |
| 71 | + add_action('admin_notices', array($this, 'tfaShowHOTPOffSyncMessage')); | |
| 72 | + add_action('login_enqueue_scripts', array($this, 'login_enqueue_scripts')); | |
| 73 | + | |
| 74 | + if (!defined('TWO_FACTOR_DISABLE') || !TWO_FACTOR_DISABLE) { | |
| 75 | + add_filter('authenticate', array($this, 'tfaVerifyCodeAndUser'), 99999999999, 3); | |
| 76 | + } | |
| 77 | + | |
| 78 | + if (file_exists(SIMBA_TFA_PLUGIN_DIR.'/updater/updater.php')) include_once(SIMBA_TFA_PLUGIN_DIR.'/updater/updater.php'); | |
| 79 | + | |
| 80 | + if (defined('DOING_AJAX') && DOING_AJAX && defined('WP_ADMIN') && WP_ADMIN && !empty($_REQUEST['action']) && 'simbatfa-init-otp' == $_REQUEST['action']) { | |
| 81 | + // Try to prevent PHP notices breaking the AJAX conversation | |
| 82 | + $this->output_buffering = true; | |
| 83 | + $this->logged = array(); | |
| 84 | + set_error_handler(array($this, 'get_php_errors'), E_ALL & ~E_STRICT); | |
| 85 | + ob_start(); | |
| 86 | + } | |
| 87 | + | |
| 88 | + } | |
| 89 | + | |
| 90 | + public function get_php_errors($errno, $errstr, $errfile, $errline) { | |
| 91 | + if (0 == error_reporting()) return true; | |
| 92 | + $logline = $this->php_error_to_logline($errno, $errstr, $errfile, $errline); | |
| 93 | + $this->logged[] = $logline; | |
| 94 | + # Don't pass it up the chain (since it's going to be output to the user always) | |
| 95 | + return true; | |
| 96 | + } | |
| 97 | + | |
| 98 | + public function php_error_to_logline($errno, $errstr, $errfile, $errline) { | |
| 99 | + switch ($errno) { | |
| 100 | + case 1: $e_type = 'E_ERROR'; break; | |
| 101 | + case 2: $e_type = 'E_WARNING'; break; | |
| 102 | + case 4: $e_type = 'E_PARSE'; break; | |
| 103 | + case 8: $e_type = 'E_NOTICE'; break; | |
| 104 | + case 16: $e_type = 'E_CORE_ERROR'; break; | |
| 105 | + case 32: $e_type = 'E_CORE_WARNING'; break; | |
| 106 | + case 64: $e_type = 'E_COMPILE_ERROR'; break; | |
| 107 | + case 128: $e_type = 'E_COMPILE_WARNING'; break; | |
| 108 | + case 256: $e_type = 'E_USER_ERROR'; break; | |
| 109 | + case 512: $e_type = 'E_USER_WARNING'; break; | |
| 110 | + case 1024: $e_type = 'E_USER_NOTICE'; break; | |
| 111 | + case 2048: $e_type = 'E_STRICT'; break; | |
| 112 | + case 4096: $e_type = 'E_RECOVERABLE_ERROR'; break; | |
| 113 | + case 8192: $e_type = 'E_DEPRECATED'; break; | |
| 114 | + case 16384: $e_type = 'E_USER_DEPRECATED'; break; | |
| 115 | + case 30719: $e_type = 'E_ALL'; break; | |
| 116 | + default: $e_type = "E_UNKNOWN ($errno)"; break; | |
| 117 | + } | |
| 118 | + | |
| 119 | + if (!is_string($errstr)) $errstr = serialize($errstr); | |
| 120 | + | |
| 121 | + if (0 === strpos($errfile, ABSPATH)) $errfile = substr($errfile, strlen(ABSPATH)); | |
| 122 | + | |
| 123 | + return "PHP event: code $e_type: $errstr (line $errline, $errfile)"; | |
| 124 | + | |
| 125 | + } | |
| 126 | + | |
| 127 | + public function init() { | |
| 128 | + if ((!is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) && is_user_logged_in() && file_exists(SIMBA_TFA_PLUGIN_DIR.'/includes/tfa_frontend.php')) { | |
| 129 | + $this->load_frontend(); | |
| 130 | + } else { | |
| 131 | + add_shortcode('twofactor_user_settings', array($this, 'shortcode_when_not_logged_in')); | |
| 132 | + } | |
| 133 | + } | |
| 134 | + | |
| 135 | + public function admin_notice_insufficient_php() { | |
| 136 | + $this->show_admin_warning('<strong>'.__('Higher PHP version required', 'updraftplus').'</strong><br> '.sprintf(__('The Two Factor Authentication plugin requires PHP version %s or higher - your current version is only %s.', SIMBA_TFA_TEXT_DOMAIN), $this->php_required, PHP_VERSION), 'error'); | |
| 137 | + } | |
| 138 | + | |
| 139 | + public function admin_notice_missing_mcrypt() { | |
| 140 | + $this->show_admin_warning('<strong>'.__('PHP Mcrypt module required', 'updraftplus').'</strong><br> '.__('The Two Factor Authentication plugin requires the PHP mcrypt module to be installed. Please ask your web hosting company to install it.', SIMBA_TFA_TEXT_DOMAIN), 'error'); | |
| 141 | + } | |
| 142 | + | |
| 143 | + public function show_admin_warning($message, $class = "updated") { | |
| 144 | + echo '<div class="tfamessage '.$class.'">'."<p>$message</p></div>"; | |
| 145 | + } | |
| 146 | + | |
| 147 | + public function getTFA() { | |
| 148 | + if (!class_exists('HOTP')) require_once(SIMBA_TFA_PLUGIN_DIR.'/hotp-php-master/hotp.php'); | |
| 149 | + if (!class_exists('Base32')) require_once(SIMBA_TFA_PLUGIN_DIR.'/Base32/Base32.php'); | |
| 150 | + if (!class_exists('Simba_TFA')) require_once(SIMBA_TFA_PLUGIN_DIR.'/includes/class.TFA.php'); | |
| 151 | + | |
| 152 | + $tfa = new Simba_TFA(new Base32(), new HOTP()); | |
| 153 | + | |
| 154 | + return $tfa; | |
| 155 | + } | |
| 156 | + | |
| 157 | + // "Shared" - i.e. could be called from either front-end or back-end | |
| 158 | + public function shared_ajax() { | |
| 159 | + if (empty($_POST['subaction']) || empty($_POST['nonce']) || !is_user_logged_in() || !wp_verify_nonce($_POST['nonce'], 'tfa_shared_nonce')) die('Security check (3).'); | |
| 160 | + | |
| 161 | + if ($_POST['subaction'] == 'refreshotp') { | |
| 162 | + | |
| 163 | + global $current_user; | |
| 164 | + | |
| 165 | + $tfa_priv_key_64 = get_user_meta($current_user->ID, 'tfa_priv_key_64', true); | |
| 166 | + | |
| 167 | + if (!$tfa_priv_key_64) { | |
| 168 | + echo json_encode(array('code' => '')); | |
| 169 | + die; | |
| 29 | 170 | } |
| 30 | 171 | |
| 31 | - // We should prevent activation if and only if either the 2FA Premium or 2FA Free plugin is active. | |
| 32 | - // We should not prevent activation if either the AIOWPS plugin is active. | |
| 33 | - if ($is_2fa_plugin_active) { | |
| 34 | - if (file_exists(__DIR__.'/simba-tfa/premium/loader.php')) { | |
| 35 | - wp_die(__('To activate Two Factor Authentication Premium, first de-activate the free version (only one can be active at once).', SIMBA_TFA_TEXT_DOMAIN)); | |
| 36 | - } else { // If the 2FA Premium plugin is active and tries to activate the 2FA Free Plugin, it throws a fatal error and stops activating the free version. | |
| 37 | - wp_die(__("You can't activate Two Factor Authentication (Free) because Two Factor Authentication Premium is active (only one can be active at once).", SIMBA_TFA_TEXT_DOMAIN)); | |
| 38 | - } | |
| 172 | + echo json_encode(array('code' => $this->getTFA()->generateOTP($current_user->ID, $tfa_priv_key_64))); | |
| 173 | + exit; | |
| 174 | + } | |
| 175 | + | |
| 176 | + } | |
| 177 | + | |
| 178 | + public function tfaInitLogin() { | |
| 179 | + | |
| 180 | + if (empty($_POST['user'])) die('Security check (2).'); | |
| 181 | + | |
| 182 | + if (defined('TWO_FACTOR_DISABLE') && TWO_FACTOR_DISABLE) { | |
| 183 | + $res = false; | |
| 184 | + } else { | |
| 185 | + $tfa = $this->getTFA(); | |
| 186 | + $res = $tfa->preAuth(array('log' => (string)$_POST['user'])); | |
| 187 | + } | |
| 188 | + | |
| 189 | + $results = array('jsonstarter' => 'justhere', 'status' => $res); | |
| 190 | + | |
| 191 | + if (!empty($this->output_buffering)) { | |
| 192 | + if (!empty($this->logged)) { | |
| 193 | + $results['php_output'] = $this->logged; | |
| 39 | 194 | } |
| 195 | + restore_error_handler(); | |
| 196 | + $buffered = ob_get_clean(); | |
| 197 | + if ($buffered) $results['extra_output'] = $buffered; | |
| 40 | 198 | } |
| 199 | + | |
| 200 | + echo json_encode($results); | |
| 201 | + exit; | |
| 41 | 202 | } |
| 42 | -} | |
| 203 | + | |
| 43 | 204 | |
| 44 | -if (!defined('SIMBA_TFA_TEXT_DOMAIN')) define('SIMBA_TFA_TEXT_DOMAIN', 'two-factor-authentication'); | |
| 45 | -if (!class_exists('Simba_Two_Factor_Authentication')) require dirname(__FILE__).'/simba-tfa/simba-tfa.php'; | |
| 205 | + // Here's where the login action happens. Called on the 'authenticate' action. | |
| 206 | + public function tfaVerifyCodeAndUser($user, $username, $password) { | |
| 46 | 207 | |
| 47 | -if (!class_exists('Simba_Two_Factor_Authentication_Plugin')): | |
| 48 | -/** | |
| 49 | - * This parent-child relationship enables the two to be split without affecting backwards compatibility for developers making direct calls | |
| 50 | - * | |
| 51 | - * This class is for the plugin encapsulation. | |
| 52 | - */ | |
| 53 | -class Simba_Two_Factor_Authentication_Plugin extends Simba_Two_Factor_Authentication { | |
| 54 | - | |
| 55 | - public $version = '1.14.4'; | |
| 56 | - | |
| 57 | - const PHP_REQUIRED = '5.6'; | |
| 58 | - | |
| 59 | - /** | |
| 60 | - * Constructor, run upon plugin initiation | |
| 61 | - * | |
| 62 | - * @uses __FILE__ | |
| 63 | - */ | |
| 64 | - public function __construct() { | |
| 208 | + $tfa = $this->getTFA(); | |
| 65 | 209 | |
| 66 | - add_action('plugins_loaded', array($this, 'plugins_loaded_load_textdomain')); | |
| 210 | + if (is_wp_error($user)) return $user; | |
| 211 | + | |
| 212 | + $params = $_POST; | |
| 213 | + $params['log'] = $username; | |
| 214 | + $params['caller'] = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['REQUEST_URI']; | |
| 67 | 215 | |
| 68 | - if (version_compare(PHP_VERSION, self::PHP_REQUIRED, '<' )) { | |
| 69 | - add_action('all_admin_notices', array($this, 'admin_notice_insufficient_php')); | |
| 70 | - $abort = true; | |
| 216 | + $code_ok = $tfa->authUserFromLogin($params); | |
| 217 | + | |
| 218 | + if (is_wp_error($code_ok)) return $code_ok; | |
| 219 | + | |
| 220 | + if (!$code_ok) return new WP_Error('authentication_failed', '<strong>'.__('Error:', SIMBA_TFA_TEXT_DOMAIN).'</strong> '.__('The one-time password (TFA code) you entered was incorrect.', SIMBA_TFA_TEXT_DOMAIN)); | |
| 221 | + | |
| 222 | + if ($user) return $user; | |
| 223 | + | |
| 224 | + return wp_authenticate_username_password(null, $username, $password); | |
| 225 | + } | |
| 226 | + | |
| 227 | + public function tfaRegisterTwoFactorAuthSettings() | |
| 228 | + { | |
| 229 | + global $wp_roles; | |
| 230 | + if (!isset($wp_roles)) | |
| 231 | + $wp_roles = new WP_Roles(); | |
| 232 | + | |
| 233 | + foreach($wp_roles->role_names as $id => $name) | |
| 234 | + { | |
| 235 | + register_setting('tfa_user_roles_group', 'tfa_'.$id); | |
| 236 | + register_setting('tfa_user_roles_required_group', 'tfa_required_'.$id); | |
| 71 | 237 | } |
| 72 | 238 | |
| 73 | - if (!function_exists('mcrypt_get_iv_size') && !function_exists('openssl_cipher_iv_length')) { | |
| 74 | - add_action('all_admin_notices', array($this, 'admin_notice_missing_mcrypt_and_openssl')); | |
| 75 | - $abort = true; | |
| 239 | + register_setting('tfa_user_roles_required_group', 'tfa_requireafter'); | |
| 240 | + register_setting('simba_tfa_default_hmac_group', 'tfa_default_hmac'); | |
| 241 | + register_setting('tfa_xmlrpc_status_group', 'tfa_xmlrpc_on'); | |
| 242 | + } | |
| 243 | + | |
| 244 | + public function tfaListEnableRadios($user_id, $long_label = false) | |
| 245 | + { | |
| 246 | + if(!$user_id) | |
| 247 | + return; | |
| 248 | + | |
| 249 | + $setting = get_user_meta($user_id, 'tfa_enable_tfa', true); | |
| 250 | + $setting = !$setting ? false : $setting; | |
| 251 | + | |
| 252 | + $tfa = $this->getTFA(); | |
| 253 | + | |
| 254 | + if ($tfa->isRequiredForUser($user_id)) { | |
| 255 | + $requireafter = absint($this->get_option('tfa_requireafter')); | |
| 256 | + | |
| 257 | + echo '<p class="tfa_required_warning" style="font-weight:bold; font-style:italics;">'.sprintf(__('N.B. This site is configured to forbid you to log in if you disable two-factor authentication after your account is %d days old', SIMBA_TFA_TEXT_DOMAIN), $requireafter).'</p>'; | |
| 76 | 258 | } |
| 259 | + | |
| 260 | + $tfa_enabled_label = ($long_label) ? __('Enable two-factor authentication', SIMBA_TFA_TEXT_DOMAIN) : __('Enabled', SIMBA_TFA_TEXT_DOMAIN); | |
| 261 | + $tfa_disabled_label = ($long_label) ? __('Disable two-factor authentication', SIMBA_TFA_TEXT_DOMAIN) : __('Disabled', SIMBA_TFA_TEXT_DOMAIN); | |
| 262 | + | |
| 263 | + print '<input type="radio" class="tfa_enable_radio" id="tfa_enable_tfa_true" name="tfa_enable_tfa" value="true" '.($setting == true ? 'checked="checked"' :'').'> <label class="tfa_enable_radio_label" for="tfa_enable_tfa_true">'.apply_filters('simbatfa_radiolabel_enabled', $tfa_enabled_label, $long_label).'</label> <br>'; | |
| 264 | + | |
| 265 | + print '<input type="radio" class="tfa_enable_radio" id="tfa_enable_tfa_false" name="tfa_enable_tfa" value="false" '.($setting == false ? 'checked="checked"' :'').'> <label class="tfa_enable_radio_label" for="tfa_enable_tfa_false">'.apply_filters('simbatfa_radiolabel_disabled', $tfa_disabled_label, $long_label).'</label> <br>'; | |
| 266 | + } | |
| 77 | 267 | |
| 78 | - if (!empty($abort)) return; | |
| 268 | + | |
| 269 | + public function tfaListAlgorithmRadios($user_id) | |
| 270 | + { | |
| 271 | + if(!$user_id) return; | |
| 272 | + | |
| 273 | + $types = array('totp' => __('TOTP (time based - most common algorithm; used by Google Authenticator)', SIMBA_TFA_TEXT_DOMAIN), 'hotp' => __('HOTP (event based)', SIMBA_TFA_TEXT_DOMAIN)); | |
| 79 | 274 | |
| 80 | - // Menu entries | |
| 81 | - add_action('admin_menu', array($this, 'menu_entry_for_admin')); | |
| 82 | - add_action('admin_menu', array($this, 'menu_entry_for_user')); | |
| 83 | - add_action('network_admin_menu', array($this, 'menu_entry_for_user')); | |
| 275 | + $setting = get_user_meta($user_id, 'tfa_algorithm_type', true); | |
| 276 | + $setting = $setting === false || !$setting ? 'totp' : $setting; | |
| 277 | + | |
| 278 | + foreach($types as $id => $name) { | |
| 279 | + print '<input type="radio" id="tfa_algorithm_type_'.esc_attr($id).'" name="tfa_algorithm_type" value="'.$id.'" '.($setting == $id ? 'checked="checked"' :'').'> <label for="tfa_algorithm_type_'.esc_attr($id).'">'.$name."</label><br>\n"; | |
| 280 | + } | |
| 281 | + } | |
| 282 | + | |
| 283 | + public function get_option($key) { | |
| 284 | + if (!is_multisite()) return get_option($key); | |
| 285 | + switch_to_blog(1); | |
| 286 | + $v = get_option($key); | |
| 287 | + restore_current_blog(); | |
| 288 | + return $v; | |
| 289 | + } | |
| 290 | + | |
| 291 | + public function tfaListUserRolesCheckboxes() | |
| 292 | + { | |
| 293 | + | |
| 294 | + if (is_multisite()) { | |
| 295 | + // Not a real WP role; needs separate handling | |
| 296 | + $id = '_super_admin'; | |
| 297 | + $name = __('Multisite Super Admin', SIMBA_TFA_TEXT_DOMAIN); | |
| 298 | + $setting = $this->get_option('tfa_'.$id); | |
| 299 | + $setting = $setting === false || $setting ? 1 : 0; | |
| 300 | + | |
| 301 | + print '<input type="checkbox" id="tfa_'.$id.'" name="tfa_'.$id.'" value="1" '.($setting ? 'checked="checked"' :'').'> <label for="tfa_'.$id.'">'.htmlspecialchars($name)."</label><br>\n"; | |
| 302 | + } | |
| 303 | + | |
| 304 | + global $wp_roles; | |
| 305 | + if (!isset($wp_roles)) $wp_roles = new WP_Roles(); | |
| 84 | 306 | |
| 85 | - // Add settings link in plugin list | |
| 86 | - $plugin = plugin_basename(__FILE__); | |
| 87 | - add_filter("plugin_action_links_$plugin", array($this, 'add_plugin_settings_link')); | |
| 88 | - add_filter("network_admin_plugin_action_links_$plugin", array($this, 'add_plugin_settings_link')); | |
| 307 | + foreach($wp_roles->role_names as $id => $name) | |
| 308 | + { | |
| 309 | + $setting = $this->get_option('tfa_'.$id); | |
| 310 | + $setting = $setting === false || $setting ? 1 : 0; | |
| 311 | + | |
| 312 | + print '<input type="checkbox" id="tfa_'.$id.'" name="tfa_'.$id.'" value="1" '.($setting ? 'checked="checked"' :'').'> <label for="tfa_'.$id.'">'.htmlspecialchars($name)."</label><br>\n"; | |
| 313 | + } | |
| 89 | 314 | |
| 90 | - $this->set_user_settings_page_slug('two-factor-auth-user'); | |
| 315 | + } | |
| 91 | 316 | |
| 92 | - $settings_page_heading = sprintf(__('Two Factor Authentication (Version: %s) - Admin Settings', 'all-in-one-wp-security-and-firewall'), $this->version); | |
| 93 | - $this->set_settings_page_heading($settings_page_heading); | |
| 317 | + public function tfaListDefaultHMACRadios() | |
| 318 | + { | |
| 319 | + $tfa = $this->getTFA(); | |
| 320 | + $setting = $this->get_option('tfa_default_hmac'); | |
| 321 | + $setting = $setting === false || !$setting ? $tfa->default_hmac : $setting; | |
| 322 | + | |
| 323 | + $types = array('totp' => __('TOTP (time based - most common algorithm; used by Google Authenticator)', SIMBA_TFA_TEXT_DOMAIN), 'hotp' => __('HOTP (event based)', SIMBA_TFA_TEXT_DOMAIN)); | |
| 324 | + | |
| 325 | + foreach($types as $id => $name) | |
| 326 | + print '<input type="radio" id="tfa_default_hmac_'.esc_attr($id).'" name="tfa_default_hmac" value="'.$id.'" '.($setting == $id ? 'checked="checked"' :'').'> '.'<label for="tfa_default_hmac_'.esc_attr($id).'">'."$name</label><br>\n"; | |
| 327 | + } | |
| 94 | 328 | |
| 95 | - $this->set_plugin_translate_url('https://translate.wordpress.org/projects/wp-plugins/two-factor-authentication/'); | |
| 96 | - $this->set_site_wide_administration_url(admin_url('options-general.php?page=two-factor-auth')); | |
| 97 | - $this->set_premium_version_url('https://www.simbahosting.co.uk/s3/product/two-factor-authentication/'); | |
| 98 | - $this->set_faq_url('https://wordpress.org/plugins/two-factor-authentication/#faq'); | |
| 99 | - parent::__construct(); | |
| 329 | + public function tfaListXMLRPCStatusRadios() | |
| 330 | + { | |
| 331 | + $tfa = $this->getTFA(); | |
| 332 | + $setting = $this->get_option('tfa_xmlrpc_on'); | |
| 333 | + $setting = $setting === false || !$setting ? 0 : 1; | |
| 100 | 334 | |
| 335 | + $types = array( | |
| 336 | + '0' => __('Do not require 2FA over XMLRPC (best option if you must use XMLRPC and your client does not support 2FA)', SIMBA_TFA_TEXT_DOMAIN), | |
| 337 | + '1' => __('Do require 2FA over XMLRPC (best option if you do not use XMLRPC or are unsure)', SIMBA_TFA_TEXT_DOMAIN) | |
| 338 | + ); | |
| 339 | + | |
| 340 | + foreach($types as $id => $name) | |
| 341 | + print '<input type="radio" name="tfa_xmlrpc_on" id="tfa_xmlrpc_on_'.$id.'" value="'.$id.'" '.($setting == $id ? 'checked="checked"' :'').'> <label for="tfa_xmlrpc_on_'.$id.'">'.$name."</label><br>\n"; | |
| 101 | 342 | } |
| 102 | - | |
| 103 | - /** | |
| 104 | - * Runs upon the WP filters plugin_action_links_(plugin) and network_plugin_action_links_(plugin) | |
| 105 | - * | |
| 106 | - * @param Array $links | |
| 107 | - * | |
| 108 | - * @return Array | |
| 109 | - */ | |
| 110 | - public function add_plugin_settings_link($links) { | |
| 111 | - if (is_multisite()) { | |
| 112 | - switch_to_blog(1); | |
| 113 | - $link = $this->get_settings_link(); | |
| 114 | - restore_current_blog(); | |
| 115 | - array_unshift($links, $link); | |
| 343 | + | |
| 344 | + public function tfaShowAdminSettingsPage() | |
| 345 | + { | |
| 346 | + $tfa = $this->getTFA(); | |
| 347 | + require_once(SIMBA_TFA_PLUGIN_DIR.'/includes/admin_settings.php'); | |
| 348 | + } | |
| 349 | + | |
| 350 | + public function tfaShowUserSettingsPage() | |
| 351 | + { | |
| 352 | + $tfa = $this->getTFA(); | |
| 353 | + include SIMBA_TFA_PLUGIN_DIR.'/includes/user_settings.php'; | |
| 354 | + } | |
| 355 | + | |
| 356 | + public function admin_menu() | |
| 357 | + { | |
| 358 | + $tfa = $this->getTFA(); | |
| 359 | + | |
| 360 | + global $current_user; | |
| 361 | + if(!$tfa->isActivatedForUser($current_user->ID)) return; | |
| 362 | + add_menu_page(__('Two Factor Authentication', SIMBA_TFA_TEXT_DOMAIN), __('Two Factor Auth', SIMBA_TFA_TEXT_DOMAIN), 'read', 'two-factor-auth-user', array($this, 'tfaShowUserSettingsPage'), SIMBA_TFA_PLUGIN_URL.'/img/tfa_admin_icon_16x16.png', 72); | |
| 363 | + } | |
| 364 | + | |
| 365 | + public function menu_entry_for_admin() { | |
| 366 | + | |
| 367 | + // On multisite, only show the entry on site ID 1 - to ensure options get saved in the right place. | |
| 368 | + global $current_site, $wpdb; | |
| 369 | + // $current_site is not the right way to do this - it is internal, and could be anything | |
| 370 | + if (is_multisite() && (!is_super_admin() || !is_object($wpdb) || !isset($wpdb->blogid) || 1 != $wpdb->blogid)) return; | |
| 371 | + | |
| 372 | + add_action( 'admin_init', array($this, 'tfaRegisterTwoFactorAuthSettings' )); | |
| 373 | + | |
| 374 | + add_options_page( | |
| 375 | + __('Two Factor Authentication', SIMBA_TFA_TEXT_DOMAIN), | |
| 376 | + __('Two Factor Authentication', SIMBA_TFA_TEXT_DOMAIN), | |
| 377 | + 'manage_options', | |
| 378 | + 'two-factor-auth', | |
| 379 | + array($this, 'tfaShowAdminSettingsPage') | |
| 380 | + ); | |
| 381 | + } | |
| 382 | + | |
| 383 | + public function addPluginSettingsLink($links) | |
| 384 | + { | |
| 385 | + if (!is_network_admin()) { | |
| 386 | + $link = '<a href="options-general.php?page=two-factor-auth">'.__('Plugin settings', SIMBA_TFA_TEXT_DOMAIN).'</a>'; | |
| 387 | + array_unshift($links, $link); | |
| 116 | 388 | } else { |
| 117 | - $link = $this->get_settings_link(); | |
| 389 | + switch_to_blog(1); | |
| 390 | + $link = '<a href="'.admin_url('options-general.php').'?page=two-factor-auth">'.__('Plugin settings', SIMBA_TFA_TEXT_DOMAIN).'</a>'; | |
| 391 | + restore_current_blog(); | |
| 118 | 392 | array_unshift($links, $link); |
| 119 | 393 | } |
| 120 | - | |
| 121 | - $link2 = '<a href="admin.php?page=two-factor-auth-user">'.__('User settings', 'two-factor-authentication').'</a>'; | |
| 394 | + | |
| 395 | + $link2 = '<a href="admin.php?page=two-factor-auth-user">'.__('User settings', SIMBA_TFA_TEXT_DOMAIN).'</a>'; | |
| 122 | 396 | array_unshift($links, $link2); |
| 397 | + | |
| 398 | + return $links; | |
| 399 | + } | |
| 400 | + | |
| 401 | + public function check_possible_reset() { | |
| 402 | + if(!empty($_GET['simbatfa_priv_key_reset']) && !empty($_REQUEST['nonce']) && wp_verify_nonce($_REQUEST['nonce'], 'simbatfa_reset_private_key')) | |
| 403 | + { | |
| 404 | + $this->reset_private_key_and_emergency_codes(); | |
| 405 | +// if (empty($_REQUEST['noredirect'])) exit; | |
| 406 | + exit; | |
| 407 | + } | |
| 123 | 408 | |
| 124 | - return $links; | |
| 125 | 409 | } |
| 126 | 410 | |
| 127 | - /** | |
| 128 | - * Get 2FA settings anchor tag link. | |
| 129 | - * | |
| 130 | - * @return string 2FA settings anchor tag link. | |
| 131 | - */ | |
| 132 | - private function get_settings_link() { | |
| 133 | - return '<a href="'.admin_url('options-general.php').'?page=two-factor-auth">'.__('Plugin settings', 'two-factor-authentication').'</a>'; | |
| 411 | + public function reset_private_key_and_emergency_codes() { | |
| 412 | + global $current_user; | |
| 413 | + delete_user_meta($current_user->ID, 'tfa_priv_key_64'); | |
| 414 | + delete_user_meta($current_user->ID, 'simba_tfa_emergency_codes_64'); | |
| 415 | + if (empty($_REQUEST['noredirect'])) { | |
| 416 | + wp_safe_redirect( admin_url('admin.php').'?page=two-factor-auth-user&settings-updated=1'); | |
| 417 | + } else { | |
| 418 | + $url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . remove_query_arg(array('simbatfa_priv_key_reset', 'noredirect', 'nonce')); | |
| 419 | + | |
| 420 | + wp_redirect(esc_url_raw($url)); | |
| 421 | + } | |
| 134 | 422 | } |
| 135 | - | |
| 136 | - /** | |
| 137 | - * Runs upon the WP actions admin_menu and network_admin_menu | |
| 138 | - */ | |
| 139 | - public function menu_entry_for_user() { | |
| 423 | + | |
| 424 | + public function reset_link($admin = true) { | |
| 425 | + | |
| 426 | + $url_base = ($admin) ? admin_url('admin.php').'?page=two-factor-auth-user&settings-updated=1' : (( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST']); | |
| 427 | + | |
| 428 | + $add_query_args = array( | |
| 429 | + 'simbatfa_priv_key_reset' => 1, | |
| 430 | + ); | |
| 431 | + if (!$admin) $add_query_args['noredirect'] = 1; | |
| 432 | + | |
| 433 | + $url = $url_base.add_query_arg($add_query_args); | |
| 434 | + | |
| 435 | + $url = wp_nonce_url($url, 'simbatfa_reset_private_key', 'nonce'); | |
| 436 | + | |
| 437 | + return '<a href="javascript:if(confirm(\''.__('Warning: if you reset this key you will have to update your apps with the new one. Are you sure you want this?', SIMBA_TFA_TEXT_DOMAIN).'\')){ window.location = \''.esc_js($url).'\'; }">'.__('Reset private key', SIMBA_TFA_TEXT_DOMAIN).'</a>'; | |
| 438 | + | |
| 439 | + } | |
| 440 | + | |
| 441 | + public function footer() { | |
| 442 | + $ajax_url = admin_url('admin-ajax.php'); | |
| 443 | + // It's possible that FORCE_ADMIN_SSL will make that SSL, whilst the user is on the front-end having logged in over non-SSL - and as a result, their login cookies won't get sent, and they're not registered as logged in. | |
| 444 | + if (!is_admin() && substr(strtolower($ajax_url), 0, 6) == 'https:' && !is_ssl()) { | |
| 445 | + $also_try = 'http:'.substr($ajax_url, 6); | |
| 446 | + } | |
| 447 | + ?> | |
| 448 | + <script> | |
| 449 | + jQuery(document).ready(function($) { | |
| 450 | + $('.simbaotp_qr_container').qrcode({ | |
| 451 | + "render": "image", | |
| 452 | + "text": $('.simbaotp_qr_container:first').data('qrcode'), | |
| 453 | + }); | |
| 454 | + $('.simbaotp_refresh').click(function(e) { | |
| 455 | + e.preventDefault(); | |
| 456 | + $(".simba_current_otp").html('<em><?php echo esc_attr(__('Updating...', SIMBA_TFA_TEXT_DOMAIN));?></em>'); | |
| 457 | + $.post('<?php echo esc_js($ajax_url);?>', { | |
| 458 | + action: "simbatfa_shared_ajax", | |
| 459 | + subaction: "refreshotp", | |
| 460 | + nonce: "<?php echo esc_js(wp_create_nonce("tfa_shared_nonce"));?>" | |
| 461 | + }, function(response) { | |
| 462 | + var got_code = ''; | |
| 463 | + try { | |
| 464 | + var resp = $.parseJSON(response); | |
| 465 | + got_code = resp.code; | |
| 466 | + } catch(err) { | |
| 467 | + <?php if (!isset($also_try)) { ?> | |
| 468 | + alert("<?php echo esc_js(__('Response:', 'SIMBA_TFA_TEXT_DOMAIN')); ?> "+response); | |
| 469 | + <?php } ?> | |
| 470 | + console.log(response); | |
| 471 | + console.log(err); | |
| 472 | + } | |
| 473 | + <?php | |
| 474 | + if (isset($also_try)) { | |
| 475 | + ?> | |
| 476 | + $.post('<?php echo esc_js($also_try);?>', { | |
| 477 | + action: "simbatfa_shared_ajax", | |
| 478 | + subaction: "refreshotp", | |
| 479 | + nonce: "<?php echo esc_js(wp_create_nonce("tfa_shared_nonce"));?>" | |
| 480 | + }, function(response) { | |
| 481 | + try { | |
| 482 | + var resp = $.parseJSON(response); | |
| 483 | + if (resp.code) { | |
| 484 | + $(".simba_current_otp").html(resp.code); | |
| 485 | + } else { | |
| 486 | + console.log(response); | |
| 487 | + console.log("TFA: no code found"); | |
| 488 | + } | |
| 489 | + } catch(err) { | |
| 490 | + alert("<?php echo esc_js(__('Response:', 'SIMBA_TFA_TEXT_DOMAIN')); ?> "+response); | |
| 491 | + console.log(response); | |
| 492 | + console.log(err); | |
| 493 | + } | |
| 494 | + }); | |
| 495 | + <?php } else { ?> | |
| 496 | + if ('' != got_code) { | |
| 497 | + $(".simba_current_otp").html(got_code); | |
| 498 | + } else { | |
| 499 | + console.log("TFA: no code found"); | |
| 500 | + } | |
| 501 | + <?php } ?> | |
| 502 | + }); | |
| 503 | + }); | |
| 504 | + }); | |
| 505 | + </script> | |
| 506 | + <?php | |
| 507 | + } | |
| 508 | + | |
| 509 | + public function print_private_keys($admin, $type = 'full', $user_id = false) { | |
| 510 | + | |
| 511 | + $tfa = $this->getTFA(); | |
| 512 | + global $current_user; | |
| 513 | + | |
| 514 | + if ($user_id == false) $user_id = $current_user->ID; | |
| 515 | + | |
| 516 | + $tfa_priv_key_64 = get_user_meta($user_id, 'tfa_priv_key_64', true); | |
| 517 | + if(!$tfa_priv_key_64) $tfa_priv_key_64 = $tfa->addPrivateKey($user_id); | |
| 518 | + | |
| 519 | + $tfa_priv_key = trim($tfa->getPrivateKeyPlain($tfa_priv_key_64, $user_id)); | |
| 520 | + | |
| 521 | + $tfa_priv_key_32 = Base32::encode($tfa_priv_key); | |
| 522 | + | |
| 523 | + if ('full' == $type) { | |
| 524 | + ?> | |
| 525 | + <strong><?php echo __('Private key (base 32 - used by Google Authenticator and Authy):', SIMBA_TFA_TEXT_DOMAIN);?></strong> | |
| 526 | + <?php echo htmlspecialchars($tfa_priv_key_32); ?><br> | |
| 527 | + | |
| 528 | + <strong><?php echo __('Private key:', SIMBA_TFA_TEXT_DOMAIN);?></strong> | |
| 529 | + <?php echo htmlspecialchars($tfa_priv_key); ?><br> | |
| 530 | + <?php | |
| 531 | + } elseif ('plain' == $type) { | |
| 532 | + echo htmlspecialchars($tfa_priv_key); | |
| 533 | + } elseif ('base32' == $type) { | |
| 534 | + echo htmlspecialchars($tfa_priv_key_32); | |
| 535 | + } elseif ('base64' == $type) { | |
| 536 | + echo htmlspecialchars($tfa_priv_key_64); | |
| 537 | + } | |
| 538 | + } | |
| 539 | + | |
| 540 | + public function current_otp_code($tfa, $user_id = false) { | |
| 541 | + global $current_user; | |
| 542 | + if (false == $user_id) $user_id = $current_user->ID; | |
| 543 | + $tfa_priv_key_64 = get_user_meta($user_id, 'tfa_priv_key_64', true); | |
| 544 | + return '<span class="simba_current_otp">'.$tfa->generateOTP($user_id, $tfa_priv_key_64).'</span>'; | |
| 545 | + } | |
| 546 | + | |
| 547 | + public function add_footer($admin) { | |
| 548 | + static $added_footer; | |
| 549 | + if (empty($added_footer)) { | |
| 550 | + $added_footer = true; | |
| 551 | +// wp_enqueue_script('jquery'); | |
| 552 | + $script_ver = (defined('WP_DEBUG') && WP_DEBUG) ? time() : $this->version; | |
| 553 | + $script_file = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? 'jquery.qrcode.js' : 'jquery.qrcode.min.js'; | |
| 554 | + wp_enqueue_script( 'jquery-qrcode', SIMBA_TFA_PLUGIN_URL.'/includes/jquery-qrcode/'.$script_file, array('jquery'), $script_ver); | |
| 555 | + add_action( $admin ? 'admin_footer' : 'wp_footer' , array($this, 'footer')); | |
| 556 | + } | |
| 557 | + } | |
| 558 | + | |
| 559 | + public function current_codes_box($admin = true, $user_id = false) { | |
| 560 | + | |
| 561 | + global $current_user; | |
| 562 | + | |
| 563 | + if (false == $user_id) { | |
| 564 | + $user_id = $current_user->ID; | |
| 565 | + } | |
| 566 | + | |
| 567 | + $tfa = $this->getTFA(); | |
| 568 | + | |
| 569 | + $this->add_footer($admin); | |
| 570 | + | |
| 571 | + $url = preg_replace('/^https?:\/\//', '', site_url()); | |
| 140 | 572 | |
| 141 | - $this->get_totp_controller()->potentially_port_private_keys(); | |
| 573 | + $tfa_priv_key_64 = get_user_meta($user_id, 'tfa_priv_key_64', true); | |
| 142 | 574 | |
| 575 | + if(!$tfa_priv_key_64) $tfa_priv_key_64 = $tfa->addPrivateKey($user_id); | |
| 576 | + | |
| 577 | + $tfa_priv_key = trim($tfa->getPrivateKeyPlain($tfa_priv_key_64, $user_id)); | |
| 578 | + | |
| 579 | + $tfa_priv_key_32 = Base32::encode($tfa_priv_key); | |
| 580 | + | |
| 581 | + $algorithm_type = $tfa->getUserAlgorithm($user_id); | |
| 582 | + | |
| 583 | + if ($admin) { | |
| 584 | + if ($current_user->ID == $user_id) { | |
| 585 | + echo '<h2>'.__('Current codes', SIMBA_TFA_TEXT_DOMAIN).'</h2>'; | |
| 586 | + } else { | |
| 587 | + $user = get_user_by('id', $user_id); | |
| 588 | + $user_descrip = htmlspecialchars($user->user_nicename.' - '.$user->user_email); | |
| 589 | + echo '<h2>'.sprintf(__('Current codes (login: %s)', SIMBA_TFA_TEXT_DOMAIN), $user_descrip).'</h2>'; | |
| 590 | + } | |
| 591 | + } else { | |
| 592 | +// echo '<h2>'.__('Current one-time password', SIMBA_TFA_TEXT_DOMAIN).' '.$this->reset_current_otp_link().'</h2>'; | |
| 593 | + } | |
| 594 | + | |
| 595 | + ?> | |
| 596 | + <div class="postbox"> | |
| 597 | + | |
| 598 | + <?php if ($admin) { ?> | |
| 599 | + <h3 style="padding: 10px 6px 0px; margin:4px 0 0; cursor: default;"> | |
| 600 | + <span style="cursor: default;"><?php echo __('Current one-time password', SIMBA_TFA_TEXT_DOMAIN).' '; | |
| 601 | + if ($current_user->ID == $user_id) { echo $this->reset_current_otp_link(); } ?> | |
| 602 | + </span> | |
| 603 | + <div class="inside"> | |
| 604 | + <p><strong style="font-size: 3em;"><?php echo $this->current_otp_code($tfa, $user_id); ?></strong></p> | |
| 605 | + </div> | |
| 606 | + </h3> | |
| 607 | + <?php } else { | |
| 608 | + ?> | |
| 609 | + <div class="inside"> | |
| 610 | + <p class="simbatfa-frontend-current-otp" style="font-size: 1.5em; margin-top:6px;"> | |
| 611 | + <strong> | |
| 612 | + <?php echo __('Current one-time password', SIMBA_TFA_TEXT_DOMAIN).' '.$this->reset_current_otp_link(); ?> | |
| 613 | + </strong> : | |
| 614 | + | |
| 615 | + <span class="simba_current_otp"><?php print $tfa->generateOTP($user_id, $tfa_priv_key_64); ?></span> | |
| 616 | + | |
| 617 | + </p> | |
| 618 | + </div> | |
| 619 | + | |
| 620 | + <?php } ?> | |
| 621 | + | |
| 622 | + <?php if ($admin) { ?> | |
| 623 | + <h3 style="padding-left: 10px; cursor: default;"> | |
| 624 | + <span style="cursor: default;"><?php _e('QR code', SIMBA_TFA_TEXT_DOMAIN); ?></span> | |
| 625 | + </h3> | |
| 626 | + <?php } else { | |
| 627 | + echo '<h2>'.__('QR code', SIMBA_TFA_TEXT_DOMAIN).'</h2>'; | |
| 628 | + } ?> | |
| 629 | + <div class="inside"> | |
| 630 | + <p> | |
| 631 | + <?php _e('For OTP apps that support scanning, scanning this code is the quickest way to set the app up (e.g. with Duo Mobile, Google Authenticator)', SIMBA_TFA_TEXT_DOMAIN); ?>. | |
| 632 | + | |
| 633 | + <?php _e('You are currently using', SIMBA_TFA_TEXT_DOMAIN); ?> <?php print strtoupper($algorithm_type).', '.($algorithm_type == 'totp' ? __('a time based', SIMBA_TFA_TEXT_DOMAIN) : __('an event based', SIMBA_TFA_TEXT_DOMAIN)); ?> <?php _e('algorithm', SIMBA_TFA_TEXT_DOMAIN); ?>. | |
| 634 | + </p> | |
| 635 | + <p title="<?php echo sprintf(__("Private key: %s (base 32: %s)", SIMBA_TFA_TEXT_DOMAIN), $tfa_priv_key, $tfa_priv_key_32);?>"> | |
| 636 | + <?php $qr_url = $this->tfa_qr_code_url($algorithm_type, $url, $tfa_priv_key) ?> | |
| 637 | + <div class="simbaotp_qr_container" data-qrcode="<?php echo esc_attr($qr_url); ?>"></div> | |
| 638 | + </p> | |
| 639 | + </div> | |
| 640 | + | |
| 641 | + <div class="inside"> | |
| 642 | + | |
| 643 | + <h3 class="normal" style="cursor: default"><?php _e('Private key - always to be kept secret - type this into your app to set it up (instead of scanning the code)', SIMBA_TFA_TEXT_DOMAIN); ?></h3> | |
| 644 | + | |
| 645 | + <p> | |
| 646 | + <?php | |
| 647 | + $this->print_private_keys($admin, 'full', $user_id); | |
| 648 | + if ($current_user->ID == $user_id) { echo $this->reset_link($admin);} | |
| 649 | + ?> | |
| 650 | + </p> | |
| 651 | + </div> | |
| 652 | + | |
| 653 | + <?php | |
| 654 | + if ($admin || apply_filters('simba_tfa_emergency_codes_user_settings', false, $user_id) !== false) { | |
| 655 | + ?> | |
| 656 | + <div class="inside"> | |
| 657 | + | |
| 658 | + <h3 class="normal" style="cursor: default"><?php _e('Emergency codes', SIMBA_TFA_TEXT_DOMAIN); ?></h3> | |
| 659 | + | |
| 660 | + <p> | |
| 661 | + <?php | |
| 662 | + $default_text = '<a href="https://www.simbahosting.co.uk/s3/product/two-factor-authentication/">'.__('One-time emergency codes are a feature of the Premium version of this plugin.', SIMBA_TFA_TEXT_DOMAIN).'</a>'; | |
| 663 | + echo apply_filters('simba_tfa_emergency_codes_user_settings', $default_text, $user_id); | |
| 664 | + ?> | |
| 665 | + </p> | |
| 666 | + | |
| 667 | + </div> | |
| 668 | + | |
| 669 | + <?php } ?> | |
| 670 | + | |
| 671 | + </div> | |
| 672 | + <?php | |
| 673 | + } | |
| 674 | + | |
| 675 | + public function reset_current_otp_link($admin = true) { | |
| 676 | + return '<a href="#" class="simbaotp_refresh">'.__('(update)', SIMBA_TFA_TEXT_DOMAIN).'</a>'; | |
| 677 | + } | |
| 678 | + | |
| 679 | + public function advanced_settings_box($submit_button_callback = false) { | |
| 680 | + $tfa = $this->getTFA(); | |
| 681 | + | |
| 143 | 682 | global $current_user; |
| 144 | - if ($this->is_activated_for_user($current_user->ID)) { | |
| 145 | - add_menu_page(__('Two Factor Authentication', 'two-factor-authentication'), __('Two Factor Auth', 'two-factor-authentication'), 'read', 'two-factor-auth-user', array($this, 'show_dashboard_user_settings_page'), $this->includes_url().'/tfa_admin_icon_16x16.png', 72); | |
| 683 | + $algorithm_type = $tfa->getUserAlgorithm($current_user->ID); | |
| 684 | + | |
| 685 | + ?> | |
| 686 | + <h2><?php _e('Advanced settings', SIMBA_TFA_TEXT_DOMAIN); ?></h2> | |
| 687 | + | |
| 688 | + <div id="tfa_advanced_box" class="tfa_settings_form" style="margin-top: 20px;"> | |
| 689 | + | |
| 690 | + <?php if (false === $submit_button_callback) { ?><form method="post" action="<?php print esc_url(add_query_arg('settings-updated', 'true', $_SERVER['REQUEST_URI'])); ?>"><?php } ?> | |
| 691 | + | |
| 692 | + <?php _e('Choose which algorithm for One Time Passwords you want to use.', SIMBA_TFA_TEXT_DOMAIN); ?> | |
| 693 | + <p> | |
| 694 | + <?php | |
| 695 | + $this->tfaListAlgorithmRadios($current_user->ID); | |
| 696 | + if($algorithm_type == 'hotp') | |
| 697 | + { | |
| 698 | + $counter = $tfa->getUserCounter($current_user->ID); | |
| 699 | + print '<br>'.__('Your counter on the server is currently on', SIMBA_TFA_TEXT_DOMAIN).': '.$counter; | |
| 700 | + } | |
| 701 | + ?> | |
| 702 | + | |
| 703 | + </p> | |
| 704 | + <?php if (false === $submit_button_callback) { submit_button(); echo '</form>'; } else { call_user_func($submit_button_callback); } ?> | |
| 705 | + </div> | |
| 706 | + <?php | |
| 707 | + } | |
| 708 | + | |
| 709 | + public function login_enqueue_scripts() | |
| 710 | + { | |
| 711 | + | |
| 712 | + if(isset($_GET['action']) && $_GET['action'] != 'logout' && $_GET['action'] != 'login') return; | |
| 713 | + | |
| 714 | + // Prevent cacheing when in debug mode | |
| 715 | + $script_ver = (defined('WP_DEBUG') && WP_DEBUG) ? time() : $this->version; | |
| 716 | + | |
| 717 | + wp_enqueue_script( 'tfa-ajax-request', SIMBA_TFA_PLUGIN_URL . '/includes/tfa.js', array( 'jquery' ), $script_ver ); | |
| 718 | + $localize = array( | |
| 719 | + 'ajaxurl' => admin_url('admin-ajax.php'), | |
| 720 | + 'click_to_enter_otp' => __("Click to enter One Time Password", SIMBA_TFA_TEXT_DOMAIN), | |
| 721 | + 'enter_username_first' => __('You have to enter a username first.', SIMBA_TFA_TEXT_DOMAIN), | |
| 722 | + 'otp' => __("One Time Password (i.e. 2FA)", SIMBA_TFA_TEXT_DOMAIN), | |
| 723 | + 'otp_login_help' => __('(check your OTP app to get this password)', SIMBA_TFA_TEXT_DOMAIN), | |
| 724 | + 'nonce' => wp_create_nonce("simba_tfa_loginform_nonce") | |
| 725 | + ); | |
| 726 | + // Spinner exists since WC 3.8. Use the proper functions to avoid SSL warnings. | |
| 727 | + if (file_exists(ABSPATH.'wp-admin/images/spinner.gif')) { | |
| 728 | + $localize['spinnerimg'] = admin_url('images/spinner.gif'); | |
| 729 | + } elseif (file_exists(ABSPATH.WPINC.'/images/spinner.gif')) { | |
| 730 | + $localize['spinnerimg'] = includes_url('images/spinner.gif'); | |
| 146 | 731 | } |
| 732 | + wp_localize_script( 'tfa-ajax-request', 'simba_tfasettings', $localize); | |
| 147 | 733 | } |
| 148 | - | |
| 149 | - /** | |
| 150 | - * Runs upon the WP action admin_menu | |
| 151 | - */ | |
| 152 | - public function menu_entry_for_admin() { | |
| 734 | + | |
| 735 | + public function tfaShowHOTPOffSyncMessage() | |
| 736 | + { | |
| 737 | + global $current_user; | |
| 738 | + $is_off_sync = get_user_meta($current_user->ID, 'tfa_hotp_off_sync', true); | |
| 739 | + if(!$is_off_sync) | |
| 740 | + return; | |
| 153 | 741 | |
| 154 | - $this->get_totp_controller()->potentially_port_private_keys(); | |
| 742 | + ?> | |
| 743 | + <div class="error"> | |
| 744 | + <h3><?php _e('Two Factor Authentication re-sync needed', SIMBA_TFA_TEXT_DOMAIN);?></h3> | |
| 745 | + <p> | |
| 746 | + <?php _e('You need to resync your device for Two Factor Authentication since the OTP you last used is many steps ahead of the server.', SIMBA_TFA_TEXT_DOMAIN); ?> | |
| 747 | + <br> | |
| 748 | + <?php _e('Please re-sync or you might not be able to log in if you generate more OTPs without logging in.', SIMBA_TFA_TEXT_DOMAIN);?> | |
| 749 | + <br><br> | |
| 750 | + <a href="admin.php?page=two-factor-auth-user&warning_button_clicked=1" class="button"><?php _e('Click here and re-scan the QR-Code', SIMBA_TFA_TEXT_DOMAIN);?></a> | |
| 751 | + </p> | |
| 752 | + </div> | |
| 155 | 753 | |
| 156 | - if (is_multisite() && (!is_super_admin() || !is_main_site())) return; | |
| 754 | + <?php | |
| 157 | 755 | |
| 158 | - add_options_page( | |
| 159 | - __('Two Factor Authentication', 'two-factor-authentication'), | |
| 160 | - __('Two Factor Authentication', 'two-factor-authentication'), | |
| 161 | - $this->get_management_capability(), | |
| 162 | - 'two-factor-auth', | |
| 163 | - array($this, 'show_admin_settings_page') | |
| 164 | - ); | |
| 165 | 756 | } |
| 166 | - | |
| 167 | - /** | |
| 168 | - * Include the admin settings page code | |
| 169 | - */ | |
| 170 | - public function show_admin_settings_page() { | |
| 171 | - $totp_controller = $this->get_totp_controller(); | |
| 172 | - $totp_controller->setUserHMACTypes(); | |
| 173 | - if (!is_admin() || !current_user_can($this->get_management_capability())) return; | |
| 174 | 757 | |
| 175 | - $admin_settings_links = array(); | |
| 176 | - if (!class_exists('Simba_Two_Factor_Authentication_Premium')) { | |
| 177 | - $admin_settings_links[] = array( | |
| 178 | - 'url' => esc_url('https://www.simbahosting.co.uk/s3/product/two-factor-authentication/'), | |
| 179 | - 'title' => __('Premium version', SIMBA_TFA_TEXT_DOMAIN), | |
| 180 | - ); | |
| 758 | + // QR code image | |
| 759 | + public function tfa_qr_code_url($algorithm_type, $url, $tfa_priv_key, $user_id = false){ | |
| 760 | + global $current_user; | |
| 761 | + | |
| 762 | + if ($user_id == false) { | |
| 763 | + $user = $current_user; | |
| 764 | + } else { | |
| 765 | + $user = get_user_by('id', $user_id); | |
| 181 | 766 | } |
| 182 | - $simba_tfa_support_url = apply_filters('simba_tfa_support_url', esc_url('https://wordpress.org/support/plugin/two-factor-authentication/')); | |
| 767 | + | |
| 768 | + $tfa = $this->getTFA(); | |
| 769 | + | |
| 770 | + // Old | |
| 771 | +// $encode = 'otpauth://'.$algorithm_type.'/'.$url.':%2520'.$user->user_login.'%3Fsecret%3D'.Base32::encode($tfa_priv_key).'%26issuer='.$url.'%26counter='.$tfa->getUserCounter($user->ID); | |
| 772 | +// | |
| 773 | +// $ret = '<img src="https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$encode.'">'; | |
| 183 | 774 | |
| 184 | - $admin_settings_links[] = array( | |
| 185 | - 'url' => $simba_tfa_support_url, | |
| 186 | - 'title' => __('Support', SIMBA_TFA_TEXT_DOMAIN), | |
| 187 | - ); | |
| 188 | - $admin_settings_links[] = array( | |
| 189 | - 'url' => esc_url('https://profiles.wordpress.org/davidanderson#content-plugins'), | |
| 190 | - 'title' => __('More free plugins', SIMBA_TFA_TEXT_DOMAIN), | |
| 191 | - ); | |
| 192 | - $admin_settings_links[] = array( | |
| 193 | - 'url' => esc_url('http://updraftplus.com'), | |
| 194 | - 'title' => 'UpdraftPlus - '.__('WordPress backups', SIMBA_TFA_TEXT_DOMAIN), | |
| 195 | - ); | |
| 196 | - $admin_settings_links[] = array( | |
| 197 | - 'url' => esc_url('https://www.simbahosting.co.uk/s3/shop/'), | |
| 198 | - 'title' => __('More premium plugins', SIMBA_TFA_TEXT_DOMAIN), | |
| 199 | - ); | |
| 200 | - $admin_settings_links[] = array( | |
| 201 | - 'url' => esc_url('https://twitter.com/updraftplus'), | |
| 202 | - 'title' => __('Twitter', SIMBA_TFA_TEXT_DOMAIN), | |
| 203 | - ); | |
| 204 | - $admin_settings_links[] = array( | |
| 205 | - 'url' => esc_url('http://david.dw-perspective.org.uk'), | |
| 206 | - 'title' => __('Lead developer\'s homepage', SIMBA_TFA_TEXT_DOMAIN), | |
| 207 | - ); | |
| 775 | + // New | |
| 776 | + $encode = 'otpauth://'.$algorithm_type.'/'.$url.':'.$user->user_login.'?secret='.Base32::encode($tfa_priv_key).'&issuer='.$url.'&counter='.$tfa->getUserCounter($user->ID); | |
| 208 | 777 | |
| 209 | - $admin_settings_links = apply_filters('simba_tfa_admin_settings_links', $admin_settings_links); | |
| 778 | +// $ret = '<script>var qr_details = "'.$encode.'"</script>'; | |
| 210 | 779 | |
| 211 | - $this->include_template('admin-settings.php', array( | |
| 212 | - 'totp_controller' => $totp_controller, | |
| 213 | - 'settings_page_heading' => $this->get_settings_page_heading(), | |
| 214 | - 'admin_settings_links' => $admin_settings_links, | |
| 215 | - )); | |
| 780 | + return $encode; | |
| 216 | 781 | } |
| 217 | - | |
| 218 | - /** | |
| 219 | - * Runs conditionally on the WP action all_admin_notices | |
| 220 | - */ | |
| 221 | - public function admin_notice_insufficient_php() { | |
| 222 | - $this->show_admin_warning('<strong>'.__('Higher PHP version required', 'two-factor-authentication').'</strong><br> '.sprintf(__('The Two Factor Authentication plugin requires PHP version %s or higher - your current version is only %s.', 'two-factor-authentication'), self::PHP_REQUIRED, PHP_VERSION), 'error'); | |
| 782 | + | |
| 783 | + public function settings_intro_notices() { | |
| 784 | + ?> | |
| 785 | + <p class="simba_tfa_personal_settings_notice simba_tfa_intro_notice"> | |
| 786 | + <?php echo __('These are your personal settings.', SIMBA_TFA_TEXT_DOMAIN).' '.__('Nothing you change here will have any effect on other users.', SIMBA_TFA_TEXT_DOMAIN); ?> | |
| 787 | + </p> | |
| 788 | + <p class="simba_tfa_verify_tfa_notice simba_tfa_intro_notice"><strong> | |
| 789 | + <?php _e('If you activate two-factor authentication, then verify that your two-factor application is showing the same One Time Password as shown on this page before you log out.', SIMBA_TFA_TEXT_DOMAIN); ?></strong> <?php if (current_user_can('manage_options')) { ?><a href="https://wordpress.org/plugins/two-factor-authentication/faq/"><?php _e('You should also bookmark the FAQs, which explain how to de-activate the plugin even if you cannot log in.', SIMBA_TFA_TEXT_DOMAIN);?></a><?php } ?> | |
| 790 | + </p> | |
| 791 | + <?php | |
| 223 | 792 | } |
| 224 | - | |
| 225 | - /** | |
| 226 | - * Runs conditionally on the WP action all_admin_notices | |
| 227 | - */ | |
| 228 | - public function admin_notice_missing_mcrypt_and_openssl() { | |
| 229 | - $this->show_admin_warning('<strong>'.__('PHP OpenSSL or mcrypt module required', 'two-factor-authentication').'</strong><br> '.__('The Two Factor Authentication plugin requires either the PHP openssl (preferred) or mcrypt module to be installed. Please ask your web hosting company to install one of them.', 'two-factor-authentication'), 'error'); | |
| 230 | - } | |
| 231 | - | |
| 232 | - /** | |
| 233 | - * Run upon the WP plugins_loaded action. This method is called even if main loading aborts - so don't put anything else in it (use a separate method). | |
| 234 | - */ | |
| 235 | - public function plugins_loaded_load_textdomain() { | |
| 793 | + | |
| 794 | + public function plugins_loaded() { | |
| 236 | 795 | load_plugin_textdomain( |
| 237 | - 'two-factor-authentication', | |
| 796 | + SIMBA_TFA_TEXT_DOMAIN, | |
| 238 | 797 | false, |
| 239 | - dirname(plugin_basename(__FILE__)).'/languages/' | |
| 798 | + dirname( plugin_basename( __FILE__ ) ) . '/languages/' | |
| 240 | 799 | ); |
| 241 | 800 | } |
| 242 | -} | |
| 243 | -endif; | |
| 244 | 801 | |
| 245 | -$GLOBALS['simba_two_factor_authentication'] = new Simba_Two_Factor_Authentication_Plugin(); | |
| 802 | + public function load_frontend() { | |
| 803 | + if (!class_exists('TFA_Frontend')) require_once(SIMBA_TFA_PLUGIN_DIR.'/includes/tfa_frontend.php'); | |
| 804 | + if (empty($this->frontend)) $this->frontend = new TFA_Frontend($this); | |
| 805 | + return $this->frontend; | |
| 806 | + } | |
| 246 | 807 | |
| 247 | -if (file_exists(__DIR__.'/simba-tfa/premium/loader.php') && empty($GLOBALS['simba_two_factor_authentication_premium'])) { | |
| 248 | - if (!class_exists('Simba_Two_Factor_Authentication_Premium')) include_once(__DIR__.'/simba-tfa/premium/loader.php'); | |
| 808 | + public function shortcode_when_not_logged_in() { | |
| 809 | + return ''; | |
| 810 | + } | |
| 249 | 811 | |
| 250 | - $GLOBALS['simba_two_factor_authentication_premium'] = new Simba_Two_Factor_Authentication_Premium($GLOBALS['simba_two_factor_authentication']); | |
| 812 | + // WooCommerce login form | |
| 813 | + public function woocommerce_before_customer_login_form() { | |
| 814 | + $script_ver = (defined('WP_DEBUG') && WP_DEBUG) ? time() : $this->version; | |
| 815 | + wp_enqueue_script( 'tfa-wc-ajax-request', SIMBA_TFA_PLUGIN_URL.'/includes/wooextend.js', array('jquery'), $script_ver); | |
| 816 | + $localize = array( | |
| 817 | + 'ajaxurl' => admin_url('admin-ajax.php'), | |
| 818 | + 'click_to_enter_otp' => __("Enter One Time Password (if you have one)", SIMBA_TFA_TEXT_DOMAIN), | |
| 819 | + 'enter_username_first' => __('You have to enter a username first.', SIMBA_TFA_TEXT_DOMAIN), | |
| 820 | + 'otp' => __("One Time Password", SIMBA_TFA_TEXT_DOMAIN), | |
| 821 | + 'nonce' => wp_create_nonce("simba_tfa_loginform_nonce"), | |
| 822 | + 'otp_login_help' => __('(check your OTP app to get this password)', SIMBA_TFA_TEXT_DOMAIN), | |
| 823 | + ); | |
| 824 | + // Spinner exists since WC 3.8. Use the proper functions to avoid SSL warnings. | |
| 825 | + if (file_exists(ABSPATH.'wp-admin/images/spinner.gif')) { | |
| 826 | + $localize['spinnerimg'] = admin_url('images/spinner.gif'); | |
| 827 | + } elseif (file_exists(ABSPATH.WPINC.'/images/spinner.gif')) { | |
| 828 | + $localize['spinnerimg'] = includes_url('images/spinner.gif'); | |
| 829 | + } | |
| 251 | 830 | |
| 252 | - if (!class_exists('Updraft_Manager_Updater_1_8')) require_once(plugin_dir_path(__FILE__).'vendor/davidanderson684/simba-plugin-manager-updater/class-udm-updater.php'); | |
| 831 | + wp_localize_script( 'tfa-wc-ajax-request', 'simbatfa_wc_settings', $localize); | |
| 832 | + } | |
| 253 | 833 | |
| 254 | - try { | |
| 255 | - new Updraft_Manager_Updater_1_8('https://www.simbahosting.co.uk/s3', 1, 'two-factor-authentication-premium/two-factor-login.php', array('require_login' => false)); | |
| 256 | - } catch (Exception $e) { | |
| 257 | - error_log($e->getMessage()); | |
| 258 | - } | |
| 259 | 834 | } |
| 835 | + | |
| 836 | +$simba_two_factor_authentication = new Simba_Two_Factor_Authentication(); | |