Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
23e7b57
NEW webportal member authentication only
hregis Nov 14, 2025
a0bd262
FIX missing translation
hregis Nov 14, 2025
7cc5e98
FIX php-stan errors
hregis Nov 14, 2025
ea7452e
FIX php-stan : cryptType is always "auto" !
hregis Nov 14, 2025
d9db102
FIX remove CrypType
hregis Nov 14, 2025
e0fcf0c
FIX php-stan error
hregis Nov 14, 2025
3b66bf1
FIX phan error
hregis Nov 14, 2025
772ab26
Merge branch 'develop' of [email protected]:Dolibarr/dolibarr.git into n…
hregis Nov 17, 2025
4f0bc81
Merge branch 'develop' of [email protected]:Dolibarr/dolibarr.git into n…
hregis Nov 17, 2025
186bb80
Merge branch 'develop' of [email protected]:Dolibarr/dolibarr.git into n…
hregis Nov 17, 2025
bcaf42e
FIX CI versions
hregis Nov 17, 2025
163f7d1
Merge branch 'develop' of [email protected]:Dolibarr/dolibarr.git into
hregis Nov 17, 2025
0dca4aa
Merge branch 'develop' of [email protected]:Dolibarr/dolibarr.git into n…
hregis Nov 17, 2025
5d1b612
Merge branch 'develop' of [email protected]:Dolibarr/dolibarr.git into n…
hregis Nov 17, 2025
03ff915
Merge branch 'develop' of [email protected]:Dolibarr/dolibarr.git into n…
hregis Nov 19, 2025
4c379f4
FIX remove wrong fix
hregis Nov 19, 2025
daf2f57
Merge branch 'develop' of [email protected]:Dolibarr/dolibarr.git into n…
hregis Nov 21, 2025
6278256
FIX pre-commit error
hregis Nov 21, 2025
54eae09
FIX phan error
hregis Nov 21, 2025
b79d9fe
FIX php-stan error
hregis Nov 21, 2025
bc50a0b
FIX avoid php warning
hregis Nov 21, 2025
be05801
FIXME this check is not valid
hregis Nov 21, 2025
dc88008
Merge branch 'develop' of [email protected]:Dolibarr/dolibarr.git into n…
hregis Nov 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions htdocs/langs/en_US/website.lang
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,14 @@ AriaPageX=Page %s
WebPortalError404=Page not found
WebPortalErrorPageNotExist=Page not exist
WebPortalErrorFetchThirdPartyAccountFromLogin=Error when loading third-party account (login : %s)
WebPortalErrorFetchMemberAccountFromLogin=Error when loading member account (login : %s)
WebPortalErrorAuthentication=Authentication error
WebPortalErrorFetchLoggedThirdPartyAccount=Error when loading third-party account (login : %s)
WebPortalErrorFetchLoggedUser=Error when loading user (Id : %s)
WebPortalErrorFetchLoggedThirdParty=Error when loading third-party (Id : %s)
WebPortalErrorFetchLoggedMember=Error when loading member (Id : %s)
WebPortalErrorFetchLoggedPartnership=Error when loading partnership (Third-party Id : %s, Member Id : %s)
WebPortalErrorNoFetchMethod=Error no fetch method found for authentication
DownloadZip=Download the zip
ExportIntoGIT=Export into server directory
WebPortalMember=Membership
Expand Down
7 changes: 6 additions & 1 deletion htdocs/public/webportal/logout.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@
}

// Not sure this is required
if (isset($_SESSION['webportal_logged_thirdparty_account_id'])) unset($_SESSION['webportal_logged_thirdparty_account_id']);
if (isset($_SESSION['webportal_logged_thirdparty_account_id'])) {
unset($_SESSION['webportal_logged_thirdparty_account_id']);
}
if (isset($_SESSION['webportal_logged_member_account_id'])) {
unset($_SESSION['webportal_logged_member_account_id']);
}

