2026-02-05 17:08:59 +03:00

522 lines
15 KiB
PHP

<?php
defined( 'ABSPATH' ) or die( 'Cheating Huh!' ); // Security
require_once __DIR__ . '/vendor/autoload.php';
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;
class OCLAUTH {
const TOKENKEY = 'onecom-auth';
const DEFAULT_USERID = 1;
const WP_PATHKEY = 'wp_path';
public static $instance = null;
private $key;
public $wpPathVal;
public $tokenVal;
public $tokenStatus;
public $token_message;
public $errors;
public $errorTemplate = array(
'error' => 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 '<div id="login_error"> ' . __( $this->token_message, 'ocl' ) . '<br>
</div>';
}
}
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 = <<<EOD
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3A3D9KpeYiAZlJsSG6yc
K0rk5X07gH+VPWyc3y5BLnpFXnjFcWPBCo00TG1PjPb7dTi2li1rO6l3wLCSz0D7
Eo9Jrd6UoqwzEuxTAfZYLf242O50NonhDVnuETu+C+MwNiPXXJyv6lDWcatInCVR
Gcb1XK/Vo06Fb7WZ29YUsFrY0niBsUQLe61uci9HqQe1vskvVc2sHSI5gCz8A/eV
qMpfwrwLUBeudyqlESfd9jGgFq9m7NMKlVlt49878iTgGGeJSqWsvQPq88e81yEl
waKJBHY7JO2dMZi7ygbuEK0Ek1dVs0aZHIs1ganu9UqPPmOfftxmfVdI54IFcOCX
fwIDAQAB
-----END PUBLIC KEY-----
EOD;
}
}