Windows group sync

This commit is contained in:
Roland Gruber 2018-11-17 20:40:09 +01:00
parent 4ced8c519f
commit bb9f1f0eff
5 changed files with 213 additions and 10 deletions

View File

@ -1,7 +1,9 @@
December 2018 6.6 December 2018 6.6
- New import/export in tools menu - New import/export in tools menu
- YubiKey support - YubiKey support
- Windows users: manage "departmentNumber" (needs to be activated via LAM server profile) - Windows users:
-> Manage "departmentNumber" (needs to be activated via LAM server profile)
-> Sync group memberships from Unix and group of names
- LAM Pro: - LAM Pro:
-> Easy setting of background color in self service profile -> Easy setting of background color in self service profile
-> Cron jobs: added Windows/Qmail/FreeRadius account expiration notification jobs -> Cron jobs: added Windows/Qmail/FreeRadius account expiration notification jobs

View File

@ -1186,7 +1186,7 @@
</section> </section>
<section> <section>
<title>Windows (Samba 4)</title> <title>Windows (Samba 4/Active Directory)</title>
<para>Please activate the account type "Users" in your LAM server <para>Please activate the account type "Users" in your LAM server
profile and then add the user module "Windows (windowsUser)(*)".</para> profile and then add the user module "Windows (windowsUser)(*)".</para>
@ -1218,10 +1218,14 @@
<para>NIS support is deactivated by default. Enable it if needed.</para> <para>NIS support is deactivated by default. Enable it if needed.</para>
<para>You can also set maximum values for user photos in advanced
options.</para>
<screenshot> <screenshot>
<mediaobject> <mediaobject>
<imageobject> <imageobject>
<imagedata fileref="images/mod_windowsUser5.png"/> <imagedata contentwidth="1172"
fileref="images/mod_windowsUser5.png"/>
</imageobject> </imageobject>
</mediaobject> </mediaobject>
</screenshot> </screenshot>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

