settings = $settings; } /** * Hook into WordPress. */ public function init() { add_action( 'init', array( $this, 'maybe_grant_access_from_url' ), 1 ); } /** * If a valid preview token is present in the URL, set a signed * cookie so the visitor can browse the whole site without it * being appended to every link. */ public function maybe_grant_access_from_url() { if ( empty( $_GET[ self::QUERY_VAR ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return; } $token = sanitize_text_field( wp_unslash( $_GET[ self::QUERY_VAR ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( $this->is_valid_token( $token ) ) { $cookie_value = $this->sign_token( $token ); $expire = time() + ( 12 * HOUR_IN_SECONDS ); setcookie( self::COOKIE_NAME, $cookie_value, array( 'expires' => $expire, 'path' => COOKIEPATH ? COOKIEPATH : '/', 'domain' => COOKIE_DOMAIN ? COOKIE_DOMAIN : '', 'secure' => is_ssl(), 'httponly' => true, 'samesite' => 'Lax', ) ); // Also make it available for the current request immediately. $_COOKIE[ self::COOKIE_NAME ] = $cookie_value; } } /** * Whether the given token string matches the currently stored token. * * @param string $token Token to check. * @return bool */ public function is_valid_token( $token ) { $stored = (string) $this->settings->get( 'private_token', '' ); if ( '' === $stored || '' === $token ) { return false; } return hash_equals( $stored, $token ); } /** * Sign a token with the WP auth salt so the cookie value can't be * forged even if the raw token were somehow guessed. * * @param string $token Raw token. * @return string */ private function sign_token( $token ) { return hash_hmac( 'sha256', $token, wp_salt( 'auth' ) ); } /** * Whether the current visitor has a valid bypass cookie for the * currently stored token. * * @return bool */ public function visitor_has_bypass_cookie() { if ( empty( $_COOKIE[ self::COOKIE_NAME ] ) ) { return false; } $token = (string) $this->settings->get( 'private_token', '' ); if ( '' === $token ) { return false; } $expected = $this->sign_token( $token ); $actual = sanitize_text_field( wp_unslash( $_COOKIE[ self::COOKIE_NAME ] ) ); return hash_equals( $expected, $actual ); } /** * Build the full private preview URL for display in the admin panel. * * @return string */ public function get_preview_url() { $token = $this->settings->get( 'private_token', '' ); if ( '' === $token ) { return ''; } return add_query_arg( self::QUERY_VAR, rawurlencode( $token ), home_url( '/' ) ); } }