fix(users): expose login forbidden characters setup and support explicit empty value (#37640)

* fix(users): expose login forbidden characters setup and support explicit empty value

* Update users.lang

* Change GETPOST type for MAIN_LOGIN_BADCHARUNAUTHORIZED

* use of getdolglobal

* FIX secure login bad character override

---------

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
This commit is contained in:
VIAL-GOUTEYRON Quentin 2026-06-02 09:44:17 +02:00 committed by GitHub
parent 47ad274b0b
commit b9cd07293a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 39 additions and 7 deletions

View file

@ -311,6 +311,18 @@ $dolibarr_main_force_https='0';
//
$dolibarr_main_prod='1';
// dolibarr_login_badcharunauthorized
// ==================================
// List of characters forbidden in Dolibarr user logins.
// This is a security-sensitive setting. Prefer LDAP/SSO mapping to the email field when possible.
// If this parameter is not defined, the secure default value is used: ,@<>"'
// Set it to an empty string only if you explicitly want to allow all characters.
// Set it to 'MAIN_LOGIN_BADCHARUNAUTHORIZED' only if you explicitly trust the Dolibarr hidden setup value.
// Examples:
// $dolibarr_login_badcharunauthorized=',@<>"\'';
// $dolibarr_login_badcharunauthorized='';
// $dolibarr_login_badcharunauthorized='MAIN_LOGIN_BADCHARUNAUTHORIZED';
// dolibarr_main_restrict_os_commands
// ==================================
// To restrict commands you can execute by the backup feature, enter allowed command here.

View file

@ -289,6 +289,26 @@ function getDolGlobalString($key, $default = '')
return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
}
/**
* Return the list of unauthorized characters in user logins.
*
* @return string
*/
function getDolGlobalLoginBadCharUnauthorized()
{
global $dolibarr_login_badcharunauthorized;
if (isset($dolibarr_login_badcharunauthorized)) {
if ($dolibarr_login_badcharunauthorized === 'MAIN_LOGIN_BADCHARUNAUTHORIZED') {
return getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
}
return (string) $dolibarr_login_badcharunauthorized;
}
return ',@<>"\'';
}
/**
* Return a Dolibarr global constant int value.
* The constants $conf->global->xxx are loaded by the script master.inc.php included at begin of any PHP page.

View file

@ -122,11 +122,11 @@ function openid_connect_create_user($db, $userinfo, $login, $entity)
$claim_firstname = getDolGlobalString('MAIN_AUTHENTICATION_OIDC_CLAIM_FIRSTNAME', 'given_name');
$claim_lastname = getDolGlobalString('MAIN_AUTHENTICATION_OIDC_CLAIM_LASTNAME', 'family_name');
// Sanitize login: Dolibarr rejects certain characters (default: ,@<>"')
$badChars = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
// Sanitize login using Dolibarr forbidden characters for logins.
$badChars = getDolGlobalLoginBadCharUnauthorized();
$sanitized_login = $login;
if (preg_match('/['.preg_quote($badChars, '/').']/', $login)) {
if ($badChars !== '' && preg_match('/['.preg_quote($badChars, '/').']/', $login)) {
// First try preferred_username from OIDC (common standard claim)
if (property_exists($userinfo, 'preferred_username') && !empty($userinfo->preferred_username)) {
$preferred = $userinfo->preferred_username;

View file

@ -1870,7 +1870,7 @@ class User extends CommonObject
}
dol_syslog(get_class($this)."::create login=".$this->login.", user=".(is_object($user) ? $user->id : ''), LOG_DEBUG);
$badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
$badCharUnauthorizedIntoLoginName = getDolGlobalLoginBadCharUnauthorized();
// Check parameters
if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
@ -1882,7 +1882,7 @@ class User extends CommonObject
$langs->load("errors");
$this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Login"));
return -1;
} elseif (preg_match('/['.preg_quote($badCharUnauthorizedIntoLoginName, '/').']/', $this->login)) {
} elseif ($badCharUnauthorizedIntoLoginName !== '' && preg_match('/['.preg_quote($badCharUnauthorizedIntoLoginName, '/').']/', $this->login)) {
$langs->load("errors");
$this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
return -1;
@ -2282,7 +2282,7 @@ class User extends CommonObject
$this->setUpperOrLowerCase();
// Check parameters
$badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
$badCharUnauthorizedIntoLoginName = getDolGlobalLoginBadCharUnauthorized();
if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
$langs->load("errors");
@ -2293,7 +2293,7 @@ class User extends CommonObject
$langs->load("errors");
$this->error = $langs->trans("ErrorFieldRequired", 'Login');
return -1;
} elseif (preg_match('/['.preg_quote($badCharUnauthorizedIntoLoginName, '/').']/', $this->login)) {
} elseif ($badCharUnauthorizedIntoLoginName !== '' && preg_match('/['.preg_quote($badCharUnauthorizedIntoLoginName, '/').']/', $this->login)) {
$langs->load("errors");
$this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
return -1;