@ -408,6 +408,10 @@ class posixAccount extends baseModule implements passwordService {
'noObjectClass' => array( 'noObjectClass' => array(
"Headline" => _("Do not add object class"), "Headline" => _("Do not add object class"),
"Text" => _("This will not add the posixAccount object class to the account.") "Text" => _("This will not add the posixAccount object class to the account.")
),
'excludeFromGroupSync' => array (
"Headline" => _('Exclude from group sync'),
"Text" => _('Enter one group per line that should be ignored when syncing groups.')
), ),
'user' => array( 'user' => array(
'uid' => array( 'uid' => array(
@ -1447,7 +1451,7 @@ class posixAccount extends baseModule implements passwordService {
* @param $allGons list of all group of names * @param $allGons list of all group of names
* @return string cn value * @return string cn value
*/ */
private function getGonName($dn, &$allGons) { public function getGonName($dn, &$allGons) {
if (!empty($allGons[$dn]['cn'][0])) { if (!empty($allGons[$dn]['cn'][0])) {
return $allGons[$dn]['cn'][0]; return $allGons[$dn]['cn'][0];
} }
@ -2249,7 +2253,7 @@ class posixAccount extends baseModule implements passwordService {
$syncGroupsCheckbox = new htmlResponsiveInputCheckbox('posixAccount_' . $typeId . '_syncGroups', false, _('Sync groups'), null, false); $syncGroupsCheckbox = new htmlResponsiveInputCheckbox('posixAccount_' . $typeId . '_syncGroups', false, _('Sync groups'), null, false);
$syncGroupsCheckbox->setTableRowsToHide(array('posixAccount_' . $typeId . '_syncGroupsExclusions')); $syncGroupsCheckbox->setTableRowsToHide(array('posixAccount_' . $typeId . '_syncGroupsExclusions'));
$configUserContainer->add($syncGroupsCheckbox, 12); $configUserContainer->add($syncGroupsCheckbox, 12);
$configUserContainer->add(new htmlResponsiveInputTextarea('posixAccount_' . $typeId . '_syncGroupsExclusions', '', 20, 4, _('Exclude from group sync')), 12); $configUserContainer->add(new htmlResponsiveInputTextarea('posixAccount_' . $typeId . '_syncGroupsExclusions', '', 20, 4, _('Exclude from group sync'), 'excludeFromGroupSync'), 12);
} }
} }
$return[] = $configUserContainer; $return[] = $configUserContainer;
@ -3485,7 +3489,7 @@ class posixAccount extends baseModule implements passwordService {
* *
* @return array groups array(dn => array('cn' => array('groupName'), 'objectclass' => array('top', 'groupOfNames'))) * @return array groups array(dn => array('cn' => array('groupName'), 'objectclass' => array('top', 'groupOfNames')))
*/ */
private function findGroupOfNames() { public function findGroupOfNames() {
if ($this->gonCache != null) { if ($this->gonCache != null) {
return $this->gonCache; return $this->gonCache;
} }
@ -3948,6 +3952,24 @@ class posixAccount extends baseModule implements passwordService {
return $replacements; return $replacements;
} }
/**
* Returns the current group names.
*
* @return string[] group names
*/
public function getGroups() {
return $this->groups;
}
/**
* Returns the list of group of names where this user is member.
*
* @return string[] list of DNs
*/
public function getGroupOfNames() {
return $this->gonList;
}
} }
?> ?>

View File

@ -400,6 +400,10 @@ class windowsUser extends baseModule implements passwordService {
"Headline" => _("Add photo"), 'attr' => 'jpegPhoto', "Headline" => _("Add photo"), 'attr' => 'jpegPhoto',
"Text" => _("Please select an image file to upload. It must be in JPG format (.jpg/.jpeg).") "Text" => _("Please select an image file to upload. It must be in JPG format (.jpg/.jpeg).")
), ),
'excludeFromGroupSync' => array (
"Headline" => _('Exclude from group sync'),
"Text" => _('Enter one group per line that should be ignored when syncing groups.')
),
); );
// upload fields // upload fields
$return['upload_columns'] = array( $return['upload_columns'] = array(
@ -1745,7 +1749,7 @@ class windowsUser extends baseModule implements passwordService {
* *
* @return htmlElement meta HTML code * @return htmlElement meta HTML code
*/ */
function display_html_group() { public function display_html_group() {
$return = new htmlTable(); $return = new htmlTable();
$groups = $this->findGroups(); $groups = $this->findGroups();
// sort by DN // sort by DN
@ -1796,6 +1800,51 @@ class windowsUser extends baseModule implements passwordService {
$return->addElement($groupContainer); $return->addElement($groupContainer);
$return->addNewLine(); $return->addNewLine();
// sync options
$typeManager = new TypeManager();
$syncTypes = $typeManager->getConfiguredTypesForScopes(array('group', 'gon', 'user'));
$syncActive = false;
$syncUnixActive = false;
$syncGonActive = false;
$possibleGonSyncModules = array('groupOfNames', 'groupOfMembers', 'groupOfUniqueNames');
foreach ($syncTypes as $syncType) {
$modules = $syncType->getModules();
foreach ($possibleGonSyncModules as $possibleModule) {
if (in_array($possibleModule, $modules)) {
$syncActive = true;
$syncGonActive = true;
break;
}
}
}
if (in_array('posixAccount', $this->getAccountContainer()->get_type()->getModules())) {
$syncActive = true;
$syncUnixActive = true;
}
$syncActive = $syncActive && !$this->isBooleanConfigOptionSet('windowsUser_syncGroups');
if ($syncActive) {
$return->addElement(new htmlSubTitle(_('Sync groups')), true);
$syncOptionTable = new htmlTable();
$syncOptionTable->addElement(new htmlTableExtendedInputCheckbox('syncDeleteGroups', true, _('Delete non-matching entries')), true);
$return->addElement($syncOptionTable, true);
$return->addVerticalSpace('1rem');
$syncButtons = new htmlGroup();
if ($syncUnixActive) {
$u2wButton = new htmlAccountPageButton(get_class($this), 'group', 'syncU2W', _('Sync Unix to Windows'));
$u2wButton->setIconClass('unixButton');
$syncButtons->addElement($u2wButton);
$syncButtons->addElement(new htmlSpacer('2rem', null));
}
if ($syncGonActive) {
$g2wButton = new htmlAccountPageButton(get_class($this), 'group', 'syncG2W', _('Sync group of names to Windows'));
$g2wButton->setIconClass('groupButton');
$syncButtons->addElement($g2wButton);
$syncButtons->addElement(new htmlSpacer('2rem', null));
}
$return->addElement($syncButtons, true);
$return->addElement(new htmlSpacer(null, '2rem'), true);
}
$backGroup = new htmlGroup(); $backGroup = new htmlGroup();
$backGroup->colspan = 10; $backGroup->colspan = 10;
$backGroup->addElement(new htmlSpacer(null, '10px'), true); $backGroup->addElement(new htmlSpacer(null, '10px'), true);
@ -1811,7 +1860,7 @@ class windowsUser extends baseModule implements passwordService {
* *
* @return array list of info/error messages * @return array list of info/error messages
*/ */
function process_group() { public function process_group() {
if (isset($_POST['addgroups']) && isset($_POST['addgroups_button'])) { // Add groups to list if (isset($_POST['addgroups']) && isset($_POST['addgroups_button'])) { // Add groups to list
// add new group // add new group
$this->groupList = @array_merge($this->groupList, $_POST['addgroups']); $this->groupList = @array_merge($this->groupList, $_POST['addgroups']);
@ -1819,9 +1868,118 @@ class windowsUser extends baseModule implements passwordService {
elseif (isset($_POST['removegroups']) && isset($_POST['removegroups_button'])) { // remove groups from list elseif (isset($_POST['removegroups']) && isset($_POST['removegroups_button'])) { // remove groups from list
$this->groupList = array_delete($_POST['removegroups'], $this->groupList); $this->groupList = array_delete($_POST['removegroups'], $this->groupList);
} }
// sync Unix to Windows
if (isset($_POST['form_subpage_windowsUser_group_syncU2W'])) {
$this->manualSyncUnixToWindows();
}
// sync group of names to Windows
if (isset($_POST['form_subpage_windowsUser_group_syncG2W'])) {
$this->manualSyncGonToWindows();
}
return array(); return array();
} }
/**
* Syncs the Unix groups to Windows.
*/
private function manualSyncUnixToWindows() {
$windowsGroups = $this->getGroupList();
$unixGroups = $this->getAccountContainer()->getAccountModule('posixAccount')->getGroups();
$allWindowsGroups = searchLDAPByAttribute('cn', '*', 'group', array('cn'), array('group'));
$dnToCn = array();
foreach ($allWindowsGroups as $windowsGroup) {
$dnToCn[$windowsGroup['dn']] = $windowsGroup['cn'][0];
}
$cnToDn = array_flip($dnToCn);
$currentGroupNames = array();
foreach ($windowsGroups as $windowsGroup) {
$currentGroupNames[] = $dnToCn[$windowsGroup];
}
$deleteNonMatching = isset($_POST['syncDeleteGroups']) && ($_POST['syncDeleteGroups'] == 'on');
$namesToIgnore = array();
if (!empty($this->moduleSettings['windowsUser_syncGroupsExclusions'])) {
$namesToIgnore = $this->moduleSettings['windowsUser_syncGroupsExclusions'];
array_map('trim', $namesToIgnore);
}
foreach ($unixGroups as $unixGroup) {
if (in_array($unixGroup, $namesToIgnore)) {
continue;
}
if (!in_array($unixGroup, $currentGroupNames) && isset($cnToDn[$unixGroup])) {
$windowsGroups[] = $cnToDn[$unixGroup];
}
}
if ($deleteNonMatching) {
foreach ($currentGroupNames as $currentGroupName) {
if (in_array($currentGroupName, $namesToIgnore)) {
continue;
}
if (!in_array($currentGroupName, $unixGroups)) {
foreach ($windowsGroups as $windowsGroup) {
if ($dnToCn[$windowsGroup] == $currentGroupName) {
$windowsGroups = array_delete(array($windowsGroup), $windowsGroups);
break;
}
}
}
}
}
$this->groupList = $windowsGroups;
}
/**
* Syncs the group of names to Windows.
*/
private function manualSyncGonToWindows() {
$windowsGroups = $this->getGroupList();
$gonGroupDns = $this->getAccountContainer()->getAccountModule('posixAccount')->getGroupOfNames();
$allGons = $this->getAccountContainer()->getAccountModule('posixAccount')->findGroupOfNames();
$gonGroups = array();
foreach ($gonGroupDns as $gonGroupDn) {
$gonGroups[] = $this->getAccountContainer()->getAccountModule('posixAccount')->getGonName($gonGroupDn, $allGons);
}
$allWindowsGroups = searchLDAPByAttribute('cn', '*', 'group', array('cn'), array('group'));
$dnToCn = array();
foreach ($allWindowsGroups as $windowsGroup) {
$dnToCn[$windowsGroup['dn']] = $windowsGroup['cn'][0];
}
$cnToDn = array_flip($dnToCn);
$currentGroupNames = array();
foreach ($windowsGroups as $windowsGroup) {
$currentGroupNames[] = $dnToCn[$windowsGroup];
}
$deleteNonMatching = isset($_POST['syncDeleteGroups']) && ($_POST['syncDeleteGroups'] == 'on');
$namesToIgnore = array();
if (!empty($this->moduleSettings['windowsUser_syncGroupsExclusions'])) {
$namesToIgnore = $this->moduleSettings['windowsUser_syncGroupsExclusions'];
array_map('trim', $namesToIgnore);
}
foreach ($gonGroups as $gonGroup) {
if (in_array($gonGroup, $namesToIgnore)) {
continue;
}
if (!in_array($gonGroup, $currentGroupNames) && isset($cnToDn[$gonGroup])) {
$windowsGroups[] = $cnToDn[$gonGroup];
}
}
if ($deleteNonMatching) {
foreach ($currentGroupNames as $currentGroupName) {
if (in_array($currentGroupName, $namesToIgnore)) {
continue;
}
if (!in_array($currentGroupName, $gonGroups)) {
foreach ($windowsGroups as $windowsGroup) {
if ($dnToCn[$windowsGroup] == $currentGroupName) {
$windowsGroups = array_delete(array($windowsGroup), $windowsGroups);
break;
}
}
}
}
}
$this->groupList = $windowsGroups;
}
/** /**
* Displays the photo upload page. * Displays the photo upload page.
* *
@ -3413,6 +3571,7 @@ class windowsUser extends baseModule implements passwordService {
* @see baseModule::get_configOptions() * @see baseModule::get_configOptions()
*/ */
public function get_configOptions($scopes, $allScopes) { public function get_configOptions($scopes, $allScopes) {
$typeManager = new TypeManager($_SESSION['conf_config']);
// configuration options // configuration options
$configContainer = new htmlResponsiveRow(); $configContainer = new htmlResponsiveRow();
$configContainer->add(new htmlResponsiveInputTextarea('windowsUser_domains', '', 30, 3, _('Domains'), 'domains'), 12); $configContainer->add(new htmlResponsiveInputTextarea('windowsUser_domains', '', 30, 3, _('Domains'), 'domains'), 12);
@ -3443,8 +3602,24 @@ class windowsUser extends baseModule implements passwordService {
$configContainer->add(new htmlResponsiveInputCheckbox('windowsUser_hideou', true, _('Organisational unit'), null, true), 12, 4); $configContainer->add(new htmlResponsiveInputCheckbox('windowsUser_hideou', true, _('Organisational unit'), null, true), 12, 4);
$configContainer->add(new htmlResponsiveInputCheckbox('windowsUser_hideo', true, _('Organisation'), null, true), 12, 4); $configContainer->add(new htmlResponsiveInputCheckbox('windowsUser_hideo', true, _('Organisation'), null, true), 12, 4);
$configContainer->add(new htmlResponsiveInputCheckbox('windowsUser_hidemanager', true, _('Manager'), null, true), 12, 4); $configContainer->add(new htmlResponsiveInputCheckbox('windowsUser_hidemanager', true, _('Manager'), null, true), 12, 4);
for ($i = 0; $i < 1; $i++) { $syncTypes = $typeManager->getConfiguredTypesForScopes(array('group', 'gon', 'user'));
$configContainer->add(new htmlOutputText(''), 0, 4); $syncActive = false;
$possibleSyncModules = array('groupOfNames', 'groupOfMembers', 'groupOfUniqueNames', 'posixAccount');
foreach ($syncTypes as $syncType) {
$modules = $syncType->getModules();
foreach ($possibleSyncModules as $possibleModule) {
if (in_array($possibleModule, $modules)) {
$syncActive = true;
break;
}
}
}
if ($syncActive) {
$syncGroupsCheckbox = new htmlResponsiveInputCheckbox('windowsUser_syncGroups', false, _('Sync groups'), null, true);
$syncGroupsCheckbox->setTableRowsToHide(array('windowsUser_syncGroupsExclusions'));
$configContainer->add($syncGroupsCheckbox, 12, 4);
$configContainer->addVerticalSpacer('2rem');
$configContainer->add(new htmlResponsiveInputTextarea('windowsUser_syncGroupsExclusions', '', 20, 4, _('Exclude from group sync'), 'excludeFromGroupSync'), 12);
} }
$advancedOptions = new htmlResponsiveRow(); $advancedOptions = new htmlResponsiveRow();
$advancedOptions->add(new htmlSubTitle(_('Photo')), 12); $advancedOptions->add(new htmlSubTitle(_('Photo')), 12);