support Samba Unix Id pool for UID/GID generation

This commit is contained in:
Roland Gruber 2013-02-17 13:19:50 +00:00
parent 95674dbf22
commit 150965e17e
3 changed files with 196 additions and 40 deletions

View File

@ -1,6 +1,7 @@
March 2013 4.1 March 2013 4.1
- updated EDU person module (RFE 3599128) - updated EDU person module (RFE 3599128)
- Personal: allow management of user certificates (RFE 1753030) - Personal: allow management of user certificates (RFE 1753030)
- Unix: Support Samba Unix Id pool for automatic UID/GID generation
- LAM Pro: - LAM Pro:
-> support Qmail groups -> support Qmail groups
- fixed bugs: - fixed bugs:

View File

@ -112,6 +112,7 @@ class posixAccount extends baseModule implements passwordService {
$this->messages['passwordDisabled'][0] = array('ERROR', _('Account %s:') . ' posixAccount_passwordDisabled', _('This value can only be "true" or "false".')); $this->messages['passwordDisabled'][0] = array('ERROR', _('Account %s:') . ' posixAccount_passwordDisabled', _('This value can only be "true" or "false".'));
$this->messages['cn'][0] = array('ERROR', _('Common name'), _('Please enter a valid common name!')); $this->messages['cn'][0] = array('ERROR', _('Common name'), _('Please enter a valid common name!'));
$this->messages['cn'][1] = array('ERROR', _('Account %s:') . ' inetOrgPerson_cn', _('Please enter a valid common name!')); $this->messages['cn'][1] = array('ERROR', _('Account %s:') . ' inetOrgPerson_cn', _('Please enter a valid common name!'));
$this->messages['sambaIDPoolDN'][0] = array('ERROR', _('Samba ID pool DN'), _('This is not a valid DN!'));
} }
/** /**
@ -180,6 +181,14 @@ class posixAccount extends baseModule implements passwordService {
// configuration options // configuration options
$configUserContainer = new htmlTable(); $configUserContainer = new htmlTable();
$configUserContainer->addElement(new htmlSubTitle(_("Users")), true); $configUserContainer->addElement(new htmlSubTitle(_("Users")), true);
$uidGeneratorSelect = new htmlTableExtendedSelect('posixAccount_uidGeneratorUsers', array(_('Fixed range') => 'range', _('Samba ID pool') => 'sambaPool'), array('range'), _('UID generator'), 'uidGenerator');
$uidGeneratorSelect->setHasDescriptiveElements(true);
$uidGeneratorSelect->setTableRowsToHide(array('range' => array('posixAccount_sambaIDPoolDNUsers'), 'sambaPool' => array('posixAccount_minUID', 'posixAccount_maxUID')));
$uidGeneratorSelect->setTableRowsToShow(array('range' => array('posixAccount_minUID', 'posixAccount_maxUID'), 'sambaPool' => array('posixAccount_sambaIDPoolDNUsers')));
$configUserContainer->addElement($uidGeneratorSelect, true);
$uidUsersGeneratorDN = new htmlTableExtendedInputField(_('Samba ID pool DN'), 'posixAccount_sambaIDPoolDNUsers', null, 'sambaIDPoolDN');
$uidUsersGeneratorDN->setRequired(true);
$configUserContainer->addElement($uidUsersGeneratorDN, true);
$minUid = new htmlTableExtendedInputField(_('Minimum UID number'), 'posixAccount_minUID', null, 'minMaxUser'); $minUid = new htmlTableExtendedInputField(_('Minimum UID number'), 'posixAccount_minUID', null, 'minMaxUser');
$minUid->setRequired(true); $minUid->setRequired(true);
$configUserContainer->addElement($minUid, true); $configUserContainer->addElement($minUid, true);
@ -190,6 +199,14 @@ class posixAccount extends baseModule implements passwordService {
$return['config_options']['user'] = $configUserContainer; $return['config_options']['user'] = $configUserContainer;
$configHostContainer = new htmlTable(); $configHostContainer = new htmlTable();
$configHostContainer->addElement(new htmlSubTitle(_("Hosts")), true); $configHostContainer->addElement(new htmlSubTitle(_("Hosts")), true);
$uidHostGeneratorSelect = new htmlTableExtendedSelect('posixAccount_uidGeneratorHosts', array(_('Fixed range') => 'range', _('Samba ID pool') => 'sambaPool'), array('range'), _('UID generator'), 'uidGenerator');
$uidHostGeneratorSelect->setHasDescriptiveElements(true);
$uidHostGeneratorSelect->setTableRowsToHide(array('range' => array('posixAccount_sambaIDPoolDNHosts'), 'sambaPool' => array('posixAccount_minMachine', 'posixAccount_maxMachine')));
$uidHostGeneratorSelect->setTableRowsToShow(array('range' => array('posixAccount_minMachine', 'posixAccount_maxMachine'), 'sambaPool' => array('posixAccount_sambaIDPoolDNHosts')));
$configHostContainer->addElement($uidHostGeneratorSelect, true);
$uidHostsGeneratorDN = new htmlTableExtendedInputField(_('Samba ID pool DN'), 'posixAccount_sambaIDPoolDNHosts', null, 'sambaIDPoolDN');
$uidHostsGeneratorDN->setRequired(true);
$configHostContainer->addElement($uidHostsGeneratorDN, true);
$minUid = new htmlTableExtendedInputField(_('Minimum UID number'), 'posixAccount_minMachine', null, 'minMaxHost'); $minUid = new htmlTableExtendedInputField(_('Minimum UID number'), 'posixAccount_minMachine', null, 'minMaxHost');
$minUid->setRequired(true); $minUid->setRequired(true);
$configHostContainer->addElement($minUid, true); $configHostContainer->addElement($minUid, true);
@ -393,6 +410,14 @@ class posixAccount extends baseModule implements passwordService {
"Headline" => _("Login shells"), "Headline" => _("Login shells"),
"Text" => _("This is the list of valid login shells.") "Text" => _("This is the list of valid login shells.")
), ),
'uidGenerator' => array (
"Headline" => _("UID generator"),
"Text" => _("LAM will automatically suggest UID/GID numbers. You can either use a fixed range of numbers or an LDAP entry with object class \"sambaUnixIdPool\".")
),
'sambaIDPoolDN' => array (
"Headline" => _("Samba ID pool DN"),
"Text" => _("Please enter the DN of the LDAP entry with object class \"sambaUnixIdPool\".")
),
'user' => array( 'user' => array(
'uid' => array( 'uid' => array(
"Headline" => _("User name"), 'attr' => 'uid', "Headline" => _("User name"), 'attr' => 'uid',
@ -1542,38 +1567,53 @@ class posixAccount extends baseModule implements passwordService {
$return = array(); $return = array();
// user settings // user settings
if (in_array('user', $scopes)) { if (in_array('user', $scopes)) {
// min/maxUID are required, check if they are numeric if ($options['posixAccount_uidGeneratorUsers'][0] == 'range') {
if (!isset($options['posixAccount_minUID'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_minUID'][0])) { // min/maxUID are required, check if they are numeric
$return[] = $this->messages['minUID'][0]; if (!isset($options['posixAccount_minUID'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_minUID'][0])) {
$return[] = $this->messages['minUID'][0];
}
if (!isset($options['posixAccount_maxUID'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_maxUID'][0])) {
$return[] = $this->messages['maxUID'][0];
}
// minUID < maxUID
if (isset($options['posixAccount_minUID'][0]) && isset($options['posixAccount_maxUID'][0])) {
if ($options['posixAccount_minUID'][0] > $options['posixAccount_maxUID'][0]) {
$return[] = $this->messages['cmp_UID'][0];
}
}
} }
if (!isset($options['posixAccount_maxUID'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_maxUID'][0])) { else {
$return[] = $this->messages['maxUID'][0]; if (!isset($options['posixAccount_sambaIDPoolDNUsers'][0]) || !get_preg($options['posixAccount_sambaIDPoolDNUsers'][0], 'dn')) {
} $return[] = $this->messages['sambaIDPoolDN'][0];
// minUID < maxUID
if (isset($options['posixAccount_minUID'][0]) && isset($options['posixAccount_maxUID'][0])) {
if ($options['posixAccount_minUID'][0] > $options['posixAccount_maxUID'][0]) {
$return[] = $this->messages['cmp_UID'][0];
} }
} }
} }
// host settings // host settings
if (in_array('host', $scopes)) { if (in_array('host', $scopes)) {
// min/maxUID are required, check if they are numeric if ($options['posixAccount_uidGeneratorHosts'][0] == 'range') {
if (!isset($options['posixAccount_minMachine'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_minMachine'][0])) { // min/maxUID are required, check if they are numeric
$return[] = $this->messages['minMachine'][0]; if (!isset($options['posixAccount_minMachine'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_minMachine'][0])) {
$return[] = $this->messages['minMachine'][0];
}
if (!isset($options['posixAccount_maxMachine'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_maxMachine'][0])) {
$return[] = $this->messages['maxMachine'][0];
}
// minUID < maxUID
if (isset($options['posixAccount_minMachine'][0]) && isset($options['posixAccount_maxMachine'][0])) {
if ($options['posixAccount_minMachine'][0] > $options['posixAccount_maxMachine'][0]) {
$return[] = $this->messages['cmp_Machine'][0];
}
}
} }
if (!isset($options['posixAccount_maxMachine'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_maxMachine'][0])) { else {
$return[] = $this->messages['maxMachine'][0]; if (!isset($options['posixAccount_sambaIDPoolDNHosts'][0]) || !get_preg($options['posixAccount_sambaIDPoolDNHosts'][0], 'dn')) {
} $return[] = $this->messages['sambaIDPoolDN'][0];
// minUID < maxUID
if (isset($options['posixAccount_minMachine'][0]) && isset($options['posixAccount_maxMachine'][0])) {
if ($options['posixAccount_minMachine'][0] > $options['posixAccount_maxMachine'][0]) {
$return[] = $this->messages['cmp_Machine'][0];
} }
} }
} }
// check if user and host ranges overlap // check if user and host ranges overlap
if (in_array('user', $scopes) && in_array('host', $scopes)) { if (in_array('user', $scopes) && ($options['posixAccount_uidGeneratorUsers'][0] == 'range')
&& in_array('host', $scopes) && ($options['posixAccount_uidGeneratorHosts'][0] == 'range')) {
if (isset($options['posixAccount_minUID'][0]) && isset($options['posixAccount_maxUID'][0]) && if (isset($options['posixAccount_minUID'][0]) && isset($options['posixAccount_maxUID'][0]) &&
isset($options['posixAccount_minMachine'][0]) && isset($options['posixAccount_maxMachine'][0])) { isset($options['posixAccount_minMachine'][0]) && isset($options['posixAccount_maxMachine'][0])) {
if (($options['posixAccount_minMachine'][0] > $options['posixAccount_minUID'][0]) && if (($options['posixAccount_minMachine'][0] > $options['posixAccount_minUID'][0]) &&
@ -2056,6 +2096,13 @@ class posixAccount extends baseModule implements passwordService {
* @return mixed Null if no UIDs are free else an array of free UIDs. * @return mixed Null if no UIDs are free else an array of free UIDs.
*/ */
function getNextUIDs($count, &$errors) { function getNextUIDs($count, &$errors) {
// check if UIDs should be taken from Samba pool entry
if (($this->get_scope() == 'user') && isset($this->moduleSettings['posixAccount_uidGeneratorUsers']) && ($this->moduleSettings['posixAccount_uidGeneratorUsers'][0] == 'sambaPool')) {
return $this->getNextSambaPoolUIDs($count, $errors);
}
if (($this->get_scope() == 'host') && isset($this->moduleSettings['posixAccount_uidGeneratorHosts']) && ($this->moduleSettings['posixAccount_uidGeneratorHosts'][0] == 'sambaPool')) {
return $this->getNextSambaPoolUIDs($count, $errors);
}
$ret = array(); $ret = array();
if ($this->get_scope() == "user") { if ($this->get_scope() == "user") {
$minID = intval($this->moduleSettings['posixAccount_minUID'][0]); $minID = intval($this->moduleSettings['posixAccount_minUID'][0]);
@ -2108,6 +2155,39 @@ class posixAccount extends baseModule implements passwordService {
return $ret; return $ret;
} }
/**
* Gets the free UID numbers from an Samba pool entry in LDAP.
*
* @param integer $count number of needed free UIDs.
* @param array $errors list of error messages where errors can be added
* @return mixed null if no UIDs are free else an array of free UIDs
*/
private function getNextSambaPoolUIDs($count, &$errors) {
if ($this->get_scope() == 'user') {
$dn = $this->moduleSettings['posixAccount_sambaIDPoolDNUsers'][0];
}
else {
$dn = $this->moduleSettings['posixAccount_sambaIDPoolDNHosts'][0];
}
$attrs = ldapGetDN($dn, array('uidNumber'));
if (isset($attrs['uidnumber'][0]) && ($attrs['uidnumber'][0] != '')) {
$newValue = $attrs['uidnumber'][0] + $count;
$ldapHandle = $_SESSION['ldap']->server();
ldap_modify($ldapHandle, $dn, array('uidnumber' => array($newValue)));
logNewMessage(LOG_DEBUG, 'Updated Samba ID pool ' . $dn . ' with UID number ' . $newValue . ' and LDAP code ' . ldap_errno($ldapHandle));
if (ldap_errno($ldapHandle) != 0) {
logNewMessage(LOG_NOTICE, 'Updating Samba ID pool ' . $dn . ' with UID number ' . $newValue . ' failed. ' . ldap_error($ldapHandle));
return null;
}
$result = array();
for ($i = 0; $i < $count; $i++) {
$result[] = $attrs['uidnumber'][0] + $i;
}
return $result;
}
return null;
}
/** /**
* Returns the meta HTML code for each input field. * Returns the meta HTML code for each input field.
* format: array(<field1> => array(<META HTML>), ...) * format: array(<field1> => array(<META HTML>), ...)

View File

@ -313,32 +313,22 @@ class posixGroup extends baseModule implements passwordService {
// configuration options // configuration options
$configContainer = new htmlTable(); $configContainer = new htmlTable();
$configContainer->addElement(new htmlSubTitle(_("Groups")), true); $configContainer->addElement(new htmlSubTitle(_("Groups")), true);
$gidGeneratorSelect = new htmlTableExtendedSelect('posixGroup_gidGenerator', array(_('Fixed range') => 'range', _('Samba ID pool') => 'sambaPool'), array('range'), _('GID generator'), 'gidGenerator');
$gidGeneratorSelect->setHasDescriptiveElements(true);
$gidGeneratorSelect->setTableRowsToHide(array('range' => array('posixGroup_sambaIDPoolDN'), 'sambaPool' => array('posixGroup_minGID', 'posixGroup_maxGID')));
$gidGeneratorSelect->setTableRowsToShow(array('range' => array('posixGroup_minGID', 'posixGroup_maxGID'), 'sambaPool' => array('posixGroup_sambaIDPoolDN')));
$configContainer->addElement($gidGeneratorSelect, true);
$minGidInput = new htmlTableExtendedInputField(_('Minimum GID number'), 'posixGroup_minGID', null, 'minMaxGID'); $minGidInput = new htmlTableExtendedInputField(_('Minimum GID number'), 'posixGroup_minGID', null, 'minMaxGID');
$minGidInput->setRequired(true); $minGidInput->setRequired(true);
$configContainer->addElement($minGidInput, true); $configContainer->addElement($minGidInput, true);
$maxGidInput = new htmlTableExtendedInputField(_('Maximum GID number'), 'posixGroup_maxGID', null, 'minMaxGID'); $maxGidInput = new htmlTableExtendedInputField(_('Maximum GID number'), 'posixGroup_maxGID', null, 'minMaxGID');
$maxGidInput->setRequired(true); $maxGidInput->setRequired(true);
$configContainer->addElement($maxGidInput, true); $configContainer->addElement($maxGidInput, true);
$gidGeneratorDN = new htmlTableExtendedInputField(_('Samba ID pool DN'), 'posixGroup_sambaIDPoolDN', null, 'sambaIDPoolDN');
$gidGeneratorDN->setRequired(true);
$configContainer->addElement($gidGeneratorDN, true);
$configContainer->addElement(new htmlTableExtendedInputField(_('Suffix for GID/group name check'), 'posixGroup_gidCheckSuffix', '', 'gidCheckSuffix'), true); $configContainer->addElement(new htmlTableExtendedInputField(_('Suffix for GID/group name check'), 'posixGroup_gidCheckSuffix', '', 'gidCheckSuffix'), true);
$return['config_options']['group'] = $configContainer; $return['config_options']['group'] = $configContainer;
// configuration checks
$return['config_checks']['group']['posixGroup_minGID'] = array (
'type' => 'ext_preg',
'regex' => 'digit',
'required' => true,
'required_message' => $this->messages['gidNumber'][5],
'error_message' => $this->messages['gidNumber'][5]);
$return['config_checks']['group']['posixGroup_maxGID'] = array (
'type' => 'ext_preg',
'regex' => 'digit',
'required' => true,
'required_message' => $this->messages['gidNumber'][6],
'error_message' => $this->messages['gidNumber'][6]);
$return['config_checks']['group']['cmpGID'] = array (
'type' => 'int_greater',
'cmp_name1' => 'posixGroup_maxGID',
'cmp_name2' => 'posixGroup_minGID',
'error_message' => $this->messages['gidNumber'][7]);
// available PDF fields // available PDF fields
$return['PDF_fields'] = array( $return['PDF_fields'] = array(
'gidNumber' => _('GID number'), 'gidNumber' => _('GID number'),
@ -434,7 +424,15 @@ class posixGroup extends baseModule implements passwordService {
'gidCheckSuffix' => array ( 'gidCheckSuffix' => array (
"Headline" => _("Suffix for GID/group name check"), "Headline" => _("Suffix for GID/group name check"),
"Text" => _("LAM checks if the entered group name and GID are unique. Here you can enter the LDAP suffix that is used to search for duplicates. By default the account type suffix is used. You only need to change this if you use multiple server profiles with different OUs but need unique group names or GIDs.") "Text" => _("LAM checks if the entered group name and GID are unique. Here you can enter the LDAP suffix that is used to search for duplicates. By default the account type suffix is used. You only need to change this if you use multiple server profiles with different OUs but need unique group names or GIDs.")
) ),
'gidGenerator' => array (
"Headline" => _("GID generator"),
"Text" => _("LAM will automatically suggest UID/GID numbers. You can either use a fixed range of numbers or an LDAP entry with object class \"sambaUnixIdPool\".")
),
'sambaIDPoolDN' => array (
"Headline" => _("Samba ID pool DN"),
"Text" => _("Please enter the DN of the LDAP entry with object class \"sambaUnixIdPool\".")
),
); );
return $return; return $return;
@ -507,6 +505,7 @@ class posixGroup extends baseModule implements passwordService {
$this->messages['cn'][3] = array('ERROR', _('Account %s:') . ' posixGroup_cn', _('Group name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); $this->messages['cn'][3] = array('ERROR', _('Account %s:') . ' posixGroup_cn', _('Group name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !'));
$this->messages['memberUID'][0] = array('ERROR', _('Account %s:') . ' posixGroup_members', _("This value must be a list of user names separated by semicolons.")); $this->messages['memberUID'][0] = array('ERROR', _('Account %s:') . ' posixGroup_members', _("This value must be a list of user names separated by semicolons."));
$this->messages['primaryGroup'][0] = array('ERROR', _('There are still users who have this group as their primary group.')); $this->messages['primaryGroup'][0] = array('ERROR', _('There are still users who have this group as their primary group.'));
$this->messages['sambaIDPoolDN'][0] = array('ERROR', _('Samba ID pool DN'), _('This is not a valid DN!'));
} }
@ -724,6 +723,50 @@ class posixGroup extends baseModule implements passwordService {
return $return; return $return;
} }
/**
* Checks input values of module settings.
*
* Calling this method does not require the existence of an enclosing {@link accountContainer}.<br>
* <br>
* If the input data is invalid the return value is an array that contains subarrays to build StatusMessages ('message type', 'message head', 'message text').
* <br>If no errors occured the function returns an empty array.
*
* @param array $scopes list of account types which are used
* @param array $options hash array (option name => value) that contains the input. The option values are all arrays containing one or more elements.
* @return array list of error messages
*
* @see baseModule::get_metaData()
*/
public function check_configOptions($scopes, &$options) {
if ($options['posixGroup_gidGenerator'][0] == 'range') {
$this->meta['config_checks']['group']['posixGroup_minGID'] = array (
'type' => 'ext_preg',
'regex' => 'digit',
'required' => true,
'required_message' => $this->messages['gidNumber'][5],
'error_message' => $this->messages['gidNumber'][5]);
$this->meta['config_checks']['group']['posixGroup_maxGID'] = array (
'type' => 'ext_preg',
'regex' => 'digit',
'required' => true,
'required_message' => $this->messages['gidNumber'][6],
'error_message' => $this->messages['gidNumber'][6]);
$this->meta['config_checks']['group']['cmpGID'] = array (
'type' => 'int_greater',
'cmp_name1' => 'posixGroup_maxGID',
'cmp_name2' => 'posixGroup_minGID',
'error_message' => $this->messages['gidNumber'][7]);
}
else {
$this->meta['config_checks']['group']['posixGroup_sambaIDPoolDN'] = array (
'type' => 'ext_preg',
'regex' => 'dn',
'required' => true,
'required_message' => $this->messages['sambaIDPoolDN'][0],
'error_message' => $this->messages['sambaIDPoolDN'][0]);
}
return parent::check_configOptions($scopes, $options);
}
/** /**
* Returns one or more free GID numbers. * Returns one or more free GID numbers.
@ -733,6 +776,10 @@ class posixGroup extends baseModule implements passwordService {
* @return mixed Null if no GIDs are free else an array of free GIDs. * @return mixed Null if no GIDs are free else an array of free GIDs.
*/ */
function getNextGIDs($count, &$errors) { function getNextGIDs($count, &$errors) {
// check if UIDs should be taken from Samba pool entry
if (isset($this->moduleSettings['posixGroup_gidGenerator']) && ($this->moduleSettings['posixGroup_gidGenerator'][0] == 'sambaPool')) {
return $this->getNextSambaPoolGIDs($count, $errors);
}
$ret = array(); $ret = array();
$minID = intval($this->moduleSettings['posixGroup_minGID'][0]); $minID = intval($this->moduleSettings['posixGroup_minGID'][0]);
$maxID = intval($this->moduleSettings['posixGroup_maxGID'][0]); $maxID = intval($this->moduleSettings['posixGroup_maxGID'][0]);
@ -778,6 +825,34 @@ class posixGroup extends baseModule implements passwordService {
} }
return $ret; return $ret;
} }
/**
* Gets the free GID numbers from an Samba pool entry in LDAP.
*
* @param integer $count number of needed free GIDs.
* @param array $errors list of error messages where errors can be added
* @return mixed null if no GIDs are free else an array of free GIDs
*/
private function getNextSambaPoolGIDs($count, &$errors) {
$dn = $this->moduleSettings['posixGroup_sambaIDPoolDN'][0];
$attrs = ldapGetDN($dn, array('gidNumber'));
if (isset($attrs['gidnumber'][0]) && ($attrs['gidnumber'][0] != '')) {
$newValue = $attrs['gidnumber'][0] + $count;
$ldapHandle = $_SESSION['ldap']->server();
ldap_modify($ldapHandle, $dn, array('gidnumber' => array($newValue)));
logNewMessage(LOG_DEBUG, 'Updated Samba ID pool ' . $dn . ' with GID number ' . $newValue . ' and LDAP code ' . ldap_errno($ldapHandle));
if (ldap_errno($ldapHandle) != 0) {
logNewMessage(LOG_NOTICE, 'Updating Samba ID pool ' . $dn . ' with GID number ' . $newValue . ' failed. ' . ldap_error($ldapHandle));
return null;
}
$result = array();
for ($i = 0; $i < $count; $i++) {
$result[] = $attrs['gidnumber'][0] + $i;
}
return $result;
}
return null;
}
/** /**
* This method specifies if a module manages password attributes. * This method specifies if a module manages password attributes.