Windows group sync
This commit is contained in:
		
							parent
							
								
									4ced8c519f
								
							
						
					
					
						commit
						bb9f1f0eff
					
				|  | @ -1,7 +1,9 @@ | |||
| December 2018 6.6 | ||||
|   - New import/export in tools menu | ||||
|   - 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: | ||||
|     -> Easy setting of background color in self service profile | ||||
|     -> Cron jobs: added Windows/Qmail/FreeRadius account expiration notification jobs | ||||
|  |  | |||
|  | @ -1186,7 +1186,7 @@ | |||
|     </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 | ||||
|       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>You can also set maximum values for user photos in advanced | ||||
|       options.</para> | ||||
| 
 | ||||
|       <screenshot> | ||||
|         <mediaobject> | ||||
|           <imageobject> | ||||
|             <imagedata fileref="images/mod_windowsUser5.png"/> | ||||
|             <imagedata contentwidth="1172" | ||||
|                        fileref="images/mod_windowsUser5.png"/> | ||||
|           </imageobject> | ||||
|         </mediaobject> | ||||
|       </screenshot> | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 95 KiB | 
|  | @ -408,6 +408,10 @@ class posixAccount extends baseModule implements passwordService { | |||
| 			'noObjectClass' => array( | ||||
| 				"Headline" => _("Do not add object class"), | ||||
| 				"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( | ||||
| 				'uid' => array( | ||||
|  | @ -1447,7 +1451,7 @@ class posixAccount extends baseModule implements passwordService { | |||
| 	 * @param $allGons list of all group of names | ||||
| 	 * @return string cn value | ||||
| 	 */ | ||||
| 	private function getGonName($dn, &$allGons) { | ||||
| 	public function getGonName($dn, &$allGons) { | ||||
| 		if (!empty($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->setTableRowsToHide(array('posixAccount_' . $typeId . '_syncGroupsExclusions')); | ||||
| 					$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; | ||||
|  | @ -3485,7 +3489,7 @@ class posixAccount extends baseModule implements passwordService { | |||
| 	 * | ||||
| 	 * @return array groups array(dn => array('cn' => array('groupName'), 'objectclass' => array('top', 'groupOfNames'))) | ||||
| 	 */ | ||||
| 	private function findGroupOfNames() { | ||||
| 	public function findGroupOfNames() { | ||||
| 		if ($this->gonCache != null) { | ||||
| 			return $this->gonCache; | ||||
| 		} | ||||
|  | @ -3948,6 +3952,24 @@ class posixAccount extends baseModule implements passwordService { | |||
| 		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; | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ?>
 | ||||
|  |  | |||
|  | @ -400,6 +400,10 @@ class windowsUser extends baseModule implements passwordService { | |||
| 				"Headline" => _("Add photo"), 'attr' => 'jpegPhoto', | ||||
| 				"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
 | ||||
| 		$return['upload_columns'] = array( | ||||
|  | @ -1745,7 +1749,7 @@ class windowsUser extends baseModule implements passwordService { | |||
| 	* | ||||
| 	* @return htmlElement meta HTML code | ||||
| 	*/ | ||||
| 	function display_html_group() { | ||||
| 	public function display_html_group() { | ||||
| 		$return = new htmlTable(); | ||||
| 		$groups = $this->findGroups(); | ||||
| 		// sort by DN
 | ||||
|  | @ -1796,6 +1800,51 @@ class windowsUser extends baseModule implements passwordService { | |||
| 		$return->addElement($groupContainer); | ||||
| 		$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->colspan = 10; | ||||
| 		$backGroup->addElement(new htmlSpacer(null, '10px'), true); | ||||
|  | @ -1811,7 +1860,7 @@ class windowsUser extends baseModule implements passwordService { | |||
| 	* | ||||
| 	* @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
 | ||||
| 			// add new group
 | ||||
| 			$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
 | ||||
| 			$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(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 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. | ||||
| 	* | ||||
|  | @ -3413,6 +3571,7 @@ class windowsUser extends baseModule implements passwordService { | |||
| 	 * @see baseModule::get_configOptions() | ||||
| 	 */ | ||||
| 	public function get_configOptions($scopes, $allScopes) { | ||||
| 		$typeManager = new TypeManager($_SESSION['conf_config']); | ||||
| 		// configuration options
 | ||||
| 		$configContainer = new htmlResponsiveRow(); | ||||
| 		$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_hideo', true, _('Organisation'), null, true), 12, 4); | ||||
| 		$configContainer->add(new htmlResponsiveInputCheckbox('windowsUser_hidemanager', true, _('Manager'), null, true), 12, 4); | ||||
| 		for ($i = 0; $i < 1; $i++) { | ||||
| 			$configContainer->add(new htmlOutputText(''), 0, 4); | ||||
| 		$syncTypes = $typeManager->getConfiguredTypesForScopes(array('group', 'gon', 'user')); | ||||
| 		$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->add(new htmlSubTitle(_('Photo')), 12); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue