Changeset 3481099
- Timestamp:
- 03/12/2026 11:24:37 AM (3 weeks ago)
- Location:
- docid/trunk
- Files:
-
- 6 edited
-
README.txt (modified) (2 diffs)
-
admin/class-docid-metaboxes.php (modified) (2 diffs)
-
docid.php (modified) (1 diff)
-
frontend/class-docid-frontend.php (modified) (8 diffs)
-
includes/class-docid-base.php (modified) (6 diffs)
-
includes/class-docid.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
docid/trunk/README.txt
r3303344 r3481099 4 4 Requires at least: 6.2 5 5 Requires PHP: 7.4 6 Tested up to: 6. 87 Stable tag: 1.1. 06 Tested up to: 6.9 7 Stable tag: 1.1.1 8 8 License: GPLv3 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 56 56 == Changelog == 57 57 58 = 1.0.2 – 1.1. 0=58 = 1.0.2 – 1.1.1 = 59 59 * Minor bugfixes 60 60 -
docid/trunk/admin/class-docid-metaboxes.php
r3303320 r3481099 169 169 170 170 $action = sanitize_key($action); 171 $can_edit_post = current_user_can('edit_post', $post_id); 171 172 $is_autosave = wp_is_post_autosave($post_id); 172 173 $is_revision = wp_is_post_revision($post_id); 173 174 174 return !($is_autosave || $is_revision) && wp_verify_nonce($nonce, $action);175 return $can_edit_post && !($is_autosave || $is_revision) && wp_verify_nonce($nonce, $action); 175 176 } 176 177 … … 222 223 223 224 } 224 -
docid/trunk/docid.php
r3303341 r3481099 14 14 * Plugin Name: DocID 15 15 * Description: The DocID plugin provides all the functionalities required for a secure and legally compliant authentication of healthcare professionals on your website. 16 * Version: 1.1. 016 * Version: 1.1.1 17 17 * Author: 8awake GmbH <support@docid.de> 18 18 * Author URI: https://docid.de -
docid/trunk/frontend/class-docid-frontend.php
r3303341 r3481099 50 50 51 51 /** 52 * Set the DocID session cookie. 53 * 54 * @param string $value 55 * @param int $expires 56 * @return void 57 */ 58 private function docid_set_session_cookie($value, $expires) 59 { 60 $cookieOptions = array( 61 'expires' => $expires, 62 'path' => '/', 63 'secure' => is_ssl(), 64 'httponly' => true, 65 'samesite' => is_ssl() ? 'None' : 'Lax', 66 ); 67 68 $cookieDomain = $this->docid_get_cookie_domain(); 69 if (is_string($cookieDomain) && $cookieDomain !== '') { 70 $cookieOptions['domain'] = $cookieDomain; 71 } 72 73 setcookie(self::DOCID_COOKIE_SESSION_ID, $value, $cookieOptions); 74 } 75 76 /** 52 77 * Add noindex meta and header whenever an authentication token is present 53 78 * … … 59 84 60 85 if ( 61 str _contains($server_request_uri, '?' . self::DOCID_TOKEN_PARAMETER_NAME . '=')||62 str _contains($server_request_uri, '&' . self::DOCID_TOKEN_PARAMETER_NAME . '=')||63 str _contains($server_request_uri, '?' . self::DOCID_SKIP_TOKEN_PARAMETER_NAME . '=')||64 str _contains($server_request_uri, '&' . self::DOCID_SKIP_TOKEN_PARAMETER_NAME . '=')86 strpos($server_request_uri, '?' . self::DOCID_TOKEN_PARAMETER_NAME . '=') !== false || 87 strpos($server_request_uri, '&' . self::DOCID_TOKEN_PARAMETER_NAME . '=') !== false || 88 strpos($server_request_uri, '?' . self::DOCID_SKIP_TOKEN_PARAMETER_NAME . '=') !== false || 89 strpos($server_request_uri, '&' . self::DOCID_SKIP_TOKEN_PARAMETER_NAME . '=') !== false 65 90 ) { 66 91 … … 85 110 { 86 111 $assetSecret = get_option('docid_general_asset_secret'); 112 $secretFromQuery = isset($_GET[self::DOCID_SECRET_PARAMETER_NAME]) 113 ? wp_unslash($_GET[self::DOCID_SECRET_PARAMETER_NAME]) 114 : null; 87 115 88 116 /** 89 117 * Check if anon secret was provided 90 118 */ 91 if(isset($_GET[self::DOCID_SECRET_PARAMETER_NAME]) && !empty($_GET[self::DOCID_SECRET_PARAMETER_NAME]) && sanitize_text_field(wp_unslash($_GET[self::DOCID_SECRET_PARAMETER_NAME])) === $assetSecret) { 119 if ( 120 is_string($secretFromQuery) && 121 $secretFromQuery !== '' && 122 sanitize_text_field($secretFromQuery) === $assetSecret 123 ) { 92 124 93 125 $guestToken = bin2hex(random_bytes(16)); … … 97 129 set_transient('docid_skip_auth_' . $this->docid_hashed($skipAuthToken), true, 30); 98 130 99 setcookie(self::DOCID_COOKIE_SESSION_ID, $guestToken, ['expires' => time() + 3600, 'path' => '/', 'domain' => $this->docid_get_cookie_domain(), 'secure' => is_ssl(), 'httponly' => true, 'samesite' => is_ssl() ? 'None' : 'Lax']);131 $this->docid_set_session_cookie($guestToken, time() + 3600); 100 132 101 133 $this->_docid_remove_auth_token($skipAuthToken); … … 105 137 } 106 138 107 $authToken = isset($_GET[self::DOCID_TOKEN_PARAMETER_NAME]) && !empty($_GET[self::DOCID_TOKEN_PARAMETER_NAME] && preg_match('/^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/', $_GET[self::DOCID_TOKEN_PARAMETER_NAME])) ? wp_unslash($_GET[self::DOCID_TOKEN_PARAMETER_NAME]) : false; 139 $rawAuthToken = isset($_GET[self::DOCID_TOKEN_PARAMETER_NAME]) 140 ? wp_unslash($_GET[self::DOCID_TOKEN_PARAMETER_NAME]) 141 : null; 142 $authToken = false; 143 144 if ( 145 is_string($rawAuthToken) && 146 $rawAuthToken !== '' && 147 preg_match('/^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/', $rawAuthToken) === 1 148 ) { 149 $authToken = $rawAuthToken; 150 } 108 151 109 152 /** … … 130 173 131 174 } else { 175 $statusCode = wp_remote_retrieve_response_code($response); 176 if ($statusCode < 200 || $statusCode >= 300) { 177 if (defined('WP_DEBUG') && WP_DEBUG === true) { 178 error_log('Error docid_handle_auth_token: unexpected response code ' . $statusCode); 179 } 180 181 $this->_docid_remove_auth_token(); 182 return; 183 } 132 184 133 185 $body = wp_remote_retrieve_body($response); 134 186 $data = json_decode($body, true); 135 187 136 if ( empty($data) || is_null($data)) {188 if (!is_array($data) || empty($data) || isset($data['error'])) { 137 189 $this->_docid_remove_auth_token(); 138 190 return; … … 148 200 set_transient('docid_skip_auth_' . $this->docid_hashed($skipAuthToken), true, 30); 149 201 150 setcookie(self::DOCID_COOKIE_SESSION_ID, $authToken, ['expires' => time() + 3600, 'path' => '/', 'domain' => $this->docid_get_cookie_domain(), 'secure' => is_ssl(), 'httponly' => true, 'samesite' => is_ssl() ? 'None' : 'Lax']);202 $this->docid_set_session_cookie($authToken, time() + 3600); 151 203 152 204 } … … 248 300 { 249 301 $logoutNonce = isset($_REQUEST['docid_logout_nonce']) ? sanitize_text_field(wp_unslash($_REQUEST['docid_logout_nonce'])) : ''; 250 251 if (wp_verify_nonce($logoutNonce, 'docid_logout_nonce')) { 302 $isValidNonce = wp_verify_nonce($logoutNonce, 'docid_logout_nonce'); 303 304 if ($isValidNonce) { 252 305 /** 253 306 * Clear session 254 307 */ 255 if(isset($_COOKIE['docid_session_id'])) { 256 delete_transient("docid_user_" . $this->docid_hashed(sanitize_text_field(wp_unslash($_COOKIE[self::DOCID_COOKIE_SESSION_ID])))); 257 } 258 setcookie(self::DOCID_COOKIE_SESSION_ID, '', ['expires' => time() + 1, 'path' => '/', 'domain' => $this->docid_get_cookie_domain(), 'secure' => is_ssl(), 'httponly' => true, 'samesite' => is_ssl() ? 'None' : 'Lax']); 259 } 260 261 wp_redirect(home_url('/')); 308 if(isset($_COOKIE[self::DOCID_COOKIE_SESSION_ID])) { 309 $sessionId = sanitize_text_field(wp_unslash($_COOKIE[self::DOCID_COOKIE_SESSION_ID])); 310 delete_transient("docid_user_" . $this->docid_hashed($sessionId)); 311 } 312 } 313 314 /** 315 * Always clear browser cookie variants. 316 * If nonce is invalid, this still allows user-initiated cleanup of stale sessions. 317 */ 318 $this->docid_clear_session_cookie(); 319 320 nocache_headers(); 321 wp_safe_redirect(home_url('/')); 322 exit; 323 } 324 325 /** 326 * Expire the DocID session cookie for all relevant domain variants. 327 * 328 * @return void 329 */ 330 private function docid_clear_session_cookie() 331 { 332 $cookieName = self::DOCID_COOKIE_SESSION_ID; 333 $cookieDomains = array(); 334 335 $configuredCookieDomain = $this->docid_get_cookie_domain(); 336 if (is_string($configuredCookieDomain) && $configuredCookieDomain !== '') { 337 $cookieDomains[] = $configuredCookieDomain; 338 $cookieDomains[] = ltrim($configuredCookieDomain, '.'); 339 } 340 341 if (defined('COOKIE_DOMAIN') && is_string(COOKIE_DOMAIN) && COOKIE_DOMAIN !== '') { 342 $cookieDomains[] = COOKIE_DOMAIN; 343 $cookieDomains[] = ltrim(COOKIE_DOMAIN, '.'); 344 } 345 346 if (isset($_SERVER['HTTP_HOST'])) { 347 $httpHostRaw = wp_unslash($_SERVER['HTTP_HOST']); 348 $httpHost = wp_parse_url('http://' . $httpHostRaw, PHP_URL_HOST); 349 if (!is_string($httpHost) || $httpHost === '') { 350 $httpHost = preg_replace('/[^a-zA-Z0-9.-]/', '', $httpHostRaw); 351 } 352 if (is_string($httpHost) && $httpHost !== '') { 353 $cookieDomains[] = $httpHost; 354 } 355 } 356 357 $cookieDomains[] = null; // host-only cookie variant (no Domain attribute) 358 $cookieDomains = array_values(array_unique($cookieDomains, SORT_REGULAR)); 359 360 $cookiePaths = array('/'); 361 $pathCandidates = array( 362 parse_url(home_url('/'), PHP_URL_PATH), 363 parse_url(site_url('/'), PHP_URL_PATH), 364 parse_url(admin_url('/'), PHP_URL_PATH), 365 defined('COOKIEPATH') ? COOKIEPATH : null, 366 defined('SITECOOKIEPATH') ? SITECOOKIEPATH : null, 367 defined('ADMIN_COOKIE_PATH') ? ADMIN_COOKIE_PATH : null, 368 defined('PLUGINS_COOKIE_PATH') ? PLUGINS_COOKIE_PATH : null, 369 ); 370 371 foreach ($pathCandidates as $pathCandidate) { 372 if (!is_string($pathCandidate) || $pathCandidate === '') { 373 continue; 374 } 375 376 $normalizedPath = '/' . ltrim($pathCandidate, '/'); 377 $normalizedPath = preg_replace('#/+#', '/', $normalizedPath); 378 $normalizedPath = rtrim($normalizedPath, '/'); 379 $normalizedPath = ($normalizedPath === '') ? '/' : $normalizedPath; 380 381 $cookiePaths[] = $normalizedPath; 382 383 if ($normalizedPath !== '/') { 384 $cookiePaths[] = $normalizedPath . '/'; 385 } 386 387 if (preg_match('#/wp-admin$#', $normalizedPath) === 1) { 388 $wpRootPath = preg_replace('#/wp-admin$#', '', $normalizedPath); 389 $wpRootPath = (is_string($wpRootPath) && $wpRootPath !== '') ? $wpRootPath : '/'; 390 $cookiePaths[] = $wpRootPath; 391 if ($wpRootPath !== '/') { 392 $cookiePaths[] = $wpRootPath . '/'; 393 } 394 } 395 } 396 397 $cookiePaths = array_values(array_unique($cookiePaths)); 398 $cookieVariants = array( 399 array( 400 'secure' => true, 401 'samesite' => 'None', 402 ), 403 array( 404 'secure' => false, 405 'samesite' => 'Lax', 406 ), 407 ); 408 409 if (headers_sent($file, $line)) { 410 if (defined('WP_DEBUG') && WP_DEBUG === true) { 411 error_log( 412 sprintf( 413 'DocID logout could not clear cookie "%s" because headers were already sent at %s:%d', 414 $cookieName, 415 $file, 416 $line 417 ) 418 ); 419 } 420 421 unset($_COOKIE[$cookieName]); 422 return; 423 } 424 425 foreach ($cookieDomains as $domain) { 426 foreach ($cookiePaths as $path) { 427 foreach ($cookieVariants as $cookieVariant) { 428 $cookieOptions = array( 429 'expires' => time() - 3600, 430 'path' => $path, 431 'secure' => $cookieVariant['secure'], 432 'httponly' => true, 433 'samesite' => $cookieVariant['samesite'], 434 ); 435 436 if (is_string($domain) && $domain !== '') { 437 $cookieOptions['domain'] = $domain; 438 } 439 440 setcookie($cookieName, '', $cookieOptions); 441 } 442 } 443 } 444 445 unset($_COOKIE[$cookieName]); 262 446 } 263 447 -
docid/trunk/includes/class-docid-base.php
r3303341 r3481099 18 18 * @since 1.0.0 19 19 */ 20 const DOCID_OPEN_URL_ENDPOINT = 'https://docid. de/open';20 const DOCID_OPEN_URL_ENDPOINT = 'https://docid.pro/open'; 21 21 22 22 /** … … 25 25 * @since 1.0.0 26 26 */ 27 const DOCID_USER_ACCOUNT_ENDPOINT = 'https://docid. de/api/accounts/user/external';27 const DOCID_USER_ACCOUNT_ENDPOINT = 'https://docid.pro/api/accounts/user/external'; 28 28 29 29 /** … … 102 102 * Get cookie domain 103 103 * 104 * @return string 104 * @return string|null 105 105 * @since 1.1.0 106 106 * @access public … … 109 109 { 110 110 $domain = parse_url(home_url(), PHP_URL_HOST); 111 if (!is_string($domain) || $domain === '') { 112 return null; 113 } 114 115 $domain = strtolower($domain); 111 116 $domain = preg_replace('/^www\./', '', $domain); 112 return "." . $domain; 117 $domain = ltrim($domain, '.'); 118 119 if ($domain === '' || $domain === 'localhost' || filter_var($domain, FILTER_VALIDATE_IP)) { 120 return null; 121 } 122 123 if (strpos($domain, '.') === false) { 124 return null; 125 } 126 127 return '.' . $domain; 128 } 129 130 /** 131 * Check whether the provided session id has a valid DocID format. 132 * 133 * Supports: 134 * - JWT user tokens 135 * - random hex guest tokens used for did_s based sessions 136 * 137 * @param string $sessionId 138 * @return bool 139 * @since 1.1.1 140 * @access private 141 */ 142 private function docid_is_valid_session_id($sessionId) 143 { 144 if (!is_string($sessionId) || $sessionId === '') { 145 return false; 146 } 147 148 if (preg_match('/^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/', $sessionId)) { 149 return true; 150 } 151 152 if (preg_match('/^[A-Fa-f0-9]{32,128}$/', $sessionId)) { 153 return true; 154 } 155 156 return false; 113 157 } 114 158 … … 125 169 * @note no nonce handling as this is an authentication handler 126 170 */ 127 if(isset($_COOKIE[self::DOCID_COOKIE_SESSION_ID]) && preg_match('/^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/', $_COOKIE[self::DOCID_COOKIE_SESSION_ID])) {171 if(isset($_COOKIE[self::DOCID_COOKIE_SESSION_ID])) { 128 172 $session_id = wp_unslash($_COOKIE[self::DOCID_COOKIE_SESSION_ID]); 129 if( get_transient("docid_user_" . $this->docid_hashed($session_id))) {173 if($this->docid_is_valid_session_id($session_id) && get_transient("docid_user_" . $this->docid_hashed($session_id))) { 130 174 return true; 131 175 } 132 } elseif(isset($_GET[self::DOCID_SKIP_TOKEN_PARAMETER_NAME])) { 176 } 177 178 if(isset($_GET[self::DOCID_SKIP_TOKEN_PARAMETER_NAME])) { 133 179 $skip_token_id = sanitize_text_field(wp_unslash($_GET[self::DOCID_SKIP_TOKEN_PARAMETER_NAME])); 134 if (get_transient("docid_skip_auth_" . $this->docid_hashed($skip_token_id))) { 135 return true; 136 } 180 181 if (preg_match('/^[A-Fa-f0-9]{32,128}$/', $skip_token_id) === 1) { 182 static $validated_skip_tokens = array(); 183 184 $skip_token_hash = $this->docid_hashed($skip_token_id); 185 if (isset($validated_skip_tokens[$skip_token_hash])) { 186 return true; 187 } 188 189 $transientKey = "docid_skip_auth_" . $skip_token_hash; 190 if (get_transient($transientKey)) { 191 delete_transient($transientKey); 192 $validated_skip_tokens[$skip_token_hash] = true; 193 return true; 194 } 195 } 196 137 197 } 138 198 … … 149 209 public function docid_get_user() 150 210 { 151 if(isset($_COOKIE[self::DOCID_COOKIE_SESSION_ID]) && preg_match('/^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/', $_COOKIE[self::DOCID_COOKIE_SESSION_ID])) {211 if(isset($_COOKIE[self::DOCID_COOKIE_SESSION_ID])) { 152 212 $session_id = wp_unslash($_COOKIE[self::DOCID_COOKIE_SESSION_ID]); 213 if(!$this->docid_is_valid_session_id($session_id)) { 214 return null; 215 } 153 216 $user = get_transient("docid_user_" . $this->docid_hashed($session_id)); 154 217 if($user) { -
docid/trunk/includes/class-docid.php
r3298583 r3481099 59 59 60 60 $this->plugin_name = 'docid'; 61 $this->version = '1. 0.1';61 $this->version = '1.1.1'; 62 62 63 63 $this->set_loader();
Note: See TracChangeset
for help on using the changeset viewer.