Your IP : 216.73.216.5


Current Path : /home/theafprt/conviviality360.com/wp-content/mu-plugins/ionos-sso/inc/
Upload File :
Current File : /home/theafprt/conviviality360.com/wp-content/mu-plugins/ionos-sso/inc/class-manager.php

<?php
/*
Plugin Name:  IONOS SSO
Plugin URI:   https://www.ionos.com
Description:  SSO for WordPress
Version:
License:      GPLv2 or later
Author:       IONOS
Author URI:   https://www.ionos.com
Text Domain:  ionos-sso
@package ionos-sso
*/

namespace Ionos\SSO;

// Do not allow direct access!

if ( ! defined( 'ABSPATH' ) ) {
	die();
}

/**
 * Manager class
 */
class Manager {
	const JUMP_HOST_DOMAIN = 'webapps-sso.hosting.ionos.com';
	const WP_PSS_API = 'https://cisapi.hosting.ionos.com/%s/managed-wordpress/provisionings?domain-binding=%s';
	const DEFAULT_OAUTH_ERROR = 'The login process via SSO failed';
	const TRANSIENT_PREFIX = 'ionos_sso_';

	/**
	 * SSO constructor.
	 */
	public function __construct() {
		add_action( 'init', array( $this, 'oauth' ) );
		add_action( 'wp_logout', array( $this, 'cleanup_after_logout' ) );
	}

	/**
	 * Handle oauth process.
	 */
	public function oauth() {
		global $pagenow;
		if ( '1' !== get_option( 'ionos_oauth_enabled' )
		     || strpos( wp_login_url(), $pagenow ) === false
		     || ! is_ssl()
		) {
			return null;
		}

		$this->single_sign_on();

		if ( Helper::compare_action( 'ionos_oauth_register' ) ) {
			$this->register_domain();
			$this->oauth_authenticate();
		} elseif ( Helper::compare_action( 'ionos_oauth_authenticate' ) ) {
			if ( ! isset( $_GET['access_token'] )
			     || ! isset( $_GET['state'] )
			     || $_GET['state'] !== get_transient(self::TRANSIENT_PREFIX . 'state')
			) {
				return $this->login_error( __( self::DEFAULT_OAUTH_ERROR,
					'ionos-sso' ) );
			}

			// decrypt access token.
			$decoded_access_token
				= $this->decrypt_access_token( $_GET['access_token'],
				get_transient(self::TRANSIENT_PREFIX . 'iv'),
				get_transient(self::TRANSIENT_PREFIX . 'secret')
			);

			if ( $decoded_access_token === false ) {
				return $this->login_error( __( self::DEFAULT_OAUTH_ERROR,
					'ionos-sso' ) );
			}

			// validate access token by wordpress-pss.
			$is_valid_access_token = $this->check_customer_access_authorization( $decoded_access_token );
			if ( $is_valid_access_token === true ) {
				// add user information to transient.
				delete_transient( self::TRANSIENT_PREFIX . 'state' );
				delete_transient( self::TRANSIENT_PREFIX . 'secret' );
				delete_transient( self::TRANSIENT_PREFIX . 'iv' );
				set_transient( self::TRANSIENT_PREFIX . 'access_token',
					base64_encode( $decoded_access_token ), 60 * 60 * 2 );
				add_filter( 'authenticate', array( $this, 'login_admin_user' ),
					1 );
			} else {
				return $this->login_error( __( self::DEFAULT_OAUTH_ERROR, 'ionos-sso' ) );
			}
		}

	}


	/**
	 * try to log in via Single Sign-On, if the conditions are given
	*/
	private function single_sign_on() {
		$referer_host = $this->get_referer_host();
		if ( '1' === get_option( 'ionos_oauth_enabled' )
		     && true === Helper::is_ionos_host( $referer_host )
		     && Helper::is_blacklisted_ionos_domain( $referer_host ) === false
		     && ! isset($_GET['action'])
		) {
			$this->register_domain();
			$this->oauth_authenticate();
		}
	}

	/**
	 * Returns hostname of the referer
	 *
	 * @return string
	 */
	private function get_referer_host() {
		$referer_host = null;
		if ( ( $referer = filter_var( wp_get_raw_referer(),
				FILTER_VALIDATE_URL ) ) !== false
		) {
			$referer_host = parse_url( $referer, PHP_URL_HOST );
		}

		return $referer_host;
	}

	/**
	 * Register Domain to JumpHost and store keys into the session.
	 * The keys are needed to encrypt access_token
	 */
	private function register_domain() {
		$oauth_register_result = $this->oauth_register();
		set_transient(self::TRANSIENT_PREFIX . 'state', $oauth_register_result['state'], 7200);
		set_transient(self::TRANSIENT_PREFIX . 'secret', $oauth_register_result['secret'], 600);
		set_transient(self::TRANSIENT_PREFIX . 'iv', $oauth_register_result['iv'], 600);
	}

