diff --git a/lam/lib/modules/posixAccount.inc b/lam/lib/modules/posixAccount.inc index bc7edcb1..14113f52 100644 --- a/lam/lib/modules/posixAccount.inc +++ b/lam/lib/modules/posixAccount.inc @@ -46,8 +46,13 @@ class posixAccount extends baseModule implements passwordService { private $groups; private $groups_orig; + /* list of group of names that the user is member of */ + private $gonList; + private $gonList_orig; + private $lamdaemonServers = array(); private $groupCache = null; + private $gonCache = null; private $clearTextPassword; /** caches the list of known UIDs */ private $cachedUIDList = null; @@ -262,6 +267,14 @@ class posixAccount extends baseModule implements passwordService { 'example' => _('Steve Miller,Room 2.14,123-123-1234,123-123-1234') ) ); + if ($this->areGroupOfNamesActive()) { + $return['upload_columns'][] = array( + 'name' => 'posixAccount_gon', + 'description' => _('Group of names'), + 'help' => 'addgroup_upload', + 'example' => _('group01,group02') + ); + } } // host specific upload options elseif ($this->get_scope() == 'host') { @@ -308,6 +321,9 @@ class posixAccount extends baseModule implements passwordService { 'cn' => _('Common name'), 'userPassword' => _('Password') ); + if ($this->areGroupOfNamesActive()) { + $return['PDF_fields']['gon'] = _('Group of names'); + } // help Entries $return['help'] = array( 'primaryGroupAsSecondary' => array( @@ -430,6 +446,8 @@ class posixAccount extends baseModule implements passwordService { StatusMessage("ERROR", _('No Unix groups found in LDAP! Please create one first.'), ''); return; } + $this->gonList = array(); + $this->gonList_orig = array(); } /** @@ -460,6 +478,17 @@ class posixAccount extends baseModule implements passwordService { $this->groups[] = $groupList[$i]['cn'][0]; } $this->groups_orig = $this->groups; + // get additional group of names memberships + if ($this->areGroupOfNamesActive()) { + $gonList1 = searchLDAPByAttribute('member', $this->getAccountContainer()->dn_orig, 'groupOfNames', array('dn'), array('gon', 'group')); + $gonList2 = searchLDAPByAttribute('uniqueMember', $this->getAccountContainer()->dn_orig, 'groupOfUniqueNames', array('dn'), array('gon', 'group')); + $gonList = array_merge($gonList1, $gonList2); + $this->gonList_orig = array(); + for ($i = 0; $i < sizeof($gonList); $i++) { + $this->gonList_orig[] = $gonList[$i]['dn']; + } + $this->gonList = $this->gonList_orig; + } } /** @@ -586,6 +615,40 @@ class posixAccount extends baseModule implements passwordService { } } } + // set group of names + if ($this->areGroupOfNamesActive()) { + $gons = $this->findGroupOfNames(); + $toAdd = array_values(array_diff($this->gonList, $this->gonList_orig)); + $toRem = array_values(array_diff($this->gonList_orig, $this->gonList)); + $ldapUser = $_SESSION['ldap']->decrypt_login(); + $ldapUser = $ldapUser[0]; + for ($i = 0; $i < sizeof($toAdd); $i++) { + if (isset($gons[$toAdd[$i]])) { + $attrName = 'member'; + if (in_array('groupOfUniqueNames', $gons[$toAdd[$i]]['objectclass'])) { + $attrName = 'uniqueMember'; + } + $success = @ldap_mod_add($_SESSION['ldap']->server(), $toAdd[$i], array($attrName => array($this->getAccountContainer()->finalDN))); + if (!$success) { + logNewMessage(LOG_ERR, '[' . $ldapUser .'] Unable to add attributes to DN: ' . $toAdd[$i] . ' (' . ldap_err2str(ldap_errno($_SESSION['ldap']->server())) . ').'); + StatusMessage('ERROR', sprintf(_('Was unable to add attributes to DN: %s.'), $toAdd[$i]), ldap_error($_SESSION['ldap']->server())); + } + } + } + for ($i = 0; $i < sizeof($toRem); $i++) { + if (isset($gons[$toRem[$i]])) { + $attrName = 'member'; + if (in_array('groupOfUniqueNames', $gons[$toRem[$i]]['objectclass'])) { + $attrName = 'uniqueMember'; + } + $success = @ldap_mod_del($_SESSION['ldap']->server(), $toRem[$i], array($attrName => array($this->getAccountContainer()->dn_orig))); + if (!$success) { + logNewMessage(LOG_ERR, '[' . $ldapUser .'] Unable to delete attributes from DN: ' . $toRem[$i] . ' (' . ldap_err2str(ldap_errno($_SESSION['ldap']->server())) . ').'); + StatusMessage('ERROR', sprintf(_('Was unable to remove attributes from DN: %s.'), $toRem[$i]), ldap_error($_SESSION['ldap']->server())); + } + } + } + } } /** @@ -880,13 +943,24 @@ class posixAccount extends baseModule implements passwordService { * @return array list of info/error messages */ function process_group() { + // Unix groups if (isset($_POST['addgroups']) && isset($_POST['addgroups_button'])) { // Add groups to list - // Add new group + // add new group $this->groups = @array_merge($this->groups, $_POST['addgroups']); } elseif (isset($_POST['removegroups']) && isset($_POST['removegroups_button'])) { // remove groups from list $this->groups = array_delete($_POST['removegroups'], $this->groups); } + // group of names + if ($this->areGroupOfNamesActive()) { + if (isset($_POST['addgons']) && isset($_POST['addgons_button'])) { // Add groups to list + // add new group + $this->gonList = @array_merge($this->gonList, $_POST['addgons']); + } + elseif (isset($_POST['removegons']) && isset($_POST['removegons_button'])) { // remove groups from list + $this->gonList = array_delete($_POST['removegons'], $this->gonList); + } + } return array(); } @@ -1124,25 +1198,77 @@ class posixAccount extends baseModule implements passwordService { unset ($groups[$group]); $groups = array_flip($groups); - $return->addElement(new htmlSubTitle(_("Additional groups")), true); - $return->addElement(new htmlOutputText(_("Selected groups"))); - $return->addElement(new htmlOutputText('')); - $return->addElement(new htmlOutputText(_("Available groups"))); - $return->addNewLine(); + $unixContainer = new htmlTable(); + $unixContainer->alignment = htmlElement::ALIGN_TOP; + $unixContainer->addElement(new htmlSubTitle(_("Unix groups")), true); + $unixContainer->addElement(new htmlOutputText(_("Selected groups"))); + $unixContainer->addElement(new htmlOutputText('')); + $unixContainer->addElement(new htmlOutputText(_("Available groups"))); + $unixContainer->addNewLine(); $remSelect = new htmlSelect('removegroups', $this->groups, null, 15); $remSelect->setMultiSelect(true); - $return->addElement($remSelect); + $remSelect->setTransformSingleSelect(false); + $unixContainer->addElement($remSelect); $buttonContainer = new htmlTable(); $buttonContainer->addElement(new htmlButton('addgroups_button', 'back.gif', true), true); $buttonContainer->addElement(new htmlButton('removegroups_button', 'forward.gif', true), true); $buttonContainer->addElement(new htmlHelpLink('addgroup')); - $return->addElement($buttonContainer); + $unixContainer->addElement($buttonContainer); $addSelect = new htmlSelect('addgroups', $groups, null, 15); $addSelect->setMultiSelect(true); - $return->addElement($addSelect); - $return->addNewLine(); + $addSelect->setTransformSingleSelect(false); + $unixContainer->addElement($addSelect); + $unixContainer->addNewLine(); + + $return->addElement($unixContainer); + + if ($this->areGroupOfNamesActive()) { + $return->addElement(new htmlSpacer('100px', null)); + + $gons = $this->findGroupOfNames(); + + $gonContainer = new htmlTable(); + $gonContainer->alignment = htmlElement::ALIGN_TOP; + $gonContainer->addElement(new htmlSubTitle(_("Group of names")), true); + $gonContainer->addElement(new htmlOutputText(_("Selected groups"))); + $gonContainer->addElement(new htmlOutputText('')); + $gonContainer->addElement(new htmlOutputText(_("Available groups"))); + $gonContainer->addNewLine(); + + $selectedGons = array(); + for ($i = 0; $i < sizeof($this->gonList); $i++) { + if (isset($gons[$this->gonList[$i]])) { + $selectedGons[$gons[$this->gonList[$i]]['cn'][0]] = $this->gonList[$i]; + } + } + $availableGons = array(); + foreach ($gons as $dn => $attr) { + if (!in_array($dn, $this->gonList)) { + $availableGons[$attr['cn'][0]] = $dn; + } + } + + $remGonSelect = new htmlSelect('removegons', $selectedGons, null, 15); + $remGonSelect->setMultiSelect(true); + $remGonSelect->setTransformSingleSelect(false); + $remGonSelect->setHasDescriptiveElements(true); + $gonContainer->addElement($remGonSelect); + $buttonGonContainer = new htmlTable(); + $buttonGonContainer->addElement(new htmlButton('addgons_button', 'back.gif', true), true); + $buttonGonContainer->addElement(new htmlButton('removegons_button', 'forward.gif', true), true); + $buttonGonContainer->addElement(new htmlHelpLink('addgroup')); + $gonContainer->addElement($buttonGonContainer); + $addGonSelect = new htmlSelect('addgons', $availableGons, null, 15); + $addGonSelect->setMultiSelect(true); + $addGonSelect->setHasDescriptiveElements(true); + $addGonSelect->setTransformSingleSelect(false); + $gonContainer->addElement($addGonSelect); + $gonContainer->addNewLine(); + $return->addElement($gonContainer); + } + $return->addNewLine(); $return->addElement(new htmlSpacer(null, '10px'), true); $backButton = new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')); @@ -1238,7 +1364,21 @@ class posixAccount extends baseModule implements passwordService { // additional group memberships $addGroupSelect = new htmlTableExtendedSelect('posixAccount_additionalGroup', $groups, array(), _('Additional groups'), 'addgroup', 10); $addGroupSelect->setMultiSelect(true); + $addGroupSelect->setTransformSingleSelect(false); $return->addElement($addGroupSelect, true); + // group of names + if ($this->areGroupOfNamesActive()) { + $gons = $this->findGroupOfNames(); + $gonList = array(); + foreach ($gons as $dn => $attr) { + $gonList[$attr['cn'][0]] = $dn; + } + $gonSelect = new htmlTableExtendedSelect('posixAccount_gon', $gonList, array(), _('Group of names'), 'addgroup', 10); + $gonSelect->setHasDescriptiveElements(true); + $gonSelect->setMultiSelect(true); + $gonSelect->setTransformSingleSelect(false); + $return->addElement($gonSelect, true); + } // home directory $return->addElement(new htmlTableExtendedInputField(_('Home directory'), 'posixAccount_homeDirectory', '/home/$user', 'homeDirectory'), true); // login shell @@ -1291,6 +1431,10 @@ class posixAccount extends baseModule implements passwordService { if (isset($profile['posixAccount_additionalGroup'][0])) { $this->groups = $profile['posixAccount_additionalGroup']; } + // group of names + if (isset($profile['posixAccount_gon'][0])) { + $this->gonList = $profile['posixAccount_gon']; + } // lamdaemon if (($this->get_scope() == 'user') && $this->getAccountContainer()->isNewAccount) { $lamdaemonServers = explode(";", $_SESSION['config']->get_scriptServers()); @@ -1325,6 +1469,16 @@ class posixAccount extends baseModule implements passwordService { 'posixAccount_homeDirectory' => array('' . _('Home directory') . '' . $this->attributes['homeDirectory'][0] . ''), 'posixAccount_loginShell' => array('' . _('Login shell') . '' . $this->attributes['loginShell'][0] . ''), ); + if ($this->areGroupOfNamesActive()) { + $allGons = $this->findGroupOfNames(); + $gons = array(); + for ($i = 0; $i < sizeof($this->gonList); $i++) { + if (isset($allGons[$this->gonList[$i]])) { + $gons[] = $allGons[$this->gonList[$i]]['cn'][0]; + } + } + $return['posixAccount_gon'] = array('' . _('Group of names') . '' . implode(", ", $gons) . ''); + } if (isset($this->clearTextPassword)) { $return['posixAccount_userPassword'] = array('' . _('Password') . '' . $this->clearTextPassword . ''); } @@ -1413,6 +1567,14 @@ class posixAccount extends baseModule implements passwordService { $groupMap[$groupList[$i][1]] = $groupList[$i][0]; } $existingGroups = array_keys($groupMap); + // get list of existing group of names + if ($this->areGroupOfNamesActive()) { + $gons = $this->findGroupOfNames(); + $gonList = array(); + foreach ($gons as $dn => $attr) { + $gonList[] = $attr['cn'][0]; + } + } // check input for ($i = 0; $i < sizeof($rawAccounts); $i++) { if (!in_array("posixAccount", $partialAccounts[$i]['objectClass'])) $partialAccounts[$i]['objectClass'][] = "posixAccount"; @@ -1507,6 +1669,15 @@ class posixAccount extends baseModule implements passwordService { } } } + // group of names + if ($this->areGroupOfNamesActive() && ($rawAccounts[$i][$ids['posixAccount_gon']] != "")) { + $groups = explode(",", $rawAccounts[$i][$ids['posixAccount_gon']]); + for ($g = 0; $g < sizeof($groups); $g++) { + if (!in_array($groups[$g], $gonList)) { + $errors[] = array('ERROR', _('Unable to find group of names in LDAP.'), $groups[$g]); + } + } + } // user name if (in_array($rawAccounts[$i][$ids['posixAccount_userName']], $existingUsers)) { $errMsg = $this->messages['uid'][9]; @@ -1657,9 +1828,11 @@ class posixAccount extends baseModule implements passwordService { // on first call generate list of ldap operations if (!isset($temp['counter'])) { $temp['groups'] = array(); + $temp['dn_gon'] = array(); $temp['createHomes'] = array(); $temp['counter'] = 0; $col = $ids['posixAccount_additionalGroups']; + $col_gon = $ids['posixAccount_gon']; $col_home = $ids['posixAccount_createHomeDir']; // get list of existing groups $groupList = $this->findGroups(); @@ -1667,6 +1840,14 @@ class posixAccount extends baseModule implements passwordService { for ($i = 0; $i < sizeof($groupList); $i++) { $groupMap[$groupList[$i][0]] = $groupList[$i][1]; } + // get list of existing group of names + if ($this->areGroupOfNamesActive()) { + $gonList = $this->findGroupOfNames(); + $gonMap = array(); + foreach ($gonList as $dn => $attr) { + $gonMap[$attr['cn'][0]] = $dn; + } + } for ($i = 0; $i < sizeof($data); $i++) { if (in_array($i, $failed)) continue; // ignore failed accounts if ($data[$i][$col] != "") { @@ -1689,10 +1870,21 @@ class posixAccount extends baseModule implements passwordService { $temp['members'][$groups[$g]][] = $data[$i][$ids['posixAccount_userName']]; } } + if ($data[$i][$col_gon] != "") { + $gons = explode(",", $data[$i][$col_gon]); + $memberAttr = 'member'; + for ($g = 0; $g < sizeof($gons); $g++) { + if (in_array('groupOfUniqueNames', $gonList[$gonMap[$gons[$g]]]['objectclass'])) { + $memberAttr = 'uniqueMember'; + } + $temp['dn_gon'][$gonMap[$gons[$g]]][$memberAttr][] = $accounts[$i]['dn']; + } + } if ($data[$i][$col_home] != "") { $temp['createHomes'][] = $i; } } + $temp['dn_gon_keys'] = array_keys($temp['dn_gon']); return array( 'status' => 'inProgress', 'progress' => 0, @@ -1728,7 +1920,7 @@ class posixAccount extends baseModule implements passwordService { $temp['counter']++; return array ( 'status' => 'inProgress', - 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups']) + sizeof($temp['createHomes'])), + 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon'])), 'errors' => $errors ); } @@ -1736,7 +1928,7 @@ class posixAccount extends baseModule implements passwordService { $temp['counter']++; return array ( 'status' => 'inProgress', - 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups'] + sizeof($temp['createHomes']))), + 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups'] + sizeof($temp['createHomes']) + sizeof($temp['dn_gon']))), 'errors' => array(array('ERROR', _('Unable to find group in LDAP.'), $temp['groups'][$temp['counter']])) ); } @@ -1768,11 +1960,33 @@ class posixAccount extends baseModule implements passwordService { $temp['counter']++; return array ( 'status' => 'inProgress', - 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups']) + sizeof($temp['createHomes'])), + 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon'])), 'errors' => $errors ); } - // all groups are modified + // add users to group of names + elseif ($temp['counter'] < (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon']))) { + $gonDn = $temp['dn_gon_keys'][$temp['counter'] - sizeof($temp['groups']) - sizeof($temp['createHomes'])]; + $gonAttr = $temp['dn_gon'][$gonDn]; + $success = @ldap_mod_add($_SESSION['ldap']->server(), $gonDn, $gonAttr); + $errors = array(); + if (!$success) { + $errors[] = array( + "ERROR", + _("LAM was unable to modify group memberships for group of names: %s"), + ldap_errno($_SESSION['ldap']->server()) . ": " . ldap_error($_SESSION['ldap']->server()), + array($temp['groups'][$temp['counter']]) + ); + } + $temp['counter']++; + $errors = array(); + return array ( + 'status' => 'inProgress', + 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon'])), + 'errors' => $errors + ); + } + // all modifications are done else { return array ( 'status' => 'finished', @@ -2025,6 +2239,35 @@ class posixAccount extends baseModule implements passwordService { return $return; } + /** + * Finds all existing LDAP group of names. + * + * @return array groups array(dn => array('cn' => array('groupName'), 'objectClass' => array('top', 'groupOfNames'))) + */ + private function findGroupOfNames() { + if ($this->gonCache != null) { + return $this->gonCache; + } + $return = array(); + $types = array(); + if (in_array('group', $_SESSION['config']->get_ActiveTypes())) { + $types[] = 'group'; + } + if (in_array('gon', $_SESSION['config']->get_ActiveTypes())) { + $types[] = 'gon'; + } + if (sizeof($types) > 0) { + $results = searchLDAPByFilter('(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames))', array('cn', 'dn', 'objectClass'), $types); + for ($i = 0; $i < sizeof($results); $i++) { + if (isset($results[$i]['cn'][0]) && isset($results[$i]['dn'])) { + $return[$results[$i]['dn']] = $results[$i]; + } + } + } + $this->gonCache = $return; + return $return; + } + /** * Returns a list of existing UID numbers. * @@ -2043,6 +2286,30 @@ class posixAccount extends baseModule implements passwordService { return $this->cachedUIDList; } + /** + * Returns if LAM manages group of names entries. + * + * @return boolean group of names are active + */ + private function areGroupOfNamesActive() { + if (!isset($_SESSION['config'])) { + return false; + } + if (in_array('group', $_SESSION['config']->get_ActiveTypes())) { + $groupModules = $_SESSION['config']->get_AccountModules('group'); + if (in_array('groupOfNames', $groupModules) || in_array('groupOfUniqueNames', $groupModules)) { + return true; + } + } + if (in_array('gon', $_SESSION['config']->get_ActiveTypes())) { + $gonModules = $_SESSION['config']->get_AccountModules('gon'); + if (in_array('groupOfNames', $gonModules) || in_array('groupOfUniqueNames', $gonModules)) { + return true; + } + } + return false; + } + /** * Returns a suggestion for the user name. * By deafult this wil be the first character of the first name plus the last name.