You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
368 lines
12 KiB
368 lines
12 KiB
<?php
|
|
namespace app\core\auth;
|
|
|
|
require_once APPPATH . 'core/data/Constants.php';
|
|
|
|
use app\core\auth\User as UserAuth;
|
|
use app\core\auth\Unregister;
|
|
use app\core\traits\Rememberme;
|
|
use app\core\contract\Sso as SsoContract;
|
|
|
|
class Sso implements SsoContract {
|
|
|
|
/**
|
|
* Initialize sso connection
|
|
* If connection has been established already, re-use it instead
|
|
*
|
|
* @return bool|array
|
|
*/
|
|
public static function connect() {
|
|
if(strpos(current_url(), 'logout') !== false)
|
|
self::unsetUserCookies();
|
|
|
|
if (self::isLoggedIn())
|
|
return true;
|
|
|
|
/**
|
|
* This is just to make sure that, querying SSO from lemonde.fr
|
|
* happens only on fresh login.
|
|
* If user has already an SSO Session, requesting to lemonde.fr
|
|
* happens only when the SSO has expired
|
|
* (print_r($_SESSION) to check for the SSO session info)
|
|
*/
|
|
if (isset($_SESSION['user_token'], $_SESSION['user_iban'])) {
|
|
|
|
$response = self::establishSSOConnection();
|
|
|
|
if (!$response)
|
|
return false;
|
|
|
|
if (isset($_COOKIE['lmd_stay_connected'])) {
|
|
$response['stay_connected'] = (bool)($_COOKIE['lmd_stay_connected'] ?? false);
|
|
$response = self::establishSSOConnection();
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* This is generally useful when user logs out
|
|
* Resetting his cookies will also logout his session in the parent site
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function unsetUserCookies() {
|
|
$past = time() - (86400 * 30);
|
|
|
|
setcookie(self::TEST_SSO_KEY, null, $past);
|
|
unset($_SESSION['user_token']);
|
|
unset($_SESSION['user_iban']);
|
|
}
|
|
|
|
/**
|
|
* Request for user details from lemonde.fr using the cookies
|
|
*
|
|
* @param string $email
|
|
*
|
|
* @return bool|object
|
|
*/
|
|
private static function establishSSOConnection($email = '') {
|
|
try {
|
|
// Get CI instance
|
|
$CI = &get_instance();
|
|
|
|
// Check if the request is from a bot
|
|
if ($CI->agent->is_robot()) {
|
|
return false;
|
|
}
|
|
|
|
// Create API data array
|
|
$apiData = json_encode([
|
|
'Autorization' => $_SESSION['user_token'], // Corrected the header name
|
|
'token' => $_SESSION['user_token'],
|
|
'app' => "cpay",
|
|
'iban' => $_SESSION['user_iban'],
|
|
'action' => "sso_info"
|
|
]);
|
|
|
|
// Prepare HTTP headers
|
|
$httpHeaders = [
|
|
"Content-Type: application/json", // Ensure the API interprets the request correctly
|
|
"Content-Length: " . strlen($apiData), // Set Content-Length for php://input
|
|
"authority: secure.c4m.mg",
|
|
"cache-control: max-age=0",
|
|
"upgrade-insecure-requests: 1",
|
|
"user-agent: " . ($_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'),
|
|
"sec-fetch-user: ?1",
|
|
"accept-language: en-US,en;q=0.9"
|
|
];
|
|
|
|
// Initialize cURL
|
|
$curl = curl_init();
|
|
curl_setopt_array($curl, [
|
|
CURLOPT_URL => self::CPAYMG,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_ENCODING => "",
|
|
CURLOPT_MAXREDIRS => 10,
|
|
CURLOPT_TIMEOUT => 0,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
|
CURLOPT_CUSTOMREQUEST => "POST",
|
|
CURLOPT_POSTFIELDS => $apiData, // Send data via php://input
|
|
CURLOPT_HTTPHEADER => $httpHeaders,
|
|
CURLOPT_SSL_VERIFYPEER => false, // Disable SSL peer verification
|
|
CURLOPT_SSL_VERIFYHOST => 0 // Disable SSL host verification
|
|
]);
|
|
|
|
// Execute cURL request
|
|
$response1 = curl_exec($curl);
|
|
$err = curl_error($curl);
|
|
curl_close($curl);
|
|
|
|
// Decode response
|
|
$response = json_decode($response1, true);
|
|
|
|
// Check for errors and return response
|
|
if (!$err && !isset($response['error']) && empty($response['error'])) {
|
|
return $response;
|
|
}
|
|
// Log error or handle unexpected cases
|
|
return false;
|
|
|
|
} catch (\Throwable $e) {
|
|
// Handle exception gracefully
|
|
log_message('error', 'Error in cURL request: ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* For Frontoffice Users Only!
|
|
* Reset SSO data in session when sso expires
|
|
* This will force the request to lemonde.fr to get the updated
|
|
* SSO data. This is necessary especially when the user change something
|
|
* on his account like a change in the services __ from premium to not premium.
|
|
* The current reset_time every 10mins
|
|
*
|
|
* @return @void
|
|
*/
|
|
public static function resetSSOInSession() {
|
|
|
|
if (!self::isLoggedIn())
|
|
return;
|
|
|
|
// Get CI instance
|
|
$CI =& get_instance();
|
|
|
|
$logged_in_user = UserAuth::auth();
|
|
|
|
// Request new SSO data
|
|
$sso = self::establishSSOConnection();
|
|
|
|
if (!$sso)
|
|
return false;
|
|
|
|
if (isset($logged_in_user['sso']['sso_expiration'])) {
|
|
// Check if reset time is reached
|
|
if (strtotime('now') <= $logged_in_user['sso']['sso_expiration']) {
|
|
return false;
|
|
}
|
|
|
|
$session_key = 'logged_in';
|
|
|
|
$logged_in_user['sso']['sso_expiration'] = strtotime('+10 minutes',strtotime('now'));
|
|
$logged_in_user['sso']['product_code'] = $sso['product_code'];
|
|
$logged_in_user['premium'] = !!$sso['premium'];
|
|
|
|
$new_session_data[$session_key] = $logged_in_user;
|
|
|
|
// Finally reset login session
|
|
$CI->session->unset_userdata('logged_in');
|
|
|
|
// set new session
|
|
$CI->session->set_userdata($new_session_data);
|
|
}
|
|
}
|
|
|
|
public static function isLoggedIn() {
|
|
return UserAuth::isAuth() && UserAuth::isFOUser();
|
|
}
|
|
|
|
/**
|
|
* Forced logout in evenements when user logs out from LeMonde.fr
|
|
* Check whether cookies are present, when not then logout the user
|
|
* $_COOKIE['lmd_stay_connected'], $_COOKIE['lmd_a_s'], $_COOKIE['lmd_a_m']
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function signOutUserWhenLoggedOutFromParentSite() {
|
|
if (self::isLoggedIn() && !isset($_SESSION['user_token'], $_SESSION['user_iban'])) {
|
|
redirect('logout', 'refresh');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Authenticate user when cookie is present but no login session is added yet.
|
|
* This is useful when user visits directly the hompage or any page in
|
|
* the FO with just the cookie from parent site
|
|
*
|
|
* @param array $sso data from LeMonde.fr
|
|
* @return void
|
|
*/
|
|
public static function authenticateBySSO($sso) {
|
|
if (!self::isLoggedIn() && countVal($sso)) {
|
|
redirect('authenticate');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Refresh the sso data every 10mins by default
|
|
* This is to avoid checking if user is logged in from the lemonde.fr every server request.
|
|
* That will cost more time for each process,
|
|
* So, we cache that in evenements and only after 10mins the checking of sso in lemonde.fr will happen
|
|
* @param array $sso provided by lemonde.fr
|
|
* @param array $session_data user session
|
|
* @return array updated user session
|
|
*/
|
|
public static function setSSORefreshTime($sso) {
|
|
|
|
if (!countVal($sso))
|
|
return [];
|
|
/**
|
|
* Make sure were not adding sso expiration for local testing
|
|
* SSO Expiration should be for real login only,
|
|
* which means the user logs in from Lemonde.fr
|
|
*/
|
|
if (!isset($_COOKIE[self::TEST_SSO_KEY])) {
|
|
return [
|
|
'sso_expiration' => strtotime('+10 minutes',strtotime('now')),
|
|
'product_code' => $sso['product_code']
|
|
];
|
|
}
|
|
|
|
// For test sso only
|
|
if (countVal($sso))
|
|
return [
|
|
'product_code' => $sso['sso']['product_code']
|
|
];
|
|
}
|
|
|
|
/**
|
|
* For local / preprod testing
|
|
* @param string $email test email
|
|
*/
|
|
public static function set_mock_cookies($sso) {
|
|
$testCookies = MOCK_TEST_EMAILS[$sso['mail']] ?? MOCK_TEST_EMAILS['guillaume.b@wylog.com'];
|
|
$expiration = time() + (86400 * 30);
|
|
|
|
if ($testCookies) {
|
|
foreach($testCookies as $c) {
|
|
$tmpCookies = explode("=", explode(" ", $c)[1]);
|
|
setcookie($tmpCookies[0], $tmpCookies[1], $expiration, '/');
|
|
}
|
|
setcookie(self::TEST_SSO_KEY, json_encode($sso) , $expiration, '/');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get user email
|
|
*/
|
|
public static function getEmail() {
|
|
if (self::isLoggedIn())
|
|
return UserAuth::auth()['email_address'];
|
|
|
|
if (isset($_COOKIE[self::TEST_SSO_KEY])) {
|
|
$testSSo = json_decode($_COOKIE[self::TEST_SSO_KEY], true);
|
|
|
|
if(isset($testSSo['mail']))
|
|
return $testSSo['mail'];
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Check if [FO-User] Regular session or Temp session is active
|
|
*
|
|
* Regular Session is when the user is loggedin
|
|
* Temp Session is when unknown / not yet registered user is loggedin from parent site (via auth cookies)
|
|
*/
|
|
public static function withSession() {
|
|
return self::isLoggedIn();
|
|
}
|
|
|
|
/**
|
|
* Get test sso
|
|
*/
|
|
public static function getTestSSO() {
|
|
if (isset($_COOKIE[self::TEST_SSO_KEY]) && !empty($_COOKIE[self::TEST_SSO_KEY]))
|
|
return json_decode($_COOKIE[self::TEST_SSO_KEY], true);
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* This should not be used in production!!!
|
|
* This is only for preprod testing to bypass SSO authentication
|
|
*
|
|
* If you want to check if account is premium or not
|
|
* Refer to app\core\auth\User::isPremium() instead!
|
|
*/
|
|
public static function isPremium($sso = []): bool {
|
|
if (countVal($sso))
|
|
return !!$sso['premium'];
|
|
|
|
$testSSo = self::getTestSSO();
|
|
if ($testSSo && countVal($testSSo))
|
|
return $testSSo['premium'];
|
|
|
|
if (self::isLoggedIn())
|
|
return UserAuth::isPremium();
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Redirect to mon-compte when user with sso is not yet registered in Masterclass
|
|
*/
|
|
public static function persistMyAccountPage(): void {
|
|
// Get CI instance
|
|
$CI =& get_instance();
|
|
|
|
$currentURL = current_url();
|
|
$whitelistedRoute = 'mon-compte';
|
|
|
|
if( strpos($currentURL, $whitelistedRoute) === false )
|
|
redirect( $whitelistedRoute );
|
|
}
|
|
|
|
public static function ssoHasUserData($sso = []) {
|
|
$required = ['firstname', 'name', 'mail', 'premium'];
|
|
if (countVal($sso)) {
|
|
if (!isset($sso['mail'], $sso['premium']))
|
|
redirect('home');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get subscription type
|
|
*
|
|
* This can be found as "product_code" in sso
|
|
*
|
|
* Essential: ESS
|
|
* Integral: INT
|
|
* Family: FAM
|
|
* Former Premium offer: PRE
|
|
* Paper + digital offer: MQ
|
|
*/
|
|
public static function getSubsriptionType() {
|
|
if (!self::isLoggedIn() || !UserAuth::auth()['sso'])
|
|
return null;
|
|
|
|
return UserAuth::auth()['sso']['product_code'];
|
|
}
|
|
}
|