diff --git a/lam/HISTORY b/lam/HISTORY
index e193a032..3bf29d54 100644
--- a/lam/HISTORY
+++ b/lam/HISTORY
@@ -3,6 +3,7 @@ December 2013 4.4
- Kolab shared folder support
- allow to set a custom label for each account type
- Unix: switch also additional membership if primary group is changed (RFE 108)
+ - Windows: fixed user name handling, sAMAccountName now optional
- LAM Pro:
-> Bind DLZ support
-> Samba/Shadow: display password change date in self service
diff --git a/lam/docs/manual-sources/howto.xml b/lam/docs/manual-sources/howto.xml
index 8559d0b5..87f3764b 100644
--- a/lam/docs/manual-sources/howto.xml
+++ b/lam/docs/manual-sources/howto.xml
@@ -698,6 +698,10 @@ Have fun!
Kolab: User accounts get the object class "mailrecipient" by
default. You can change this behaviour in the module settings
section of your LAM server profile.
+
+ Windows: sAMAccountName is no longer set by default. Enable it
+ in server profile if needed. The possible domains for the user name
+ can also be set in server profile.
@@ -2441,8 +2445,21 @@ Have fun!
- Now you can manage your Windows users and e.g. assign
- groups.
+ On tab "Module settings" you can specify the possible Windows
+ domain names and if pre-Windows 2000 user names should be
+ managed.
+
+
+
+
+
+
+
+
+
+ Now you can manage your Windows users and e.g. assign groups.
+ You might want to set the default domain name in the profile editor.
Attention: Password changes
require a secure connection via ldaps://. Check your LAM server
diff --git a/lam/docs/manual-sources/images/mod_windowsUser2.png b/lam/docs/manual-sources/images/mod_windowsUser2.png
index a5715edf..43d10859 100644
Binary files a/lam/docs/manual-sources/images/mod_windowsUser2.png and b/lam/docs/manual-sources/images/mod_windowsUser2.png differ
diff --git a/lam/docs/manual-sources/images/mod_windowsUser3.png b/lam/docs/manual-sources/images/mod_windowsUser3.png
index d62ebede..6d17e707 100644
Binary files a/lam/docs/manual-sources/images/mod_windowsUser3.png and b/lam/docs/manual-sources/images/mod_windowsUser3.png differ
diff --git a/lam/lib/baseModule.inc b/lam/lib/baseModule.inc
index d452e70f..1b603af7 100644
--- a/lam/lib/baseModule.inc
+++ b/lam/lib/baseModule.inc
@@ -1696,18 +1696,16 @@ abstract class baseModule {
* Returns if the given configuration option is set.
* This function returns false if the configuration options cannot be read.
*
- * @param String $optionName name of the option
+ * @param String $optionName name of the option
+ * @param boolean $default default value if config option is not set at all (default: false)
* @return boolean true if option is set
*/
- protected function isBooleanConfigOptionSet($optionName) {
+ protected function isBooleanConfigOptionSet($optionName, $default = false) {
// abort if configuration is not available
- if (!isset($this->moduleSettings) || !is_array($this->moduleSettings)) {
- return false;
+ if (!isset($this->moduleSettings) || !is_array($this->moduleSettings) || !isset($this->moduleSettings[$optionName][0])) {
+ return $default;
}
- if (isset($this->moduleSettings[$optionName][0]) && ($this->moduleSettings[$optionName][0] == 'true')) {
- return true;
- }
- return false;
+ return ($this->moduleSettings[$optionName][0] == 'true');
}
}
diff --git a/lam/lib/modules/windowsUser.inc b/lam/lib/modules/windowsUser.inc
index 4ffe1bbb..e496e897 100644
--- a/lam/lib/modules/windowsUser.inc
+++ b/lam/lib/modules/windowsUser.inc
@@ -85,16 +85,28 @@ class windowsUser extends baseModule implements passwordService {
// managed object classes
$return['objectClasses'] = array('user', 'securityPrincipal');
// managed attributes
- $return['attributes'] = array('cn', 'sAMAccountName', 'description', 'displayName', 'givenName', 'initials',
+ $return['attributes'] = array('userPrincipalName', 'cn', 'sAMAccountName', 'description', 'displayName', 'givenName', 'initials',
'l', 'mail', 'otherTelephone', 'physicalDeliveryOfficeName', 'postalCode', 'postOfficeBox', 'sn', 'st',
'streetAddress', 'telephoneNumber', 'url', 'wWWHomePage', 'userAccountControl', 'profilePath', 'scriptPath',
'pwdLastSet', 'otherMailbox');
// help Entries
$return['help'] = array(
'cn' => array(
- "Headline" => _('User name'), 'attr' => 'cn, sAMAccountName',
+ "Headline" => _('Common name'), 'attr' => 'cn',
+ "Text" => _('This is the natural name of the user. If empty, the first and last name or user name is used.')
+ ),
+ 'userPrincipalName' => array(
+ "Headline" => _('User name'), 'attr' => 'userPrincipalName',
"Text" => _('Please enter the user\'s name.')
),
+ 'userPrincipalNameDomain' => array(
+ "Headline" => _('Domain'), 'attr' => 'userPrincipalName',
+ "Text" => _('Windows domain name of account.')
+ ),
+ 'sAMAccountName' => array(
+ "Headline" => _('User name (pre W2K)'), 'attr' => 'sAMAccountName',
+ "Text" => _('Please enter the user\'s name.') . ' ' . _('This user name is only used for old Windows versions (e.g. NT4, W98).')
+ ),
'description' => array(
"Headline" => _('Description'), 'attr' => 'description',
"Text" => _('Please enter a descriptive text for this user.')
@@ -206,13 +218,21 @@ class windowsUser extends baseModule implements passwordService {
"Headline" => _("Email alias"), 'attr' => 'otherMailbox',
"Text" => _("Email alias for this account.") . ' ' . _("Multiple values are separated by semicolon.")
),
+ 'hiddenOptions' => array(
+ "Headline" => _("Hidden options"),
+ "Text" => _("The selected options will not be managed inside LAM. You can use this to reduce the number of displayed input fields.")
+ ),
+ 'domains' => array(
+ "Headline" => _('Domains'),
+ "Text" => _('Please enter a list of Windows domains that can be selected for your user accounts.')
+ ),
);
// upload fields
$return['upload_columns'] = array(
array(
- 'name' => 'windowsUser_name',
+ 'name' => 'windowsUser_userPrincipalName',
'description' => _('User name'),
- 'help' => 'cn',
+ 'help' => 'userPrincipalName',
'example' => _('smiller'),
'required' => true,
'unique' => true,
@@ -235,6 +255,12 @@ class windowsUser extends baseModule implements passwordService {
'help' => 'sn',
'example' => _('Miller'),
),
+ array(
+ 'name' => 'windowsUser_cn',
+ 'description' => _('Common name'),
+ 'help' => 'cn',
+ 'example' => _('Steve Miller'),
+ ),
array(
'name' => 'windowsUser_displayName',
'description' => _('Display name'),
@@ -375,9 +401,19 @@ class windowsUser extends baseModule implements passwordService {
'help' => 'groupsUpload',
),
);
+ if (!$this->isBooleanConfigOptionSet('windowsUser_hidesAMAccountName', true)) {
+ $return['upload_columns'][] = array(
+ 'name' => 'windowsUser_sAMAccountName',
+ 'description' => _('User name (pre W2K)'),
+ 'help' => 'sAMAccountName',
+ 'example' => _('smiller'),
+ 'unique' => true,
+ );
+ }
// available PDF fields
$return['PDF_fields'] = array(
- 'cn' => _('User name'),
+ 'userPrincipalName' => _('User name'),
+ 'cn' => _('Common name'),
'description' => _('Description'),
'displayName' => _('Display name'),
'givenName' => _('First name'),
@@ -404,6 +440,9 @@ class windowsUser extends baseModule implements passwordService {
'groups' => _('Groups'),
'password' => _('Password'),
);
+ if (!$this->isBooleanConfigOptionSet('windowsUser_hidesAMAccountName', true)) {
+ $return['PDF_fields']['sAMAccountName'] = _('User name (pre W2K)');
+ }
// self service search attributes
$return['selfServiceSearchAttributes'] = array('sAMAccountName');
// self service field settings
@@ -421,6 +460,20 @@ class windowsUser extends baseModule implements passwordService {
// possible self service read-only fields
$return['selfServiceReadOnlyFields'] = array('physicalDeliveryOfficeName', 'telephoneNumber',
'wWWHomePage', 'streetAddress', 'st', 'l', 'postOfficeBox', 'postalCode');
+ // configuration options
+ $configContainer = new htmlTable();
+ $configContainerHead = new htmlTable();
+ $configContainerHead->addElement(new htmlTableExtendedInputTextarea('windowsUser_domains', '', 30, 3, _('Domains'), 'domains'));
+ $configContainer->addElement($configContainerHead, true);
+ $configContainer->addVerticalSpace('10px');
+ $configHiddenGroup = new htmlGroup();
+ $configHiddenGroup->addElement(new htmlOutputText(_('Hidden options')));
+ $configHiddenGroup->addElement(new htmlHelpLink('hiddenOptions'));
+ $configContainer->addElement($configHiddenGroup, true);
+ $configContainerOptions = new htmlTable();
+ $configContainerOptions->addElement(new htmlTableExtendedInputCheckbox('windowsUser_hidesAMAccountName', true, _('User name (pre W2K)'), null, false));
+ $configContainer->addElement($configContainerOptions, true);
+ $return['config_options']['all'] = $configContainer;
return $return;
}
@@ -440,8 +493,12 @@ class windowsUser extends baseModule implements passwordService {
* This function fills the $messages variable with output messages from this module.
*/
public function load_Messages() {
- $this->messages['cn'][0] = array('ERROR', _('User name'), _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !'));
- $this->messages['cn'][1] = array('ERROR', _('Account %s:') . ' windowsUser_cn', _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !'));
+ $this->messages['userPrincipalName'][0] = array('ERROR', _('User name'), _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !'));
+ $this->messages['userPrincipalName'][1] = array('ERROR', _('Account %s:') . ' windowsUser_userPrincipalName', _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !'));
+ $this->messages['cn'][0] = array('ERROR', _('Common name'), _('Please enter a valid common name!'));
+ $this->messages['cn'][1] = array('ERROR', _('Account %s:') . ' windowsUser_cn', _('Please enter a valid common name!'));
+ $this->messages['sAMAccountName'][0] = array('ERROR', _('User name (pre W2K)'), _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !'));
+ $this->messages['sAMAccountName'][1] = array('ERROR', _('Account %s:') . ' windowsUser_sAMAccountName', _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !'));
$this->messages['displayName'][0] = array('ERROR', _('Display name'), _('Please enter a valid display name!'));
$this->messages['displayName'][1] = array('ERROR', _('Account %s:') . ' windowsUser_displayName', _('Please enter a valid display name!'));
$this->messages['givenName'][0] = array('ERROR', _('First name'), _('First name contains invalid characters!'));
@@ -516,9 +573,35 @@ class windowsUser extends baseModule implements passwordService {
$this->attributes['userAccountControl'][0] = windowsUser::DEFAULT_ACCOUNT_CONTROL;
}
$containerLeft->addElement(new htmlSubTitle(_('General')), true);
- $this->addSimpleInputTextField($containerLeft, 'cn', _('User name'), true);
+ // user name
+ $userPrincipalName = '';
+ $userPrincipalNameDomain = '';
+ $domains = $this->getDomains();
+ $domains[] = '';
+ if (!empty($this->attributes['userPrincipalName'][0])) {
+ $parts = explode('@', $this->attributes['userPrincipalName'][0]);
+ $userPrincipalName = $parts[0];
+ if (!empty($parts[1])) {
+ $userPrincipalNameDomain = $parts[1];
+ if (!in_array($userPrincipalNameDomain, $domains)) {
+ $domains[] = $userPrincipalNameDomain;
+ }
+ }
+ }
+ $userPrincipalNameLabel = new htmlOutputText(_('User name'));
+ $userPrincipalNameLabel->setMarkAsRequired(true);
+ $containerLeft->addElement($userPrincipalNameLabel);
+ $userPrincipalNameGroup = new htmlGroup();
+ $userPrincipalNameGroup->addElement(new htmlInputField('userPrincipalName', $userPrincipalName, '15'));
+ $userPrincipalNameGroup->addElement(new htmlSelect('userPrincipalNameDomain', $domains, array($userPrincipalNameDomain)));
+ $containerLeft->addElement($userPrincipalNameGroup);
+ $containerLeft->addElement(new htmlHelpLink('userPrincipalName'), true);
+ if (!$this->isBooleanConfigOptionSet('windowsUser_hidesAMAccountName', true)) {
+ $this->addSimpleInputTextField($containerLeft, 'sAMAccountName', _('User name (pre W2K)'));
+ }
$this->addSimpleInputTextField($containerLeft, 'givenName', _('First name'));
$this->addSimpleInputTextField($containerLeft, 'sn', _('Last name'));
+ $this->addSimpleInputTextField($containerLeft, 'cn', _('Common name'), true);
$this->addSimpleInputTextField($containerLeft, 'displayName', _('Display name'));
$this->addSimpleInputTextField($containerLeft, 'initials', _('Initials'));
$this->addSimpleInputTextField($containerLeft, 'description', _('Description'));
@@ -608,12 +691,42 @@ class windowsUser extends baseModule implements passwordService {
*/
public function process_attributes() {
$return = array();
+ // user name
+ $userPrincipalName = $_POST['userPrincipalName'];
+ if (!get_preg($userPrincipalName, 'username')) {
+ $return[] = $this->messages['userPrincipalName'][0];
+ }
+ if (!empty($_POST['userPrincipalNameDomain'])) {
+ $userPrincipalName .= '@' . $_POST['userPrincipalNameDomain'];
+ }
+ $this->attributes['userPrincipalName'][0] = $userPrincipalName;
// cn
$this->attributes['cn'][0] = $_POST['cn'];
- $this->attributes['sAMAccountName'][0] = $_POST['cn'];
- if (!get_preg($_POST['cn'], 'username')) {
+ if (empty($this->attributes['cn'][0])) {
+ $cn = '';
+ if (!empty($_POST['givenName'])) {
+ $cn = $_POST['givenName'];
+ }
+ if (!empty($_POST['sn'])) {
+ $cn .= ' ' . $_POST['sn'];
+ }
+ $this->attributes['cn'][0] = trim($cn);
+ }
+ if (!get_preg($this->attributes['cn'][0], 'cn')) {
$return[] = $this->messages['cn'][0];
}
+ // sAMAccountName
+ if (!$this->isBooleanConfigOptionSet('windowsUser_hidesAMAccountName', true)) {
+ if ($this->getAccountContainer()->isNewAccount && !isset($this->attributes['sAMAccountName']) && empty($_POST['sAMAccountName'])) {
+ $this->attributes['sAMAccountName'][0] = $_POST['userPrincipalName'];
+ }
+ else {
+ $this->attributes['sAMAccountName'][0] = $_POST['sAMAccountName'];
+ }
+ if (!empty($this->attributes['sAMAccountName'][0]) && !get_preg($this->attributes['sAMAccountName'][0], 'username')) {
+ $return[] = $this->messages['sAMAccountName'][0];
+ }
+ }
// description
$this->attributes['description'][0] = $_POST['description'];
// display name
@@ -621,6 +734,9 @@ class windowsUser extends baseModule implements passwordService {
if (!empty($this->attributes['displayName'][0]) && !get_preg($_POST['displayName'], 'realname')) {
$return[] = $this->messages['displayName'][0];
}
+ if (empty($this->attributes['displayName'][0]) && !empty($this->attributes['cn'][0])) {
+ $this->attributes['displayName'][0] = $this->attributes['cn'][0];
+ }
// first name
$this->attributes['givenName'][0] = $_POST['givenName'];
if (!empty($this->attributes['givenName'][0]) && !get_preg($_POST['givenName'], 'realname')) {
@@ -869,11 +985,19 @@ class windowsUser extends baseModule implements passwordService {
for ($i = 0; $i < sizeof($rawAccounts); $i++) {
// add object class
if (!in_array('user', $partialAccounts[$i]['objectClass'])) $partialAccounts[$i]['objectClass'][] = 'user';
- // cn + sAMAccountName
- if ($rawAccounts[$i][$ids['windowsUser_name']] != "") {
- if (get_preg($rawAccounts[$i][$ids['windowsUser_name']], 'username')) {
- $partialAccounts[$i]['cn'] = $rawAccounts[$i][$ids['windowsUser_name']];
- $partialAccounts[$i]['sAMAccountName'] = $rawAccounts[$i][$ids['windowsUser_name']];
+ // userPrincipalName
+ if (get_preg($rawAccounts[$i][$ids['windowsUser_userPrincipalName']], 'username')) {
+ $partialAccounts[$i]['userPrincipalName'] = $rawAccounts[$i][$ids['windowsUser_userPrincipalName']];
+ }
+ else {
+ $errMsg = $this->messages['userPrincipalName'][1];
+ array_push($errMsg, array($i));
+ $errors[] = $errMsg;
+ }
+ // cn
+ if ($rawAccounts[$i][$ids['windowsUser_cn']] != "") {
+ if (get_preg($rawAccounts[$i][$ids['windowsUser_cn']], 'cn')) {
+ $partialAccounts[$i]['cn'] = $rawAccounts[$i][$ids['windowsUser_cn']];
}
else {
$errMsg = $this->messages['cn'][1];
@@ -881,6 +1005,36 @@ class windowsUser extends baseModule implements passwordService {
$errors[] = $errMsg;
}
}
+ else {
+ $cn = '';
+ if (!empty($rawAccounts[$i][$ids['windowsUser_firstName']])) {
+ $cn = $rawAccounts[$i][$ids['windowsUser_firstName']];
+ }
+ if (!empty($rawAccounts[$i][$ids['windowsUser_lastName']])) {
+ $cn .= ' ' . $rawAccounts[$i][$ids['windowsUser_lastName']];
+ }
+ $cn = trim($cn);
+ if (!empty($cn)) {
+ $partialAccounts[$i]['cn'] = $cn;
+ }
+ }
+ // sAMAccountName
+ if (!$this->isBooleanConfigOptionSet('windowsUser_hidesAMAccountName', true)) {
+ if (!empty($rawAccounts[$i][$ids['windowsUser_sAMAccountName']])) {
+ if (get_preg($rawAccounts[$i][$ids['windowsUser_sAMAccountName']], 'username')) {
+ $partialAccounts[$i]['sAMAccountName'] = $rawAccounts[$i][$ids['windowsUser_sAMAccountName']];
+ }
+ else {
+ $errMsg = $this->messages['sAMAccountName'][1];
+ array_push($errMsg, array($i));
+ $errors[] = $errMsg;
+ }
+ }
+ else {
+ $samUser = explode('@', $partialAccounts[$i]['userPrincipalName']);
+ $partialAccounts[$i]['sAMAccountName'] = $samUser[0];
+ }
+ }
// password
if (($rawAccounts[$i][$ids['windowsUser_password']] != "") && (get_preg($rawAccounts[$i][$ids['windowsUser_password']], 'password'))) {
$partialAccounts[$i]['unicodePwd'] = self::pwdAttributeValue($rawAccounts[$i][$ids['windowsUser_password']]);
@@ -918,6 +1072,9 @@ class windowsUser extends baseModule implements passwordService {
if ($rawAccounts[$i][$ids['windowsUser_displayName']] != "") {
$partialAccounts[$i]['displayName'] = $rawAccounts[$i][$ids['windowsUser_displayName']];
}
+ elseif (!empty($partialAccounts[$i]['cn'])) {
+ $partialAccounts[$i]['displayName'] = $partialAccounts[$i]['cn'];
+ }
// initials
if ($rawAccounts[$i][$ids['windowsUser_initials']] != "") {
$partialAccounts[$i]['initials'] = $rawAccounts[$i][$ids['windowsUser_initials']];
@@ -1212,7 +1369,9 @@ class windowsUser extends baseModule implements passwordService {
*/
public function get_pdfEntries() {
$return = array();
- $this->addSimplePDFField($return, 'cn', _('User name'));
+ $this->addSimplePDFField($return, 'userPrincipalName', _('User name'));
+ $this->addSimplePDFField($return, 'cn', _('Common name'));
+ $this->addSimplePDFField($return, 'sAMAccountName', _('User name (pre W2K)'));
$this->addSimplePDFField($return, 'description', _('Description'));
$this->addSimplePDFField($return, 'displayName', _('Display name'));
$this->addSimplePDFField($return, 'givenName', _('First name'));
@@ -1274,6 +1433,11 @@ class windowsUser extends baseModule implements passwordService {
*/
function get_profileOptions() {
$return = new htmlTable();
+ // domain
+ $domains = $this->getDomains();
+ $domains[] = '';
+ $return->addElement(new htmlTableExtendedSelect('windowsUser_userPrincipalNameDomain', $domains, array(), _('Domain'), 'userPrincipalNameDomain'), true);
+ // group memberships
$groups = $this->findGroups();
$groupList = array();
foreach ($groups as $dn) {
@@ -1294,6 +1458,13 @@ class windowsUser extends baseModule implements passwordService {
function load_profile($profile) {
// profile mappings in meta data
parent::load_profile($profile);
+ // load domain
+ if (isset($profile['windowsUser_userPrincipalNameDomain'][0])) {
+ $user = empty($this->attributes['userPrincipalName'][0]) ? '' : $this->attributes['userPrincipalName'][0];
+ $user = explode('@', $user);
+ $user = $user[0] . '@' . $profile['windowsUser_userPrincipalNameDomain'][0];
+ $this->attributes['userPrincipalName'][0] = $user;
+ }
// load groups
if (isset($profile['windowsUser_groups'][0])) {
$this->groupList = $profile['windowsUser_groups'];
@@ -1686,6 +1857,24 @@ class windowsUser extends baseModule implements passwordService {
return $return;
}
+ /**
+ * Gets the list of possible domains from the config setting.
+ *
+ * @return array domain list
+ */
+ private function getDomains() {
+ $domains = array();
+ if (!empty($this->moduleSettings['windowsUser_domains'])) {
+ foreach ($this->moduleSettings['windowsUser_domains'] as $domain) {
+ $domain = trim(str_replace('@', '', $domain));
+ if (!empty($domain)) {
+ $domains[] = $domain;
+ }
+ }
+ }
+ return array_values(array_unique($domains));
+ }
+
}
?>