From b74d47a709c7016d9f28ea79a5a0ae32f74cdebc Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Sun, 7 Jun 2015 07:38:49 +0000 Subject: [PATCH] allow to edit memberOf for Windows groups --- lam/lib/modules/windowsGroup.inc | 221 ++++++++++++++++++++++++++++++- lam/lib/modules/windowsUser.inc | 3 +- 2 files changed, 220 insertions(+), 4 deletions(-) diff --git a/lam/lib/modules/windowsGroup.inc b/lam/lib/modules/windowsGroup.inc index 88d8cb14..fbc4828c 100644 --- a/lam/lib/modules/windowsGroup.inc +++ b/lam/lib/modules/windowsGroup.inc @@ -38,6 +38,8 @@ class windowsGroup extends baseModule { private $groupTypes; /** possible group scopes (e.g. universal) */ private $groupScopes; + /** group cache */ + private $groupCache; /** security group */ const TYPE_SECURITY = 'security'; @@ -102,8 +104,8 @@ class windowsGroup extends baseModule { // managed object classes $return['objectClasses'] = array('group', 'securityPrincipal', 'mailRecipient'); // managed attributes - $return['attributes'] = array('cn', 'description', 'info', 'mail', 'member', 'sAMAccountName', 'groupType', - 'managedBy', 'msSFU30Name', 'msSFU30NisDomain'); + $return['attributes'] = array('cn', 'description', 'info', 'mail', 'member', 'memberOf', 'sAMAccountName', + 'groupType', 'managedBy', 'msSFU30Name', 'msSFU30NisDomain'); // help Entries $return['help'] = array( 'hiddenOptions' => array( @@ -130,6 +132,10 @@ class windowsGroup extends baseModule { "Headline" => _('Members'), 'attr' => 'member', "Text" => _('This is a list of members of this group.') ), + 'memberOf' => array( + "Headline" => _('Member of'), 'attr' => 'memberOf', + "Text" => _('This is a list of groups this group is member of.') + ), 'memberList' => array( "Headline" => _('Members'), 'attr' => 'member', "Text" => _('This is a list of members of this group. Multiple members are separated by semicolons.') @@ -398,6 +404,29 @@ class windowsGroup extends baseModule { } $container->addElement(new htmlOutputText('')); $container->addElement($members, true); + // member of + $container->addVerticalSpace('10px'); + $container->addElement(new htmlOutputText(_("Member of"))); + $container->addElement(new htmlAccountPageButton(get_class($this), 'memberof', 'open', _('Edit member of'))); + $container->addElement(new htmlHelpLink('memberOf'), true); + $memberList = array(); + if (isset($this->attributes['memberOf'])) { + for ($i = 0; $i < sizeof($this->attributes['memberOf']); $i++) { + $memberList[] = $this->attributes['memberOf'][$i]; + } + usort($memberList, 'compareDN'); + } + $memberOf = new htmlTable(); + $memberOf->alignment = htmlElement::ALIGN_RIGHT; + $memberOf->colspan = 3; + for ($i = 0; $i < sizeof($memberList); $i++) { + $member = new htmlOutputText(getAbstractDN($memberList[$i])); + $member->alignment = htmlElement::ALIGN_RIGHT; + $memberOf->addElement($member, true); + } + $container->addElement(new htmlOutputText('')); + $container->addElement($memberOf, true); + $container->addElement(new htmlEqualWidth(array('groupType', 'groupScope'))); return $container; } @@ -468,6 +497,91 @@ class windowsGroup extends baseModule { return $return; } + /** + * Displays the memberof selection. + * + * @return htmlElement meta HTML code + */ + function display_html_memberof() { + $return = new htmlTable(); + $groups = $this->findGroups(); + // sort by DN + usort($groups, 'compareDN'); + + $groupContainer = new htmlTable(); + $groupContainer->alignment = htmlElement::ALIGN_TOP; + $groupContainer->addElement(new htmlSubTitle(_("Groups")), true); + $groupContainer->addElement(new htmlOutputText(_("Selected groups"))); + $groupContainer->addElement(new htmlOutputText('')); + $groupContainer->addElement(new htmlOutputText(_("Available groups"))); + $groupContainer->addNewLine(); + + $selectedGroups = array(); + if (empty($this->attributes['memberOf'])) { + $this->attributes['memberOf'] = array(); + } + // sort by DN + usort($this->attributes['memberOf'], 'compareDN'); + for ($i = 0; $i < sizeof($this->attributes['memberOf']); $i++) { + if (in_array($this->attributes['memberOf'][$i], $groups)) { + $selectedGroups[getAbstractDN($this->attributes['memberOf'][$i])] = $this->attributes['memberOf'][$i]; + } + } + $availableGroups = array(); + foreach ($groups as $dn) { + if (!in_array($dn, $this->attributes['memberOf'])) { + $availableGroups[getAbstractDN($dn)] = $dn; + } + } + + $remGroupSelect = new htmlSelect('removegroups', $selectedGroups, null, 15); + $remGroupSelect->setMultiSelect(true); + $remGroupSelect->setTransformSingleSelect(false); + $remGroupSelect->setHasDescriptiveElements(true); + $remGroupSelect->setRightToLeftTextDirection(true); + $remGroupSelect->setSortElements(false); + $groupContainer->addElement($remGroupSelect); + $buttonGroupContainer = new htmlTable(); + $buttonGroupContainer->addElement(new htmlButton('addgroups_button', 'back.gif', true), true); + $buttonGroupContainer->addElement(new htmlButton('removegroups_button', 'forward.gif', true), true); + $groupContainer->addElement($buttonGroupContainer); + $addGroupSelect = new htmlSelect('addgroups', $availableGroups, null, 15); + $addGroupSelect->setMultiSelect(true); + $addGroupSelect->setHasDescriptiveElements(true); + $addGroupSelect->setTransformSingleSelect(false); + $addGroupSelect->setRightToLeftTextDirection(true); + $addGroupSelect->setSortElements(false); + $groupContainer->addElement($addGroupSelect); + $groupContainer->addNewLine(); + $return->addElement($groupContainer); + $return->addNewLine(); + + $backGroup = new htmlGroup(); + $backGroup->colspan = 10; + $backGroup->addElement(new htmlSpacer(null, '10px'), true); + $backButton = new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')); + $backGroup->addElement($backButton); + $return->addElement($backGroup); + return $return; + } + + /** + * Processes user input of the memberof selection page. + * It checks if all input values are correct and updates the associated LDAP attributes. + * + * @return array list of info/error messages + */ + function process_memberof() { + if (isset($_POST['addgroups']) && isset($_POST['addgroups_button'])) { // Add groups to list + // add new group + $this->attributes['memberOf'] = @array_merge($this->attributes['memberOf'], $_POST['addgroups']); + } + elseif (isset($_POST['removegroups']) && isset($_POST['removegroups_button'])) { // remove groups from list + $this->attributes['memberOf'] = array_delete($_POST['removegroups'], $this->attributes['memberOf']); + } + return array(); + } + /** * This function will create the meta HTML code to show a page to change the member attribute. * @@ -807,6 +921,109 @@ class windowsGroup extends baseModule { return $return; } + /** + * Finds all existing groups. + * + * @return array group DNs + */ + private function findGroups() { + if ($this->groupCache != null) { + return $this->groupCache; + } + $return = array(); + $types = array('group'); + $results = searchLDAPByFilter('(objectClass=group)', array('dn'), $types); + $count = sizeof($results); + for ($i = 0; $i < $count; $i++) { + if (isset($results[$i]['dn'])) { + $return[] = $results[$i]['dn']; + } + } + $this->groupCache = $return; + return $return; + } + + /** + * Returns a list of modifications which have to be made to the LDAP account. + * + * Calling this method requires the existence of an enclosing {@link accountContainer}.
+ *
+ * + *
This function returns an array with 3 entries: + *
array( DN1 ('add' => array($attr), 'remove' => array($attr), 'modify' => array($attr)), DN2 .... ) + *
DN is the DN to change. It is possible to change several DNs (e.g. create a new user and add him + * to some groups via attribute memberUid)
+ *
"add" are attributes which have to be added to the LDAP entry + *
"remove" are attributes which have to be removed from the LDAP entry + *
"modify" are attributes which have to be modified in the LDAP entry + *
"notchanged" are attributes which stay unchanged + *
"info" values with informational value (e.g. to be used later by pre/postModify actions) + *
+ *
This builds the required comands from $this-attributes and $this->orig. + * + * @return array list of modifications + */ + public function save_attributes() { + $attrs = $this->attributes; + $orig = $this->orig; + $attrs['memberOf'] = array(); + $orig['memberOf'] = array(); + return $this->getAccountContainer()->save_module_attributes($attrs, $orig); + } + + /** + * Runs the postmodify actions. + * + * @see baseModule::postModifyActions() + * + * @param boolean $newAccount + * @param array $attributes LDAP attributes of this entry + * @return array array which contains status messages. Each entry is an array containing the status message parameters. + */ + public function postModifyActions($newAccount, $attributes) { + $messages = array(); + // set groups + $groups = $this->findGroups(); + if (!isset($this->orig['memberOf'])) { + $this->orig['memberOf'] = array(); + } + if (!isset($this->attributes['memberOf'])) { + $this->attributes['memberOf'] = array(); + } + $toAdd = array_values(array_diff($this->attributes['memberOf'], $this->orig['memberOf'])); + $toRem = array_values(array_diff($this->orig['memberOf'], $this->attributes['memberOf'])); + $toUpdate = array_values(array_intersect($this->attributes['memberOf'], $this->orig['memberOf'])); + $ldapUser = $_SESSION['ldap']->decrypt_login(); + $ldapUser = $ldapUser[0]; + // add groups + for ($i = 0; $i < sizeof($toAdd); $i++) { + if (in_array($toAdd[$i], $groups)) { + $success = @ldap_mod_add($_SESSION['ldap']->server(), $toAdd[$i], array('member' => array($this->getAccountContainer()->finalDN))); + if (!$success) { + logNewMessage(LOG_ERR, '[' . $ldapUser .'] Unable to add group ' . $this->getAccountContainer()->finalDN . ' to group: ' . $toAdd[$i] . ' (' . ldap_error($_SESSION['ldap']->server()) . ').'); + $messages[] = array('ERROR', sprintf(_('Was unable to add attributes to DN: %s.'), $toAdd[$i]), getDefaultLDAPErrorString($_SESSION['ldap']->server())); + } + else { + logNewMessage(LOG_NOTICE, '[' . $ldapUser .'] Added group ' . $this->getAccountContainer()->finalDN . ' to group: ' . $toAdd[$i]); + } + } + } + // remove groups + for ($i = 0; $i < sizeof($toRem); $i++) { + if (in_array($toRem[$i], $groups)) { + $success = @ldap_mod_del($_SESSION['ldap']->server(), $toRem[$i], array('member' => array($this->getAccountContainer()->dn_orig))); + if (!$success) { + logNewMessage(LOG_ERR, '[' . $ldapUser .'] Unable to delete group ' . $this->getAccountContainer()->finalDN . ' from group: ' . $toRem[$i] . ' (' . ldap_error($_SESSION['ldap']->server()) . ').'); + $messages[] = array('ERROR', sprintf(_('Was unable to remove attributes from DN: %s.'), $toRem[$i]), getDefaultLDAPErrorString($_SESSION['ldap']->server())); + } + else { + logNewMessage(LOG_NOTICE, '[' . $ldapUser .'] Removed group ' . $this->getAccountContainer()->finalDN . ' from group: ' . $toRem[$i]); + } + } + } + return $messages; + } + } diff --git a/lam/lib/modules/windowsUser.inc b/lam/lib/modules/windowsUser.inc index 8374d623..e6dbd1db 100644 --- a/lam/lib/modules/windowsUser.inc +++ b/lam/lib/modules/windowsUser.inc @@ -1030,7 +1030,7 @@ class windowsUser extends baseModule implements passwordService { $groupContainer = new htmlTable(); $groupContainer->alignment = htmlElement::ALIGN_TOP; - $groupContainer->addElement(new htmlSubTitle(_("Groups of names")), true); + $groupContainer->addElement(new htmlSubTitle(_("Groups")), true); $groupContainer->addElement(new htmlOutputText(_("Selected groups"))); $groupContainer->addElement(new htmlOutputText('')); $groupContainer->addElement(new htmlOutputText(_("Available groups"))); @@ -1061,7 +1061,6 @@ class windowsUser extends baseModule implements passwordService { $buttonGroupContainer = new htmlTable(); $buttonGroupContainer->addElement(new htmlButton('addgroups_button', 'back.gif', true), true); $buttonGroupContainer->addElement(new htmlButton('removegroups_button', 'forward.gif', true), true); - $buttonGroupContainer->addElement(new htmlHelpLink('addgroup')); $groupContainer->addElement($buttonGroupContainer); $addGroupSelect = new htmlSelect('addgroups', $availableGroups, null, 15); $addGroupSelect->setMultiSelect(true);