403], ); } $url = rest_url('apeari/v1/ownership'); if ( 'https' !== wp_parse_url($url, PHP_URL_SCHEME) || wp_parse_url($url, PHP_URL_HOST) !== wp_parse_url(home_url(), PHP_URL_HOST) ) { return new WP_Error( 'apeari_https', __( 'Potwierdzenie domeny wymaga publicznego HTTPS i API REST na tej samej domenie.', 'apeari', ), ); } $secret = bin2hex(random_bytes(32)); $token = 'apeari_claim_' . rtrim( strtr(base64_encode(wp_json_encode(['url' => $url, 'secret' => $secret])), '+/', '-_'), '=', ); $cipher = APEARI_Provider_Secrets::encrypt('ownership', $token); if (is_wp_error($cipher)) { return $cipher; } update_option( self::OPTION, [ 'hash' => hash('sha256', $secret), 'expires' => time() + 15 * MINUTE_IN_SECONDS, 'site' => home_url('/'), ], false, ); update_option('apeari_secret_ownership', $cipher, false); return $token; } public static function token(): string { $proof = get_option(self::OPTION); if ( !current_user_can('manage_options') || (is_multisite() && !is_super_admin()) || !is_array($proof) || ($proof['expires'] ?? 0) <= time() || ($proof['site'] ?? '') !== home_url('/') ) { return ''; } $token = APEARI_Provider_Secrets::key('ownership'); return is_wp_error($token) ? '' : $token; } public static function allowed(WP_REST_Request $request) { $proof = get_option(self::OPTION); $secret = (string) $request->get_header('X-Apeari-Ownership'); return is_array($proof) && ($proof['expires'] ?? 0) > time() && ($proof['site'] ?? '') === home_url('/') && preg_match('/^[a-f0-9]{64}$/D', $secret) && hash_equals($proof['hash'] ?? '', hash('sha256', $secret)) ? true : new WP_Error( 'apeari_ownership', __('Potwierdzenie domeny jest nieprawidłowe lub wygasło.', 'apeari'), ['status' => 403], ); } public static function verify(WP_REST_Request $request) { if (true !== self::allowed($request)) { return new WP_Error('apeari_ownership', __('Nieprawidłowe potwierdzenie domeny.', 'apeari'), [ 'status' => 403, ]); } $challenge = $request['challenge']; if (!is_string($challenge) || !preg_match('/^[a-f0-9]{64}$/D', $challenge)) { return new WP_Error('apeari_challenge', __('Nieprawidłowe żądanie.', 'apeari'), [ 'status' => 400, ]); } global $wpdb; $proof = get_option(self::OPTION); $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name = %s AND BINARY option_value = %s", self::OPTION, maybe_serialize($proof), ), ); wp_cache_delete(self::OPTION, 'options'); if (1 !== $deleted) { return new WP_Error( 'apeari_ownership_used', __('Potwierdzenie zostało już wykorzystane. Wygeneruj nowe.', 'apeari'), ['status' => 409], ); } delete_option('apeari_secret_ownership'); return new WP_REST_Response( ['verified' => true, 'challenge' => $challenge, 'siteUrl' => home_url('/')], 200, ['Cache-Control' => 'no-store'], ); } public static function submit(): void { if (!current_user_can('manage_options')) { wp_die(esc_html__('Brak dostępu.', 'apeari')); } check_admin_referer('apeari_ownership'); $result = self::generate(); if (is_wp_error($result)) { wp_die(esc_html($result->get_error_message())); } wp_safe_redirect(admin_url('options-general.php?page=apeari-settings')); exit(); } }