	/**
	 * Redirect to IONOS OAuth login form.
	 *
	 * @return mixed [ $state, $iv, $secret ]
	 */
	private function oauth_register() {
		try {
			$response = wp_remote_post(
				'https://' . self::JUMP_HOST_DOMAIN . '/api/wordpress/register',
				array(
					'method' => 'POST',
					'body'   => array(
						'wp_callback' => wp_login_url()
						                 . '?action=ionos_oauth_authenticate',
						'market'      => get_option( 'ionos_assistant_market', 'US' ),
					),
				)
			);
		} catch ( Exception $e ) {
			return $this->login_error( __( self::DEFAULT_OAUTH_ERROR,
				'ionos-sso' ) );
		}

		if ( is_array( $response ) && isset( $response['response'] )
		     && isset( $response['response']['code'] )
		     && 200 === $response['response']['code']
		     && is_array(
			     $response_decoded = json_decode( $response['body'], true ) )
		     && isset( $response_decoded['state'], $response_decoded['secret'], $response_decoded['iv'] )
		) {
			return $response_decoded;
		} else {
			return $this->login_error( __( self::DEFAULT_OAUTH_ERROR,
				'ionos-sso' ) );
		}
	}

	/**
	 * Throw error message.
	 *
	 * @param  string  $text  // Error message.
	 *
	 * @return WP_ERROR
	 */
	private function login_error( $text = '' ) {
		$this->error_message = $text;

		return add_filter(
			'wp_login_errors',
			function ( $errors ) {
				$msg = '<strong>Error</strong>: ' . $this->error_message;
				$errors->add(
					'ionos_oauth_error',
					$msg
				);

				return $errors;
			}
		);
	}

	/**
	 * Redirect to jumphost.
	 */
	private function oauth_authenticate() {
		$param = [
			'state'  => get_transient(self::TRANSIENT_PREFIX . 'state'),
			'action' => 'ionos_oauth_authenticate',
		];
		wp_redirect( 'https://' . self::JUMP_HOST_DOMAIN . '/wordpress/login?'
		             . http_build_query( $param ) );
	}

	/**
	 * Decrypt the access token.
	 *
	 * @param  string  $string  // String that have to decrypted.
	 * @param  string  $iv  // Individual value needed for decrypting.
	 * @param  string  $secret  // needed for decrypting.
	 *
	 * @return string|false
	 */
	private function decrypt_access_token( $string, $iv, $secret ) {
		return openssl_decrypt( $string, 'AES-256-CBC', $secret, 0, $iv );
	}

	/**
	 * Validate access token via WP_PSS_API
	 *
	 * @param  string  $access_token  // Decrypted access token.
	 *
	 * @return bool
	 */
	private function check_customer_access_authorization( string $access_token = null ) {
		if ( is_null( $access_token ) ) {
			return false;
		}

		$customer_domain = parse_url( filter_var( get_home_url(), FILTER_VALIDATE_URL ), PHP_URL_HOST );
		$response        = wp_remote_get(
			sprintf(
				self::WP_PSS_API,
				self::JUMP_HOST_DOMAIN,
				$customer_domain
			),
			array(
				'headers' => array(
					'Authorization' => 'Bearer ' . $access_token,
				),
			)
		);

		if ( isset( $response['response']['code'] )
		     && 200 === $response['response']['code']
		) {
			$provisions = json_decode( $response['body'], true );

			return $this->validate_provisioning( $provisions,
				$customer_domain );
		}

		return false;
	}

	private function validate_provisioning(
		array $provisions = [],
		string $customer_domain = null
	) {
		if ( empty( $provisions ) || is_null( $customer_domain ) ) {
			return false;
		}

		foreach ( $provisions as $provisioning ) {
			if ( isset( $provisioning['projects'] ) ) {
				foreach ( $provisioning['projects'] as $project ) {
					if ( isset( $project['domain_binding'] )
					     && $project['domain_binding'] === $customer_domain
					) {
						return true;
					}
				}
			}
		}

		return false;
	}

	/**
	 * Remove session parameter and revoke access token by jumphost.
	 *
	 * @return void
	 */
	public function cleanup_after_logout() {
		$access_token = get_transient(self::TRANSIENT_PREFIX . 'access_token');
		wp_remote_post(
			'https://' . self::JUMP_HOST_DOMAIN . '/api/wordpress/revoke',
			array(
				'headers' => array(
					'Authorization' => 'Bearer ' . $access_token,
				),
			)
		);
		delete_transient(self::TRANSIENT_PREFIX . 'state');
		delete_transient(self::TRANSIENT_PREFIX . 'secret');
		delete_transient(self::TRANSIENT_PREFIX . 'iv');

	}

	/**
	 * Login first admin user
	 */
	public function login_admin_user() {
		foreach ( get_users() as $user ) {
			if ( user_can( $user->ID, 'administrator' ) ) {
				return $user;
			}
		}

		return $this->login_error( __( self::DEFAULT_OAUTH_ERROR,
			'ionos-sso' ) );
	}
}