if (GETPOST('noredirect')) {
return;
Expand Down
166 changes: 107 additions & 59 deletions htdocs/public/webportal/webportal.main.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ function dol_getprefix($mode = '') // @phan-suppress-current-line PhanRedefineF
} elseif (empty($reshook)) {
$admin_error_messages = array();
$webportal_logged_thirdparty_account_id = isset($_SESSION["webportal_logged_thirdparty_account_id"]) && $_SESSION["webportal_logged_thirdparty_account_id"] > 0 ? $_SESSION["webportal_logged_thirdparty_account_id"] : 0;
$webportal_logged_member_account_id = isset($_SESSION["webportal_logged_member_account_id"]) && $_SESSION["webportal_logged_member_account_id"] > 0 ? $_SESSION["webportal_logged_member_account_id"] : 0;

if (!$context->userIsLog()) {
// It is not already authenticated and it requests the login / password
Expand Down Expand Up @@ -188,23 +189,41 @@ function dol_getprefix($mode = '') // @phan-suppress-current-line PhanRedefineF
// $error++;
//}

if (!$error) {
if (!$error && (isModEnabled('societe') && !getDolGlobalInt('WEBPORTAL_LOGIN_BY_MEMBER_ACCOUNT'))) {
// fetch third-party account from login and account type
$thirdparty_account_id = $context->getThirdPartyAccountFromLogin($login, $password);
if ($thirdparty_account_id <= 0) {
$error++;
dol_syslog($langs->transnoentitiesnoconv('WebPortalErrorFetchThirdPartyAccountFromLogin', $login), LOG_WARNING);
$context->setEventMessage($langs->transnoentitiesnoconv('WebPortalErrorAuthentication'), 'errors');
} else {
$_SESSION["webportal_logged_thirdparty_account_id"] = $thirdparty_account_id;
$webportal_logged_thirdparty_account_id = $thirdparty_account_id;
$context->controller = 'default';
$context->initController();
}
} elseif (!$error && isModEnabled('member') && getDolGlobalInt('WEBPORTAL_LOGIN_BY_MEMBER_ACCOUNT') && !getDolGlobalString('ADHERENT_LOGIN_NOT_REQUIRED')) {
// fetch member account from login
$member_account_id = $context->getMemberAccountFromLogin($login, $password);
if ($member_account_id <= 0) {
$error++;
dol_syslog($langs->transnoentitiesnoconv('WebPortalErrorFetchMemberAccountFromLogin', $login), LOG_WARNING);
} else {
$_SESSION["webportal_logged_member_account_id"] = $member_account_id;
$webportal_logged_member_account_id = $member_account_id;
$context->controller = 'default';
$context->initController();
}
} else {
$error++;
dol_syslog($langs->transnoentitiesnoconv('WebPortalErrorNoFetchMethod'), LOG_WARNING);
}

if ($error) {
$context->setEventMessage($langs->transnoentitiesnoconv('WebPortalErrorAuthentication'), 'errors');
}
}

if (empty($webportal_logged_thirdparty_account_id)) {
if (empty($webportal_logged_thirdparty_account_id) && empty($webportal_logged_member_account_id)) {
// Set cookie for timeout management
if (getDolGlobalString('MAIN_SESSION_TIMEOUT')) {
dolSetCookie($sessiontimeout, getDolGlobalString('MAIN_SESSION_TIMEOUT'), 0);
Expand All @@ -216,21 +235,43 @@ function dol_getprefix($mode = '') // @phan-suppress-current-line PhanRedefineF
}

if (!$error && $context->userIsLog()) {
$logged_member = null;
$websiteaccount = null;
// We are already into an authenticated session
$websiteaccount = new SocieteAccount($db);
$result = $websiteaccount->fetch($webportal_logged_thirdparty_account_id);
if (isModEnabled('member') && getDolGlobalInt('WEBPORTAL_LOGIN_BY_MEMBER_ACCOUNT') && !getDolGlobalString('ADHERENT_LOGIN_NOT_REQUIRED')) {
// get member
$logged_member = new WebPortalMember($db);
$result = $logged_member->fetch($webportal_logged_member_account_id);
if ($result <= 0) {
$error++;

if ($result <= 0) {
$error++;
// Account has been removed after login
dol_syslog("Can't load member account (ID: $webportal_logged_member_account_id) even if session logged.", LOG_WARNING);
session_destroy();
session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
session_name($sessionname);
session_start();

// Account has been removed after login
dol_syslog("Can't load third-party account (ID: $webportal_logged_thirdparty_account_id) even if session logged.", LOG_WARNING);
session_destroy();
session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
session_name($sessionname);
session_start();
$error_msg = $langs->transnoentitiesnoconv('WebPortalErrorFetchLoggedMember', (string) $webportal_logged_member_account_id);
dol_syslog($error_msg, LOG_ERR);
$context->setEventMessage($error_msg, 'errors');
}
} else {
$websiteaccount = new SocieteAccount($db);
$result = $websiteaccount->fetch($webportal_logged_thirdparty_account_id);

if ($result <= 0) {
$error++;

$context->setEventMessage($langs->transnoentitiesnoconv('WebPortalErrorFetchLoggedThirdPartyAccount', $webportal_logged_thirdparty_account_id), 'errors');
// Account has been removed after login
dol_syslog("Can't load third-party account (ID: $webportal_logged_thirdparty_account_id) even if session logged.", LOG_WARNING);
session_destroy();
session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
session_name($sessionname);
session_start();

$context->setEventMessage($langs->transnoentitiesnoconv('WebPortalErrorFetchLoggedThirdPartyAccount', $webportal_logged_thirdparty_account_id), 'errors');
}
}

if (!$error) {
Expand All @@ -253,69 +294,76 @@ function dol_getprefix($mode = '') // @phan-suppress-current-line PhanRedefineF
}
}

if (!$error) {
// get third-party
$logged_thirdparty = $websiteaccount->thirdparty;
if (!$logged_thirdparty || !($logged_thirdparty->id > 0)) {
$result = $websiteaccount->fetch_thirdparty();

if ($result < 0) {
$error_msg = $langs->transnoentitiesnoconv('WebPortalErrorFetchLoggedThirdParty', (string) $websiteaccount->fk_soc);
//dol_syslog("Can't load third-party (ID: ".$websiteaccount->fk_soc.") even if session logged.", LOG_ERR);
dol_syslog($error_msg, LOG_ERR);
$context->setEventMessage($error_msg, 'errors');
$error++;
}
}

if (!getDolGlobalInt('WEBPORTAL_LOGIN_BY_MEMBER_ACCOUNT')) {
if (!$error) {
// get third-party
$logged_thirdparty = $websiteaccount->thirdparty;
if (!$logged_thirdparty || !($logged_thirdparty->id > 0)) {
$result = $websiteaccount->fetch_thirdparty();

// get member
$logged_member = new WebPortalMember($db);
$result = $logged_member->fetch(0, '', $websiteaccount->thirdparty->id);
if ($result < 0) {
$error++;
$error_msg = $langs->transnoentitiesnoconv('WebPortalErrorFetchLoggedMember', (string) $websiteaccount->thirdparty->id);
dol_syslog($error_msg, LOG_ERR);
$context->setEventMessage($error_msg, 'errors');
if ($result < 0) {
$error_msg = $langs->transnoentitiesnoconv('WebPortalErrorFetchLoggedThirdParty', (string) $websiteaccount->fk_soc);
//dol_syslog("Can't load third-party (ID: ".$websiteaccount->fk_soc.") even if session logged.", LOG_ERR);
dol_syslog($error_msg, LOG_ERR);
$context->setEventMessage($error_msg, 'errors');
$error++;
}
}

if (isModEnabled('partnership') && !$error && $logged_member->id > 0) {
// get partnership
$logged_partnership = new WebPortalPartnership($db);
// @phan-suppress-next-line PhanPluginSuspiciousParamPosition
$result = $logged_partnership->fetch(0, '', $logged_member->id, $websiteaccount->thirdparty->id);
if (!$error) {
$logged_thirdparty = $websiteaccount->thirdparty;

// get member
$logged_member = new WebPortalMember($db);
$result = $logged_member->fetch(0, '', $websiteaccount->thirdparty->id);
if ($result < 0) {
$error++;
$error_msg = $langs->transnoentitiesnoconv('WebPortalErrorFetchLoggedPartnership', (string) $websiteaccount->thirdparty->id, (string) $logged_member->id);
$error_msg = $langs->transnoentitiesnoconv('WebPortalErrorFetchLoggedMember', (string) $websiteaccount->thirdparty->id);
dol_syslog($error_msg, LOG_ERR);
$context->setEventMessage($error_msg, 'errors');
}
}

if (!$error) {
if ($logged_thirdparty->default_lang != $langs->defaultlang && !defined('WEBPORTAL_NOREQUIRETRAN')) {
if (!is_object($langs)) { // This can occurs when calling page with NOREQUIRETRAN defined, however we need langs for error messages.
include_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
$langs = new Translate("", $conf);
$langs->setDefaultLang($logged_thirdparty->default_lang);
if (isModEnabled('partnership') && !$error && $logged_member->id > 0) {
// get partnership
$logged_partnership = new WebPortalPartnership($db);
// @phan-suppress-next-line PhanPluginSuspiciousParamPosition
$result = $logged_partnership->fetch(0, '', $logged_member->id, $websiteaccount->thirdparty->id);
if ($result < 0) {
$error++;
$error_msg = $langs->transnoentitiesnoconv('WebPortalErrorFetchLoggedPartnership', (string) $websiteaccount->thirdparty->id, (string) $logged_member->id);
dol_syslog($error_msg, LOG_ERR);
$context->setEventMessage($error_msg, 'errors');
}
$langs->loadLangs(array('website', 'main'));
}

$context->logged_user = $logged_user;
$context->logged_thirdparty = $logged_thirdparty;
$context->logged_member = $logged_member;
if (!empty($logged_partnership)) {
$context->logged_partnership = $logged_partnership;
}
if (!$error) {
if ($logged_thirdparty->default_lang != $langs->defaultlang && !defined('WEBPORTAL_NOREQUIRETRAN')) {
if (!is_object($langs)) { // This can occurs when calling page with NOREQUIRETRAN defined, however we need langs for error messages.
include_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
$langs = new Translate("", $conf);
$langs->setDefaultLang($logged_thirdparty->default_lang);
}
$langs->loadLangs(array('website', 'main'));
}

global $user; // set global user as logged user (used for hooks in external modules)
$user = $context->logged_user;
$context->logged_thirdparty = $logged_thirdparty;
if (!empty($logged_partnership)) {
$context->logged_partnership = $logged_partnership;
}
}
}
}
}

if (!$error) {
$context->logged_user = $logged_user;
if (!empty($logged_member)) {
$context->logged_member = $logged_member;
}

global $user; // set global user as logged user (used for hooks in external modules)
$user = $context->logged_user;
}
}
}
}
Expand Down
67 changes: 54 additions & 13 deletions htdocs/webportal/class/context.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,8 @@ public function userIsLog()

if (!empty($_SESSION["webportal_logged_thirdparty_account_id"])) {
return true;
} elseif (!empty($_SESSION["webportal_logged_member_account_id"])) {
return true;
} else {
return false;
}
Expand Down Expand Up @@ -720,35 +722,74 @@ public function getThirdPartyAccountFromLogin($login, $pass)
if ($obj) {
$passcrypted = $obj->pass_crypted;

// Check crypted password
$cryptType = '';
if (getDolGlobalString('DATABASE_PWD_ENCRYPTED')) {
$cryptType = getDolGlobalString('DATABASE_PWD_ENCRYPTED');
// Check crypted password according to crypt algorithm
if ($passcrypted && dol_verifyHash($pass, $passcrypted, '0')) {
$passok = true;
}

// By default, we use default setup for encryption rule
if (!in_array($cryptType, array('auto'))) {
$cryptType = 'auto';
// Password ok ?
if ($passok) {
$id = $obj->id;
} else {
dol_syslog(__METHOD__ .' Authentication KO bad password for ' . $login . ', cryptType=auto', LOG_NOTICE);
sleep(1); // Brut force protection. Must be same delay when login is not valid
return -3;
}
}
} else {
dol_syslog(__METHOD__ . ' Many third-party account found for login"' . $login . '" and site="dolibarr_portal"', LOG_ERR);
return -2;
}
} else {
$this->error = $this->db->lasterror();
return -1;
}

return $id;
}

/**
* Try to find the member account id from
*
* @param string $login Login
* @param string $pass Password
* @return int Member account id || <0 if error
*/
public function getMemberAccountFromLogin($login, $pass)
{
$id = 0;

$sql = "SELECT a.rowid as id, a.pass_crypted";
$sql .= " FROM " . $this->db->prefix() . "adherent as a";
$sql .= " WHERE a.login = '" . $this->db->escape($login) . "'";
$sql .= " AND a.statut = 1";
$sql .= " AND a.entity IN (" . getEntity('member') . ")";

dol_syslog(__METHOD__ . ' Try to find the member account id for login"' . $login . '"', LOG_DEBUG);
$result = $this->db->query($sql);
if ($result) {
if ($this->db->num_rows($result) == 1) {
$passok = false;
$obj = $this->db->fetch_object($result);
if ($obj) {
$passcrypted = $obj->pass_crypted;

// Check crypted password according to crypt algorithm
if ($cryptType == 'auto') {
if ($passcrypted && dol_verifyHash($pass, $passcrypted, '0')) {
$passok = true;
}
if ($passcrypted && dol_verifyHash($pass, $passcrypted, '0')) {
$passok = true;
}

// Password ok ?
if ($passok) {
$id = $obj->id;
} else {
dol_syslog(__METHOD__ .' Authentication KO bad password for ' . $login . ', cryptType=' . $cryptType, LOG_NOTICE);
dol_syslog(__METHOD__ .' Authentication KO bad password for ' . $login . ', cryptType=auto', LOG_NOTICE);
sleep(1); // Brut force protection. Must be same delay when login is not valid
return -3;
}
}
} else {
dol_syslog(__METHOD__ . ' Many third-party account found for login"' . $login . '" and site="dolibarr_portal"', LOG_ERR);
dol_syslog(__METHOD__ . ' Many member account found for login"' . $login . '"', LOG_ERR);
return -2;
}
} else {
Expand Down
Loading