From 97f666b078ba96cd4c2547dfd087f11333c36b90 Mon Sep 17 00:00:00 2001 From: Hasan Misbah Date: Mon, 14 Aug 2023 16:46:29 +0600 Subject: [PATCH] Recaptcha Added --- app/Hooks/Handlers/AdminMenuHandler.php | 3 + app/Hooks/hooks.php | 1 + app/Http/Controllers/ReCaptchaController.php | 34 ++++ app/Http/routes.php | 4 +- app/Services/ReCaptcha/MenuPage.php | 40 +++++ app/Services/ReCaptcha/Recaptcha.php | 59 +++++++ app/Services/ReCaptcha/RecaptchaService.php | 172 +++++++++++++++++++ app/Services/ReCaptcha/Settings.php | 102 +++++++++++ mix-manifest.json | 1 - src/admin/App.vue | 4 + src/admin/Components/ReCaptcha.vue | 146 ++++++++++++++++ src/admin/Components/Setttings.vue | 2 +- src/admin/routes.js | 9 + src/public/login_helper.scss | 4 + src/public/recaptcha.js | 65 +++++++ 15 files changed, 643 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controllers/ReCaptchaController.php create mode 100644 app/Services/ReCaptcha/MenuPage.php create mode 100644 app/Services/ReCaptcha/Recaptcha.php create mode 100644 app/Services/ReCaptcha/RecaptchaService.php create mode 100644 app/Services/ReCaptcha/Settings.php create mode 100644 src/admin/Components/ReCaptcha.vue create mode 100644 src/public/recaptcha.js diff --git a/app/Hooks/Handlers/AdminMenuHandler.php b/app/Hooks/Handlers/AdminMenuHandler.php index 2c1a99f..45630a8 100644 --- a/app/Hooks/Handlers/AdminMenuHandler.php +++ b/app/Hooks/Handlers/AdminMenuHandler.php @@ -3,6 +3,7 @@ namespace FluentAuth\App\Hooks\Handlers; use FluentAuth\App\Helpers\Helper; +use FluentAuth\App\Services\ReCaptcha\Recaptcha; class AdminMenuHandler { @@ -47,6 +48,8 @@ public function addMenu() array($this, 'render') ); + Recaptcha::renderMenuPage($permission, 'fluent-auth', array($this, 'render')); + add_submenu_page( 'fluent-auth', __('Social Login', 'fluent-support'), diff --git a/app/Hooks/hooks.php b/app/Hooks/hooks.php index 9c3d8d7..1b24bb1 100644 --- a/app/Hooks/hooks.php +++ b/app/Hooks/hooks.php @@ -11,3 +11,4 @@ (new \FluentAuth\App\Hooks\Handlers\TwoFaHandler())->register(); (new \FluentAuth\App\Hooks\Handlers\BasicTasksHandler())->register(); +\FluentAuth\App\Services\ReCaptcha\Recaptcha::register(); diff --git a/app/Http/Controllers/ReCaptchaController.php b/app/Http/Controllers/ReCaptchaController.php new file mode 100644 index 0000000..c79bfb0 --- /dev/null +++ b/app/Http/Controllers/ReCaptchaController.php @@ -0,0 +1,34 @@ + Recaptcha::settings()->get() + ]; + } + + public static function updateSettings(\WP_REST_Request $request) + { + // :Todo validate request before save + + $data = [ + 'site_key' => sanitize_text_field($request['site_key']), + 'secret_key' => sanitize_text_field($request['secret_key']), + 'enable_recaptcha' => sanitize_text_field($request['enable_recaptcha']), + 'enable_on_shortcode_login' => sanitize_text_field($request['enable_on_shortcode_login']), + ]; + + $response = Recaptcha::settings()->update($data); + + return [ + 'message' => __('Settings has been updated', 'fluent-security'), + 'data' => $response + ]; + } +} diff --git a/app/Http/routes.php b/app/Http/routes.php index b332d4d..0e10db6 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -13,4 +13,6 @@ ->get('social-auth-settings', ['\FluentAuth\App\Http\Controllers\SocialAuthApiController', 'getSettings'], $permissions) ->post('social-auth-settings', ['\FluentAuth\App\Http\Controllers\SocialAuthApiController', 'saveSettings'], $permissions) ->get('auth-forms-settings', ['\FluentAuth\App\Http\Controllers\SettingsController', 'getAuthFormSettings'], $permissions) - ->post('auth-forms-settings', ['\FluentAuth\App\Http\Controllers\SettingsController', 'saveAuthFormSettings'], $permissions); + ->post('auth-forms-settings', ['\FluentAuth\App\Http\Controllers\SettingsController', 'saveAuthFormSettings'], $permissions) + ->get('recaptcha-settings', ['\FluentAuth\App\Http\Controllers\ReCaptchaController', 'getSettings'], $permissions) + ->post('recaptcha-settings', ['\FluentAuth\App\Http\Controllers\ReCaptchaController', 'updateSettings'], $permissions); diff --git a/app/Services/ReCaptcha/MenuPage.php b/app/Services/ReCaptcha/MenuPage.php new file mode 100644 index 0000000..6f7af7e --- /dev/null +++ b/app/Services/ReCaptcha/MenuPage.php @@ -0,0 +1,40 @@ +permission = $permission; + $this->menuSlug = $menuSlug; + $this->cb = $cb; + } + + public function registerSubmenuPage($pageTitle, $menuTitle, $menuSlug, $cb = null) + { + add_submenu_page( + $this->menuSlug, + $pageTitle, + $menuTitle, + $this->permission, + $menuSlug, + $cb ?: $this->cb + ); + } + + public function registerMenuPage($pageTitle, $menuTitle, $menuSlug, $cb = null) + { + add_menu_page( + $pageTitle, + $menuTitle, + $this->permission, + $menuSlug, + $cb ?: $this->cb + ); + } + +} diff --git a/app/Services/ReCaptcha/Recaptcha.php b/app/Services/ReCaptcha/Recaptcha.php new file mode 100644 index 0000000..7a84417 --- /dev/null +++ b/app/Services/ReCaptcha/Recaptcha.php @@ -0,0 +1,59 @@ +{$name}; + } + + public function __set($name, $value) + { + self::$service->{$name} = $value; + } + + public function __isset($name) + { + return isset(self::$service->{$name}); + } + + public function __unset($name) + { + unset(self::$service->{$name}); + } +} diff --git a/app/Services/ReCaptcha/RecaptchaService.php b/app/Services/ReCaptcha/RecaptchaService.php new file mode 100644 index 0000000..058187b --- /dev/null +++ b/app/Services/ReCaptcha/RecaptchaService.php @@ -0,0 +1,172 @@ +registerSubmenuPage('ReCaptcha', 'ReCaptcha', 'fluent-auth#/recaptcha'); + } + + public function settings() + { + if (is_null(self::$settings)) { + self::$settings = new Settings(); + } + + return self::$settings; + } + + + public function register() + { + add_action('login_form', array($this, 'renderCaptcha')); + add_action('register_form', array($this, 'renderCaptcha')); + add_action('signup_extra_fields', array($this, 'renderCaptcha')); + add_action('lostpassword_form', array($this, 'renderCaptcha')); + add_action('login_enqueue_scripts', array($this, 'enqueueScripts')); + add_action('wp_ajax_nopriv_fluent_auth_recaptcha_verify', array($this, 'verifyRecaptcha')); + + } + + public function enqueueScripts() + { + $settings = self::settings(); + + if (!$settings->enabled()) { + return; + } + + wp_enqueue_script('fluent-auth-recaptcha', 'https://www.google.com/recaptcha/api.js', [], '1.0', true); + wp_enqueue_script('fluent-auth-recaptcha-script', FLUENT_AUTH_PLUGIN_URL . '/src/public/recaptcha.js', ['fluent-auth-recaptcha', 'jquery'], '1.0', true); + + wp_localize_script('fluent-auth-recaptcha-script', 'fluent_auth_recaptcha', [ + 'fls_ajax_url' => admin_url('admin-ajax.php'), + 'fls_action' => 'fluent_auth_recaptcha_verify' + ]); + } + + public function renderCaptcha() + { + + $this->renderCaptchaStyle(); + + $settings = self::settings(); + + if ($settings->enabled()) { + $siteKey = $settings->site_key; + return printf('
', $siteKey); + } + + return $this; + } + + public function verifyRecaptcha() + { + $settings = self::settings(); + + $secretKey = $settings->secret_key; + $token = $_POST['token']; + $remoteIp = $_SERVER['REMOTE_ADDR']; + + + if(!$secretKey) { + wp_send_json([ + 'success' => false, + 'message' => 'Please add the secret key' + ], 422); + + return; + } + + + if(!$token) { + wp_send_json([ + 'success' => false, + 'message' => 'Please Check the Captcha' + ], 422); + + return; + } + + $url = "https://www.google.com/recaptcha/api/siteverify?secret=$secretKey&response=$token&remoteip=$remoteIp"; + + $response = wp_remote_get($url); + + if (is_wp_error($response)) { + + wp_send_json([ + 'success' => false, + 'message' => 'Captcha verification failed' + ], 500); + + return; + } + + $response = json_decode(wp_remote_retrieve_body($response), true); + + if (!$response['success']) { + + wp_send_json([ + 'success' => false, + 'message' => 'Invalid captcha' + ], 422); + + return; + } + + wp_send_json($response); + } + + public function registerShortcodeCaptcha() + { + $settings = self::settings(); + + if(!$settings->enabled()) { + return; + } + + if($settings->enable_on_shortcode_login !== 'yes') { + return; + } + + add_action('wp_loaded', function() use ($settings){ + + add_filter('login_form_middle', function($data) use ($settings){ + $data .= '
'; + return $data; + }); + + wp_enqueue_script('fluent-auth-recaptcha', 'https://www.google.com/recaptcha/api.js', [], '1.0', true); + wp_enqueue_script('fluent-auth-recaptcha-script', FLUENT_AUTH_PLUGIN_URL . '/src/public/recaptcha.js', ['fluent-auth-recaptcha', 'jquery'], '1.0', true); + + wp_localize_script('fluent-auth-recaptcha-script', 'fluent_auth_recaptcha', [ + 'fls_ajax_url' => admin_url('admin-ajax.php'), + 'fls_action' => 'fluent_auth_recaptcha_verify' + ]); + }); + } + + public function renderCaptchaStyle() + { + echo ''; + } +} diff --git a/app/Services/ReCaptcha/Settings.php b/app/Services/ReCaptcha/Settings.php new file mode 100644 index 0000000..12551f9 --- /dev/null +++ b/app/Services/ReCaptcha/Settings.php @@ -0,0 +1,102 @@ +settings = $this->getSettings(); + } + + public function get($key = null) + { + if (empty($this->settings)) { + $this->settings = $this->getSettings(); + } + + if ($key) { + return $this->settings[$key] ?: null; + } + + return $this->settings; + } + + public function set($key, $value) + { + $this->settings[$key] = $value; + update_option($this->settingsKey, maybe_serialize($this->settings)); + $this->settings = $this->getSettings(); + return $this; + } + + public function getSettings() + { + $savedSettings = get_option($this->settingsKey, []); + $savedSettings = maybe_unserialize($savedSettings); + + $defaultSettings = [ + 'white_label_domains' => [], + 'site_key' => '', + 'secret_key' => '', + 'enable_recaptcha' => 'no', + 'enable_on_shortcode_login' => 'no', + ]; + + return wp_parse_args($savedSettings, $defaultSettings); + } + + public function __get($name) + { + return $this->get($name); + } + + public function __set($name, $value) + { + $this->set($name, $value); + } + + public function save() + { + update_option($this->settingsKey, maybe_serialize($this->settings), false); + return $this; + } + + public function enabled() + { + return $this->settings['enable_recaptcha'] === 'yes' && (!empty($this->settings['site_key'])); + } + + public function update($data = []) + { + $settings = $this->getSettings(); + + foreach ($data as $key => $value) { + + if (isset($settings[$key])) { + $settings[$key] = $value; + } + } + + update_option($this->settingsKey, maybe_serialize($settings)); + $this->settings = $this->getSettings(); + + return $this->getSettings(); + } + + public function refreshData() + { + $this->settings = $this->getSettings(); + return $this; + } +} diff --git a/mix-manifest.json b/mix-manifest.json index 2f631d3..33eb67b 100644 --- a/mix-manifest.json +++ b/mix-manifest.json @@ -2,7 +2,6 @@ "/dist/admin/app.js": "/dist/admin/app.js", "/dist/public/fls_login.js": "/dist/public/fls_login.js", "/dist/public/login_helper.js": "/dist/public/login_helper.js", - "/dist/images/index.php": "/dist/images/index.php", "/dist/images/logo.png": "/dist/images/logo.png", "/dist/images/logo.svg": "/dist/images/logo.svg", "/dist/images/success.png": "/dist/images/success.png" diff --git a/src/admin/App.vue b/src/admin/App.vue index 355490e..d2e3698 100644 --- a/src/admin/App.vue +++ b/src/admin/App.vue @@ -38,6 +38,10 @@ export default { route: 'settings', title: this.$t('Settings') }, + { + route: 'recaptcha', + title: this.$t('Recaptcha') + }, { route: 'social_auth_settings', title: this.$t('Social Login') diff --git a/src/admin/Components/ReCaptcha.vue b/src/admin/Components/ReCaptcha.vue new file mode 100644 index 0000000..c845092 --- /dev/null +++ b/src/admin/Components/ReCaptcha.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/src/admin/Components/Setttings.vue b/src/admin/Components/Setttings.vue index 2e7db05..3e04c6c 100644 --- a/src/admin/Components/Setttings.vue +++ b/src/admin/Components/Setttings.vue @@ -118,7 +118,7 @@ + v-if="settings.notification_user_roles.length || settings.notify_on_blocked == 'yes' || settings.settings?.digest_summary">