HEX
Server: nginx
System: Linux pool195-106-36.bur.atomicsites.net 6.12.57+deb12-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.57-1~bpo12+1 (2025-11-17) x86_64
User: (0)
PHP: 8.3.32
Disabled: pcntl_fork
Upload Files
File: //wordpress/plugins/wp-cloud-client/beta/src/Access/LoginPageGuard.php
<?php

declare(strict_types=1);

namespace VPlugins\WPCloudClient\Access;

use VPlugins\WPCloudClient\Settings\SettingsRepository;

class LoginPageGuard {

	/**
	 * Login-page actions that must always reach native handling, even when the
	 * login page is hidden (logout, lost-password and reset-password flows).
	 *
	 * @var string[]
	 */
	private const ALLOWED_ACTIONS = [ 'logout', 'lostpassword', 'rp', 'resetpass' ];

	/**
	 * Construct the login page guard.
	 *
	 * @param SettingsRepository $settings Settings repository.
	 */
	public function __construct(
		private readonly SettingsRepository $settings,
	) {}

	/**
	 * Hooked to `init` and `login_init`. Redirects unauthenticated visitors
	 * away from wp-login.php when the login page is hidden.
	 *
	 * The redirect is governed by the manager's expose_login flag (set via
	 * set_login_visibility) and only ever applies to production sites.
	 * Staging sites always use WordPress's native login page — expose_login
	 * has no bearing on staging, since set_login_visibility is never called
	 * during staging site creation. One-click login, logout and
	 * password-reset flows are always allowed through.
	 *
	 * @return void
	 */
	public function handle(): void {
		$pagenow = $GLOBALS['pagenow'] ?? basename( $_SERVER['PHP_SELF'] ?? '' );
		if ( 'wp-login.php' !== $pagenow ) {
			return;
		}

		// Authenticated users keep full access to the login page.
		if ( is_user_logged_in() ) {
			return;
		}

		// Staging sites always use the native WordPress login page —
		// Advanced Login (expose_login) only applies to production sites.
		if ( $this->settings->isStaging() ) {
			return;
		}

		// Respect the manager's expose_login flag.
		if ( $this->settings->isLoginExposed() ) {
			return;
		}

		// One-click login (handled by LoginInterceptor on the same hook).
		if ( isset( $_GET['wpcc_login_token'] ) ) {
			return;
		}

		// A just-logged-out request (WordPress lands on wp-login.php?loggedout=true)
		// must never be bounced into the sign-in SSO entry — that silently
		// re-authenticates the user from a still-live upstream session. Send
		// them to the logout destination instead (LogoutRedirect normally
		// handles this via the logout_redirect filter; this is the safety net
		// for direct landings and un-provisioned sites).
		if ( isset( $_GET['loggedout'] ) ) {
			$logout_url = $this->settings->getLogoutRedirectUrl();
			wp_redirect( esc_url_raw( '' !== $logout_url ? $logout_url : home_url() ) );
			exit;
		}

		// Logout and password-reset flows must continue to work.
		$action = isset( $_REQUEST['action'] )
			? sanitize_key( wp_unslash( $_REQUEST['action'] ) )
			: '';
		if ( in_array( $action, self::ALLOWED_ACTIONS, true ) ) {
			return;
		}

		$target = $this->settings->getLoginRedirectUrl();
		if ( '' === $target ) {
			return;
		}

		// External host, so wp_redirect (not wp_safe_redirect), matching the
		// bootstrapper's redirect to the Portal. esc_url_raw() guards against a
		// malformed or javascript:-scheme URL supplied by the manager.
		wp_redirect( esc_url_raw( $target ) );
		exit;
	}
}