true, 'data' => null, 'message' => 'Some error occurred.', ); public $additional_info = array(); public $request_host; public $request_uri; public $prefix; public $site_url; public $home_url; public $onecom_domain; public $onecom_subdomain = ''; private $is_own_dir; private $is_req_host_different; private $is_rest_request; private $is_wpadmin = false; /** * Returns the *Singleton* instance of this class. */ public static function getInstance() { if ( self::$instance === null ) { self::$instance = new OCLAUTH(); } return self::$instance; } /* * Private constructor to prevent initiation with outer code. */ public function __construct() { // error_log("------------------------"); // error_log("Entered inside OCL"); // error_log("------------------------"); global $wpdb; //set table prefix $this->prefix = $wpdb->prefix; $this->preliminaryChecks(); // check and correct request URL // covers support for subdirectory installations $this->validateRequestURL(); /* Push stats - 1-click-login init after login button click */ /*(class_exists('OCPushStats')?\OCPushStats::push_stats_performance_cache("click_login",'login','login','control-panel', $this->additional_info):'');*/ $this->generateKey(); if ( $this->tokenExist() ) { $this->tokenVal = $_GET[ self::TOKENKEY ]; $this->wpPathVal = isset( $_GET[ self::WP_PATHKEY ] ) ? urldecode( $_GET[ self::WP_PATHKEY ] ) : ''; $this->validateToken(); } } public function __destruct() { } /** * Strip off http:// and www from URL */ public function cleanURL( $url = '' ): string { return rtrim( str_replace( 'www.', '', str_replace( 'https://', '', str_replace( 'http://', '', $url ) ) ), '/' ); } /** * Preliminary checks before processing the auth request */ public function preliminaryChecks() { // original request vars $this->request_host = $_SERVER['HTTP_HOST']; $this->request_uri = $_SERVER['REQUEST_URI']; // get WP's site_url & home_url $this->site_url = get_site_option( 'siteurl' ); $this->home_url = is_multisite() ? get_home_url() : get_site_option( 'home' ); // is wp-admin url $this->is_wpadmin = ( false !== strpos( $this->request_uri, '/wp-admin' ) ); // is rest request $this->is_rest_request = isset( $_REQUEST['rest_route'] ); /* is WP having its own directory */ if ( $this->cleanURL( $this->site_url ) !== $this->cleanURL( $this->home_url ) ) { $this->is_own_dir = true; } // parse hosts for matching in next step $site_url_host = parse_url( $this->site_url )['host']; $home_url_host = parse_url( $this->home_url )['host']; // if WP site URL doesn't match with requested URL if ( $home_url_host != $this->cleanURL( $this->request_host ) ) { $this->is_req_host_different = true; } // onecom domain and subdomain/subdirectory $this->onecom_domain = $this->get_sitedomain(); $this->onecom_subdomain = $this->get_sitesubdomain(); } /* * Check and correct request URL * Covers support for subdirectory installations detected by CMS Scanner * */ public function validateRequestURL() { if ( $this->is_wpadmin ) { // error_log("Reached inside is_wpadmin==true"); return true; } // parse hosts for matching in next step $site_url_host = parse_url( $this->site_url )['host']; $home_url_host = parse_url( $this->home_url )['host']; // in general we'll rely on WP's site_url $wp_url_host = $site_url_host; // but check if this installation is having its own directory // in such a case site_url and home_url are different /*if($site_url_host !== $home_url_host){ // we'll prefer using home_url instead of site_url // because this installation is having its own directory. $wp_url_host = $home_url_host; $wp_url = $home_url; }*/ // get request URL sent by CP $request_host = $_SERVER['HTTP_HOST']; // if the current is a rest_api call if ( $this->is_rest_request ) { // then use home_url to get rest_api response $correct_url = rtrim( $this->home_url, '/' ) . $this->request_uri; // error_log("1st case --- rest api call"); } // if the WP is having its own directory elseif ( $this->is_own_dir ) { // if the current call is only for wp-admin login // then first redirect to wp-admin URL and then attempt login // error_log("2nd case --- WP having its own dir call"); $correct_url = rtrim( $this->site_url, '/' ) . '/wp-admin/' . $this->request_uri; $correct_url = str_replace( 'wp-admin//', 'wp-admin/', $correct_url ); $correct_url = str_replace( 'wp-admin/wp-admin/', 'wp-admin/', $correct_url ); // error_log('#### REDIRECTING TO ==> '.$correct_url); wp_redirect( $correct_url ); exit; } // if WP site URL doesn't match with requested URL // possibly a case when subdirectory installation was treated as subdomain // then redirect to site url first and then attempt request (i.e., login + rest_api) elseif ( $wp_url_host != $request_host ) { // convert the request URL into WP site URL and append the request URI // error_log("3rd case --- Request URL doesnt match with siteurl"); $correct_url = rtrim( $this->site_url, '/' ) . $this->request_uri; // error_log('#### REDIRECTING TO ==> '.$correct_url); wp_redirect( $correct_url ); exit; // redirect to functional site URL } else { // error_log("4th case --- None of above"); } } /** * Update status and prepare response * @return array */ public function responseHandler( $is_success = true, $data = array() ): array { $this->tokenStatus = $is_success; $this->token_message = isset( $data['message'] ) ? $data['message'] : $this->errorTemplate['message']; $response = $this->errorTemplate; $response['error'] = (bool) ! ( $this->tokenStatus ); $response['message'] = $this->token_message; // Push stats - Authentication status (valid/invalid) push array_push( $this->additional_info, $this->token_message ); // error_log("ResponseHandler Status --> " . json_encode($this->additional_info)); ( class_exists( 'OCPushStats' ) ? \OCPushStats::push_stats_performance_cache( 'lookup', 'login', 'authentication', 'control-panel', $this->additional_info ) : '' ); return $response; } /** * Function to check token key exist * Entry point */ public function tokenExist() { $getParam = $_GET; if ( isset( $getParam[ self::TOKENKEY ] ) && ! empty( $getParam[ self::TOKENKEY ] ) ) { return true; } return false; } /** * function to validate token */ public function validateToken() { if ( defined( 'REST_REQUEST' ) || ( isset( $_GET['rest_route'] ) && ! empty( $_GET['rest_route'] ) ) ) { //handle token return $this->checkToken( $this->tokenVal ); } //handle token $this->checkToken( $this->tokenVal ); if ( ! $this->tokenStatus ) { return null; } $user = get_userdata( self::DEFAULT_USERID ); // condition improved as per case WPIN-2551 if ( ! $user || ! isset( $user->roles ) || ! in_array( 'administrator', $user->roles ) ) { $get_user_id = $this->getAdminIds(); if ( $get_user_id > 0 ) { $this->oclAuthCheck( $get_user_id ); } } else { //call oclauthCheck on default userid 1 $this->oclAuthCheck( self::DEFAULT_USERID ); } } /** * Update status and prepare response * @return array */ public function checkTokenData( $decoded_token ) { // if required data exists if ( ! ( property_exists( $decoded_token, 'domain' ) && property_exists( $decoded_token, 'subdomain' ) ) ) { return $this->responseHandler( false, array( 'message' => 'Incorrect Installation URL provided in token.', ) ); } // if required data intended for current installation if ( true === $this->checkValidDomain( $decoded_token->domain, $decoded_token->subdomain ) ) { return $this->responseHandler( true, array( 'message' => 'Valid Status', ) ); } // anything bad return $this->responseHandler( false, array( 'message' => 'Incorrect Installation URL provided or unknown error occured.', ) ); } /** * Check token is valid or not */ public function checkToken( $get_token_val ) { // seat belt if ( empty( $get_token_val ) ) { return $this->responseHandler( false, array( 'message' => 'Token missing.', ) ); } try { JWT::$leeway = 20; // allow 10 seconds clock skew //Ref:https://github.com/firebase/php-jwt/ $decoded_token = JWT::decode( $get_token_val, new Key( $this->key, 'RS256' ) ); // check if token decoded if ( empty( $decoded_token ) || ! is_object( $decoded_token ) ) { return $this->responseHandler( false, array( 'message' => 'Invalid token. Failed to decode token.', ) ); } return $this->checkTokenData( $decoded_token ); } catch ( Exception $e ) { return $this->responseHandler( false, array( 'message' => $e->getMessage(), ) ); } } /** * Function to chech valid domain */ public function checkValidDomain( $token_domain, $token_subdomain ): bool { $actualSubdomain = trim( $this->onecom_subdomain ); if ( $this->onecom_subdomain === 'www' ) { $actualSubdomain = str_replace( 'www', '', $actualSubdomain ); } if ( $token_subdomain === 'www' ) { $token_subdomain = str_replace( 'www', '', $token_subdomain ); } if ( $this->onecom_domain === $token_domain && ( $this->is_own_dir || $actualSubdomain === $token_subdomain ) ) { return true; } // error_log("Token domain --> {$token_domain}"); // error_log("Token sub-domain --> {$token_subdomain}"); // error_log("Request domain --> {$this->onecom_domain}"); // error_log("Request sub-domain --> {$actualSubdomain}"); return false; } /** * Function to get domain */ public function get_sitedomain() { if ( isset( $_SERVER['SERVER_NAME'] ) && $_SERVER['SERVER_NAME'] === 'localhost' ) { return 'localhost'; } elseif ( isset( $_SERVER['ONECOM_DOMAIN_NAME'] ) && ! empty( $_SERVER['ONECOM_DOMAIN_NAME'] ) ) { return $_SERVER['ONECOM_DOMAIN_NAME']; } else { return 'localhost'; } } /** * Function to get sub domain */ public function get_sitesubdomain() { if ( $this->get_sitedomain() === 'localhost' ) { return null; } // parse WP site URL // but if it is a WP with its own directory, then prefer using home_url of WP as trusted destination. $wp_siteurl = ( $this->is_own_dir/* && !$this->is_rest_request*/ ) ? parse_url( $this->home_url ) : parse_url( $this->site_url ); // error_log("Going to use SiteURL ---> ".json_encode($wp_siteurl)); // get subdirectory from site URL if ( ! empty( $wp_siteurl['path'] ) ) { // return the subdirectory to caller function // so that it can be matched against the subdomain sent by CP in token return str_replace( '/', '', $wp_siteurl['path'] ); } $subdomain = substr( $_SERVER['SERVER_NAME'], 0, -( strlen( $_SERVER['ONECOM_DOMAIN_NAME'] ) ) ); if ( $subdomain && $subdomain !== '' ) { return rtrim( $subdomain, '.' ); } else { return 'www'; } } /** * Function to set error message on login page */ public function ocl_login_error_callback( $message ) { if ( ! isset( $_GET['redirect_to'] ) && empty( $_GET['redirect_to'] ) ) { return $message; } $redirectto = $_GET['redirect_to']; $query = parse_url( $redirectto, PHP_URL_QUERY ); $queries = array(); parse_str( $query, $queries ); if ( isset( $queries[ self::TOKENKEY ] ) && ! empty( $queries[ self::TOKENKEY ] ) ) { $tokenPassVal = $queries[ self::TOKENKEY ]; $this->checkToken( $tokenPassVal ); if ( ! $this->tokenStatus ) { return '
' . __( $this->token_message, 'ocl' ) . '
'; } } return $message; } /** * function for oclauthCheck */ public function oclAuthCheck( $user_id ) { $user = get_user_by( 'ID', $user_id ); $loginuser = $user->data->user_login; wp_set_current_user( $user_id, $loginuser ); wp_set_auth_cookie( $user_id ); do_action( 'wp_login', $loginuser, $user ); // Go to admin area $redirect_page = is_multisite() ? get_admin_url() : admin_url(); if ( is_user_logged_in() ) { if ( ! empty( $this->wpPathVal ) ) { $redirect_page = $redirect_page . $this->wpPathVal; // handle "/" for frontpage url if ( '/' === $this->wpPathVal ) { // parse WP site URL $wp_siteurl = parse_url( get_site_option( 'siteurl' ) ); // get subdirectory from site URL if ( ! empty( $wp_siteurl['path'] ) ) { //if subdirectory exit then simply return site url $redirect_page = get_site_option( 'siteurl' ); } else { $redirect_page = $_SERVER['HTTP_SCHEME'] . '://' . $_SERVER['HTTP_HOST']; } } } // error_log("Redirect to ---> {$redirect_page}"); nocache_headers(); // Push stats - redirect to dashboard ( class_exists( 'OCPushStats' ) ? \OCPushStats::push_stats_performance_cache( 'redirect', 'login', 'wordpress', 'control-panel', $this->additional_info ) : '' ); wp_redirect( $redirect_page ); exit; } } /** * Function to get admin id */ public function getAdminIds() { global $wpdb; $getAdminQuery = 'SELECT u.ID, u.user_login FROM ' . $this->prefix . 'users u, ' . $this->prefix . 'usermeta m WHERE u.ID = m.user_id AND m.meta_key LIKE "' . $this->prefix . 'capabilities" AND m.meta_value LIKE "%administrator%" order by ID ASC'; //Get all admin users ids from DB $wpAuthUser = $wpdb->get_results( $getAdminQuery ); //return first found user id if ( ! empty( $wpAuthUser ) ) { return $wpAuthUser[0]->ID; } return 0; } /** * Public key generator */ private function generateKey() { $this->key = <<