Subversion Repositories oidplus

Rev

Rev 1210 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. namespace ViaThinkSoft\OIDplus;
  21.  
  22. // phpcs:disable PSR1.Files.SideEffects
  23. \defined('INSIDE_OIDPLUS') or die;
  24. // phpcs:enable PSR1.Files.SideEffects
  25.  
  26. class OIDplusCaptchaPluginHCaptcha extends OIDplusCaptchaPlugin
  27.         implements INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8  /* getNotifications */
  28. {
  29.  
  30.         /**
  31.          * @return string
  32.          */
  33.         public static function id(): string {
  34.                 return 'hCaptcha';
  35.         }
  36.  
  37.         /**
  38.          * @return bool
  39.          */
  40.         public function isVisible(): bool {
  41.                 return true;
  42.         }
  43.  
  44.         /**
  45.          * @param string|null $header_text
  46.          * @param string|null $footer_text
  47.          * @return string
  48.          * @throws OIDplusException
  49.          */
  50.         public function captchaGenerate(string $header_text=null, string $footer_text=null): string {
  51.                 return ($header_text ? '<p>'.$header_text.'</p>' : '') .
  52.                        '<noscript>'.
  53.                        '<p><font color="red">'._L('You need to enable JavaScript to solve the CAPTCHA.').'</font></p>'.
  54.                        '</noscript>'.
  55.                        '<div id="h-captcha"></div>'.
  56.                        '<script src="https://js.hcaptcha.com/1/api.js"></script>'.
  57.                        '<script>'.
  58.                        'OIDplusCaptchaPluginHCaptcha.captchaShow('.js_escape(OIDplus::baseConfig()->getValue('HCAPTCHA_SITEKEY', '')).')'.
  59.                        '</script>'.
  60.                        ($footer_text ? '<p>'.$footer_text.'</p>' : '');
  61.         }
  62.  
  63.         /**
  64.          * @param string[] $params
  65.          * @param string|null $fieldname
  66.          * @return void
  67.          * @throws OIDplusException
  68.          */
  69.         public function captchaVerify(array $params, string $fieldname=null) {
  70.                 $sitekey=OIDplus::baseConfig()->getValue('HCAPTCHA_SITEKEY', '');
  71.                 $secret=OIDplus::baseConfig()->getValue('HCAPTCHA_SECRET', '');
  72.  
  73.                 // Yes, it is really "g-recaptcha-response"!
  74.                 if (is_null($fieldname)) $fieldname = 'g-recaptcha-response'; // no individual field name (created by oidplus_captcha_response()) means that it is a plain POST event (e.g. by oobe.php)
  75.                 _CheckParamExists($params, $fieldname);
  76.                 $response=$params[$fieldname];
  77.  
  78.                 $res = url_post_contents(
  79.                         'https://hcaptcha.com/siteverify',
  80.                         array(
  81.                                 "secret"   => $secret,
  82.                                 "response" => $response,
  83.                                 "remoteip" => OIDplus::getClientIpAddress() ?: '',
  84.                                 "sitekey"  => $sitekey
  85.                         )
  86.                 );
  87.  
  88.                 if ($res === false) {
  89.                         throw new OIDplusException(_L('Communication with %1 server failed', 'hCaptcha'));
  90.                 }
  91.  
  92.                 $captcha_success=@json_decode($res);
  93.                 if (!$captcha_success || !$captcha_success->success) {
  94.                         throw new OIDplusException(_L('CAPTCHA not successfully verified').' ('.implode(", ",$captcha_success->{'error-codes'}).')');
  95.                 }
  96.         }
  97.  
  98.         /**
  99.          * @return string
  100.          */
  101.         public static function setupHTML(): string {
  102.                 $curl_status = url_post_contents_available(true, $reason) ? 1 : 0;
  103.                 return '<div id="CAPTCHAPLUGIN_PARAMS_HCAPTCHA">'.
  104.                        '<p>(<a href="https://www.hcaptcha.com/" target="_blank">'._L('more information and obtain key').'</a>)</p>'.
  105.                        '<p>'._L('hCaptcha Site key').'<br><input id="hcaptcha_sitekey" type="text" onkeypress="rebuild()" onkeyup="rebuild()"> <span id="hcaptcha_sitekey_warn"></span></p>'.
  106.                        '<p>'._L('hCaptcha Secret').'<br><input id="hcaptcha_secret" type="text" onkeypress="rebuild()" onkeyup="rebuild()"> <span id="hcaptcha_secret_warn"></span></p>'.
  107.                        '<input id="hcaptcha_curl_status" value="'.$curl_status.'" type="hidden">'.
  108.                        (!$curl_status ? '<p><font color="red">'._L('The %1 plugin cannot connect to the Internet.', self::id()).' '.$reason.'</font></p>' : '').
  109.                        '</div>';
  110.         }
  111.  
  112.         /**
  113.          * @param array $http_headers
  114.          * @return void
  115.          */
  116.         function httpHeaderCheck(array &$http_headers) {
  117.  
  118.                 // If you use CSP headers, please add the following to your configuration:
  119.                 // script-src should include https://hcaptcha.com, https://*.hcaptcha.com
  120.                 $http_headers["Content-Security-Policy"]["script-src"][] = "https://hcaptcha.com";
  121.                 $http_headers["Content-Security-Policy"]["script-src"][] = "https://*.hcaptcha.com";
  122.                 // frame-src should include https://hcaptcha.com, https://*.hcaptcha.com
  123.                 $http_headers["Content-Security-Policy"]["frame-src"][] = "https://hcaptcha.com";
  124.                 $http_headers["Content-Security-Policy"]["frame-src"][] = "https://*.hcaptcha.com";
  125.                 // style-src should include https://hcaptcha.com, https://*.hcaptcha.com
  126.                 $http_headers["Content-Security-Policy"]["style-src"][] = "https://hcaptcha.com";
  127.                 $http_headers["Content-Security-Policy"]["style-src"][] = "https://*.hcaptcha.com";
  128.                 // connect-src should include https://hcaptcha.com, https://*.hcaptcha.com
  129.                 $http_headers["Content-Security-Policy"]["connect-src"][] = "https://hcaptcha.com";
  130.                 $http_headers["Content-Security-Policy"]["connect-src"][] = "https://*.hcaptcha.com";
  131.  
  132.                 //If you are an enterprise customer and would like to enable additional verification to be performed, you can optionally choose the following CSP strategy:
  133.                 //unsafe-eval and unsafe-inline should include https://hcaptcha.com, https://*.hcaptcha.com
  134.                 //$http_headers["Content-Security-Policy"]["unsafe-eval"][] = "https://hcaptcha.com";
  135.                 //$http_headers["Content-Security-Policy"]["unsafe-eval"][] = "https://*.hcaptcha.com";
  136.                 //$http_headers["Content-Security-Policy"]["unsafe-inline"][] = "https://hcaptcha.com";
  137.                 //$http_headers["Content-Security-Policy"]["unsafe-inline"][] = "https://*.hcaptcha.com";
  138.  
  139.         }
  140.  
  141.         /**
  142.          * Implements interface INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_8
  143.          * @param string|null $user
  144.          * @return array
  145.          * @throws OIDplusException
  146.          */
  147.         public function getNotifications(string $user=null): array {
  148.                 $notifications = array();
  149.                 if ((!$user || ($user == 'admin')) && OIDplus::authUtils()->isAdminLoggedIn()) {
  150.                         if ($this->isActive()) {
  151.                                 if (!url_post_contents_available(true, $reason)) {
  152.                                         $notifications[] = new OIDplusNotification('CRIT', _L('OIDplus plugin "%1" is enabled, but OIDplus cannot connect to the Internet.', htmlentities(self::id())) . ' ' . $reason);
  153.                                 }
  154.                         }
  155.                 }
  156.                 return $notifications;
  157.         }
  158.  
  159. }
  160.