diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index ed739e7ff..cec5fa068 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -478,97 +478,101 @@ EOF { //echo "User: ".$_SESSION['auth_user']."\n"; // Already authentified - UserRights::Login($_SESSION['auth_user']); // Login & set the user's language - return self::EXIT_CODE_OK; - } - else - { - $index = 0; - $sLoginMode = ''; - $sAuthentication = 'internal'; - while(($sLoginMode == '') && ($index < count($aAllowedLoginTypes))) + $bRet = UserRights::Login($_SESSION['auth_user']); // Login & set the user's language + if ($bRet) { - $sLoginType = $aAllowedLoginTypes[$index]; - switch($sLoginType) - { - case 'cas': - utils::InitCASClient(); - // check CAS authentication - if (phpCAS::isAuthenticated()) - { - $sAuthUser = phpCAS::getUser(); - $sAuthPwd = ''; - $sLoginMode = 'cas'; - $sAuthentication = 'external'; - } - break; - - case 'form': - // iTop standard mode: form based authentication - $sAuthUser = utils::ReadPostedParam('auth_user', '', false, 'raw_data'); - $sAuthPwd = utils::ReadPostedParam('auth_pwd', null, false, 'raw_data'); - if (($sAuthUser != '') && ($sAuthPwd !== null)) - { - $sLoginMode = 'form'; - } - break; - - case 'basic': - // Standard PHP authentication method, works with Apache... - // Case 1) Apache running in CGI mode + rewrite rules in .htaccess - if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) - { - list($sAuthUser, $sAuthPwd) = explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6))); - $sLoginMode = 'basic'; - } - else if (isset($_SERVER['PHP_AUTH_USER'])) - { - $sAuthUser = $_SERVER['PHP_AUTH_USER']; - // Unfortunately, the RFC is not clear about the encoding... - // IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8 - // So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base - if (!self::LooksLikeUTF8($sAuthUser)) - { - // Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8 - // Supposed to be harmless in case of a plain ASCII string... - $sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser); - } - $sAuthPwd = $_SERVER['PHP_AUTH_PW']; - if (!self::LooksLikeUTF8($sAuthPwd)) - { - // Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8 - // Supposed to be harmless in case of a plain ASCII string... - $sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd); - } - $sLoginMode = 'basic'; - } - break; - - case 'external': - // Web server supplied authentication - $bExternalAuth = false; - $sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ? - eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value - if ($sAuthUser && (strlen($sAuthUser) > 0)) - { - $sAuthPwd = ''; // No password in this case the web server already authentified the user... - $sLoginMode = 'external'; - $sAuthentication = 'external'; - } - break; - - case 'url': - // Credentials passed directly in the url - $sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data'); - $sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data'); - if (($sAuthUser != '') && ($sAuthPwd !== null)) - { - $sLoginMode = 'url'; - } - break; - } - $index++; + return self::EXIT_CODE_OK; } + // The user account is no longer valid/enabled + static::ResetSession(); + } + + $index = 0; + $sLoginMode = ''; + $sAuthentication = 'internal'; + while(($sLoginMode == '') && ($index < count($aAllowedLoginTypes))) + { + $sLoginType = $aAllowedLoginTypes[$index]; + switch($sLoginType) + { + case 'cas': + utils::InitCASClient(); + // check CAS authentication + if (phpCAS::isAuthenticated()) + { + $sAuthUser = phpCAS::getUser(); + $sAuthPwd = ''; + $sLoginMode = 'cas'; + $sAuthentication = 'external'; + } + break; + + case 'form': + // iTop standard mode: form based authentication + $sAuthUser = utils::ReadPostedParam('auth_user', '', false, 'raw_data'); + $sAuthPwd = utils::ReadPostedParam('auth_pwd', null, false, 'raw_data'); + if (($sAuthUser != '') && ($sAuthPwd !== null)) + { + $sLoginMode = 'form'; + } + break; + + case 'basic': + // Standard PHP authentication method, works with Apache... + // Case 1) Apache running in CGI mode + rewrite rules in .htaccess + if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) + { + list($sAuthUser, $sAuthPwd) = explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6))); + $sLoginMode = 'basic'; + } + else if (isset($_SERVER['PHP_AUTH_USER'])) + { + $sAuthUser = $_SERVER['PHP_AUTH_USER']; + // Unfortunately, the RFC is not clear about the encoding... + // IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8 + // So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base + if (!self::LooksLikeUTF8($sAuthUser)) + { + // Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8 + // Supposed to be harmless in case of a plain ASCII string... + $sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser); + } + $sAuthPwd = $_SERVER['PHP_AUTH_PW']; + if (!self::LooksLikeUTF8($sAuthPwd)) + { + // Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8 + // Supposed to be harmless in case of a plain ASCII string... + $sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd); + } + $sLoginMode = 'basic'; + } + break; + + case 'external': + // Web server supplied authentication + $bExternalAuth = false; + $sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ? + eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value + if ($sAuthUser && (strlen($sAuthUser) > 0)) + { + $sAuthPwd = ''; // No password in this case the web server already authentified the user... + $sLoginMode = 'external'; + $sAuthentication = 'external'; + } + break; + + case 'url': + // Credentials passed directly in the url + $sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data'); + $sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data'); + if (($sAuthUser != '') && ($sAuthPwd !== null)) + { + $sLoginMode = 'url'; + } + break; + } + $index++; + //echo "\nsLoginMode: $sLoginMode (user: $sAuthUser / pwd: $sAuthPwd\n)"; if ($sLoginMode == '') { diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index 4799c7862..04cbfd4b6 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -188,15 +188,16 @@ abstract class User extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeString("login", array("allowed_values"=>null, "sql"=>"login", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>"EN US", "is_null_allowed"=>false, "depends_on"=>array()))); - + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values" => new ValueSetEnum('enabled,disabled'), "sql"=>"status", "default_value"=>"enabled", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("profile_list", array("linked_class"=>"URP_UserProfile", "ext_key_to_me"=>"userid", "ext_key_to_remote"=>"profileid", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("allowed_org_list", array("linked_class"=>"URP_UserOrg", "ext_key_to_me"=>"userid", "ext_key_to_remote"=>"allowed_org_id", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'login')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form } @@ -432,10 +433,10 @@ abstract class UserInternal extends User MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values"=>null, "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'login')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'status', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form } @@ -591,7 +592,17 @@ class UserRights $oUser = self::FindUser($sName, $sAuthentication); if (is_null($oUser)) { - return self::CheckCredentialsAndCreateUser($sName, $sPassword, $sLoginMode, $sAuthentication); + // Check if the user does not exist at all or if it is just disabled + if (self::FindUser($sName, $sAuthentication, true) == null) + { + // User does not exist at all + return self::CheckCredentialsAndCreateUser($sName, $sPassword, $sLoginMode, $sAuthentication); + } + else + { + // User is actually disabled + return false; + } } if (!$oUser->CheckCredentials($sPassword)) @@ -1084,9 +1095,10 @@ class UserRights * Find a user based on its login and its type of authentication * @param string $sLogin Login/identifier of the user * @param string $sAuthentication Type of authentication used: internal|external|any + * @param bool $bAllowDisabledUsers Whether or not to retrieve disabled users (status != enabled) * @return User The found user or null */ - protected static function FindUser($sLogin, $sAuthentication = 'any') + protected static function FindUser($sLogin, $sAuthentication = 'any', $bAllowDisabledUsers = false) { if ($sAuthentication == 'any') { @@ -1120,6 +1132,10 @@ class UserRights assert(false); // should never happen } $oSearch = DBObjectSearch::FromOQL("SELECT $sBaseClass WHERE login = :login"); + if (!$bAllowDisabledUsers) + { + $oSearch->AddCondition('status', 'enabled'); + } $oSet = new DBObjectSet($oSearch, array(), array('login' => $sLogin)); $oUser = $oSet->fetch(); self::$m_aCacheUsers[$sAuthentication][$sLogin] = $oUser; diff --git a/datamodels/1.x/authent-external/model.authent-external.php b/datamodels/1.x/authent-external/model.authent-external.php index d1e683cc0..84737db9c 100644 --- a/datamodels/1.x/authent-external/model.authent-external.php +++ b/datamodels/1.x/authent-external/model.authent-external.php @@ -51,10 +51,10 @@ class UserExternal extends User MetaModel::Init_InheritAttributes(); // Display lists - MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form } diff --git a/datamodels/1.x/authent-ldap/model.authent-ldap.php b/datamodels/1.x/authent-ldap/model.authent-ldap.php index 3aec98f75..f77d6b72c 100644 --- a/datamodels/1.x/authent-ldap/model.authent-ldap.php +++ b/datamodels/1.x/authent-ldap/model.authent-ldap.php @@ -46,10 +46,10 @@ class UserLDAP extends UserInternal MetaModel::Init_InheritAttributes(); // Display lists - MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form } diff --git a/datamodels/1.x/authent-local/model.authent-local.php b/datamodels/1.x/authent-local/model.authent-local.php index b271a4314..297a9f8ca 100644 --- a/datamodels/1.x/authent-local/model.authent-local.php +++ b/datamodels/1.x/authent-local/model.authent-local.php @@ -48,10 +48,10 @@ class UserLocal extends UserInternal MetaModel::Init_AddAttribute(new AttributeOneWayPassword("password", array("allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'password', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'password', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form } diff --git a/datamodels/2.x/authent-external/model.authent-external.php b/datamodels/2.x/authent-external/model.authent-external.php index d1e683cc0..84737db9c 100755 --- a/datamodels/2.x/authent-external/model.authent-external.php +++ b/datamodels/2.x/authent-external/model.authent-external.php @@ -51,10 +51,10 @@ class UserExternal extends User MetaModel::Init_InheritAttributes(); // Display lists - MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form } diff --git a/datamodels/2.x/authent-ldap/model.authent-ldap.php b/datamodels/2.x/authent-ldap/model.authent-ldap.php index 6434fe8de..673ac541d 100755 --- a/datamodels/2.x/authent-ldap/model.authent-ldap.php +++ b/datamodels/2.x/authent-ldap/model.authent-ldap.php @@ -46,10 +46,10 @@ class UserLDAP extends UserInternal MetaModel::Init_InheritAttributes(); // Display lists - MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form } diff --git a/datamodels/2.x/authent-local/model.authent-local.php b/datamodels/2.x/authent-local/model.authent-local.php index b271a4314..9d0c95f63 100755 --- a/datamodels/2.x/authent-local/model.authent-local.php +++ b/datamodels/2.x/authent-local/model.authent-local.php @@ -48,10 +48,10 @@ class UserLocal extends UserInternal MetaModel::Init_AddAttribute(new AttributeOneWayPassword("password", array("allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'password', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'password', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form } diff --git a/dictionaries/de.dictionary.itop.ui.php b/dictionaries/de.dictionary.itop.ui.php index 30b1de8cd..0013592fa 100644 --- a/dictionaries/de.dictionary.itop.ui.php +++ b/dictionaries/de.dictionary.itop.ui.php @@ -108,6 +108,10 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:User/Attribute:language/Value:EN US+' => 'English (U.S.)', 'Class:User/Attribute:language/Value:FR FR' => 'French', 'Class:User/Attribute:language/Value:FR FR+' => 'French (France)', + 'Class:User/Attribute:status' => 'Status', + 'Class:User/Attribute:status+' => 'Ist das Benutzer aktiviert oder deaktiviert ?', + 'Class:User/Attribute:status/Value:enabled' => 'Akitv', + 'Class:User/Attribute:status/Value:disabled' => 'Inaktiv', 'Class:User/Attribute:profile_list' => 'Profile', 'Class:User/Attribute:profile_list+' => 'Rollen, Rechtemanagement für diese Person', 'Class:User/Attribute:allowed_org_list' => 'Erlaubte Organisationen', diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index b853d9ee9..e5132f713 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -131,7 +131,11 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:User/Attribute:profile_list+' => 'Roles, granting rights for that person', 'Class:User/Attribute:allowed_org_list' => 'Allowed Organizations', 'Class:User/Attribute:allowed_org_list+' => 'The end user is allowed to see data belonging to the following organizations. If no organization is specified, there is no restriction.', - + 'Class:User/Attribute:status' => 'Status', + 'Class:User/Attribute:status+' => 'Whether the user account is enabled or disabled.', + 'Class:User/Attribute:status/Value:enabled' => 'Enabled', + 'Class:User/Attribute:status/Value:disabled' => 'Disabled', + 'Class:User/Error:LoginMustBeUnique' => 'Login must be unique - "%1s" is already being used.', 'Class:User/Error:AtLeastOneProfileIsNeeded' => 'At least one profile must be assigned to this user.', diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index 514709dc1..798903a87 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -156,6 +156,11 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:User/Attribute:profile_list+' => 'Rôles, ouvrants les droits d\'accès', 'Class:User/Attribute:allowed_org_list' => 'Organisations permises', 'Class:User/Attribute:allowed_org_list+' => 'L\'utilisateur a le droit de voir les données des organisations listées ici. Si aucune organisation n\'est spécifiée, alors aucune restriction ne s\'applique.', + 'Class:User/Attribute:status' => 'Etat', + 'Class:User/Attribute:status+' => 'Est-ce que ce compte utilisateur est actif, ou non?', + 'Class:User/Attribute:status/Value:enabled' => 'Actif', + 'Class:User/Attribute:status/Value:disabled' => 'Désactivé', + 'Class:User/Attribute:status' => 'Etat', 'Class:User/Error:LoginMustBeUnique' => 'Le login doit être unique - "%1s" est déjà utilisé.', 'Class:User/Error:AtLeastOneProfileIsNeeded' => 'L\'utilisateur doit avoir au moins un profil.', 'Class:UserInternal' => 'Utilisateur interne',