Windows user: support userPrincipalName

This commit is contained in:
Roland Gruber 2013-11-09 13:26:31 +00:00
parent fe843a990a
commit 0ccb20a343
6 changed files with 231 additions and 26 deletions

View File

@ -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

View File

@ -698,6 +698,10 @@ Have fun!
<para>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.</para>
<para>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.</para>
</section>
<section>
@ -2441,8 +2445,21 @@ Have fun!
</mediaobject>
</screenshot>
<para>Now you can manage your Windows users and e.g. assign
groups.</para>
<para>On tab "Module settings" you can specify the possible Windows
domain names and if pre-Windows 2000 user names should be
managed.</para>
<screenshot>
<mediaobject>
<imageobject>
<imagedata fileref="images/mod_windowsUser5.png" />
</imageobject>
</mediaobject>
</screenshot>
<para>Now you can manage your Windows users and e.g. assign groups.
You might want to set the default domain name in the <link
linkend="a_accountProfile">profile editor</link>.</para>
<para><emphasis role="bold">Attention:</emphasis> Password changes
require a secure connection via ldaps://. Check your LAM server

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -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');
}
}

View File

@ -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));
}
}
?>