1<?php 2use \LAM\TYPES\TypeManager; 3use function LAM\TYPES\getScopeFromTypeId; 4use LAM\TYPES\ConfiguredType; 5/* 6 7 This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) 8 Copyright (C) 2003 - 2006 Tilo Lutz 9 Copyright (C) 2005 - 2020 Roland Gruber 10 11 This program is free software; you can redistribute it and/or modify 12 it under the terms of the GNU General Public License as published by 13 the Free Software Foundation; either version 2 of the License, or 14 (at your option) any later version. 15 16 This program is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License for more details. 20 21 You should have received a copy of the GNU General Public License 22 along with this program; if not, write to the Free Software 23 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24*/ 25 26/** 27* Manages Unix accounts for users and hosts. 28* 29* @package modules 30* 31* @author Tilo Lutz 32* @author Roland Gruber 33* @author Michael Duergner 34* @author Thomas Manninger 35*/ 36 37/** 38* Manages the object class "posixAccount" for users and hosts. 39* 40* @package modules 41*/ 42class posixAccount extends baseModule implements passwordService { 43 44 // Variables 45 /** delimiter for lamdaemon commands */ 46 private static $SPLIT_DELIMITER = "###x##y##x###"; 47 48 /* These two variables keep an array of groups the user is also member of. */ 49 /** current group list */ 50 private $groups; 51 /** original group list */ 52 private $groups_orig; 53 54 /* list of group of names that the user is member of */ 55 /** current group of names list */ 56 private $gonList = array(); 57 /** original group of names list */ 58 private $gonList_orig = array(); 59 /** lamdaemon servers */ 60 private $lamdaemonServers = array(); 61 /** cache for group objects */ 62 private $groupCache = null; 63 /** cache for group of names objects */ 64 private $gonCache = null; 65 /** clear text password */ 66 private $clearTextPassword; 67 /** caches the list of known UIDs */ 68 private $cachedUIDList = null; 69 /** caches the list of known user names */ 70 private $cachedUserNameList = null; 71 72 /** replacements for common umlauts */ 73 private $umlautReplacements = array( 74 'ä' => 'ae', 'Ä' => 'Ae', 'ö' => 'oe', 'Ö' => 'Oe', 'ü' => 'ue', 'Ü' => 'Ue', 75 'ß' => 'ss', 'é' => 'e', 'è' => 'e', 'ô' => 'o', 'ç' => 'c' 76 ); 77 78 /** 79 * This function fills the error message array with messages. 80 **/ 81 function load_Messages() { 82 // error messages for input checks 83 $this->messages['minUID'][0] = array('ERROR', _('Users') . ': ' . _('Minimum UID number'), _("Minimum UID number is invalid!")); 84 $this->messages['maxUID'][0] = array('ERROR', _('Users') . ': ' . _('Maximum UID number'), _("Maximum UID number is invalid!")); 85 $this->messages['minMachine'][0] = array('ERROR', _('Hosts') . ': ' . _('Minimum UID number'), _("Minimum UID number is invalid!")); 86 $this->messages['maxMachine'][0] = array('ERROR', _('Hosts') . ': ' . _('Maximum UID number'), _("Maximum UID number is invalid!")); 87 $this->messages['cmp_UID'][0] = array('ERROR', _('Users') . ': ' . _('Maximum UID number'), _("Maximum UID number must be greater than minimum UID number!")); 88 $this->messages['cmp_Machine'][0] = array('ERROR', _('Hosts') . ': ' . _('Maximum UID number'), _("Maximum UID number must be greater than minimum UID number!")); 89 $this->messages['cmp_both'][0] = array('ERROR', _('UID ranges for Unix accounts'), _("The UID ranges for users and hosts overlap! This is a problem because LAM uses the highest UID in use + 1 for new accounts. Please set the minimum UID to equal values or use independent ranges.")); 90 $this->messages['homeDirectory'][0] = array('ERROR', _('Home directory'), _('Homedirectory contains invalid characters.')); 91 $this->messages['homeDirectory'][1] = array('INFO', _('Home directory'), _('Replaced $user or $group in homedir.')); 92 $this->messages['homeDirectory'][2] = array('ERROR', _('Account %s:') . ' posixAccount_homedir', _('Homedirectory contains invalid characters.')); 93 $this->messages['homeDirectory'][3] = array('INFO', _('Home directory'), _('Home directory changed. To keep home directory you have to run the following command as root: \'mv %s %s\'')); 94 $this->messages['uidNumber'][1] = array('ERROR', _('ID-Number'), _('No free ID-Number!')); 95 $this->messages['uidNumber'][2] = array('WARN', _('ID-Number'), _('It is possible that this ID-number is reused. This can cause several problems because files with old permissions might still exist. To avoid this warning set maxUID to a higher value.')); 96 $this->messages['uidNumber'][3] = array('ERROR', _('ID-Number'), _('ID is already in use')); 97 $this->messages['uidNumber'][4] = array('ERROR', _('Account %s:') . ' posixAccount_uid', _('UID must be a number. It has to be inside the UID range which is defined in your configuration profile.')); 98 $this->messages['uidNumber'][5] = array('INFO', _('UID number'), _('UID number has changed. To keep file ownership you have to run the following command as root: \'find / -uid %s -exec chown %s {} \;\'')); 99 $this->messages['userPassword'][0] = array('ERROR', _('Password'), _('Please enter the same password in both password fields.')); 100 $this->messages['userPassword'][1] = array('ERROR', _('Password'), _('Password contains invalid characters. Valid characters are:') . ' a-z, A-Z, 0-9 and #*,.;:_-+!%&/|?{[()]}=@$ §°!'); 101 $this->messages['userPassword'][4] = array('ERROR', _('Account %s:') . ' posixAccount_password', _('Password contains invalid characters. Valid characters are:') . ' a-z, A-Z, 0-9 and #*,.;:_-+!%&/|?{[()]}=@$ §°!'); 102 $this->messages['uid'][0] = array('INFO', _('UID'), _('UID has changed. Do you want to change home directory?')); 103 $this->messages['uid'][1] = array('WARN', _('User name'), _('You are using capital letters. This can cause problems because Windows is not case-sensitive.')); 104 $this->messages['uid'][2] = array('ERROR', _('User name'), _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); 105 $this->messages['uid'][3] = array('WARN', _('Host name'), _('You are using capital letters. This can cause problems because Windows is not case-sensitive.')); 106 $this->messages['uid'][4] = array('ERROR', _('Host name'), _('Host name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); 107 $this->messages['uid'][5] = array('WARN', _('User name'), _('User name in use (%s). Selected next free user name.')); 108 $this->messages['uid'][6] = array('WARN', _('Host name'), _('Host name in use (%s). Selected next free host name.')); 109 $this->messages['uid'][7] = array('ERROR', _('Account %s:') . ' posixAccount_userName', _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); 110 $this->messages['uid'][8] = array('ERROR', _('Account %s:') . ' posixAccount_hostName', _('Host name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); 111 $this->messages['uid'][9] = array('WARN', _('Account %s:') . ' posixAccount_userName', _('User name already exists!') . ' ' . _('You might want to use %s instead of %s.') . ' %s'); 112 $this->messages['uid'][10] = array('WARN', _('Account %s:') . ' posixAccount_hostName', _('Host name already exists!') . ' ' . _('You might want to use %s instead of %s.') . ' %s'); 113 $this->messages['gidNumber'][0] = array('ERROR', _('Account %s:') . ' posixAccount_group', _('LAM was unable to find a group with this name!')); 114 $this->messages['gidNumber'][1] = array('ERROR', _('Account %s:') . ' posixAccount_group', _('This GID number is invalid! Please provide either a number or a group name.')); 115 $this->messages['gidNumber'][2] = array('INFO', _('GID number'), _('GID number has changed. To keep file ownership you have to run the following command as root: \'find / -gid %s -uid %s -exec chgrp %s {} \;\'')); 116 $this->messages['gecos'][0] = array('ERROR', _('Account %s:') . ' posixAccount_gecos', _('This gecos value is invalid!')); 117 $this->messages['shell'][0] = array('ERROR', _('Account %s:') . ' posixAccount_shell', _('This login shell is invalid!')); 118 $this->messages['passwordDisabled'][0] = array('ERROR', _('Account %s:') . ' posixAccount_passwordDisabled', _('This value can only be "true" or "false".')); 119 $this->messages['cn'][0] = array('ERROR', _('Common name'), _('Please enter a valid common name!')); 120 $this->messages['cn'][1] = array('ERROR', _('Account %s:') . ' posixAccount_cn', _('Please enter a valid common name!')); 121 $this->messages['sambaIDPoolDN'][0] = array('ERROR', _('Samba ID pool DN'), _('This is not a valid DN!')); 122 $this->messages['windowsIDPoolDN'][0] = array('ERROR', _('Windows domain info DN'), _('This is not a valid DN!')); 123 } 124 125 /** 126 * Returns true if this module can manage accounts of the current type, otherwise false. 127 * 128 * @return boolean true if module fits 129 */ 130 public function can_manage() { 131 return in_array($this->get_scope(), array('user', 'host')); 132 } 133 134 /** 135 * Returns meta data that is interpreted by parent class 136 * 137 * @return array array with meta data 138 * 139 * @see baseModule::get_metaData() 140 */ 141 function get_metaData() { 142 $return = array(); 143 // icon 144 $return['icon'] = 'tux.png'; 145 // user specific data 146 if ($this->get_scope() == "user") { 147 // LDAP filter 148 $return["ldap_filter"] = array('or' => "(objectClass=posixAccount)", 'and' => "(!(uid=*$))"); 149 // module dependencies 150 $return['dependencies'] = array('depends' => array(), 'conflicts' => array()); 151 } 152 elseif ($this->get_scope() == "host") { 153 // LDAP filter 154 $return["ldap_filter"] = array('or' => "(objectClass=posixAccount)"); 155 // module dependencies 156 $return['dependencies'] = array('depends' => array(), 'conflicts' => array()); 157 } 158 // alias name 159 $return["alias"] = _("Unix"); 160 // RDN attributes 161 $return["RDN"] = array("uid" => "high", "cn" => "low"); 162 // managed object classes 163 $return['objectClasses'] = array('posixAccount'); 164 // LDAP aliases 165 $return['LDAPaliases'] = array('commonName' => 'cn', 'userid' => 'uid'); 166 // managed attributes 167 $return['attributes'] = array('uid', 'uidNumber', 'gidNumber', 168 'loginShell', 'gecos', 'INFO.userPasswordClearText'); 169 if ($this->get_scope() == "user") { 170 // self service search attributes 171 $return['selfServiceSearchAttributes'] = array('uid'); 172 // self service field settings 173 $return['selfServiceFieldSettings'] = array( 174 'password' => _('Password'), 175 'cn' => _('Common name'), 176 'loginShell' => _('Login shell'), 177 'syncWindowsPassword' => _('Sync Unix password with Windows password'), 178 'unixgroups' => _('Groups (read-only)') 179 ); 180 // possible self service read-only fields 181 $return['selfServiceReadOnlyFields'] = array('cn', 'loginShell'); 182 // self service configuration settings 183 $selfServiceContainer = new htmlResponsiveRow(); 184 $selfServiceContainer->add(new htmlResponsiveSelect('posixAccount_pwdHash', getSupportedHashTypes(), 185 array('SSHA'), _("Password hash type"), array('pwdHash', get_class($this))), 12); 186 $selfServiceContainer->add(new htmlResponsiveInputTextarea('posixAccount_shells', implode("\r\n", $this->getShells()), 30, 4, _('Login shells'), array('loginShells', get_class($this))), 12); 187 $selfServiceContainer->add(new htmlResponsiveInputField(_('Group DN'), 'posixAccount_groupDn', '', array('groupDn', get_class($this))), 12); 188 $selfServiceContainer->add(new htmlResponsiveInputCheckbox('posixAccount_useOldPwd', false, _('Password change with old password'), array('useOldPwd', get_class($this))), 12); 189 $return['selfServiceSettings'] = $selfServiceContainer; 190 } 191 // profile checks 192 $return['profile_checks']['posixAccount_homeDirectory'] = array('type' => 'ext_preg', 'regex' => 'homeDirectory', 193 'error_message' => $this->messages['homeDirectory'][0]); 194 // profile mappings 195 $return['profile_mappings'] = array( 196 'posixAccount_loginShell' => 'loginShell' 197 ); 198 // upload 199 $return['upload_preDepends'] = array('inetOrgPerson'); 200 // user specific upload options 201 if (($this->get_scope() == 'user') && isLoggedIn()) { 202 $lamdaemonServers = $_SESSION['config']->getConfiguredScriptServers(); 203 $lamdaemonOptions = array(); 204 foreach ($lamdaemonServers as $lamdaemonServer) { 205 $lamdaemonOptions[] = $lamdaemonServer->getServer(); 206 } 207 $return['upload_columns'] = array( 208 array( 209 'name' => 'posixAccount_userName', 210 'description' => _('User name'), 211 'help' => 'uid', 212 'example' => _('smiller'), 213 'required' => true, 214 'unique' => true 215 ), 216 array( 217 'name' => 'posixAccount_uid', 218 'description' => _('UID number'), 219 'help' => 'uidNumber', 220 'example' => '1234' 221 ), 222 array( 223 'name' => 'posixAccount_group', 224 'description' => _('Primary group'), 225 'help' => 'group_upload', 226 'example' => _('users'), 227 'required' => true 228 ), 229 array( 230 'name' => 'posixAccount_additionalGroups', 231 'description' => _('Additional groups'), 232 'help' => 'addgroup_upload', 233 'example' => _('group01,group02') 234 ), 235 array( 236 'name' => 'posixAccount_homedir', 237 'description' => _('Home directory'), 238 'help' => 'homeDirectory_upload', 239 'example' => _('/home/smiller'), 240 'default' => '/home/{posixAccount_userName}' 241 ), 242 array( 243 'name' => 'posixAccount_createHomeDir', 244 'description' => _('Create home directory'), 245 'help' => 'createhomedir', 246 'example' => 'localhost', 247 'values' => implode(', ', $lamdaemonOptions) 248 ), 249 array( 250 'name' => 'posixAccount_shell', 251 'description' => _('Login shell'), 252 'help' => 'loginShell', 253 'example' => '/bin/bash', 254 'values' => implode(", ", $this->getShells()), 255 'default' => '/bin/bash' 256 ), 257 ); 258 if (self::areGroupOfNamesActive()) { 259 $return['upload_columns'][] = array( 260 'name' => 'posixAccount_gon', 261 'description' => _('Groups of names'), 262 'help' => 'addgroup_upload', 263 'example' => _('group01,group02') 264 ); 265 } 266 } 267 // host specific upload options 268 elseif ($this->get_scope() == 'host') { 269 $return['upload_columns'] = array( 270 array( 271 'name' => 'posixAccount_hostName', 272 'description' => _('Host name'), 273 'help' => 'uid', 274 'example' => _('pc01$'), 275 'required' => true, 276 'unique' => true 277 ), 278 array( 279 'name' => 'posixAccount_uid', 280 'description' => _('UID number'), 281 'help' => 'uidNumber', 282 'example' => '1234' 283 ), 284 array( 285 'name' => 'posixAccount_group', 286 'description' => _('Primary group'), 287 'help' => 'group_upload', 288 'example' => _('machines'), 289 'required' => true 290 ), 291 ); 292 } 293 // available PDF fields 294 if ($this->get_scope() == 'host') { 295 $return['PDF_fields'] = array('uid' => _('Host name')); 296 } 297 else { 298 $return['PDF_fields'] = array('uid' => _('User name')); 299 } 300 $return['PDF_fields'] = array_merge($return['PDF_fields'], array( 301 'uidNumber' => _('UID number'), 302 'gidNumber' => _('GID number'), 303 'primaryGroup' => _('Primary group'), 304 'additionalGroups' => _('Additional groups'), 305 'homeDirectory' => _('Home directory'), 306 'loginShell' => _('Login shell'), 307 'userPassword' => _('Password') 308 )); 309 if (self::areGroupOfNamesActive()) { 310 $return['PDF_fields']['gon'] = _('Groups of names'); 311 } 312 // help Entries 313 $return['help'] = array( 314 'autoAdd' => array( 315 "Headline" => _("Automatically add this extension"), 316 "Text" => _("This will enable the extension automatically if this profile is loaded.") 317 ), 318 'userNameSuggestion' => array( 319 "Headline" => _("User name suggestion"), 320 "Text" => _("LAM will suggest a user name based on e.g. first and last name. Here you can specify the suggestion. %sn% will be replaced by the last name. @givenname@ will be replaced by the first character of first name. Only attributes of tab Personal may be used.") 321 . '<br>' . _('Common examples are "@givenname@%sn%" or "%givenname%.%sn%".') 322 ), 323 'hiddenOptions' => array( 324 "Headline" => _("Hidden options"), 325 "Text" => _("The selected options will not be managed inside LAM. You can use this to reduce the number of displayed input fields.") 326 ), 327 'primaryGroupAsSecondary' => array( 328 'Headline' => _('Set primary group as memberUid'), 329 'Text' => _('Usually, users are not added to groups as memberUid if they have this group as primary group. If your application ignores primary groups then you can select this option to override this behaviour.') 330 ), 331 'minMaxUser' => array( 332 'Headline' => _('UID number'), 333 'Text' => _('These are the minimum and maximum numbers to use for user IDs when creating new user accounts. The range should be different from that of machines. New user accounts will always get the highest number in use plus one.') 334 ), 335 'minMaxHost' => array( 336 'Headline' => _('UID number'), 337 'Text' => _('These are the minimum and maximum numbers to use for machine IDs when creating new accounts for hosts. The range should be different from that of users. New host accounts will always get the highest number in use plus one.') 338 ), 339 'pwdHash' => array( 340 "Headline" => _("Password hash type"), 341 "Text" => _("LAM supports a large number of possibilities to generate the hash value of passwords. CRYPT-SHA512 and SSHA are the most common. We do not recommend to use plain text passwords unless passwords are hashed server-side.") 342 . ' ' . _('K5KEY is only needed if you use Kerberos with smbk5pwd.') 343 ), 344 'uidNumber' => array( 345 "Headline" => _("UID number"), 'attr' => 'uidNumber', 346 "Text" => _("If empty UID number will be generated automatically.") 347 ), 348 'group_upload' => array( 349 "Headline" => _("Primary group"), 350 "Text" => _("The primary group for this account. You can insert a GID number or a group name.") 351 ), 352 'addgroup_upload' => array( 353 "Headline" => _("Additional groups"), 354 "Text" => _("Here you can enter a list of additional group memberships. The group names are separated by commas.") 355 ), 356 'homeDirectory_upload' => array( 357 "Headline" => _("Home directory"), 358 "Text" => _("Please enter the path to the user's home directory.") 359 ), 360 'homeDirectory' => array( 361 "Headline" => _("Home directory"), 362 "Text" => _("Please enter the path to the user's home directory.") 363 ), 364 'deletehomedir' => array( 365 "Headline" => _("Home directory"), 366 "Text" => _("Activating this checkbox will remove the user's home directory.") 367 ), 368 'createhomedir' => array( 369 "Headline" => _("Home directory"), 370 "Text" => _("This will create the user's home directory on the specified server.") 371 ), 372 'deleteSudoers' => array( 373 "Headline" => _("Delete sudo rights"), 374 "Text" => _("Deletes the user from all existing sudo rights.") 375 ), 376 'uidCheckSuffix' => array ( 377 "Headline" => _("Suffix for UID/user name check"), 378 "Text" => _("LAM checks if the entered user name and UID 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 user names or UIDs.") 379 ), 380 'loginShells' => array( 381 "Headline" => _("Login shells"), 382 "Text" => _("This is the list of valid login shells.") 383 ), 384 'uidGenerator' => array ( 385 "Headline" => _("UID generator"), 386 "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\" or \"msSFU30DomainInfo\".") 387 . ' ' . _('Magic number will set a fixed value that must match your server configuration.') 388 ), 389 'sambaIDPoolDN' => array ( 390 "Headline" => _("Samba ID pool DN"), 391 "Text" => _("Please enter the DN of the LDAP entry with object class \"sambaUnixIdPool\".") 392 ), 393 'windowsIDPoolDN' => array ( 394 "Headline" => _("Windows domain info DN"), 395 "Text" => _("Please enter the DN of the LDAP entry with object class \"msSFU30DomainInfo\".") 396 ), 397 'magicNumber' => array( 398 "Headline" => _("Magic number"), 399 "Text" => _("Please enter the magic number you configured on server side.") 400 ), 401 'noObjectClass' => array( 402 "Headline" => _("Do not add object class"), 403 "Text" => _("This will not add the posixAccount object class to the account.") 404 ), 405 'excludeFromGroupSync' => array ( 406 "Headline" => _('Exclude from group sync'), 407 "Text" => _('Enter one group per line that should be ignored when syncing groups.') 408 ), 409 'groupDn' => array ( 410 "Headline" => _('Group DN'), 411 "Text" => _('Enter the base DN of your groups here. This is only required if you want to display memberships on the self service page.') 412 ), 413 'user' => array( 414 'uid' => array( 415 "Headline" => _("User name"), 'attr' => 'uid', 416 "Text" => _("User name of the user who should be created. Valid characters are: a-z,A-Z,0-9, @.-_. If user name is already used user name will be expanded with a number. The next free number will be used.") 417 ), 418 'gecos' => array( 419 "Headline" => _("Gecos"), 420 "Text" => _("User description. If left empty first and last name will be used.") 421 ), 422 'gidNumber' => array( 423 "Headline" => _("Primary group"), 'attr' => 'gidNumber', 424 "Text" => _("The primary group the user should be member of.") 425 ), 426 'userPassword' => array( 427 "Headline" => _("Password"), 428 "Text" => _("Please enter the password which you want to set for this account.") 429 ), 430 'userPassword_lock' => array( 431 "Headline" => _("Account deactivated"), 432 "Text" => _("If checked account will be deactivated by putting a \"!\" before the encrypted password.") 433 ), 434 'loginShell' => array( 435 "Headline" => _("Login shell"), 436 "Text" => _("To disable login use /bin/false.") 437 ), 438 'addgroup' => array( 439 "Headline" => _("Additional groups"), 440 "Text" => _("Hold the CTRL-key to (de)select multiple groups."). ' '. _("Can be left empty.") 441 ), 442 'cn' => array ( 443 "Headline" => _("Common name"), 'attr' => 'cn', 444 "Text" => _("This is the natural name of the user. If empty, the first and last name or user name is used.") 445 ), 446 'useOldPwd' => array ( 447 "Headline" => _('Password change with old password'), 448 "Text" => _('Sends the old password together with the new password when the user sets a new password.') 449 ) 450 ), 451 'host' => array( 452 'uid' => array( 453 "Headline" => _("Host name"), 'attr' => 'uid', 454 "Text" => _("Host name of the host which should be created. Valid characters are: a-z,A-Z,0-9, .-_$. Host names are always ending with $. If last character is not $ it will be added. If host name is already used host name will be expanded with a number. The next free number will be used.") 455 ), 456 'gecos' => array( 457 "Headline" => _("Gecos"), 458 "Text" => _("Host description. If left empty host name will be used.") 459 ), 460 'gidNumber' => array( 461 "Headline" => _("Primary group"), 'attr' => 'gidNumber', 462 "Text" => _("The primary group the host should be member of.") 463 ), 464 'description' => array ( 465 "Headline" => _("Description"), 466 "Text" => _("Host description. If left empty host name will be used.") 467 ), 468 'cn' => array ( 469 "Headline" => _("Common name"), 'attr' => 'cn', 470 "Text" => _("This is the natural name of the host. If empty, the host name will be used.") 471 ) 472 ) 473 ); 474 475 return $return; 476 } 477 478 /** 479 * Initializes the module after it became part of an accountContainer 480 * 481 * @param string $base the name of the accountContainer object ($_SESSION[$base]) 482 */ 483 function init($base) { 484 // make optional if needed 485 $modules = $_SESSION[$base]->get_type()->getModules(); 486 $this->autoAddObjectClasses = !$this->isOptional($modules) && !$this->skipObjectClass(); 487 // call parent init 488 parent::init($base); 489 $this->groups = array(); 490 $this->groups_orig = array(); 491 // list of all group names 492 $groups = $this->findGroups($modules); 493 if (count($groups)==0) { 494 StatusMessage("ERROR", _('No Unix groups found in LDAP! Please create one first.'), ''); 495 return; 496 } 497 $this->gonList = array(); 498 $this->gonList_orig = array(); 499 } 500 501 /** 502 * {@inheritDoc} 503 * @see baseModule::getManagedAttributes() 504 */ 505 public function getManagedAttributes($typeId) { 506 $attrs = parent::getManagedAttributes($typeId); 507 $typeManager = new TypeManager(); 508 if (!$typeManager->hasConfig()) { 509 return $attrs; 510 } 511 $modules = $typeManager->getConfiguredType($typeId)->getModules(); 512 if ($this->manageCn($modules)) { 513 $attrs[] = 'cn'; 514 } 515 $attrs[] = $this->getHomedirAttrName($modules); 516 $attrs[] = $this->getPasswordAttrName($modules); 517 return $attrs; 518 } 519 520 /** 521 * This functions is used to check if all settings for this module have been made. 522 * 523 * @return boolean true, if settings are complete 524 */ 525 function module_complete() { 526 if (!$this->skipObjectClass() && (!isset($this->attributes['objectClass']) || !in_array('posixAccount', $this->attributes['objectClass']))) { 527 // no checks if object class is not set 528 return true; 529 } 530 if (!isset($this->attributes['uid'][0]) || ($this->attributes['uid'][0] == '')) { 531 return false; 532 } 533 if (!isset($this->attributes['uidNumber'][0]) || ($this->attributes['uidNumber'][0] == '')) { 534 return false; 535 } 536 if (!isset($this->attributes['gidNumber'][0]) || ($this->attributes['gidNumber'][0] == '')) { 537 return false; 538 } 539 if (!isset($this->attributes['loginShell'][0]) || ($this->attributes['loginShell'][0] == '')) { 540 return false; 541 } 542 return true; 543 } 544 545 /** 546 * This function loads all needed LDAP attributes. 547 * 548 * @param array $attr list of attributes 549 */ 550 function load_attributes($attr) { 551 parent::load_attributes($attr); 552 $typeSettings = $_SESSION['config']->get_typeSettings(); 553 // get additional group memberships 554 if (!isset($attr['uid'][0])) { 555 return; 556 } 557 $groupFilter = '(&(objectClass=posixGroup)(memberUid=' . $attr['uid'][0] . '))'; 558 if (!empty($typeSettings['filter_group'])) { 559 $typeFilter = $typeSettings['filter_group']; 560 if (strpos($typeFilter, '(') !== 0) { 561 $typeFilter = '(' . $typeFilter . ')'; 562 } 563 $groupFilter = '(&' . $groupFilter . $typeFilter . ')'; 564 } 565 $groupList = searchLDAPByFilter($groupFilter, array('cn'), array('group')); 566 for ($i = 0; $i < sizeof($groupList); $i++) { 567 $this->groups[] = $groupList[$i]['cn'][0]; 568 } 569 $this->groups_orig = $this->groups; 570 // get additional group of names memberships 571 if (self::areGroupOfNamesActive()) { 572 $types = array('gon', 'group'); 573 $gonList = array(); 574 foreach ($types as $type) { 575 $gonFilter = '(|(&(objectClass=groupOfNames)(member=' . ldap_escape($this->getAccountContainer()->dn_orig) . '))(&(objectClass=groupOfMembers)(member=' . $this->getAccountContainer()->dn_orig . '))(&(objectClass=groupOfUniqueNames)(uniqueMember=' . $this->getAccountContainer()->dn_orig . ')))'; 576 if (!empty($typeSettings['filter_' . $type])) { 577 $typeFilter = $typeSettings['filter_' . $type]; 578 if (strpos($typeFilter, '(') !== 0) { 579 $typeFilter = '(' . $typeFilter . ')'; 580 } 581 $gonFilter = '(&' . $gonFilter . $typeFilter . ')'; 582 } 583 $gonListPart = searchLDAPByFilter($gonFilter, array('dn'), array($type)); 584 $gonList = array_merge($gonList, $gonListPart); 585 } 586 $this->gonList_orig = array(); 587 for ($i = 0; $i < sizeof($gonList); $i++) { 588 $this->gonList_orig[] = $gonList[$i]['dn']; 589 } 590 $this->gonList_orig = array_values(array_unique($this->gonList_orig)); 591 $this->gonList = $this->gonList_orig; 592 } 593 } 594 595 /** 596 * Returns a list of modifications which have to be made to the LDAP account. 597 * 598 * @return array list of modifications 599 * <br>This function returns an array with 3 entries: 600 * <br>array( DN1 ('add' => array($attr), 'remove' => array($attr), 'modify' => array($attr)), DN2 .... ) 601 * <br>DN is the DN to change. It may be possible to change several DNs (e.g. create a new user and add him to some groups via attribute memberUid) 602 * <br>"add" are attributes which have to be added to LDAP entry 603 * <br>"remove" are attributes which have to be removed from LDAP entry 604 * <br>"modify" are attributes which have to been modified in LDAP entry 605 * <br>"info" are values with informational value (e.g. to be used later by pre/postModify actions) 606 */ 607 function save_attributes() { 608 if (!$this->skipObjectClass() && (!in_array('posixAccount', $this->attributes['objectClass']) && !in_array('posixAccount', $this->orig['objectClass']))) { 609 // skip saving if the extension was not added/modified 610 return array(); 611 } 612 $modules = $this->getAccountContainer()->get_type()->getModules(); 613 // get default changes 614 $return = $this->getAccountContainer()->save_module_attributes($this->attributes, $this->orig); 615 // add information about clear text password and password status change 616 $return[$this->getAccountContainer()->dn_orig]['info']['userPasswordClearText'][0] = $this->clearTextPassword; 617 $pwdAttrName = $this->getPasswordAttrName($modules); 618 if (isset($this->orig[$pwdAttrName][0]) && isset($this->attributes[$pwdAttrName][0])) { 619 if ((pwd_is_enabled($this->orig[$pwdAttrName][0]) && pwd_is_enabled($this->attributes[$pwdAttrName][0])) 620 || (!pwd_is_enabled($this->orig[$pwdAttrName][0]) && !pwd_is_enabled($this->attributes[$pwdAttrName][0]))) { 621 $return[$this->getAccountContainer()->dn_orig]['info']['userPasswordStatusChange'][0] = 'unchanged'; 622 } 623 elseif (pwd_is_enabled($this->orig[$pwdAttrName][0])) { 624 $return[$this->getAccountContainer()->dn_orig]['info']['userPasswordStatusChange'][0] = 'locked'; 625 } 626 else { 627 $return[$this->getAccountContainer()->dn_orig]['info']['userPasswordStatusChange'][0] = 'unlocked'; 628 } 629 } 630 if ($this->skipObjectClass() || in_array('posixAccount', $this->attributes['objectClass'])) { 631 // Remove primary group from additional groups 632 if (!isset($this->moduleSettings['posixAccount_primaryGroupAsSecondary'][0]) 633 || ($this->moduleSettings['posixAccount_primaryGroupAsSecondary'][0] != 'true')) { 634 for ($i = 0; $i < count($this->groups); $i++) { 635 if ($this->groups[$i] == $this->getGroupName($this->attributes['gidNumber'][0])) { 636 unset($this->groups[$i]); 637 } 638 } 639 } 640 else { 641 // add user as memberuid in primary group 642 $primaryGroupName = $this->getGroupName($this->attributes['gidNumber'][0]); 643 if (!in_array($primaryGroupName, $this->groups)) { 644 $this->groups[] = $primaryGroupName; 645 } 646 // add user as member in group of names if auto-sync is activated 647 if ($this->isBooleanConfigOptionSet('posixGroup_autoSyncGon')) { 648 $allGons = $this->findGroupOfNames(); 649 foreach ($allGons as $gonDn => $gonData) { 650 if (in_array_ignore_case('posixGroup', $gonData['objectclass'])) { 651 $gonCn = $gonData['cn'][0]; 652 if (($gonCn === $primaryGroupName) && !in_array($gonDn, $this->gonList)) { 653 $this->gonList[] = $gonDn; 654 } 655 } 656 } 657 } 658 } 659 660 // Set additional group memberships 661 if (isset($this->orig['uid'][0]) && ($this->orig['uid'][0] != '') && ($this->attributes['uid'][0] != $this->orig['uid'][0])) { 662 // find affected groups 663 $groupList = searchLDAPByAttribute('memberUid', $this->orig['uid'][0], 'posixGroup', array('dn'), array('group')); 664 for ($i = 0; $i < sizeof($groupList); $i++) { 665 // replace old user name with new one 666 $return[$groupList[$i]['dn']]['remove']['memberUid'][] = $this->orig['uid'][0]; 667 $return[$groupList[$i]['dn']]['add']['memberUid'][] = $this->attributes['uid'][0]; 668 } 669 } 670 else { 671 // update groups. 672 $add = array_delete($this->groups_orig, $this->groups); 673 $remove = array_delete($this->groups, $this->groups_orig); 674 $groupList = searchLDAPByAttribute('cn', '*', 'posixGroup', array('cn', 'dn'), array('group')); 675 $cn2dn = array(); 676 for ($i = 0; $i < sizeof($groupList); $i++) { 677 $cn2dn[$groupList[$i]['cn'][0]] = $groupList[$i]['dn']; 678 } 679 for ($i = 0; $i < sizeof($add); $i++) { 680 if (isset($cn2dn[$add[$i]])) { 681 $return[$cn2dn[$add[$i]]]['add']['memberUid'][] = $this->attributes['uid'][0]; 682 } 683 } 684 for ($i = 0; $i < sizeof($remove); $i++) { 685 if (isset($cn2dn[$remove[$i]])) { 686 $return[$cn2dn[$remove[$i]]]['remove']['memberUid'][] = $this->attributes['uid'][0]; 687 } 688 } 689 } 690 } 691 elseif (in_array('posixAccount', $this->orig['objectClass']) && !empty($this->orig['uid'][0])) { 692 // Unix extension was removed, clean group memberships 693 $groupList = searchLDAPByAttribute('memberUid', $this->orig['uid'][0], 'posixGroup', array('dn'), array('group')); 694 for ($i = 0; $i < sizeof($groupList); $i++) { 695 // remove user name 696 $return[$groupList[$i]['dn']]['remove']['memberUid'][] = $this->orig['uid'][0]; 697 } 698 } 699 return $return; 700 } 701 702 /** 703 * Runs the postmodify actions. 704 * 705 * @see baseModule::postModifyActions() 706 * 707 * @param boolean $newAccount 708 * @param array $attributes LDAP attributes of this entry 709 * @return array array which contains status messages. Each entry is an array containing the status message parameters. 710 */ 711 public function postModifyActions($newAccount, $attributes) { 712 $messages = array(); 713 $accountContainer = $this->getAccountContainer(); 714 if ($accountContainer == null) { 715 return $messages; 716 } 717 $modules = $accountContainer->get_type()->getModules(); 718 // set exop password 719 $messages = array_merge($messages, $this->setExopPassword($this->moduleSettings)); 720 // create home directories if needed 721 $homeDirAttr = $this->getHomedirAttrName($modules); 722 $lamdaemonServerList = $_SESSION['config']->getConfiguredScriptServers(); 723 if (sizeof($this->lamdaemonServers) > 0) { 724 foreach ($lamdaemonServerList as $lamdaemonServer) { 725 if (!in_array($lamdaemonServer->getServer(), $this->lamdaemonServers)) { 726 continue; 727 } 728 $remote = new \LAM\REMOTE\Remote(); 729 $remote->connect($lamdaemonServer); 730 $result = $remote->execute( 731 implode( 732 self::$SPLIT_DELIMITER, 733 array( 734 $this->attributes['uid'][0], 735 "home", 736 "add", 737 $lamdaemonServer->getHomeDirPrefix() . $this->attributes[$homeDirAttr][0], 738 "0".$_SESSION['config']->get_scriptRights(), 739 $this->attributes['uidNumber'][0], 740 $this->attributes['gidNumber'][0]) 741 )); 742 $remote->disconnect(); 743 // lamdaemon results 744 if (!empty($result)) { 745 $singleresult = explode(",", $result); 746 if (($singleresult[0] == 'ERROR') || ($singleresult[0] == 'INFO') || ($singleresult[0] == 'WARN')) { 747 $messages[] = $singleresult; 748 } 749 else { 750 $messages[] = array('ERROR', $result[0]); 751 } 752 } 753 } 754 } 755 // move home directory if needed 756 if (!empty($this->orig[$homeDirAttr][0]) && !empty($this->attributes[$homeDirAttr][0]) 757 && ($this->orig[$homeDirAttr][0] != $this->attributes[$homeDirAttr][0])) { 758 foreach ($lamdaemonServerList as $lamdaemonServer) { 759 $remote = new \LAM\REMOTE\Remote(); 760 $remote->connect($lamdaemonServer); 761 $result = $remote->execute( 762 implode( 763 self::$SPLIT_DELIMITER, 764 array( 765 $this->attributes['uid'][0], 766 "home", 767 "move", 768 $lamdaemonServer->getHomeDirPrefix() . $this->orig[$homeDirAttr][0], 769 $this->attributes['uidNumber'][0], 770 $lamdaemonServer->getHomeDirPrefix() . $this->attributes[$homeDirAttr][0]) 771 )); 772 $remote->disconnect(); 773 // lamdaemon results 774 if (!empty($result)) { 775 $singleresult = explode(",", $result); 776 if (($singleresult[0] == 'ERROR') || ($singleresult[0] == 'INFO') || ($singleresult[0] == 'WARN')) { 777 $messages[] = $singleresult; 778 } 779 } 780 } 781 } 782 // set new group on homedirectory 783 if (!empty($this->orig[$homeDirAttr][0]) && !empty($this->attributes[$homeDirAttr][0]) 784 && ($this->orig['gidNumber'][0] != $this->attributes['gidNumber'][0])) { 785 foreach ($lamdaemonServerList as $lamdaemonServer) { 786 $remote = new \LAM\REMOTE\Remote(); 787 $remote->connect($lamdaemonServer); 788 $result = $remote->execute( 789 implode( 790 self::$SPLIT_DELIMITER, 791 array( 792 $this->attributes['uid'][0], 793 "home", 794 "chgrp", 795 $lamdaemonServer->getHomeDirPrefix() . $this->attributes[$homeDirAttr][0], 796 $this->attributes['uidNumber'][0], 797 $this->attributes['gidNumber'][0]) 798 )); 799 $remote->disconnect(); 800 // lamdaemon results 801 if (!empty($result)) { 802 $singleresult = explode(",", $result); 803 if (($singleresult[0] == 'ERROR') || ($singleresult[0] == 'INFO') || ($singleresult[0] == 'WARN')) { 804 $messages[] = $singleresult; 805 } 806 } 807 } 808 } 809 // set group of names 810 if (self::areGroupOfNamesActive()) { 811 $gons = $this->findGroupOfNames(); 812 $toAdd = array_values(array_diff($this->gonList, $this->gonList_orig)); 813 $toRem = array_values(array_diff($this->gonList_orig, $this->gonList)); 814 // update groups if DN changed 815 if (isset($accountContainer->dn_orig) && (strtolower($accountContainer->dn_orig) != strtolower($accountContainer->finalDN))) { 816 // update owner/member/uniqueMember attributes 817 $searchAttrs = array('member', 'uniquemember', 'owner'); 818 foreach ($searchAttrs as $searchAttr) { 819 $ownerGroups = searchLDAPByAttribute($searchAttr, $accountContainer->dn_orig, null, array('dn', $searchAttr), array('gon', 'group')); 820 for ($i = 0; $i < sizeof($ownerGroups); $i++) { 821 $found = false; 822 $newOwners = $ownerGroups[$i][$searchAttr]; 823 for ($o = 0; $o < sizeof($newOwners); $o++) { 824 if ($newOwners[$o] == $accountContainer->dn_orig) { 825 $newOwners[$o] = $accountContainer->finalDN; 826 $found = true; 827 break; 828 } 829 } 830 if ($found) { 831 $attributesToModify = array($searchAttr => $newOwners); 832 $success = @ldap_mod_replace($_SESSION['ldap']->server(), $ownerGroups[$i]['dn'], $attributesToModify); 833 if (!$success) { 834 $ldapError = getDefaultLDAPErrorString($_SESSION['ldap']->server()); 835 logNewMessage(LOG_ERR, 'Unable to modify attributes of DN: ' . $ownerGroups[$i]['dn'] . ' (' . $ldapError . ').'); 836 logNewMessage(LOG_DEBUG, print_r($attributesToModify, true)); 837 $messages[] = array('ERROR', sprintf(_('Was unable to modify attributes of DN: %s.'), $ownerGroups[$i]['dn']), $ldapError); 838 } 839 } 840 } 841 } 842 } 843 // add groups 844 for ($i = 0; $i < sizeof($toAdd); $i++) { 845 if (isset($gons[$toAdd[$i]])) { 846 $attrName = 'member'; 847 if (in_array_ignore_case('groupOfUniqueNames', $gons[$toAdd[$i]]['objectclass'])) { 848 $attrName = 'uniqueMember'; 849 } 850 $success = @ldap_mod_add($_SESSION['ldap']->server(), $toAdd[$i], array($attrName => array($accountContainer->finalDN))); 851 if (!$success) { 852 logNewMessage(LOG_ERR, 'Unable to add user ' . $accountContainer->finalDN . ' to group: ' . $toAdd[$i] . ' (' . ldap_error($_SESSION['ldap']->server()) . ').'); 853 $messages[] = array('ERROR', sprintf(_('Was unable to add attributes to DN: %s.'), $toAdd[$i]), getDefaultLDAPErrorString($_SESSION['ldap']->server())); 854 } 855 else { 856 logNewMessage(LOG_NOTICE, 'Added user ' . $accountContainer->finalDN . ' to group: ' . $toAdd[$i]); 857 } 858 } 859 } 860 // remove groups 861 for ($i = 0; $i < sizeof($toRem); $i++) { 862 if (isset($gons[$toRem[$i]])) { 863 $attrName = 'member'; 864 if (in_array_ignore_case('groupOfUniqueNames', $gons[$toRem[$i]]['objectclass'])) { 865 $attrName = 'uniqueMember'; 866 } 867 $success = @ldap_mod_del($_SESSION['ldap']->server(), $toRem[$i], array($attrName => array($accountContainer->dn_orig))); 868 if (!$success) { 869 logNewMessage(LOG_ERR, 'Unable to delete user ' . $accountContainer->finalDN . ' from group: ' . $toRem[$i] . ' (' . ldap_error($_SESSION['ldap']->server()) . ').'); 870 $messages[] = array('ERROR', sprintf(_('Was unable to remove attributes from DN: %s.'), $toRem[$i]), getDefaultLDAPErrorString($_SESSION['ldap']->server())); 871 } 872 else { 873 logNewMessage(LOG_NOTICE, 'Removed user ' . $accountContainer->finalDN . ' from group: ' . $toRem[$i]); 874 } 875 } 876 } 877 } 878 return $messages; 879 } 880 881 /** 882 * Sets the password via ldap_exop if configured. 883 * 884 * @param array $settings settings 885 * @return array error message parameters if any 886 */ 887 private function setExopPassword($settings) { 888 if (!empty($this->clearTextPassword) && !empty($settings['posixAccount_pwdHash'][0]) 889 && ($settings['posixAccount_pwdHash'][0] === 'LDAP_EXOP')) { 890 $success = ldap_exop_passwd($_SESSION['ldap']->server(), $this->getAccountContainer()->finalDN, null, $this->clearTextPassword); 891 if (!$success) { 892 return array(array('ERROR', _('Unable to set password'), getExtendedLDAPErrorMessage($_SESSION['ldap']->server()))); 893 } 894 } 895 return array(); 896 } 897 898 /** 899 * Additional LDAP operations on delete. 900 * 901 * @return List of LDAP operations, same as for save_attributes() 902 */ 903 function delete_attributes() { 904 $return = array(); 905 // remove memberUids if set 906 $groups = searchLDAPByAttribute('memberUid', $this->attributes['uid'][0], 'posixGroup', array('dn'), array('group')); 907 for ($i = 0; $i < sizeof($groups); $i++) { 908 $return[$groups[$i]['dn']]['remove']['memberUid'][] = $this->attributes['uid'][0]; 909 } 910 // stop here if referential integrity overlay is active 911 $config = $this->getAccountContainer()->get_type()->getTypeManager()->getConfig(); 912 if ($config->isReferentialIntegrityOverlayActive()) { 913 return $return; 914 } 915 // remove from group of names 916 $dn = $this->getAccountContainer()->dn_orig; 917 $gons = searchLDAPByFilter('(|(member=' . $dn . ')(uniqueMember=' . $dn . '))', array('member', 'uniqueMember'), array('group', 'gon')); 918 for ($i = 0; $i < sizeof($gons); $i++) { 919 if (isset($gons[$i]['member'])) { 920 $return[$gons[$i]['dn']]['remove']['member'][] = $dn; 921 } 922 elseif (isset($gons[$i]['uniquemember'])) { 923 $return[$gons[$i]['dn']]['remove']['uniqueMember'][] = $dn; 924 } 925 } 926 return $return; 927 } 928 929 /** 930 * Allows the module to run commands before the LDAP entry is deleted. 931 * 932 * @return array Array which contains status messages. Each entry is an array containing the status message parameters. 933 */ 934 function preDeleteActions() { 935 $return = array(); 936 // delete home directory 937 if (isset($_POST['deletehomedir']) && ($_POST['deletehomedir'] == 'on')) { 938 $modules = $this->getAccountContainer()->get_type()->getModules(); 939 $homeDirAttr = $this->getHomedirAttrName($modules); 940 // get list of lamdaemon servers 941 $lamdaemonServers = $_SESSION['config']->getConfiguredScriptServers(); 942 // try to delete directory on all servers 943 foreach ($lamdaemonServers as $lamdaemonServer) { 944 $remote = new \LAM\REMOTE\Remote(); 945 try { 946 $remote->connect($lamdaemonServer); 947 $result = $remote->execute( 948 implode( 949 self::$SPLIT_DELIMITER, 950 array( 951 $this->attributes['uid'][0], 952 "home", 953 "rem", 954 $lamdaemonServer->getHomeDirPrefix() . $this->attributes[$homeDirAttr][0], 955 $this->attributes['uidNumber'][0] 956 ) 957 )); 958 $remote->disconnect(); 959 } 960 catch (LAMException $e) { 961 $return[] = array('ERROR', $e->getTitle(), $e->getMessage()); 962 } 963 // lamdaemon results 964 if (!empty($result)) { 965 $singleresult = explode(",", $result); 966 if (is_array($singleresult)) { 967 if (($singleresult[0] == 'ERROR') || ($singleresult[0] == 'WARN') || ($singleresult[0] == 'INFO')) { 968 $return[] = $singleresult; 969 } 970 } 971 } 972 } 973 } 974 // delete sudo rights 975 if (isset($_POST['deleteSudoers']) && ($_POST['deleteSudoers'] == 'on')) { 976 $result = searchLDAPByAttribute('sudoUser', $this->attributes['uid'][0], 'sudoRole', array('dn'), array('sudo')); 977 foreach ($result as $attrs) { 978 $dn = $attrs['dn']; 979 $success = @ldap_mod_del($_SESSION['ldap']->server(), $dn, array('sudoUser' => array($this->attributes['uid'][0]))); 980 if (!$success) { 981 $return[] = array('ERROR', getDefaultLDAPErrorString($_SESSION['ldap']->server())); 982 } 983 } 984 } 985 return $return; 986 } 987 988 /** 989 * Processes user input of the primary module page. 990 * It checks if all input values are correct and updates the associated LDAP attributes. 991 * 992 * @return array list of info/error messages 993 */ 994 function process_attributes() { 995 $keysToReplace = array('cn', 'gecos', 'homeDirectory'); 996 $this->getAccountContainer()->replaceWildcardsInPOST($keysToReplace); 997 $modules = $this->getAccountContainer()->get_type()->getModules(); 998 $typeId = $this->getAccountContainer()->get_type()->getId(); 999 $errors = array(); 1000 if (isset($_POST['addObjectClass'])) { 1001 if (!isset($this->attributes['objectClass'])) { 1002 $this->attributes['objectClass'] = array(); 1003 } 1004 if (!in_array('posixAccount', $this->attributes['objectClass'])) { 1005 $this->attributes['objectClass'][] = 'posixAccount'; 1006 } 1007 return $errors; 1008 } 1009 if (isset($_POST['remObjectClass'])) { 1010 $this->attributes['objectClass'] = array_delete(array('posixAccount'), $this->attributes['objectClass']); 1011 $attrs = $this->getManagedAttributes($this->getAccountContainer()->get_type()->getId()); 1012 foreach ($attrs as $name) { 1013 if (isset($this->attributes[$name])) { 1014 unset($this->attributes[$name]); 1015 } 1016 } 1017 return $errors; 1018 } 1019 // skip processing if object class is not set 1020 if ($this->isOptional($modules) && !$this->skipObjectClass() && (!isset($this->attributes['objectClass']) || !in_array('posixAccount', $this->attributes['objectClass']))) { 1021 return $errors; 1022 } 1023 $groups = $this->findGroups($modules); // list of all group names 1024 if (count($groups)==0) { 1025 // abort if no groups were found 1026 return array(); 1027 } 1028 if (isset($_POST['loginShell'])) { 1029 $this->attributes['loginShell'][0] = $_POST['loginShell']; 1030 } 1031 if (!$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegecos')) { 1032 if (isset($_POST['gecos'])) { 1033 $this->attributes['gecos'][0] = $_POST['gecos']; 1034 } 1035 } 1036 if (isset($this->orig['uid'][0]) && ($this->orig['uid'][0] != '') && (trim($_POST['uid']) != $this->attributes['uid'][0])) { 1037 $errors[] = $this->messages['uid'][0]; 1038 } 1039 if (isset($this->orig['gidNumber'][0]) && ($this->orig['gidNumber'][0] != '') && ($_POST['gidNumber'] != $this->attributes['gidNumber'][0])) { 1040 $errorMessage = $this->messages['gidNumber'][2]; 1041 $errorMessage[] = array($this->orig['gidNumber'][0], $this->orig['uidNumber'][0], $_POST['gidNumber']); 1042 $errors[] = $errorMessage; 1043 if ($this->isBooleanConfigOptionSet('posixAccount_primaryGroupAsSecondary') && !empty($this->attributes['gidNumber'][0])) { 1044 // change primary group in $this->groups 1045 $oldGroupName = $this->getGroupName($this->attributes['gidNumber'][0]); 1046 $newGroupName = $this->getGroupName($_POST['gidNumber']); 1047 if (!empty($oldGroupName) && !empty($newGroupName)) { 1048 $this->groups = array_delete(array($oldGroupName), $this->groups); 1049 $this->groups[] = $newGroupName; 1050 // sync group of names if needed 1051 if ($this->isBooleanConfigOptionSet('posixGroup_autoSyncGon')) { 1052 $allGons = $this->findGroupOfNames(); 1053 foreach ($allGons as $gonDn => $gonData) { 1054 if (in_array_ignore_case('posixGroup', $gonData['objectclass'])) { 1055 $gonCn = $gonData['cn'][0]; 1056 if (($gonCn === $newGroupName) && !in_array($gonDn, $this->gonList)) { 1057 $this->gonList[] = $gonDn; 1058 } 1059 if (($gonCn === $oldGroupName) && in_array($gonDn, $this->gonList)) { 1060 $this->gonList = array_delete(array($gonDn), $this->gonList); 1061 } 1062 } 1063 } 1064 } 1065 } 1066 } 1067 } 1068 if (isset($this->orig['uidNumber'][0]) && $this->orig['uidNumber'][0]!='' && trim($_POST['uidNumber'])!=$this->attributes['uidNumber'][0]) { 1069 $errorMessage = $this->messages['uidNumber'][5]; 1070 $errorMessage[] = array($this->orig['uidNumber'][0], $_POST['uidNumber']); 1071 $errors[] = $errorMessage; 1072 } 1073 $homedirAttrName = $this->getHomedirAttrName($modules); 1074 if (isset($_POST['homeDirectory']) && isset($this->orig[$homedirAttrName][0]) && ($this->orig[$homedirAttrName][0] != '') && ($_POST['homeDirectory'] != $this->attributes[$homedirAttrName][0])) { 1075 $errorMessage = $this->messages['homeDirectory'][3]; 1076 $errorMessage[] = array($this->orig[$homedirAttrName][0], $_POST['homeDirectory']); 1077 $errors[] = $errorMessage; 1078 } 1079 // get list of DNS names or IPs 1080 $lamdaemonServers = $_SESSION['config']->getConfiguredScriptServers(); 1081 $this->lamdaemonServers = array(); 1082 for ($h = 0; $h < sizeof($lamdaemonServers); $h++) { 1083 if (isset($_POST['createhomedir_' . $h]) && ($_POST['createhomedir_' . $h] = 'on')) { 1084 $this->lamdaemonServers[] = $lamdaemonServers[$h]->getServer(); 1085 } 1086 } 1087 if (isset($_POST['homeDirectory'])) { 1088 $this->attributes[$homedirAttrName][0] = $_POST['homeDirectory']; 1089 } 1090 // Load attributes 1091 if ($this->isPasswordManaged()) { 1092 if (isset($_POST['lockPassword'])) { 1093 $this->lock($modules); 1094 } 1095 if (isset($_POST['unlockPassword'])) { 1096 $this->unlock($modules); 1097 } 1098 if (isset($_POST['removePassword'])) { 1099 unset($this->attributes[$this->getPasswordAttrName($modules)]); 1100 } 1101 } 1102 if ($this->manageCn($modules)) { 1103 $this->processMultiValueInputTextField('cn', $errors, 'cn'); 1104 } 1105 $this->attributes['uidNumber'][0] = trim($_POST['uidNumber']); 1106 $this->attributes['gidNumber'][0] = $_POST['gidNumber']; 1107 if ($this->get_scope() == 'user') { 1108 if (($this->attributes['uid'][0] != $_POST['uid']) && !get_preg($_POST['uid'], '!upper')) { 1109 $errors[] = $this->messages['uid'][1]; 1110 } 1111 if ( !get_preg($this->attributes[$homedirAttrName][0], 'homeDirectory' )) { 1112 $errors[] = $this->messages['homeDirectory'][0]; 1113 } 1114 } 1115 $this->attributes['uid'][0] = trim($_POST['uid']); 1116 // Check if UID is valid. If none value was entered, the next usable value will be inserted 1117 // load min and may uidNumber 1118 if ($this->get_scope()=='user') { 1119 $minID = intval($this->moduleSettings['posixAccount_' . $typeId . '_minUID'][0]); 1120 $maxID = intval($this->moduleSettings['posixAccount_' . $typeId . '_maxUID'][0]); 1121 } 1122 if ($this->get_scope()=='host') { 1123 $minID = intval($this->moduleSettings['posixAccount_' . $typeId . '_minMachine'][0]); 1124 $maxID = intval($this->moduleSettings['posixAccount_' . $typeId . '_maxMachine'][0]); 1125 } 1126 $uids = $this->getUIDs($typeId); 1127 if ($this->attributes['uidNumber'][0]=='') { 1128 // No id-number given 1129 if (!isset($this->orig['uidNumber'][0]) || ($this->orig['uidNumber'][0] == '')) { 1130 // new account -> we have to find a free id-number 1131 $newUID = $this->getNextUIDs(1, $errors, $typeId); 1132 if (is_array($newUID)) { 1133 $this->attributes['uidNumber'][0] = $newUID[0]; 1134 } 1135 else { 1136 $errors[] = $this->messages['uidNumber'][3]; 1137 } 1138 } 1139 else $this->attributes['uidNumber'][0] = $this->orig['uidNumber'][0]; 1140 // old account -> return id-number which has been used 1141 } 1142 else { 1143 // check manual ID 1144 if ($this->getAccountContainer()->isNewAccount || !isset($this->orig['uidNumber'][0]) || ($this->orig['uidNumber'][0] != $this->attributes['uidNumber'][0])) { 1145 // check range 1146 if (($this->get_scope() == 'user') && (!isset($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorUsers']) || ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorUsers'][0] == 'range'))) { 1147 if (!is_numeric($this->attributes['uidNumber'][0]) || ($this->attributes['uidNumber'][0] < $minID) || ($this->attributes['uidNumber'][0] > $maxID)) { 1148 $errors[] = array('ERROR', _('ID-Number'), sprintf(_('Please enter a value between %s and %s!'), $minID, $maxID)); 1149 } 1150 } 1151 if (($this->get_scope() == 'host') && (!isset($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorHosts']) || ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorHosts'][0] == 'range'))) { 1152 if (!is_numeric($this->attributes['uidNumber'][0]) || ($this->attributes['uidNumber'][0] < $minID) || ($this->attributes['uidNumber'][0] > $maxID)) { 1153 $errors[] = array('ERROR', _('ID-Number'), sprintf(_('Please enter a value between %s and %s!'), $minID, $maxID)); 1154 } 1155 } 1156 // id-number is in use and account is a new account 1157 if ((in_array($this->attributes['uidNumber'][0], $uids)) && !isset($this->orig['uidNumber'][0])) $errors[] = $this->messages['uidNumber'][3]; 1158 // id-number is in use, account is existing account and id-number is not used by itself 1159 if ((in_array($this->attributes['uidNumber'][0], $uids)) && isset($this->orig['uidNumber'][0]) && ($this->orig['uidNumber'][0] != $this->attributes['uidNumber'][0]) ) { 1160 $errors[] = $this->messages['uidNumber'][3]; 1161 $this->attributes['uidNumber'][0] = $this->orig['uidNumber'][0]; 1162 } 1163 } 1164 } 1165 // Create automatic useraccount with number if original user already exists 1166 // Reset name to original name if new name is in use 1167 // Set username back to original name if new username is in use 1168 if ($this->userNameExists($this->attributes['uid'][0], $typeId) && isset($this->orig['uid'][0]) && ($this->orig['uid'][0]!='')) { 1169 $this->attributes['uid'][0] = $this->orig['uid'][0]; 1170 } 1171 else { 1172 // Change uid to a new uid until a free uid is found 1173 while ($this->userNameExists($this->attributes['uid'][0], $typeId)) { 1174 $this->attributes['uid'][0] = $this->getNextUserName($this->attributes['uid'][0], array_keys($this->getAccountContainer()->getAccountModules())); 1175 } 1176 } 1177 // Show warning if LAM has changed username 1178 if ($this->attributes['uid'][0] != trim($_POST['uid'])) { 1179 $userNames = $this->getUserNames($typeId); 1180 if ($this->get_scope() == 'user') { 1181 $error = $this->messages['uid'][5]; 1182 $error[] = array(htmlspecialchars($userNames[trim($_POST['uid'])])); 1183 $errors[] = $error; 1184 } 1185 if ($this->get_scope() == 'host') { 1186 $error = $this->messages['uid'][6]; 1187 $error[] = array(htmlspecialchars($userNames[trim($_POST['uid'])])); 1188 $errors[] = $error; 1189 } 1190 } 1191 if (($this->get_scope() == 'user') && !get_preg($this->attributes['uid'][0], 'username')) { 1192 // Check if Username contains only valid characters 1193 $errors[] = $this->messages['uid'][2]; 1194 } 1195 if ($this->get_scope() == 'host') { 1196 // Check if Hostname contains only valid characters 1197 if (!get_preg($this->attributes['uid'][0], 'hostname')) { 1198 $errors[] = $this->messages['uid'][4]; 1199 } 1200 if (!isset($this->attributes[$homedirAttrName][0])) { 1201 $this->attributes[$homedirAttrName][0] = '/dev/null'; 1202 } 1203 if (!isset($this->attributes['loginShell'][0])) { 1204 $this->attributes['loginShell'][0] = '/bin/false'; 1205 } 1206 } 1207 $attributeList = array($homedirAttrName); 1208 if (!$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegecos')) { 1209 $attributeList[] = 'gecos'; 1210 } 1211 for ($i = 0; $i < sizeof($attributeList); $i++) { 1212 if (isset($this->attributes[$attributeList[$i]][0])) { 1213 $value = $this->attributes[$attributeList[$i]][0]; 1214 $replacedValue = $this->checkASCII($value); 1215 if ($value != $replacedValue) { 1216 $this->attributes[$attributeList[$i]][0] = $replacedValue; 1217 $errors[] = array('WARN', $attributeList[$i], _('Changed value because only ASCII characters are allowed.')); 1218 } 1219 } 1220 } 1221 if ($this->get_scope() == 'user') { 1222 // set SASL password for new and renamed users 1223 if (!empty($this->attributes['uid'][0]) && !empty($this->moduleSettings['posixAccount_pwdHash'][0]) 1224 && ($this->moduleSettings['posixAccount_pwdHash'][0] === 'SASL') 1225 && ($this->getAccountContainer()->isNewAccount || ($this->attributes['uid'][0] != $this->orig['uid'][0]))) { 1226 $this->attributes[$this->getPasswordAttrName($modules)][0] = '{SASL}' . $this->attributes['uid'][0]; 1227 } 1228 // set K5KEY password for new users 1229 if (!empty($this->moduleSettings['posixAccount_pwdHash'][0]) && ($this->moduleSettings['posixAccount_pwdHash'][0] === 'K5KEY')) { 1230 $this->attributes[$this->getPasswordAttrName($modules)][0] = pwd_hash('x', true, $this->moduleSettings['posixAccount_pwdHash'][0]); 1231 } 1232 } 1233 if (isset($_POST['posixAccount_createGroup']) 1234 && !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hideCreateGroup') 1235 && ($this->get_scope() == 'user') 1236 && $this->getAccountContainer()->isNewAccount && get_preg($this->attributes['uid'][0], 'username')) { 1237 $groupType = $this->getPosixGroupType(); 1238 $sessionKey = 'TMP' . getRandomNumber(); 1239 $accountContainerTmp = new accountContainer($groupType, $sessionKey); 1240 $_SESSION[$sessionKey] = &$accountContainerTmp; 1241 $accountContainerTmp->new_account(); 1242 $posixGroupModule = $accountContainerTmp->getAccountModule('posixGroup'); 1243 $nextGid = $posixGroupModule->getNextGIDs(1, $errors, $groupType); 1244 if ($nextGid !== null) { 1245 $newGroupName = $this->attributes['uid'][0]; 1246 $dnNewGroup = 'cn=' . $newGroupName . ',' . $groupType->getSuffix(); 1247 $attributesNewGroup = array( 1248 'cn' => array($newGroupName), 1249 'gidNumber' => $nextGid[0], 1250 'objectClass' => array('posixGroup'), 1251 ); 1252 $newGroupSuccess = @ldap_add(getLDAPServerHandle(), $dnNewGroup, $attributesNewGroup); 1253 if ($newGroupSuccess) { 1254 $errors[] = array('INFO', _('Created new group.'), htmlspecialchars($newGroupName)); 1255 $this->attributes['gidNumber'][0] = $nextGid[0]; 1256 $this->groupCache = null; 1257 } 1258 else { 1259 $errors[] = array('ERROR', _('Unable to create new group.'), getDefaultLDAPErrorString(getLDAPServerHandle())); 1260 } 1261 } 1262 } 1263 // Return error-messages 1264 return $errors; 1265 } 1266 1267 /** 1268 * Returns the first found group type that contains posixGroup. 1269 * 1270 * @return ConfiguredType|null type 1271 */ 1272 private function getPosixGroupType() { 1273 $typeManager = new TypeManager(); 1274 $groupTypes = $typeManager->getConfiguredTypesForScope('group'); 1275 foreach ($groupTypes as $groupType) { 1276 $modules = $groupType->getModules(); 1277 if (in_array('posixGroup', $modules)) { 1278 return $groupType; 1279 } 1280 } 1281 return null; 1282 } 1283 1284 /** 1285 * Checks if an attribute contains only ASCII characters and replaces invalid characters. 1286 * 1287 * @param string $attribute attribute value 1288 * @return string attribute value with replaced non-ASCII characters 1289 */ 1290 function checkASCII($attribute) { 1291 if ($attribute == null) { 1292 return ''; 1293 } 1294 // replace special characters 1295 $attribute = str_replace(array_keys($this->umlautReplacements), array_values($this->umlautReplacements), $attribute); 1296 // remove remaining UTF-8 characters 1297 for ($c = 0; $c < strlen($attribute); $c++) { 1298 if (ord($attribute[$c]) > 127) { 1299 $attribute = str_replace($attribute[$c], "", $attribute); 1300 $c--; 1301 } 1302 } 1303 return $attribute; 1304 } 1305 1306 /** 1307 * Processes user input of the group selection page. 1308 * It checks if all input values are correct and updates the associated LDAP attributes. 1309 * 1310 * @return array list of info/error messages 1311 */ 1312 function process_group() { 1313 $typeId = $this->getAccountContainer()->get_type()->getId(); 1314 // Unix groups 1315 if ($this->isBooleanConfigOptionSet('posixGroup_autoSyncGon')) { 1316 $this->syncGonToGroups(); 1317 } 1318 elseif (!$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hideposixGroups')) { 1319 if (isset($_POST['addgroups']) && isset($_POST['addgroups_button'])) { // Add groups to list 1320 // add new group 1321 $this->groups = @array_merge($this->groups, $_POST['addgroups']); 1322 } 1323 elseif (isset($_POST['removegroups']) && isset($_POST['removegroups_button'])) { // remove groups from list 1324 $this->groups = array_delete($_POST['removegroups'], $this->groups); 1325 } 1326 } 1327 // group of names 1328 if (self::areGroupOfNamesActive() && !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegon')) { 1329 if (isset($_POST['addgons']) && isset($_POST['addgons_button'])) { // Add groups to list 1330 // add new group 1331 $this->gonList = @array_merge($this->gonList, $_POST['addgons']); 1332 } 1333 elseif (isset($_POST['removegons']) && isset($_POST['removegons_button'])) { // remove groups from list 1334 $this->gonList = array_delete($_POST['removegons'], $this->gonList); 1335 } 1336 } 1337 // sync Unix to GoN 1338 if (isset($_POST['form_subpage_posixAccount_group_syncU2GON'])) { 1339 $this->manualSyncUnixToGon($typeId); 1340 } 1341 // sync GoN to Unix 1342 if (isset($_POST['form_subpage_posixAccount_group_syncGON2U'])) { 1343 $this->manualSyncGonToUnix($this->getAccountContainer()->get_type()); 1344 } 1345 // sync Windows to Unix 1346 if (isset($_POST['form_subpage_posixAccount_group_syncWin2U'])) { 1347 $this->manualSyncWindowsToUnix($this->getAccountContainer()->get_type()); 1348 } 1349 return array(); 1350 } 1351 1352 /** 1353 * Syncs the Unix groups to group of names. 1354 * 1355 * @param string $typeId type ID 1356 */ 1357 private function manualSyncUnixToGon($typeId) { 1358 $allGons = $this->findGroupOfNames(); 1359 $namesToIgnore = array(); 1360 if (!empty($this->moduleSettings['posixAccount_' . $typeId . '_syncGroupsExclusions'])) { 1361 $namesToIgnore = $this->moduleSettings['posixAccount_' . $typeId . '_syncGroupsExclusions']; 1362 array_map('trim', $namesToIgnore); 1363 } 1364 // remove all groups that are not in Unix 1365 if (isset($_POST['syncDeleteGroups']) && ($_POST['syncDeleteGroups'] == 'on')) { 1366 $toDelete = array(); 1367 foreach ($this->gonList as $currentGon) { 1368 $gonName = $this->getGonName($currentGon, $allGons); 1369 if (in_array($gonName, $namesToIgnore) || in_array($gonName, $this->groups)) { 1370 continue; 1371 } 1372 $toDelete[] = $currentGon; 1373 } 1374 $this->gonList = array_delete($toDelete, $this->gonList); 1375 } 1376 // add groups that are not yet in groups of names 1377 foreach ($this->groups as $currentName) { 1378 if (in_array($currentName, $namesToIgnore)) { 1379 continue; 1380 } 1381 $found = false; 1382 foreach ($this->gonList as $currentGon) { 1383 if ($currentName == $this->getGonName($currentGon, $allGons)) { 1384 $found = true; 1385 break; 1386 } 1387 } 1388 if ($found) { 1389 continue; 1390 } 1391 foreach ($allGons as $currentGon) { 1392 if ($currentName == $this->getGonName($currentGon['dn'], $allGons)) { 1393 $this->gonList[] = $currentGon['dn']; 1394 break; 1395 } 1396 } 1397 } 1398 } 1399 1400 /** 1401 * Syncs the group of names to Unix groups. 1402 * 1403 * @param ConfiguredType $type type 1404 */ 1405 private function manualSyncGonToUnix($type) { 1406 $allGons = $this->findGroupOfNames(); 1407 $modules = $type->getModules(); 1408 $allGroups = $this->findGroups($modules); 1409 foreach ($allGroups as $index => $groupData) { 1410 $allGroups[$index] = $groupData[1]; 1411 } 1412 $namesToIgnore = array(); 1413 if (!empty($this->moduleSettings['posixAccount_' . $type->getId() . '_syncGroupsExclusions'])) { 1414 $namesToIgnore = $this->moduleSettings['posixAccount_' . $type->getId() . '_syncGroupsExclusions']; 1415 array_map('trim', $namesToIgnore); 1416 } 1417 // remove all groups that are not in group of names 1418 if (isset($_POST['syncDeleteGroups']) && ($_POST['syncDeleteGroups'] == 'on')) { 1419 $toDelete = array(); 1420 foreach ($this->groups as $currentName) { 1421 if (in_array($currentName, $namesToIgnore)) { 1422 continue; 1423 } 1424 $found = false; 1425 foreach ($this->gonList as $currentGon) { 1426 $gonName = $this->getGonName($currentGon, $allGons); 1427 if ($gonName == $currentName) { 1428 $found = true; 1429 break; 1430 } 1431 } 1432 if (!$found) { 1433 $toDelete[] = $currentName; 1434 } 1435 } 1436 $this->groups = array_delete($toDelete, $this->groups); 1437 } 1438 // add groups that are not yet in Unix groups 1439 foreach ($this->gonList as $currentGon) { 1440 $gonName = $this->getGonName($currentGon, $allGons); 1441 if (in_array($gonName, $namesToIgnore)) { 1442 continue; 1443 } 1444 if (!in_array($gonName, $this->groups) && in_array($gonName, $allGroups)) { 1445 $this->groups[] = $gonName; 1446 } 1447 } 1448 } 1449 1450 /** 1451 * Syncs the Windows to Unix groups. 1452 * 1453 * @param ConfiguredType $type type 1454 */ 1455 private function manualSyncWindowsToUnix($type) { 1456 $windowsGroups = $this->getAccountContainer()->getAccountModule('windowsUser')->getGroupList(); 1457 $allWindowsGroups = searchLDAPByAttribute('gidNumber', '*', null, array('cn'), array('group')); 1458 $allGroups = $this->findGroups($modules); 1459 foreach ($allGroups as $index => $groupData) { 1460 $allGroups[$index] = $groupData[1]; 1461 } 1462 $namesToIgnore = array(); 1463 if (!empty($this->moduleSettings['posixAccount_' . $type->getId() . '_syncGroupsExclusions'])) { 1464 $namesToIgnore = $this->moduleSettings['posixAccount_' . $type->getId() . '_syncGroupsExclusions']; 1465 array_map('trim', $namesToIgnore); 1466 } 1467 // remove all groups that are not in Windows groups 1468 if (isset($_POST['syncDeleteGroups']) && ($_POST['syncDeleteGroups'] == 'on')) { 1469 $toDelete = array(); 1470 foreach ($this->groups as $currentName) { 1471 if (in_array($currentName, $namesToIgnore)) { 1472 continue; 1473 } 1474 $found = false; 1475 foreach ($windowsGroups as $currentWindowsGroup) { 1476 $windowsGroupName = $this->getWindowsGroupName($allWindowsGroups, $currentWindowsGroup); 1477 if ($windowsGroupName == $currentName) { 1478 $found = true; 1479 break; 1480 } 1481 } 1482 if (!$found) { 1483 $toDelete[] = $currentName; 1484 } 1485 } 1486 $this->groups = array_delete($toDelete, $this->groups); 1487 } 1488 // add groups that are not yet in Unix groups 1489 foreach ($windowsGroups as $currentWindowsGroup) { 1490 $windowsGroupName = $this->getWindowsGroupName($allWindowsGroups, $currentWindowsGroup); 1491 if (in_array($windowsGroupName, $namesToIgnore)) { 1492 continue; 1493 } 1494 if (!in_array($windowsGroupName, $this->groups) && in_array($windowsGroupName, $allGroups)) { 1495 $this->groups[] = $windowsGroupName; 1496 } 1497 } 1498 } 1499 1500 /** 1501 * Returns the cn of the given group of names. 1502 * 1503 * @param string $dn DN of group of names 1504 * @param $allGons list of all group of names 1505 * @return string cn value 1506 */ 1507 public function getGonName($dn, &$allGons) { 1508 if (!empty($allGons[$dn]['cn'][0])) { 1509 return $allGons[$dn]['cn'][0]; 1510 } 1511 return extractRDNValue($dn); 1512 } 1513 1514 /** 1515 * Returns the Windows group name. 1516 * 1517 * @param array $allWindowsGroups LDAP data of all Windows groups 1518 * @param string $dn DN 1519 */ 1520 private function getWindowsGroupName(&$allWindowsGroups, $dn) { 1521 foreach ($allWindowsGroups as $data) { 1522 if ($data['dn'] == $dn) { 1523 return $data['cn'][0]; 1524 } 1525 } 1526 return null; 1527 } 1528 1529 /** 1530 * Processes user input of the homedir check page. 1531 * It checks if all input values are correct and updates the associated LDAP attributes. 1532 * 1533 * @return array list of info/error messages 1534 */ 1535 function process_homedir() { 1536 $return = array(); 1537 // get list of lamdaemon servers 1538 $lamdaemonServers = $_SESSION['config']->getConfiguredScriptServers(); 1539 $modules = $this->getAccountContainer()->get_type()->getModules(); 1540 $homeDirAttr = $this->getHomedirAttrName($modules); 1541 for ($i = 0; $i < sizeof($lamdaemonServers); $i++) { 1542 if (isset($_POST['form_subpage_' . get_class($this) . '_homedir_create_' . $i])) { 1543 $remote = new \LAM\REMOTE\Remote(); 1544 $remote->connect($lamdaemonServers[$i]); 1545 $result = $remote->execute( 1546 implode( 1547 self::$SPLIT_DELIMITER, 1548 array( 1549 $this->attributes['uid'][0], 1550 "home", 1551 "add", 1552 $lamdaemonServers[$i]->getHomeDirPrefix() . $this->attributes[$homeDirAttr][0], 1553 "0".$_SESSION['config']->get_scriptRights(), 1554 $this->attributes['uidNumber'][0], 1555 $this->attributes['gidNumber'][0]) 1556 )); 1557 $remote->disconnect(); 1558 // lamdaemon results 1559 if (!empty($result)) { 1560 $singleresult = explode(",", $result); 1561 if (is_array($singleresult)) { 1562 if (($singleresult[0] == 'ERROR') || ($singleresult[0] == 'WARN') || ($singleresult[0] == 'INFO')) { 1563 $return[] = $singleresult; 1564 } 1565 } 1566 } 1567 } 1568 elseif (isset($_POST['form_subpage_' . get_class($this) . '_homedir_delete_' . $i])) { 1569 $remote = new \LAM\REMOTE\Remote(); 1570 $remote->connect($lamdaemonServers[$i]); 1571 $result = $remote->execute( 1572 implode( 1573 self::$SPLIT_DELIMITER, 1574 array( 1575 $this->attributes['uid'][0], 1576 "home", 1577 "rem", 1578 $lamdaemonServers[$i]->getHomeDirPrefix() . $this->attributes[$homeDirAttr][0], 1579 $this->attributes['uidNumber'][0] 1580 ) 1581 )); 1582 $remote->disconnect(); 1583 // lamdaemon results 1584 if (!empty($result)) { 1585 $singleresult = explode(",", $result); 1586 if (is_array($singleresult)) { 1587 if (($singleresult[0] == 'ERROR') || ($singleresult[0] == 'WARN') || ($singleresult[0] == 'INFO')) { 1588 $return[] = $singleresult; 1589 } 1590 } 1591 } 1592 } 1593 } 1594 return $return; 1595 } 1596 1597 /** 1598 * Returns the HTML meta data for the main account page. 1599 * 1600 * @return htmlElement HTML meta data 1601 */ 1602 function display_html_attributes() { 1603 $return = new htmlResponsiveRow(); 1604 $this->checkForInvalidConfiguration($return); 1605 $modules = $this->getAccountContainer()->get_type()->getModules(); 1606 $typeId = $this->getAccountContainer()->get_type()->getId(); 1607 if (!$this->isOptional($modules) || $this->skipObjectClass() || (isset($this->attributes['objectClass']) && in_array('posixAccount', $this->attributes['objectClass']))) { 1608 $homeDirAttr = $this->getHomedirAttrName($modules); 1609 $groupList = $this->findGroups($modules); // list of all group names 1610 $groups = array(); 1611 for ($i = 0; $i < sizeof($groupList); $i++) { 1612 $groups[$groupList[$i][1]] = $groupList[$i][0]; 1613 } 1614 if (count($groups)==0) { 1615 $return->add(new htmlStatusMessage("ERROR", _('No Unix groups found in LDAP! Please create one first.')), 12); 1616 return $return; 1617 } 1618 $shelllist = $this->getShells(); // list of all valid shells 1619 1620 // set default values 1621 if (empty($this->attributes['uid'][0])) { 1622 if ($this->getAccountContainer()->getAccountModule('inetOrgPerson') != null) { 1623 // fill default value for user ID with first/last name 1624 $attrs = $this->getAccountContainer()->getAccountModule('inetOrgPerson')->getAttributes(); 1625 $this->attributes['uid'][0] = $this->getUserNameSuggestion($attrs, $typeId); 1626 $firstSuggestion = $this->attributes['uid'][0]; 1627 if (!empty($this->attributes['uid'][0]) && $this->userNameExists($this->attributes['uid'][0], $typeId)) { 1628 while ($this->userNameExists($this->attributes['uid'][0], $typeId)) { 1629 $this->attributes['uid'][0] = $this->getNextUserName($this->attributes['uid'][0], array_keys($this->getAccountContainer()->getAccountModules())); 1630 } 1631 $users = $this->getUserNames($typeId); 1632 $msg = new htmlStatusMessage($this->messages['uid'][5][0], 1633 $this->messages['uid'][5][1], $this->messages['uid'][5][2], 1634 array(htmlspecialchars($users[$firstSuggestion]))); 1635 $return->add($msg, 12); 1636 } 1637 } 1638 elseif ($this->getAccountContainer()->getAccountModule('windowsUser') != null) { 1639 // fill default value for user ID with AD user name 1640 $attrs = $this->getAccountContainer()->getAccountModule('windowsUser')->getAttributes(); 1641 if (!empty($attrs['userPrincipalName'][0])) { 1642 $parts = explode('@', $attrs['userPrincipalName'][0]); 1643 $this->attributes['uid'][0] = $parts[0]; 1644 } 1645 } 1646 } 1647 if ($this->manageCn($modules) && (!isset($this->attributes['cn'][0]) || ($this->attributes['cn'][0] == ''))) { 1648 // set a default value for common name 1649 if (($this->get_scope() == 'host') && isset($_POST['uid'])) { 1650 if (substr($_POST['uid'], -1, 1) == '$') { 1651 $this->attributes['cn'][0] = substr($_POST['uid'], 0, strlen($_POST['uid']) - 1); 1652 } 1653 else { 1654 $this->attributes['cn'][0] = $_POST['uid']; 1655 } 1656 } 1657 elseif ($this->getAccountContainer()->getAccountModule('inetOrgPerson') != null) { 1658 $attrs = $this->getAccountContainer()->getAccountModule('inetOrgPerson')->getAttributes(); 1659 if ($attrs['givenName'][0]) { 1660 $this->attributes['cn'][0] = $attrs['givenName'][0] . " " . $attrs['sn'][0]; 1661 } 1662 elseif ($attrs['sn'][0]) { 1663 $this->attributes['cn'][0] = $attrs['sn'][0]; 1664 } 1665 else { 1666 $this->attributes['cn'][0] = $_POST['uid']; 1667 } 1668 } 1669 elseif (isset($_POST['uid'])) { 1670 $this->attributes['cn'][0] = $_POST['uid']; 1671 } 1672 } 1673 1674 $userName = ''; 1675 if (isset($this->attributes['uid'][0])) { 1676 $userName = $this->attributes['uid'][0]; 1677 } 1678 $uidLabel = _("User name"); 1679 if ($this->get_scope() == 'host') { 1680 $uidLabel = _("Host name"); 1681 } 1682 $uidInput = new htmlResponsiveInputField($uidLabel, 'uid', $userName, 'uid'); 1683 $uidInput->setRequired(true); 1684 $uidInput->setFieldMaxLength(100); 1685 $return->add($uidInput, 12); 1686 if ($this->manageCn($modules)) { 1687 $this->addMultiValueInputTextField($return, 'cn', _("Common name")); 1688 } 1689 $uidNumber = ''; 1690 if (isset($this->attributes['uidNumber'][0])) { 1691 $uidNumber = $this->attributes['uidNumber'][0]; 1692 } 1693 $uidNumberInput = new htmlResponsiveInputField(_('UID number'), 'uidNumber', $uidNumber, 'uidNumber'); 1694 $uidNumberInput->setFieldMaxLength(20); 1695 $uidNumberInput->setValidationRule(htmlElement::VALIDATE_NUMERIC); 1696 $return->add($uidNumberInput, 12); 1697 if (!$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegecos')) { 1698 $gecos = ''; 1699 if (isset($this->attributes['gecos'][0])) { 1700 $gecos = $this->attributes['gecos'][0]; 1701 } 1702 $return->add(new htmlResponsiveInputField(_('Gecos'), 'gecos', $gecos, 'gecos'), 12); 1703 } 1704 $primaryGroup = array(); 1705 if (isset($this->attributes['gidNumber'][0])) { 1706 $primaryGroup[] = $this->attributes['gidNumber'][0]; 1707 } 1708 $gidNumberSelect = new htmlResponsiveSelect('gidNumber', $groups, $primaryGroup, _('Primary group'), 'gidNumber'); 1709 $gidNumberSelect->setHasDescriptiveElements(true); 1710 $return->add($gidNumberSelect, 12); 1711 1712 if ($this->get_scope() == 'user') { 1713 // new Unix group with same name 1714 $posixGroupType = $this->getPosixGroupType(); 1715 if ($this->getAccountContainer()->isNewAccount 1716 && !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hideCreateGroup') 1717 && ($posixGroupType !== null) 1718 && (!isset($this->attributes['uid'][0]) || !isset($groups[$this->attributes['uid'][0]]))) { 1719 $return->addLabel(new htmlOutputText(' ', false)); 1720 $return->addField(new htmlButton('posixAccount_createGroup', _('Create group with same name'))); 1721 } 1722 // additional groups 1723 if (!$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegon') || !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hideposixGroups')) { 1724 $return->addLabel(new htmlOutputText(_('Additional groups'))); 1725 $additionalGroups = new htmlGroup(); 1726 $additionalGroups->addElement(new htmlAccountPageButton(get_class($this), 'group', 'open', _('Edit groups'))); 1727 $additionalGroups->addElement(new htmlHelpLink('addgroup')); 1728 $return->addField($additionalGroups); 1729 } 1730 // home directory 1731 $homeDir = isset($this->attributes[$homeDirAttr][0]) ? $this->attributes[$homeDirAttr][0] : ''; 1732 $homedirInput = new htmlResponsiveInputField(_('Home directory'), 'homeDirectory', $homeDir, 'homeDirectory'); 1733 $homedirInput->setRequired(true); 1734 $return->add($homedirInput, 12); 1735 if (($_SESSION['config']->get_scriptPath() != null) && ($_SESSION['config']->get_scriptPath() != '')) { 1736 if ($this->getAccountContainer()->isNewAccount) { 1737 // get list of lamdaemon servers 1738 $lamdaemonServers = $_SESSION['config']->getConfiguredScriptServers(); 1739 $homeDirLabel = new htmlOutputText(_('Create home directory')); 1740 $return->addLabel($homeDirLabel); 1741 $homeServerContainer = new htmlTable(); 1742 for ($h = 0; $h < sizeof($lamdaemonServers); $h++) { 1743 $homeServerContainer->addElement(new htmlTableExtendedInputCheckbox('createhomedir_' . $h, in_array($lamdaemonServers[$h]->getServer(), $this->lamdaemonServers), $lamdaemonServers[$h]->getLabel(), null, false)); 1744 if ($h === 0) { 1745 $homeDirHelp = new htmlHelpLink('createhomedir'); 1746 $homeServerContainer->addElement($homeDirHelp); 1747 } 1748 $homeServerContainer->addNewLine(); 1749 } 1750 $return->addField($homeServerContainer); 1751 } 1752 else { 1753 $return->addLabel(new htmlOutputText(' ', false)); 1754 $return->addField(new htmlAccountPageButton(get_class($this), 'homedir', 'open', _('Check home directories'))); 1755 } 1756 } 1757 $selectedShell = array(); 1758 if (isset($this->attributes['loginShell'][0])) { 1759 $selectedShell = array($this->attributes['loginShell'][0]); 1760 } 1761 $return->add(new htmlResponsiveSelect('loginShell', $shelllist, $selectedShell, _('Login shell'), 'loginShell'), 12); 1762 } 1763 // password buttons 1764 if (checkIfWriteAccessIsAllowed($this->get_scope()) 1765 && isset($this->attributes[$this->getPasswordAttrName($modules)][0]) 1766 && $this->isPasswordManaged()) { 1767 $return->addLabel(new htmlOutputText(_('Password'))); 1768 $pwdContainer = new htmlGroup(); 1769 if (pwd_is_enabled($this->attributes[$this->getPasswordAttrName($modules)][0])) { 1770 $pwdContainer->addElement(new htmlButton('lockPassword', _('Lock password'))); 1771 } 1772 else { 1773 $pwdContainer->addElement(new htmlButton('unlockPassword', _('Unlock password'))); 1774 } 1775 $pwdContainer->addElement(new htmlButton('removePassword', _('Remove password'))); 1776 $return->addField($pwdContainer); 1777 } 1778 // remove button 1779 if ($this->isOptional($modules) && !$this->skipObjectClass()) { 1780 $return->addVerticalSpacer('2rem'); 1781 $remButton = new htmlButton('remObjectClass', _('Remove Unix extension')); 1782 $return->add($remButton, 12, 12, 12, 'text-center'); 1783 } 1784 } 1785 else { 1786 // add button 1787 $return->add(new htmlButton('addObjectClass', _('Add Unix extension')), 12); 1788 } 1789 return $return; 1790 } 1791 1792 /** 1793 * Checks if the configuration is valid and prints an error if not. 1794 * 1795 * @param htmlResponsiveRow $content content area 1796 */ 1797 private function checkForInvalidConfiguration(htmlResponsiveRow $content) { 1798 $typeId = $this->getAccountContainer()->get_type()->getId(); 1799 if ($this->get_scope() == 'user') { 1800 $generatorOption = 'posixAccount_' . $typeId . '_uidGeneratorUsers'; 1801 } 1802 else { 1803 $generatorOption = 'posixAccount_' . $typeId . '_uidGeneratorHosts'; 1804 } 1805 if (empty($this->moduleSettings[$generatorOption])) { 1806 $message = new htmlStatusMessage('ERROR', _('Invalid configuration detected. Please edit your server profile (module settings) and fill all required fields.')); 1807 $content->add($message, 12); 1808 } 1809 } 1810 1811 /** 1812 * Displays the delete homedir option for the delete page. 1813 * 1814 * @return htmlElement meta HTML code 1815 */ 1816 function display_html_delete() { 1817 $return = new htmlResponsiveRow(); 1818 if ($this->get_scope() == 'user' && ($_SESSION['config']->get_scriptPath() != null)) { 1819 $return->add(new htmlResponsiveInputCheckbox('deletehomedir', true, _('Delete home directory'), 'deletehomedir'), 12); 1820 } 1821 $typeManager = new TypeManager(); 1822 $sudoTypes = $typeManager->getConfiguredTypesForScope('sudo'); 1823 if (($this->get_scope() == 'user') && !empty($sudoTypes)) { 1824 $return->add(new htmlResponsiveInputCheckbox('deleteSudoers', true, _('Delete sudo rights'), 'deleteSudoers'), 12); 1825 } 1826 return $return; 1827 } 1828 1829 /** 1830 * Displays the group selection. 1831 * 1832 * @return htmlElement meta HTML code 1833 */ 1834 function display_html_group() { 1835 $return = new htmlResponsiveRow(); 1836 $modules = $this->getAccountContainer()->get_type()->getModules(); 1837 $typeId = $this->getAccountContainer()->get_type()->getId(); 1838 $showUnix = !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hideposixGroups'); 1839 $autoSyncGon = $this->isBooleanConfigOptionSet('posixGroup_autoSyncGon'); 1840 if ($showUnix) { 1841 // load list with all groups 1842 $groups = $this->findGroups($modules); 1843 for ($i = 0; $i < sizeof($groups); $i++) { 1844 $groups[$i] = $groups[$i][1]; 1845 } 1846 // remove groups the user is member of from grouplist 1847 $groups = array_delete($this->groups, $groups); 1848 // Remove primary group from grouplist 1849 $group = $this->getGroupName($this->attributes['gidNumber'][0]); 1850 $groups = array_flip($groups); 1851 unset ($groups[$group]); 1852 $groups = array_flip($groups); 1853 1854 $unixContainer = new htmlTable(); 1855 $unixContainer->alignment = htmlElement::ALIGN_TOP; 1856 $unixContainer->addElement(new htmlSubTitle(_("Unix groups")), true); 1857 if ($autoSyncGon) { 1858 $this->syncGonToGroups(); 1859 foreach ($this->groups as $group) { 1860 $unixContainer->addElement(new htmlOutputText($group), true); 1861 } 1862 } 1863 else { 1864 $unixContainer->addElement(new htmlOutputText(_("Selected groups"))); 1865 $unixContainer->addElement(new htmlOutputText('')); 1866 $unixContainer->addElement(new htmlOutputText(_("Available groups"))); 1867 $unixContainer->addNewLine(); 1868 1869 $remSelect = new htmlSelect('removegroups', $this->groups, null, 15); 1870 $remSelect->setMultiSelect(true); 1871 $remSelect->setTransformSingleSelect(false); 1872 $unixContainer->addElement($remSelect); 1873 $buttonContainer = new htmlTable(); 1874 $buttonContainer->addElement(new htmlButton('addgroups_button', 'back.gif', true), true); 1875 $buttonContainer->addElement(new htmlButton('removegroups_button', 'forward.gif', true), true); 1876 $buttonContainer->addElement(new htmlHelpLink('addgroup')); 1877 $unixContainer->addElement($buttonContainer); 1878 $addSelect = new htmlSelect('addgroups', $groups, null, 15); 1879 $addSelect->setMultiSelect(true); 1880 $addSelect->setTransformSingleSelect(false); 1881 $unixContainer->addElement($addSelect, true); 1882 } 1883 1884 $return->add($unixContainer, 12); 1885 $return->addVerticalSpacer('3rem'); 1886 } 1887 1888 $showGon = self::areGroupOfNamesActive() && !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegon'); 1889 if ($showGon) { 1890 $gons = $this->findGroupOfNames(); 1891 1892 $gonContainer = new htmlTable(); 1893 $gonContainer->alignment = htmlElement::ALIGN_TOP; 1894 $gonContainer->addElement(new htmlSubTitle(_("Groups of names")), true); 1895 $gonContainer->addElement(new htmlOutputText(_("Selected groups"))); 1896 $gonContainer->addElement(new htmlOutputText('')); 1897 $gonContainer->addElement(new htmlOutputText(_("Available groups"))); 1898 $gonContainer->addNewLine(); 1899 1900 $selectedGons = array(); 1901 for ($i = 0; $i < sizeof($this->gonList); $i++) { 1902 if (isset($gons[$this->gonList[$i]])) { 1903 $selectedGons[getAbstractDN($this->gonList[$i])] = $this->gonList[$i]; 1904 } 1905 } 1906 uksort($selectedGons, 'compareDN'); 1907 $availableGons = array(); 1908 foreach ($gons as $dn => $attr) { 1909 if (!in_array($dn, $this->gonList)) { 1910 $availableGons[getAbstractDN($dn)] = $dn; 1911 } 1912 } 1913 uksort($availableGons, 'compareDN'); 1914 1915 $remGonSelect = new htmlSelect('removegons', $selectedGons, null, 15); 1916 $remGonSelect->setMultiSelect(true); 1917 $remGonSelect->setTransformSingleSelect(false); 1918 $remGonSelect->setHasDescriptiveElements(true); 1919 $remGonSelect->setSortElements(false); 1920 $remGonSelect->setRightToLeftTextDirection(true); 1921 $gonContainer->addElement($remGonSelect); 1922 $buttonGonContainer = new htmlTable(); 1923 $buttonGonContainer->addElement(new htmlButton('addgons_button', 'back.gif', true), true); 1924 $buttonGonContainer->addElement(new htmlButton('removegons_button', 'forward.gif', true), true); 1925 $buttonGonContainer->addElement(new htmlHelpLink('addgroup')); 1926 $gonContainer->addElement($buttonGonContainer); 1927 $addGonSelect = new htmlSelect('addgons', $availableGons, null, 15); 1928 $addGonSelect->setMultiSelect(true); 1929 $addGonSelect->setHasDescriptiveElements(true); 1930 $addGonSelect->setTransformSingleSelect(false); 1931 $addGonSelect->setSortElements(false); 1932 $addGonSelect->setRightToLeftTextDirection(true); 1933 $gonContainer->addElement($addGonSelect); 1934 $gonContainer->addNewLine(); 1935 $return->add($gonContainer, 12); 1936 $return->addVerticalSpacer('3rem'); 1937 } 1938 1939 $showGonSync = $showGon && !$autoSyncGon; 1940 $showUnixSync = $showUnix && !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_syncGroups'); 1941 $moduleList = $this->getAccountContainer()->get_type()->getModules(); 1942 $showWindowsSync = $this->isWindows($moduleList); 1943 if ($showUnixSync && ($showGonSync || $showWindowsSync)) { 1944 $return->add(new htmlSubTitle(_('Sync groups')), 12); 1945 $return->add(new htmlResponsiveInputCheckbox('syncDeleteGroups', true, _('Delete non-matching entries')), 12); 1946 $return->addVerticalSpacer('1rem'); 1947 if ($showGonSync) { 1948 $syncButtons = new htmlGroup(); 1949 $u2gonButton = new htmlAccountPageButton(get_class($this), 'group', 'syncU2GON', _('Sync Unix to group of names')); 1950 $u2gonButton->setIconClass('unixButton'); 1951 $syncButtons->addElement($u2gonButton); 1952 $syncButtons->addElement(new htmlSpacer('2rem', null)); 1953 $gon2uButton = new htmlAccountPageButton(get_class($this), 'group', 'syncGON2U', _('Sync group of names to Unix')); 1954 $gon2uButton->setIconClass('groupButton'); 1955 $syncButtons->addElement($gon2uButton); 1956 $return->add($syncButtons, 12, 12, 12, 'text-center'); 1957 if ($showWindowsSync) { 1958 $syncButtons->addElement(new htmlSpacer('2rem', null)); 1959 } 1960 } 1961 if ($showWindowsSync) { 1962 $syncButtons = new htmlGroup(); 1963 $gon2uButton = new htmlAccountPageButton(get_class($this), 'group', 'syncWin2U', _('Sync Windows to Unix')); 1964 $gon2uButton->setIconClass('sambaButton'); 1965 $syncButtons->addElement($gon2uButton); 1966 $return->add($syncButtons, 12, 12, 12, 'text-center'); 1967 } 1968 } 1969 1970 $return->addVerticalSpacer('2rem'); 1971 $backButton = new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')); 1972 $return->add($backButton, 12); 1973 return $return; 1974 } 1975 1976 /** 1977 * Displays the delete homedir option for the homedir page. 1978 * 1979 * @return htmlElement meta HTML code 1980 */ 1981 function display_html_homedir() { 1982 $modules = $this->getAccountContainer()->get_type()->getModules(); 1983 $homeDirAttr = $this->getHomedirAttrName($modules); 1984 $return = new htmlResponsiveRow(); 1985 $return->addLabel(new htmlOutputText(_('Home directory'))); 1986 $return->addField(new htmlOutputText($this->attributes[$homeDirAttr][0])); 1987 $return->addVerticalSpacer('2rem'); 1988 // get list of lamdaemon servers 1989 $lamdaemonServers = $_SESSION['config']->getConfiguredScriptServers(); 1990 for ($i = 0; $i < sizeof($lamdaemonServers); $i++) { 1991 $label = $lamdaemonServers[$i]->getLabel(); 1992 $remote = new \LAM\REMOTE\Remote(); 1993 $remote->connect($lamdaemonServers[$i]); 1994 $result = $remote->execute( 1995 implode( 1996 self::$SPLIT_DELIMITER, 1997 array( 1998 $this->attributes['uid'][0], 1999 "home", 2000 "check", 2001 $lamdaemonServers[$i]->getHomeDirPrefix() . $this->attributes[$homeDirAttr][0]) 2002 )); 2003 $remote->disconnect(); 2004 // lamdaemon results 2005 if (!empty($result)) { 2006 $returnValue = trim($result); 2007 if ($returnValue == 'ok') { 2008 $return->addLabel(new htmlOutputText($label)); 2009 $okGroup = new htmlGroup(); 2010 $okGroup->addElement(new htmlImage('../../graphics/pass.png', 16, 16)); 2011 $okGroup->addElement(new htmlSpacer('5px', null)); 2012 $okGroup->addElement(new htmlAccountPageButton(get_class($this), 'homedir', 'delete_' . $i, _('Delete'))); 2013 $return->addField($okGroup); 2014 } 2015 elseif ($returnValue == 'missing') { 2016 $return->addLabel(new htmlOutputText($label)); 2017 $failGroup = new htmlGroup(); 2018 $failGroup->addElement(new htmlImage('../../graphics/fail.png', 16, 16)); 2019 $failGroup->addElement(new htmlSpacer('5px', null)); 2020 $failGroup->addElement(new htmlAccountPageButton(get_class($this), 'homedir', 'create_' . $i, _('Create'))); 2021 $return->addField($failGroup); 2022 } 2023 elseif (trim($returnValue) != '') { 2024 $messageParams = explode(",", $returnValue); 2025 if (isset($messageParams[2])) { 2026 $message = new htmlStatusMessage($messageParams[0], htmlspecialchars($messageParams[1]), htmlspecialchars($messageParams[2])); 2027 } 2028 elseif (($messageParams[0] == 'ERROR') || ($messageParams[0] == 'WARN') || ($messageParams[0] == 'INFO')) { 2029 $message = new htmlStatusMessage($messageParams[0], htmlspecialchars($messageParams[1])); 2030 } 2031 else { 2032 $message = new htmlStatusMessage('WARN', htmlspecialchars($messageParams[0])); 2033 } 2034 $return->add($message, 12); 2035 } 2036 } 2037 } 2038 $return->addVerticalSpacer('2rem'); 2039 $return->add(new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')), 12, 12, 12, 'text-center'); 2040 return $return; 2041 } 2042 2043 /** 2044 * {@inheritDoc} 2045 */ 2046 function get_profileOptions($typeId) { 2047 $return = new htmlResponsiveRow(); 2048 $typeManager = new TypeManager(); 2049 $modules = $typeManager->getConfiguredType($typeId)->getModules(); 2050 $groupList = $this->findGroups($modules); 2051 $groups = array(); 2052 for ($i = 0; $i < sizeof($groupList); $i++) { 2053 $groups[] = $groupList[$i][1]; 2054 } 2055 if ($this->get_scope() == 'user') { 2056 $shelllist = $this->getShells(); // list of all valid shells 2057 // primary Unix group 2058 $return->add(new htmlResponsiveSelect('posixAccount_primaryGroup', $groups, array(), _('Primary group'), 'gidNumber'), 12); 2059 // additional group memberships 2060 $addGroupSelect = new htmlResponsiveSelect('posixAccount_additionalGroup', $groups, array(), _('Additional groups'), 'addgroup', 10); 2061 $addGroupSelect->setMultiSelect(true); 2062 $addGroupSelect->setTransformSingleSelect(false); 2063 $return->add($addGroupSelect, 12); 2064 // group of names 2065 if (self::areGroupOfNamesActive()) { 2066 $gons = $this->findGroupOfNames(); 2067 $gonList = array(); 2068 foreach ($gons as $dn => $attr) { 2069 $gonList[$attr['cn'][0]] = $dn; 2070 } 2071 $gonSelect = new htmlResponsiveSelect('posixAccount_gon', $gonList, array(), _('Groups of names'), 'addgroup', 10); 2072 $gonSelect->setHasDescriptiveElements(true); 2073 $gonSelect->setMultiSelect(true); 2074 $gonSelect->setTransformSingleSelect(false); 2075 $return->add($gonSelect, 12); 2076 } 2077 // common name 2078 if ($this->manageCn($modules)) { 2079 $return->add(new htmlResponsiveInputField(_('Common name'), 'posixAccount_cn', '', 'cn'), 12); 2080 } 2081 // home directory 2082 $return->add(new htmlResponsiveInputField(_('Home directory'), 'posixAccount_homeDirectory', '/home/$user', 'homeDirectory'), 12); 2083 // login shell 2084 $return->add(new htmlResponsiveSelect('posixAccount_loginShell', $shelllist, array("/bin/bash"), _('Login shell'), 'loginShell'), 12); 2085 // lamdaemon settings 2086 if ($_SESSION['config']->get_scriptPath() != null) { 2087 $return->add(new htmlSubTitle(_('Create home directory')), 12); 2088 $lamdaemonServers = $_SESSION['config']->getConfiguredScriptServers(); 2089 for ($h = 0; $h < sizeof($lamdaemonServers); $h++) { 2090 $server = $lamdaemonServers[$h]->getServer(); 2091 $label = $lamdaemonServers[$h]->getLabel(); 2092 $return->add(new htmlResponsiveInputCheckbox('posixAccount_createHomedir_' . $h, in_array($server, $this->lamdaemonServers), $label, 'createhomedir', false), 12); 2093 } 2094 } 2095 } 2096 elseif ($this->get_scope() == 'host') { 2097 // primary Unix group 2098 $return->add(new htmlResponsiveSelect('posixAccount_primaryGroup', $groups, array(), _('Primary group'), 'gidNumber'), 12); 2099 } 2100 if ($this->isOptional($modules)) { 2101 $return->add(new htmlResponsiveInputCheckbox('posixAccount_addExt', false, _('Automatically add this extension'), 'autoAdd'), 12); 2102 } 2103 return $return; 2104 } 2105 2106 /** 2107 * Loads the values of an account profile into internal variables. 2108 * 2109 * @param array $profile hash array with profile values (identifier => value) 2110 */ 2111 function load_profile($profile) { 2112 // profile mappings in meta data 2113 parent::load_profile($profile); 2114 $modules = $this->getAccountContainer()->get_type()->getModules(); 2115 // cn 2116 if ($this->manageCn($modules) && !empty($profile['posixAccount_cn'][0])) { 2117 $this->attributes['cn'][0] = $profile['posixAccount_cn'][0]; 2118 } 2119 // home directory 2120 $homeDirAttr = $this->getHomedirAttrName($modules); 2121 if (!empty($profile['posixAccount_homeDirectory'][0])) { 2122 $this->attributes[$homeDirAttr][0] = $profile['posixAccount_homeDirectory'][0]; 2123 } 2124 // special profile options 2125 // GID 2126 if (isset($profile['posixAccount_primaryGroup'][0])) { 2127 $gid = $this->getGID($profile['posixAccount_primaryGroup'][0]); 2128 if ($gid != null) { 2129 $this->attributes['gidNumber'][0] = $gid; 2130 } 2131 } 2132 // other group memberships 2133 if (isset($profile['posixAccount_additionalGroup'][0])) { 2134 $this->groups = $profile['posixAccount_additionalGroup']; 2135 } 2136 // group of names 2137 if (isset($profile['posixAccount_gon'][0])) { 2138 $this->gonList = $profile['posixAccount_gon']; 2139 } 2140 // lamdaemon 2141 if (($this->get_scope() == 'user') && $this->getAccountContainer()->isNewAccount) { 2142 $lamdaemonServers = $_SESSION['config']->getConfiguredScriptServers(); 2143 $this->lamdaemonServers = array(); 2144 for ($h = 0; $h < sizeof($lamdaemonServers); $h++) { 2145 if (isset($profile['posixAccount_createHomedir_' . $h][0]) && ($profile['posixAccount_createHomedir_' . $h][0] == 'true')) { 2146 $this->lamdaemonServers[] = $lamdaemonServers[$h]->getServer(); 2147 } 2148 } 2149 } 2150 // add extension 2151 if (isset($profile['posixAccount_addExt'][0]) && ($profile['posixAccount_addExt'][0] == "true")) { 2152 if (!$this->skipObjectClass() && !in_array('posixAccount', $this->attributes['objectClass'])) { 2153 $this->attributes['objectClass'][] = 'posixAccount'; 2154 } 2155 } 2156 } 2157 2158 /** 2159 * {@inheritDoc} 2160 * @see baseModule::get_pdfFields() 2161 */ 2162 public function get_pdfFields($typeId) { 2163 $fields = parent::get_pdfFields($typeId); 2164 $typeManager = new TypeManager(); 2165 $modules = $typeManager->getConfiguredType($typeId)->getModules(); 2166 if ($this->manageCn($modules)) { 2167 $fields['cn'] = _('Common name'); 2168 } 2169 if (!$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegecos')) { 2170 $fields['gecos'] = _('Gecos'); 2171 } 2172 return $fields; 2173 } 2174 2175 /** 2176 * {@inheritDoc} 2177 * @see baseModule::get_pdfEntries() 2178 */ 2179 function get_pdfEntries($pdfKeys, $typeId) { 2180 $uidLabel = _('User name'); 2181 if ($this->get_scope() == 'host') { 2182 $uidLabel = _('Host name'); 2183 } 2184 $additionalGroups = array(); 2185 if (!empty($this->groups)) { 2186 $additionalGroups = $this->groups; 2187 natcasesort($additionalGroups); 2188 } 2189 $modules = $this->getAccountContainer()->get_type()->getModules(); 2190 $homeDirAttr = $this->getHomedirAttrName($modules); 2191 $return = array(); 2192 if (isset($this->attributes['gidNumber'][0])) { 2193 $this->addPDFKeyValue($return, 'primaryGroup', _('Primary group'), $this->getGroupName($this->attributes['gidNumber'][0])); 2194 } 2195 $this->addPDFKeyValue($return, 'additionalGroups', _('Additional groups'), implode(", ", $additionalGroups)); 2196 $this->addSimplePDFField($return, 'uid', $uidLabel); 2197 $this->addSimplePDFField($return, 'cn', _('Common name')); 2198 $this->addSimplePDFField($return, 'uidNumber', _('UID number')); 2199 $this->addSimplePDFField($return, 'gidNumber', _('GID number')); 2200 $this->addSimplePDFField($return, 'homeDirectory', _('Home directory'), $homeDirAttr); 2201 $this->addSimplePDFField($return, 'loginShell', _('Login shell')); 2202 $this->addSimplePDFField($return, 'gecos', _('Gecos')); 2203 if (self::areGroupOfNamesActive()) { 2204 $gons = array(); 2205 for ($i = 0; $i < sizeof($this->gonList); $i++) { 2206 $gons[] = $this->gonList[$i]; 2207 } 2208 usort($gons, 'compareDN'); 2209 $gonCount = sizeof($gons); 2210 for ($i = 0; $i < $gonCount; $i++) { 2211 $gons[$i] = getAbstractDN($gons[$i]); 2212 } 2213 $this->addPDFKeyValue($return, 'gon', _('Groups of names'), $gons, "\n"); 2214 } 2215 if (isset($this->clearTextPassword)) { 2216 $this->addPDFKeyValue($return, 'userPassword', _('Password'), $this->clearTextPassword); 2217 } 2218 else if (isset($this->attributes['INFO.userPasswordClearText'])) { 2219 $this->addPDFKeyValue($return, 'userPassword', _('Password'), $this->attributes['INFO.userPasswordClearText']); 2220 } 2221 return $return; 2222 } 2223 2224 /** 2225 * {@inheritDoc} 2226 * @see baseModule::get_configOptions() 2227 */ 2228 public function get_configOptions($scopes, $allScopes) { 2229 $typeManager = new TypeManager($_SESSION['conf_config']); 2230 $isWindows = array_key_exists('windowsUser', $allScopes); 2231 $return = array(); 2232 $generatorOptions = array( 2233 _('Fixed range') => 'range', 2234 _('Samba ID pool') => 'sambaPool', 2235 _('Windows domain info') => 'windowsDomain', 2236 _('Magic number') => 'magicNumber' 2237 ); 2238 $hasUserConfig = false; 2239 $hasHostConfig = false; 2240 foreach ($scopes as $typeId) { 2241 if (getScopeFromTypeId($typeId) === 'user') { 2242 $hasUserConfig = true; 2243 } 2244 elseif (getScopeFromTypeId($typeId) === 'host') { 2245 $hasHostConfig = true; 2246 } 2247 } 2248 if ($hasUserConfig) { 2249 // user options 2250 $configUserContainer = new htmlResponsiveRow(); 2251 $configUserContainer->add(new htmlSubTitle(_("Users")), 12); 2252 foreach ($allScopes[get_class($this)] as $typeId) { 2253 if (!(getScopeFromTypeId($typeId) === 'user')) { 2254 continue; 2255 } 2256 if (sizeof($allScopes[get_class($this)]) > 1) { 2257 $title = new htmlDiv(null, new htmlOutputText($typeManager->getConfiguredType($typeId)->getAlias())); 2258 $title->setCSSClasses(array('bold', 'responsiveLabel')); 2259 $configUserContainer->add($title, 12, 6); 2260 $configUserContainer->add(new htmlOutputText(' ', false), 0, 6); 2261 } 2262 $uidGeneratorSelect = new htmlResponsiveSelect('posixAccount_' . $typeId . '_uidGeneratorUsers', $generatorOptions, array('range'), _('UID generator'), 'uidGenerator'); 2263 $uidGeneratorSelect->setHasDescriptiveElements(true); 2264 $uidGeneratorSelect->setTableRowsToHide(array( 2265 'range' => array('posixAccount_' . $typeId . '_sambaIDPoolDNUsers', 'posixAccount_' . $typeId . '_windowsIDPoolDNUsers', 2266 'posixAccount_' . $typeId . '_magicNumberUser'), 2267 'sambaPool' => array('posixAccount_' . $typeId . '_minUID', 'posixAccount_' . $typeId . '_maxUID', 2268 'posixAccount_' . $typeId . '_windowsIDPoolDNUsers', 'posixAccount_' . $typeId . '_magicNumberUser'), 2269 'windowsDomain' => array('posixAccount_' . $typeId . '_minUID', 'posixAccount_' . $typeId . '_maxUID', 2270 'posixAccount_' . $typeId . '_sambaIDPoolDNUsers', 'posixAccount_' . $typeId . '_magicNumberUser'), 2271 'magicNumber' => array('posixAccount_' . $typeId . '_minUID', 'posixAccount_' . $typeId . '_maxUID', 2272 'posixAccount_' . $typeId . '_windowsIDPoolDNUsers', 'posixAccount_' . $typeId . '_sambaIDPoolDNUsers') 2273 )); 2274 $uidGeneratorSelect->setTableRowsToShow(array( 2275 'range' => array('posixAccount_' . $typeId . '_minUID', 'posixAccount_' . $typeId . '_maxUID'), 2276 'sambaPool' => array('posixAccount_' . $typeId . '_sambaIDPoolDNUsers'), 2277 'windowsDomain' => array('posixAccount_' . $typeId . '_windowsIDPoolDNUsers'), 2278 'magicNumber' => array('posixAccount_' . $typeId . '_magicNumberUser') 2279 )); 2280 $configUserContainer->add($uidGeneratorSelect, 12); 2281 $uidUsersGeneratorDN = new htmlResponsiveInputField(_('Samba ID pool DN'), 'posixAccount_' . $typeId . '_sambaIDPoolDNUsers', null, 'sambaIDPoolDN'); 2282 $uidUsersGeneratorDN->setRequired(true); 2283 $configUserContainer->add($uidUsersGeneratorDN, 12); 2284 $uidUsersGeneratorWinDN = new htmlResponsiveInputField(_('Windows domain info DN'), 'posixAccount_' . $typeId . '_windowsIDPoolDNUsers', null, 'windowsIDPoolDN'); 2285 $uidUsersGeneratorWinDN->setRequired(true); 2286 $configUserContainer->add($uidUsersGeneratorWinDN, 12); 2287 $minUid = new htmlResponsiveInputField(_('Minimum UID number'), 'posixAccount_' . $typeId . '_minUID', null, 'minMaxUser'); 2288 $minUid->setRequired(true); 2289 $configUserContainer->add($minUid, 12); 2290 $maxUid = new htmlResponsiveInputField(_('Maximum UID number'), 'posixAccount_' . $typeId . '_maxUID', null, 'minMaxUser'); 2291 $maxUid->setRequired(true); 2292 $configUserContainer->add($maxUid, 12); 2293 $magicNumberUser = new htmlResponsiveInputField(_('Magic number'), 'posixAccount_' . $typeId . '_magicNumberUser', null, 'magicNumber'); 2294 $magicNumberUser->setRequired(true); 2295 $configUserContainer->add($magicNumberUser, 12); 2296 $configUserContainer->add(new htmlResponsiveInputField(_('Suffix for UID/user name check'), 'posixAccount_' . $typeId . '_uidCheckSuffixUser', '', 'uidCheckSuffix'), 12); 2297 $configUserContainer->add(new htmlResponsiveInputField(_('User name suggestion'), 'posixAccount_' . $typeId . '_userNameSuggestion', '@givenname@%sn%', 'userNameSuggestion'), 12); 2298 $configUserContainer->addVerticalSpacer('2rem'); 2299 $hiddenOptionsContainerHead = new htmlGroup(); 2300 $hiddenOptionsContainerHead->addElement(new htmlOutputText(_('Hidden options'))); 2301 $hiddenOptionsContainerHead->addElement(new htmlHelpLink('hiddenOptions')); 2302 $configUserContainer->add($hiddenOptionsContainerHead, 12); 2303 $configUserContainer->addVerticalSpacer('0.5rem'); 2304 $configUserContainer->add(new htmlResponsiveInputCheckbox('posixAccount_' . $typeId . '_hidegecos', false, _('Gecos'), null, true), 12, 4, 4); 2305 $configUserContainer->add(new htmlResponsiveInputCheckbox('posixAccount_' . $typeId . '_hidepassword', false, _('Password'), null, true), 12, 4, 4); 2306 $confActiveGONModules = array_merge($_SESSION['conf_config']->get_AccountModules('group'), $_SESSION['conf_config']->get_AccountModules('gon')); 2307 if (in_array('groupOfNames', $confActiveGONModules) || in_array('groupOfMembers', $confActiveGONModules) || in_array('groupOfUniqueNames', $confActiveGONModules)) { 2308 $configUserContainer->add(new htmlResponsiveInputCheckbox('posixAccount_' . $typeId . '_hidegon', false, _('Groups of names'), null, true), 12, 4, 4); 2309 $configUserContainer->add(new htmlResponsiveInputCheckbox('posixAccount_' . $typeId . '_hideCreateGroup', false, _('Create group with same name'), null, true), 12, 4, 4); 2310 $configUserContainer->add(new htmlResponsiveInputCheckbox('posixAccount_' . $typeId . '_hideposixGroups', false, _('Unix groups'), null, true), 12, 4, 4); 2311 $syncGroupsCheckbox = new htmlResponsiveInputCheckbox('posixAccount_' . $typeId . '_syncGroups', false, _('Sync groups'), null, true); 2312 $syncGroupsCheckbox->setTableRowsToHide(array('posixAccount_' . $typeId . '_syncGroupsExclusions')); 2313 $configUserContainer->add($syncGroupsCheckbox, 12, 4, 4); 2314 $configUserContainer->add(new htmlResponsiveInputTextarea('posixAccount_' . $typeId . '_syncGroupsExclusions', '', 20, 4, _('Exclude from group sync'), 'excludeFromGroupSync'), 12); 2315 } 2316 } 2317 $return[] = $configUserContainer; 2318 } 2319 // host options 2320 if ($hasHostConfig) { 2321 $configHostContainer = new htmlResponsiveRow(); 2322 $configHostContainer->add(new htmlSubTitle(_("Hosts")), 12); 2323 foreach ($allScopes[get_class($this)] as $typeId) { 2324 if (!(getScopeFromTypeId($typeId) === 'host')) { 2325 continue; 2326 } 2327 if (sizeof($allScopes[get_class($this)]) > 1) { 2328 $title = new htmlDiv(null, new htmlOutputText($typeManager->getConfiguredType($typeId)->getAlias())); 2329 $title->setCSSClasses(array('bold', 'responsiveLabel')); 2330 $configHostContainer->add($title, 12, 6); 2331 $configHostContainer->add(new htmlOutputText(' ', false), 0, 6); 2332 } 2333 $uidHostGeneratorSelect = new htmlResponsiveSelect('posixAccount_' . $typeId . '_uidGeneratorHosts', $generatorOptions, array('range'), _('UID generator'), 'uidGenerator'); 2334 $uidHostGeneratorSelect->setHasDescriptiveElements(true); 2335 $uidHostGeneratorSelect->setTableRowsToHide(array( 2336 'range' => array('posixAccount_' . $typeId . '_sambaIDPoolDNHosts', 'posixAccount_' . $typeId . '_windowsIDPoolDNHosts', 2337 'posixAccount_' . $typeId . '_magicNumberHost'), 2338 'sambaPool' => array('posixAccount_' . $typeId . '_minMachine', 'posixAccount_' . $typeId . '_maxMachine', 2339 'posixAccount_' . $typeId . '_windowsIDPoolDNHosts', 'posixAccount_' . $typeId . '_magicNumberHost'), 2340 'windowsDomain' => array('posixAccount_' . $typeId . '_minMachine', 'posixAccount_' . $typeId . '_maxMachine', 2341 'posixAccount_' . $typeId . '_sambaIDPoolDNHosts', 'posixAccount_' . $typeId . '_magicNumberHost'), 2342 'magicNumber' => array('posixAccount_' . $typeId . '_minMachine', 'posixAccount_' . $typeId . '_maxMachine', 2343 'posixAccount_' . $typeId . '_windowsIDPoolDNHosts', 'posixAccount_' . $typeId . '_sambaIDPoolDNHosts') 2344 )); 2345 $uidHostGeneratorSelect->setTableRowsToShow(array( 2346 'range' => array('posixAccount_' . $typeId . '_minMachine', 'posixAccount_' . $typeId . '_maxMachine'), 2347 'sambaPool' => array('posixAccount_' . $typeId . '_sambaIDPoolDNHosts'), 2348 'windowsDomain' => array('posixAccount_' . $typeId . '_windowsIDPoolDNHosts'), 2349 'magicNumber' => array('posixAccount_' . $typeId . '_magicNumberHost') 2350 )); 2351 $configHostContainer->add($uidHostGeneratorSelect, 12); 2352 $uidHostsGeneratorDN = new htmlResponsiveInputField(_('Samba ID pool DN'), 'posixAccount_' . $typeId . '_sambaIDPoolDNHosts', null, 'sambaIDPoolDN'); 2353 $uidHostsGeneratorDN->setRequired(true); 2354 $configHostContainer->add($uidHostsGeneratorDN, 12); 2355 $uidHostsGeneratorWinDN = new htmlResponsiveInputField(_('Windows domain info DN'), 'posixAccount_' . $typeId . '_windowsIDPoolDNHosts', null, 'windowsIDPoolDN'); 2356 $uidHostsGeneratorWinDN->setRequired(true); 2357 $configHostContainer->add($uidHostsGeneratorWinDN, 12); 2358 $minUid = new htmlResponsiveInputField(_('Minimum UID number'), 'posixAccount_' . $typeId . '_minMachine', null, 'minMaxHost'); 2359 $minUid->setRequired(true); 2360 $configHostContainer->add($minUid, 12); 2361 $maxUid = new htmlResponsiveInputField(_('Maximum UID number'), 'posixAccount_' . $typeId . '_maxMachine', null, 'minMaxHost'); 2362 $maxUid->setRequired(true); 2363 $configHostContainer->add($maxUid, 12); 2364 $magicNumberHost = new htmlResponsiveInputField(_('Magic number'), 'posixAccount_' . $typeId . '_magicNumberHost', null, 'magicNumber'); 2365 $magicNumberHost->setRequired(true); 2366 $configHostContainer->add($magicNumberHost, 12); 2367 $configHostContainer->add(new htmlResponsiveInputField(_('Suffix for UID/user name check'), 'posixAccount_' . $typeId . '_uidCheckSuffixHost', '', 'uidCheckSuffix'), 12); 2368 $hiddenOptionsContainerHead = new htmlGroup(); 2369 $hiddenOptionsContainerHead->addElement(new htmlOutputText(_('Hidden options'))); 2370 $hiddenOptionsContainerHead->addElement(new htmlHelpLink('hiddenOptions')); 2371 $configHostContainer->addLabel($hiddenOptionsContainerHead, 12); 2372 $configHostContainer->addField(new htmlOutputText('')); 2373 $configHostContainer->addVerticalSpacer('0.5rem'); 2374 $configHostContainer->add(new htmlResponsiveInputCheckbox('posixAccount_' . $typeId . '_hidegecos', false, _('Gecos'), null, false), 12); 2375 $configHostContainer->addVerticalSpacer('2rem'); 2376 } 2377 $return[] = $configHostContainer; 2378 } 2379 // common options 2380 $configOptionsContainer = new htmlResponsiveRow(); 2381 $configOptionsContainer->add(new htmlSubTitle(_('Options')), 12); 2382 $configOptionsContainer->add(new htmlResponsiveSelect('posixAccount_pwdHash', getSupportedHashTypes(), 2383 array('CRYPT-SHA512'), _("Password hash type"), 'pwdHash'), 12); 2384 $configOptionsContainer->add(new htmlResponsiveInputTextarea('posixAccount_shells', implode("\r\n", $this->getShells()), 30, 4, _('Login shells'), 'loginShells'), 12); 2385 $configOptionsContainer->add(new htmlResponsiveInputCheckbox('posixAccount_primaryGroupAsSecondary', false, _('Set primary group as memberUid'), 'primaryGroupAsSecondary'), 12); 2386 if ($isWindows) { 2387 $configOptionsContainer->add(new htmlResponsiveInputCheckbox('posixAccount_noObjectClass', false, _('Do not add object class'), 'noObjectClass'), 12); 2388 } 2389 $return[] = $configOptionsContainer; 2390 2391 return $return; 2392 } 2393 2394 /** 2395 * {@inheritDoc} 2396 * @see baseModule::check_configOptions() 2397 */ 2398 public function check_configOptions($typeIds, &$options) { 2399 $return = array(); 2400 $scopes = array(); 2401 $ranges = array(); 2402 foreach ($typeIds as $typeId) { 2403 $scopes[] = getScopeFromTypeId($typeId); 2404 // user settings 2405 if (getScopeFromTypeId($typeId) === 'user') { 2406 if ($options['posixAccount_' . $typeId . '_uidGeneratorUsers'][0] == 'range') { 2407 // min/maxUID are required, check if they are numeric 2408 if (!isset($options['posixAccount_' . $typeId . '_minUID'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_' . $typeId . '_minUID'][0])) { 2409 $return[] = $this->messages['minUID'][0]; 2410 } 2411 if (!isset($options['posixAccount_' . $typeId . '_maxUID'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_' . $typeId . '_maxUID'][0])) { 2412 $return[] = $this->messages['maxUID'][0]; 2413 } 2414 // minUID < maxUID 2415 if (isset($options['posixAccount_' . $typeId . '_minUID'][0]) && isset($options['posixAccount_' . $typeId . '_maxUID'][0])) { 2416 if ($options['posixAccount_' . $typeId . '_minUID'][0] >= $options['posixAccount_' . $typeId . '_maxUID'][0]) { 2417 $return[] = $this->messages['cmp_UID'][0]; 2418 } 2419 } 2420 $ranges[] = array($options['posixAccount_' . $typeId . '_minUID'][0], $options['posixAccount_' . $typeId . '_maxUID'][0]); 2421 } 2422 elseif ($options['posixAccount_' . $typeId . '_uidGeneratorUsers'][0] == 'sambaPool') { 2423 if (!isset($options['posixAccount_' . $typeId . '_sambaIDPoolDNUsers'][0]) || !get_preg($options['posixAccount_' . $typeId . '_sambaIDPoolDNUsers'][0], 'dn')) { 2424 $return[] = $this->messages['sambaIDPoolDN'][0]; 2425 } 2426 } 2427 elseif ($options['posixAccount_' . $typeId . '_uidGeneratorUsers'][0] == 'windowsDomain') { 2428 if (!isset($options['posixAccount_' . $typeId . '_windowsIDPoolDNUsers'][0]) || !get_preg($options['posixAccount_' . $typeId . '_windowsIDPoolDNUsers'][0], 'dn')) { 2429 $return[] = $this->messages['windowsIDPoolDN'][0]; 2430 } 2431 } 2432 } 2433 // host settings 2434 if (getScopeFromTypeId($typeId) === 'host') { 2435 if ($options['posixAccount_' . $typeId . '_uidGeneratorHosts'][0] == 'range') { 2436 // min/maxUID are required, check if they are numeric 2437 if (!isset($options['posixAccount_' . $typeId . '_minMachine'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_' . $typeId . '_minMachine'][0])) { 2438 $return[] = $this->messages['minMachine'][0]; 2439 } 2440 if (!isset($options['posixAccount_' . $typeId . '_maxMachine'][0]) || !preg_match('/^[0-9]+$/', $options['posixAccount_' . $typeId . '_maxMachine'][0])) { 2441 $return[] = $this->messages['maxMachine'][0]; 2442 } 2443 // minUID < maxUID 2444 if (isset($options['posixAccount_' . $typeId . '_minMachine'][0]) && isset($options['posixAccount_' . $typeId . '_maxMachine'][0])) { 2445 if ($options['posixAccount_' . $typeId . '_minMachine'][0] >= $options['posixAccount_' . $typeId . '_maxMachine'][0]) { 2446 $return[] = $this->messages['cmp_Machine'][0]; 2447 } 2448 } 2449 $ranges[] = array($options['posixAccount_' . $typeId . '_minMachine'][0], $options['posixAccount_' . $typeId . '_maxMachine'][0]); 2450 } 2451 elseif ($options['posixAccount_' . $typeId . '_uidGeneratorHosts'][0] == 'sambaPool') { 2452 if (!isset($options['posixAccount_' . $typeId . '_sambaIDPoolDNHosts'][0]) || !get_preg($options['posixAccount_' . $typeId . '_sambaIDPoolDNHosts'][0], 'dn')) { 2453 $return[] = $this->messages['sambaIDPoolDN'][0]; 2454 } 2455 } 2456 elseif ($options['posixAccount_' . $typeId . '_uidGeneratorHosts'][0] == 'windowsDomain') { 2457 if (!isset($options['posixAccount_' . $typeId . '_windowsIDPoolDNHosts'][0]) || !get_preg($options['posixAccount_' . $typeId . '_windowsIDPoolDNHosts'][0], 'dn')) { 2458 $return[] = $this->messages['windowsIDPoolDN'][0]; 2459 } 2460 } 2461 } 2462 } 2463 // check if user and host ranges overlap 2464 foreach ($ranges as $range) { 2465 foreach ($ranges as $rangeToCompare) { 2466 // check if minimum is inside other range 2467 if (($rangeToCompare[0] > $range[0]) && ($rangeToCompare[0] < $range[1])) { 2468 $return[] = $this->messages['cmp_both'][0]; 2469 break 2; 2470 } 2471 // check if maximum is inside other range 2472 if (($rangeToCompare[1] > $range[0]) && ($rangeToCompare[1] < $range[1])) { 2473 $return[] = $this->messages['cmp_both'][0]; 2474 break 2; 2475 } 2476 } 2477 } 2478 return $return; 2479 } 2480 2481 /** 2482 * {@inheritDoc} 2483 * @see baseModule::getManagedAttributes() 2484 */ 2485 function get_uploadColumns($selectedModules, &$type) { 2486 $typeId = $type->getId(); 2487 $return = parent::get_uploadColumns($selectedModules, $type); 2488 if ($this->isPasswordManaged($typeId)) { 2489 $return[] = array( 2490 'name' => 'posixAccount_password', 2491 'description' => _('Password'), 2492 'help' => 'userPassword', 2493 'example' => _('secret') 2494 ); 2495 $return[] = array( 2496 'name' => 'posixAccount_passwordDisabled', 2497 'description' => _('Lock password'), 2498 'help' => 'userPassword_lock', 2499 'example' => 'false', 2500 'values' => 'true, false', 2501 'default' => 'false' 2502 ); 2503 } 2504 if (($this->get_scope() == 'user') && $this->manageCn($selectedModules)) { 2505 array_unshift($return, array( 2506 'name' => 'posixAccount_cn', 2507 'description' => _('Common name'), 2508 'help' => 'cn', 2509 'example' => _('Steve Miller') 2510 )); 2511 } 2512 if (($this->get_scope() == 'user') && !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegecos')) { 2513 $return[] = array( 2514 'name' => 'posixAccount_gecos', 2515 'description' => _('Gecos'), 2516 'help' => 'gecos', 2517 'example' => _('Steve Miller,Room 2.14,123-123-1234,123-123-1234') 2518 ); 2519 } 2520 if (($this->get_scope() == 'host') && !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegecos')) { 2521 $return[] = array( 2522 'name' => 'posixAccount_gecos', 2523 'description' => _('Gecos'), 2524 'help' => 'gecos', 2525 'example' => _('pc01,Room 2.34') 2526 ); 2527 } 2528 return $return; 2529 } 2530 2531 /** 2532 * {@inheritDoc} 2533 * @see baseModule::build_uploadAccounts() 2534 */ 2535 function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules, &$type) { 2536 $errors = array(); 2537 $typeId = $type->getId(); 2538 $pwdAttrName = $this->getPasswordAttrName($selectedModules); 2539 $homedirAttrName = $this->getHomedirAttrName($selectedModules); 2540 $needAutoUID = array(); 2541 // get list of existing users 2542 $existingUsers = $this->getUserNames($typeId); 2543 // get list of existing groups 2544 $groupList = $this->findGroups($selectedModules); 2545 $groupMap = array(); 2546 for ($i = 0; $i < sizeof($groupList); $i++) { 2547 $groupMap[$groupList[$i][1]] = $groupList[$i][0]; 2548 } 2549 $existingGroups = array_keys($groupMap); 2550 // get list of existing group of names 2551 if (self::areGroupOfNamesActive()) { 2552 $gons = $this->findGroupOfNames(); 2553 $gonList = array(); 2554 foreach ($gons as $attr) { 2555 $gonList[] = $attr['cn'][0]; 2556 } 2557 } 2558 // check input 2559 foreach ($rawAccounts as $i => $rawAccount) { 2560 if (!$this->skipObjectClass() && !in_array("posixAccount", $partialAccounts[$i]['objectClass'])) { 2561 $partialAccounts[$i]['objectClass'][] = "posixAccount"; 2562 } 2563 // UID 2564 if ($rawAccount[$ids['posixAccount_uid']] == "") { 2565 // autoUID 2566 $needAutoUID[] = $i; 2567 } 2568 elseif (get_preg($rawAccount[$ids['posixAccount_uid']], 'digit')) { 2569 if (($this->get_scope() == 'user') && ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorUsers'][0] == 'range')) { 2570 if (($rawAccount[$ids['posixAccount_uid']] > $this->moduleSettings['posixAccount_' . $typeId . '_minUID'][0]) && 2571 ($rawAccount[$ids['posixAccount_uid']] < $this->moduleSettings['posixAccount_' . $typeId . '_maxUID'][0])) { 2572 $partialAccounts[$i]['uidNumber'] = trim($rawAccount[$ids['posixAccount_uid']]); 2573 } 2574 else { 2575 $errMsg = $this->messages['uidNumber'][4]; 2576 array_push($errMsg, array($i)); 2577 $errors[] = $errMsg; 2578 } 2579 } 2580 elseif (($this->get_scope() == 'host') && ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorHosts'][0] == 'range')) { 2581 if (($rawAccount[$ids['posixAccount_uid']] > $this->moduleSettings['posixAccount_' . $typeId . '_minMachine'][0]) && 2582 ($rawAccount[$ids['posixAccount_uid']] < $this->moduleSettings['posixAccount_' . $typeId . '_maxMachine'][0])) { 2583 $partialAccounts[$i]['uidNumber'] = trim($rawAccount[$ids['posixAccount_uid']]); 2584 } 2585 else { 2586 $errMsg = $this->messages['uidNumber'][4]; 2587 array_push($errMsg, array($i)); 2588 $errors[] = $errMsg; 2589 } 2590 } 2591 } 2592 else { 2593 $errMsg = $this->messages['uidNumber'][4]; 2594 array_push($errMsg, array($i)); 2595 $errors[] = $errMsg; 2596 } 2597 // GID number 2598 if (get_preg($rawAccount[$ids['posixAccount_group']], 'digit')) { 2599 $partialAccounts[$i]['gidNumber'] = $rawAccount[$ids['posixAccount_group']]; 2600 } 2601 if (get_preg($rawAccount[$ids['posixAccount_group']], 'groupname')) { 2602 $groupName = $rawAccount[$ids['posixAccount_group']]; 2603 $gid = nuLL; 2604 if (isset($groupMap[$groupName])) { 2605 $gid = $groupMap[$groupName]; 2606 } 2607 if (is_numeric($gid)) { 2608 $partialAccounts[$i]['gidNumber'] = $gid; 2609 } 2610 else { 2611 $errMsg = $this->messages['gidNumber'][0]; 2612 array_push($errMsg, array($i)); 2613 $errors[] = $errMsg; 2614 } 2615 } 2616 else { 2617 $errMsg = $this->messages['gidNumber'][1]; 2618 array_push($errMsg, array($i)); 2619 $errors[] = $errMsg; 2620 } 2621 // GECOS 2622 if (!$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidegecos')) { 2623 if (!empty($rawAccount[$ids['posixAccount_gecos']])) { 2624 if (get_preg($rawAccount[$ids['posixAccount_gecos']], 'gecos')) { 2625 $partialAccounts[$i]['gecos'] = $this->checkASCII($rawAccount[$ids['posixAccount_gecos']]); 2626 } 2627 else { 2628 $errMsg = $this->messages['gecos'][0]; 2629 array_push($errMsg, array($i)); 2630 $errors[] = $errMsg; 2631 } 2632 } 2633 else { 2634 $gecos = ""; 2635 if (($rawAccount[$ids['inetOrgPerson_firstName']] != "") && ($rawAccount[$ids['inetOrgPerson_lastName']] != "")) { 2636 $gecos = $rawAccount[$ids['inetOrgPerson_firstName']] . " " . $rawAccount[$ids['inetOrgPerson_lastName']]; 2637 if (!empty($rawAccount[$ids['inetOrgPerson_telephone']])) { 2638 $gecos = $gecos . ",," . $rawAccount[$ids['inetOrgPerson_telephone']]; // double "," because room is unknown 2639 if (!empty($rawAccount[$ids['inetOrgPerson_fax']])) { 2640 $gecos = $gecos . "," . $rawAccount[$ids['inetOrgPerson_fax']]; 2641 } 2642 } 2643 } 2644 if (!empty($gecos)) { 2645 $partialAccounts[$i]['gecos'] = $this->checkASCII($gecos); 2646 } 2647 } 2648 } 2649 // user specific attributes 2650 if ($this->get_scope() == 'user') { 2651 // additional groups 2652 if ($rawAccount[$ids['posixAccount_additionalGroups']] != "") { 2653 $groups = explode(",", $rawAccount[$ids['posixAccount_additionalGroups']]); 2654 for ($g = 0; $g < sizeof($groups); $g++) { 2655 if (!in_array($groups[$g], $existingGroups)) { 2656 $errors[] = array('ERROR', _('Unable to find group in LDAP.'), $groups[$g]); 2657 } 2658 } 2659 } 2660 // group of names 2661 if (self::areGroupOfNamesActive() && ($rawAccount[$ids['posixAccount_gon']] != "")) { 2662 $groups = explode(",", $rawAccount[$ids['posixAccount_gon']]); 2663 for ($g = 0; $g < sizeof($groups); $g++) { 2664 if (!in_array($groups[$g], $gonList)) { 2665 $errors[] = array('ERROR', _('Unable to find group in LDAP.'), $groups[$g]); 2666 } 2667 } 2668 } 2669 // user name 2670 if (array_key_exists($rawAccount[$ids['posixAccount_userName']], $existingUsers)) { 2671 $userName = $rawAccount[$ids['posixAccount_userName']]; 2672 while (array_key_exists($userName, $existingUsers)) { 2673 $userName = $this->getNextUserName($userName, $selectedModules); 2674 } 2675 $errMsg = $this->messages['uid'][9]; 2676 array_push($errMsg, array($i, $userName, $rawAccount[$ids['posixAccount_userName']], 2677 htmlspecialchars($existingUsers[$rawAccount[$ids['posixAccount_userName']]]))); 2678 $errors[] = $errMsg; 2679 } 2680 if (get_preg($rawAccount[$ids['posixAccount_userName']], 'username')) { 2681 $partialAccounts[$i]['uid'] = $rawAccount[$ids['posixAccount_userName']]; 2682 } 2683 else { 2684 $errMsg = $this->messages['uid'][7]; 2685 array_push($errMsg, array($i)); 2686 $errors[] = $errMsg; 2687 } 2688 // home directory 2689 if ($rawAccount[$ids['posixAccount_homedir']] == "") { 2690 $partialAccounts[$i][$homedirAttrName] = '/home/' . $partialAccounts[$i]['uid']; 2691 } 2692 elseif (get_preg($rawAccount[$ids['posixAccount_homedir']], 'homeDirectory')) { 2693 $partialAccounts[$i][$homedirAttrName] = $rawAccount[$ids['posixAccount_homedir']]; 2694 } 2695 else { 2696 $errMsg = $this->messages['homeDirectory'][2]; 2697 array_push($errMsg, array($i)); 2698 $errors[] = $errMsg; 2699 } 2700 // login shell 2701 if ($rawAccount[$ids['posixAccount_shell']] == "") { 2702 $partialAccounts[$i]['loginShell'] = '/bin/bash'; 2703 } 2704 elseif (in_array($rawAccount[$ids['posixAccount_shell']], $this->getShells())) { 2705 $partialAccounts[$i]['loginShell'] = $rawAccount[$ids['posixAccount_shell']]; 2706 } 2707 else { 2708 $errMsg = $this->messages['shell'][0]; 2709 array_push($errMsg, array($i)); 2710 $errors[] = $errMsg; 2711 } 2712 if ($this->isPasswordManaged($typeId)) { 2713 $pwd_enabled = true; 2714 // password enabled/disabled 2715 if ($rawAccount[$ids['posixAccount_passwordDisabled']] == "") { 2716 $pwd_enabled = true; 2717 } 2718 elseif (in_array($rawAccount[$ids['posixAccount_passwordDisabled']], array('true', 'false'))) { 2719 if ($rawAccount[$ids['posixAccount_passwordDisabled']] == 'true') { 2720 $pwd_enabled = false; 2721 } 2722 else { 2723 $pwd_enabled = true; 2724 } 2725 } 2726 else { 2727 $errMsg = $this->messages['passwordDisabled'][0]; 2728 array_push($errMsg, array($i)); 2729 $errors[] = $errMsg; 2730 } 2731 // password 2732 // delay exop passwords 2733 if (!empty($this->moduleSettings['posixAccount_pwdHash'][0]) && ($this->moduleSettings['posixAccount_pwdHash'][0] === 'LDAP_EXOP')) { 2734 // changed in post action 2735 } 2736 // set SASL passwords 2737 elseif (!empty($this->moduleSettings['posixAccount_pwdHash'][0]) && ($this->moduleSettings['posixAccount_pwdHash'][0] === 'SASL')) { 2738 $partialAccounts[$i][$pwdAttrName] = '{SASL}' . $partialAccounts[$i]['uid']; 2739 } 2740 // set K5KEY password 2741 elseif (!empty($this->moduleSettings['posixAccount_pwdHash'][0]) && ($this->moduleSettings['posixAccount_pwdHash'][0] === 'K5KEY')) { 2742 $partialAccounts[$i][$pwdAttrName] = pwd_hash('x', true, $this->moduleSettings['posixAccount_pwdHash'][0]); 2743 } 2744 // set normal password 2745 else { 2746 if (($rawAccount[$ids['posixAccount_password']] != "") && (get_preg($rawAccount[$ids['posixAccount_password']], 'password'))) { 2747 $partialAccounts[$i][$pwdAttrName] = pwd_hash($rawAccount[$ids['posixAccount_password']], $pwd_enabled, $this->moduleSettings['posixAccount_pwdHash'][0]); 2748 $partialAccounts[$i]['INFO.userPasswordClearText'] = $rawAccount[$ids['posixAccount_password']]; // for custom scripts etc. 2749 } 2750 elseif ($rawAccount[$ids['posixAccount_password']] != "") { 2751 $errMsg = $this->messages['userPassword'][4]; 2752 $errMsg[2] = str_replace('%', '%%', $errMsg[2]); // double "%" because of later sprintf 2753 array_push($errMsg, array($i)); 2754 $errors[] = $errMsg; 2755 } 2756 } 2757 } 2758 // cn 2759 if ($this->manageCn($selectedModules)) { 2760 if ($rawAccount[$ids['posixAccount_cn']] != "") { 2761 if (get_preg($rawAccount[$ids['posixAccount_cn']], 'cn')) { 2762 $partialAccounts[$i]['cn'] = $rawAccount[$ids['posixAccount_cn']]; 2763 } 2764 else { 2765 $errMsg = $this->messages['cn'][1]; 2766 array_push($errMsg, array($i)); 2767 $errors[] = $errMsg; 2768 } 2769 } 2770 else { 2771 if ($partialAccounts[$i]['givenName']) { 2772 $partialAccounts[$i]['cn'] = $partialAccounts[$i]['givenName'] . " " . $partialAccounts[$i]['sn']; 2773 } 2774 elseif ($partialAccounts[$i]['sn']) { 2775 $partialAccounts[$i]['cn'] = $partialAccounts[$i]['sn']; 2776 } 2777 else { 2778 $partialAccounts[$i]['cn'] = $partialAccounts[$i]['uid']; 2779 } 2780 } 2781 } 2782 } 2783 // host specific attributes 2784 elseif ($this->get_scope() == 'host') { 2785 // host name 2786 if (array_key_exists($rawAccount[$ids['posixAccount_hostName']], $existingUsers)) { 2787 $userName = $rawAccount[$ids['posixAccount_hostName']]; 2788 while (array_key_exists($userName, $existingUsers)) { 2789 $userName = $this->getNextUserName($userName, $selectedModules); 2790 } 2791 $errMsg = $this->messages['uid'][10]; 2792 array_push($errMsg, array($i, $userName, $rawAccount[$ids['posixAccount_hostName']], 2793 htmlspecialchars($existingUsers[$rawAccount[$ids['posixAccount_hostName']]]))); 2794 $errors[] = $errMsg; 2795 } 2796 if (get_preg($rawAccount[$ids['posixAccount_hostName']], 'hostname')) { 2797 $partialAccounts[$i]['uid'] = $rawAccount[$ids['posixAccount_hostName']]; 2798 $partialAccounts[$i]['cn'] = $rawAccount[$ids['posixAccount_hostName']]; 2799 } 2800 else { 2801 $errMsg = $this->messages['uid'][8]; 2802 array_push($errMsg, array($i)); 2803 $errors[] = $errMsg; 2804 } 2805 // description 2806 if (isset($ids['posixAccount_description']) && isset($rawAccount[$ids['posixAccount_description']]) && ($rawAccount[$ids['posixAccount_description']] != '')) { 2807 $partialAccounts[$i]['description'] = $rawAccount[$ids['posixAccount_description']]; 2808 } 2809 else { 2810 $partialAccounts[$i]['description'] = $rawAccount[$ids['posixAccount_hostName']]; 2811 } 2812 $partialAccounts[$i][$homedirAttrName] = '/dev/null'; 2813 $partialAccounts[$i]['loginShell'] = '/bin/false'; 2814 } 2815 } 2816 // fill in autoUIDs 2817 if (sizeof($needAutoUID) > 0) { 2818 $errorsTemp = array(); 2819 $uids = $this->getNextUIDs(sizeof($needAutoUID), $errorsTemp, $typeId); 2820 if (is_array($uids)) { 2821 foreach ($needAutoUID as $i => $index) { 2822 $partialAccounts[$index]['uidNumber'] = $uids[$i]; 2823 } 2824 } 2825 else { 2826 $errors[] = $this->messages['uidNumber'][2]; 2827 } 2828 } 2829 return $errors; 2830 } 2831 2832 /** 2833 * {@inheritDoc} 2834 * @see baseModule::doUploadPostActions() 2835 */ 2836 function doUploadPostActions(&$data, $ids, $failed, &$temp, &$accounts, $selectedModules, $type) { 2837 if (!checkIfWriteAccessIsAllowed($this->get_scope())) { 2838 die(); 2839 } 2840 $homeDirAttr = $this->getHomedirAttrName($selectedModules); 2841 if ($this->get_scope() != 'user') { 2842 return array( 2843 'status' => 'finished', 2844 'progress' => 100, 2845 'errors' => array() 2846 ); 2847 } 2848 // on first call generate list of ldap operations 2849 if (!isset($temp['counter'])) { 2850 $temp['groups'] = array(); 2851 $temp['dn_gon'] = array(); 2852 $temp['createHomes'] = array(); 2853 $temp['exop'] = array(); 2854 $temp['counter'] = 0; 2855 $col = $ids['posixAccount_additionalGroups']; 2856 $col_home = $ids['posixAccount_createHomeDir']; 2857 // get list of existing groups 2858 $groupList = $this->findGroups($selectedModules); 2859 $groupMap = array(); 2860 for ($i = 0; $i < sizeof($groupList); $i++) { 2861 $groupMap[$groupList[$i][0]] = $groupList[$i][1]; 2862 } 2863 // get list of existing group of names 2864 if (self::areGroupOfNamesActive()) { 2865 $gonList = $this->findGroupOfNames(); 2866 $gonMap = array(); 2867 foreach ($gonList as $dn => $attr) { 2868 $gonMap[$attr['cn'][0]] = $dn; 2869 } 2870 } 2871 foreach ($data as $i => $dataRow) { 2872 if (in_array($i, $failed)) continue; // ignore failed accounts 2873 if ($dataRow[$col] != "") { 2874 $groups = explode(",", $dataRow[$col]); 2875 if (isset($this->moduleSettings['posixAccount_primaryGroupAsSecondary'][0]) 2876 && ($this->moduleSettings['posixAccount_primaryGroupAsSecondary'][0] == 'true')) { 2877 if (get_preg($dataRow[$ids['posixAccount_group']], 'digit')) { 2878 if (!in_array($groupMap[$dataRow[$ids['posixAccount_group']]], $groups)) { 2879 $groups[] = $groupMap[$dataRow[$ids['posixAccount_group']]]; 2880 } 2881 } 2882 else { 2883 if (!in_array($groupMap[$dataRow[$ids['posixAccount_group']]], $groups)) { 2884 $groups[] = $dataRow[$ids['posixAccount_group']]; 2885 } 2886 } 2887 } 2888 for ($g = 0; $g < sizeof($groups); $g++) { 2889 if (!in_array($groups[$g], $temp['groups'])) $temp['groups'][] = $groups[$g]; 2890 $temp['members'][$groups[$g]][] = $dataRow[$ids['posixAccount_userName']]; 2891 } 2892 } 2893 if (isset($ids['posixAccount_gon']) && ($dataRow[$ids['posixAccount_gon']] != "")) { 2894 $gons = explode(",", $dataRow[$ids['posixAccount_gon']]); 2895 $memberAttr = 'member'; 2896 for ($g = 0; $g < sizeof($gons); $g++) { 2897 if (in_array_ignore_case('groupOfUniqueNames', $gonList[$gonMap[$gons[$g]]]['objectclass'])) { 2898 $memberAttr = 'uniqueMember'; 2899 } 2900 $temp['dn_gon'][$gonMap[$gons[$g]]][$memberAttr][] = $accounts[$i]['dn']; 2901 } 2902 } 2903 if (!empty($dataRow[$col_home])) { 2904 $temp['createHomes'][] = $i; 2905 } 2906 if (!empty($this->moduleSettings['posixAccount_pwdHash'][0]) && ($this->moduleSettings['posixAccount_pwdHash'][0] === 'LDAP_EXOP')) { 2907 if (isset($ids['posixAccount_password']) && !empty($dataRow[$ids['posixAccount_password']])) { 2908 $temp['exop'][] = array($accounts[$i]['dn'], $dataRow[$ids['posixAccount_password']]); 2909 } 2910 } 2911 } 2912 $temp['dn_gon_keys'] = array_keys($temp['dn_gon']); 2913 return array( 2914 'status' => 'inProgress', 2915 'progress' => 0, 2916 'errors' => array() 2917 ); 2918 } 2919 // get DNs of groups 2920 elseif (!isset($temp['dn'])) { 2921 $temp['dn'] = array(); 2922 $ldapEntries = searchLDAPByAttribute('cn', '*', 'posixGroup', array('dn', 'cn'), array('group')); 2923 for ($i = 0; $i < sizeof($ldapEntries); $i++) { 2924 $temp['dn'][$ldapEntries[$i]['cn'][0]] = $ldapEntries[$i]['dn']; 2925 } 2926 return array( 2927 'status' => 'inProgress', 2928 'progress' => 0, 2929 'errors' => array() 2930 ); 2931 } 2932 // add users to groups 2933 elseif ($temp['counter'] < sizeof($temp['groups'])) { 2934 if (isset($temp['dn'][$temp['groups'][$temp['counter']]])) { 2935 $memberUid = $temp['members'][$temp['groups'][$temp['counter']]]; 2936 $dnToUpdate = $temp['dn'][$temp['groups'][$temp['counter']]]; 2937 $groupAttrs = ldapGetDN($dnToUpdate, array('memberUID')); 2938 if (!empty($groupAttrs['memberuid'])) { 2939 // skip members that are already set 2940 $memberUid = array_delete($groupAttrs['memberuid'], $memberUid); 2941 } 2942 if (!empty($memberUid)) { 2943 $toAdd = array('memberUID' => $memberUid); 2944 $success = @ldap_mod_add($_SESSION['ldap']->server(), $dnToUpdate, $toAdd); 2945 $errors = array(); 2946 if (!$success) { 2947 $errors[] = array( 2948 "ERROR", 2949 _("LAM was unable to modify group memberships for group: %s"), 2950 getDefaultLDAPErrorString($_SESSION['ldap']->server()), 2951 array($temp['groups'][$temp['counter']]) 2952 ); 2953 } 2954 } 2955 $temp['counter']++; 2956 return array ( 2957 'status' => 'inProgress', 2958 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon']) + sizeof($temp['exop'])), 2959 'errors' => $errors 2960 ); 2961 } 2962 else { 2963 $temp['counter']++; 2964 return array ( 2965 'status' => 'inProgress', 2966 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups'] + sizeof($temp['createHomes']) + sizeof($temp['dn_gon']) + sizeof($temp['exop']))), 2967 'errors' => array(array('ERROR', _('Unable to find group in LDAP.'), $temp['groups'][$temp['counter']])) 2968 ); 2969 } 2970 } 2971 // create home directories 2972 elseif ($temp['counter'] < (sizeof($temp['groups']) + sizeof($temp['createHomes']))) { 2973 $pos = $temp['createHomes'][$temp['counter'] - sizeof($temp['groups'])]; 2974 try { 2975 $remote = new \LAM\REMOTE\Remote(); 2976 $remoteServer = $_SESSION['config']->getScriptServerByName($data[$pos][$ids['posixAccount_createHomeDir']]); 2977 $remote->connect($remoteServer); 2978 $result = $remote->execute( 2979 implode( 2980 self::$SPLIT_DELIMITER, 2981 array( 2982 $data[$pos][$ids['posixAccount_userName']], 2983 "home", 2984 "add", 2985 $remoteServer->getHomeDirPrefix() . $accounts[$pos][$homeDirAttr], 2986 "0".$_SESSION['config']->get_scriptRights(), 2987 $accounts[$pos]['uidNumber'], 2988 $accounts[$pos]['gidNumber'], 2989 ) 2990 )); 2991 $remote->disconnect(); 2992 $errors = array(); 2993 if (!empty($result)) { 2994 $parts = explode(",", $result); 2995 if (in_array($parts[0], array('ERROR', 'WARN'))) { 2996 $errors[] = $parts; 2997 } 2998 } 2999 } 3000 catch (LAMException $e) { 3001 $errors[] = array('ERROR', $e->getTitle(), $e->getMessage()); 3002 } 3003 $temp['counter']++; 3004 return array ( 3005 'status' => 'inProgress', 3006 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon']) + sizeof($temp['exop'])), 3007 'errors' => $errors 3008 ); 3009 } 3010 // add users to group of names 3011 elseif ($temp['counter'] < (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon']))) { 3012 $gonDn = $temp['dn_gon_keys'][$temp['counter'] - sizeof($temp['groups']) - sizeof($temp['createHomes'])]; 3013 $gonAttrToAdd = $temp['dn_gon'][$gonDn]; 3014 $gonAttrNames = array_keys($gonAttrToAdd); 3015 $gonAttrs = ldapGetDN($gonDn, $gonAttrNames); 3016 foreach ($gonAttrNames as $gonAttrName) { 3017 $gonAttrNameLower = strtolower($gonAttrName); 3018 if (!empty($gonAttrs[$gonAttrNameLower])) { 3019 $gonAttrToAdd[$gonAttrName] = array_delete($gonAttrs[$gonAttrNameLower], $gonAttrToAdd[$gonAttrName]); 3020 } 3021 if (empty($gonAttrToAdd[$gonAttrName])) { 3022 unset($gonAttrToAdd[$gonAttrName]); 3023 } 3024 } 3025 if (!empty($gonAttrToAdd)) { 3026 $success = @ldap_mod_add($_SESSION['ldap']->server(), $gonDn, $gonAttrToAdd); 3027 $errors = array(); 3028 if (!$success) { 3029 $errors[] = array( 3030 "ERROR", 3031 _("LAM was unable to modify group memberships for group: %s"), 3032 getDefaultLDAPErrorString($_SESSION['ldap']->server()), 3033 array($temp['groups'][$temp['counter']]) 3034 ); 3035 } 3036 } 3037 $temp['counter']++; 3038 return array ( 3039 'status' => 'inProgress', 3040 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon']) + sizeof($temp['exop'])), 3041 'errors' => $errors 3042 ); 3043 } 3044 // run password exop commands 3045 elseif ($temp['counter'] < (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon']) + sizeof($temp['exop']))) { 3046 $data = $temp['exop'][$temp['counter'] - sizeof($temp['groups']) - sizeof($temp['createHomes']) - sizeof($temp['dn_gon'])]; 3047 $dn = $data[0]; 3048 $password = $data[1]; 3049 $success = ldap_exop_passwd($_SESSION['ldap']->server(), $dn, null, $password); 3050 $errors = array(); 3051 if (!$success) { 3052 $errors[] = array( 3053 "ERROR", 3054 _('Unable to set password'), 3055 $dn . '<br>' . getDefaultLDAPErrorString($_SESSION['ldap']->server()), 3056 array($temp['groups'][$temp['counter']]) 3057 ); 3058 } 3059 $temp['counter']++; 3060 return array ( 3061 'status' => 'inProgress', 3062 'progress' => ($temp['counter'] * 100) / (sizeof($temp['groups']) + sizeof($temp['createHomes']) + sizeof($temp['dn_gon']) + sizeof($temp['exop'])), 3063 'errors' => $errors 3064 ); 3065 } 3066 // all modifications are done 3067 else { 3068 return array ( 3069 'status' => 'finished', 3070 'progress' => 100, 3071 'errors' => array() 3072 ); 3073 } 3074 } 3075 3076 /** 3077 * Returns one or more free UID numbers. 3078 * 3079 * @param integer $count Number of needed free UIDs. 3080 * @param array $errors list of error messages where errors can be added 3081 * @param string $typeId type id (e.g. user) 3082 * @return mixed Null if no UIDs are free else an array of free UIDs. 3083 */ 3084 function getNextUIDs($count, &$errors, $typeId) { 3085 // check if UIDs should be taken from Samba pool entry 3086 if (($this->get_scope() == 'user') && isset($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorUsers']) && ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorUsers'][0] == 'sambaPool')) { 3087 return $this->getNextSambaPoolUIDs($count, $errors, $typeId); 3088 } 3089 if (($this->get_scope() == 'host') && isset($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorHosts']) && ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorHosts'][0] == 'sambaPool')) { 3090 return $this->getNextSambaPoolUIDs($count, $errors, $typeId); 3091 } 3092 // check if UIDs should be taken from domain info pool entry 3093 if (($this->get_scope() == 'user') && isset($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorUsers']) && ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorUsers'][0] == 'windowsDomain')) { 3094 return $this->getNextDomainInfoUIDs($count, $errors, $typeId); 3095 } 3096 if (($this->get_scope() == 'host') && isset($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorHosts']) && ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorHosts'][0] == 'windowsDomain')) { 3097 return $this->getNextDomainInfoUIDs($count, $errors, $typeId); 3098 } 3099 // check if a magic number should be used 3100 if (($this->get_scope() == 'user') && isset($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorUsers']) && ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorUsers'][0] == 'magicNumber')) { 3101 $return = array(); 3102 for ($i = 0; $i < $count; $i++) { 3103 $return[] = $this->moduleSettings['posixAccount_' . $typeId . '_magicNumberUser'][0]; 3104 } 3105 return $return; 3106 } 3107 if (($this->get_scope() == 'host') && isset($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorHosts']) && ($this->moduleSettings['posixAccount_' . $typeId . '_uidGeneratorHosts'][0] == 'magicNumber')) { 3108 $return = array(); 3109 for ($i = 0; $i < $count; $i++) { 3110 $return[] = $this->moduleSettings['posixAccount_' . $typeId . '_magicNumberHost'][0]; 3111 } 3112 return $return; 3113 } 3114 $ret = array(); 3115 if ($this->get_scope() == "user") { 3116 $minID = intval($this->moduleSettings['posixAccount_' . $typeId . '_minUID'][0]); 3117 $maxID = intval($this->moduleSettings['posixAccount_' . $typeId . '_maxUID'][0]); 3118 } 3119 else { 3120 $minID = intval($this->moduleSettings['posixAccount_' . $typeId . '_minMachine'][0]); 3121 $maxID = intval($this->moduleSettings['posixAccount_' . $typeId . '_maxMachine'][0]); 3122 } 3123 $uidList = $this->getUIDs($typeId); 3124 $uids = array(); 3125 foreach ($uidList as $uid) { 3126 if (($uid <= $maxID) && ($uid >= $minID)) $uids[] = $uid; // ignore UIDs > maxID and UIDs < minID 3127 } 3128 for ($i = 0; $i < $count; $i++) { 3129 if (count($uids) != 0) { 3130 // there already are some uids 3131 // store highest id-number 3132 $id = $uids[count($uids)-1]; 3133 // Return minimum allowed id-number if all found id-numbers are too low 3134 if ($id < $minID) { 3135 $ret[] = $minID; 3136 $uids[] = $minID; 3137 } 3138 // return highest used id-number + 1 if it's still in valid range 3139 elseif ($id < $maxID) { 3140 $ret[] = $id + 1; 3141 $uids[] = $id + 1; 3142 } 3143 // find free numbers between existing ones 3144 else { 3145 $k = intval($minID); 3146 while (in_array($k, $uids)) $k++; 3147 if ($k > $maxID) return null; 3148 else { 3149 $ret[] = $k; 3150 $uids[] = $k; 3151 sort ($uids, SORT_NUMERIC); 3152 } 3153 // show warning message 3154 $errors[] = $this->messages['uidNumber'][2]; 3155 } 3156 } 3157 else { 3158 // return minimum allowed id-number if no id-numbers are found 3159 $ret[] = $minID; 3160 $uids[] = $minID; 3161 } 3162 } 3163 return $ret; 3164 } 3165 3166 /** 3167 * Gets the free UID numbers from an Samba pool entry in LDAP. 3168 * 3169 * @param integer $count number of needed free UIDs. 3170 * @param array $errors list of error messages where errors can be added 3171 * @param string $typeId type id (e.g. user) 3172 * @return mixed null if no UIDs are free else an array of free UIDs 3173 */ 3174 private function getNextSambaPoolUIDs($count, &$errors, $typeId) { 3175 if ($this->get_scope() == 'user') { 3176 $dn = $this->moduleSettings['posixAccount_' . $typeId . '_sambaIDPoolDNUsers'][0]; 3177 } 3178 else { 3179 $dn = $this->moduleSettings['posixAccount_' . $typeId . '_sambaIDPoolDNHosts'][0]; 3180 } 3181 $attrs = ldapGetDN($dn, array('uidNumber')); 3182 if (isset($attrs['uidnumber'][0]) && ($attrs['uidnumber'][0] != '')) { 3183 $newValue = $attrs['uidnumber'][0] + $count; 3184 $ldapHandle = $_SESSION['ldap']->server(); 3185 ldap_modify($ldapHandle, $dn, array('uidnumber' => array($newValue))); 3186 logNewMessage(LOG_DEBUG, 'Updated Samba ID pool ' . $dn . ' with UID number ' . $newValue . ' and LDAP code ' . ldap_errno($ldapHandle)); 3187 if (ldap_errno($ldapHandle) != 0) { 3188 logNewMessage(LOG_NOTICE, 'Updating Samba ID pool ' . $dn . ' with UID number ' . $newValue . ' failed. ' . ldap_error($ldapHandle)); 3189 return null; 3190 } 3191 $result = array(); 3192 for ($i = 0; $i < $count; $i++) { 3193 $result[] = $attrs['uidnumber'][0] + $i; 3194 } 3195 return $result; 3196 } 3197 return null; 3198 } 3199 3200 /** 3201 * Gets the free UID numbers from a domain info entry in LDAP. 3202 * 3203 * @param integer $count number of needed free UIDs. 3204 * @param array $errors list of error messages where errors can be added 3205 * @param string $typeId type id (e.g. user) 3206 * @return mixed null if no UIDs are free else an array of free UIDs 3207 */ 3208 private function getNextDomainInfoUIDs($count, &$errors, $typeId) { 3209 if ($this->get_scope() == 'user') { 3210 $dn = $this->moduleSettings['posixAccount_' . $typeId . '_windowsIDPoolDNUsers'][0]; 3211 } 3212 else { 3213 $dn = $this->moduleSettings['posixAccount_' . $typeId . '_windowsIDPoolDNHosts'][0]; 3214 } 3215 $attrs = ldapGetDN($dn, array('msSFU30MaxUidNumber')); 3216 if (!empty($attrs['mssfu30maxuidnumber'][0])) { 3217 $newValue = $attrs['mssfu30maxuidnumber'][0] + $count; 3218 $ldapHandle = $_SESSION['ldap']->server(); 3219 ldap_modify($ldapHandle, $dn, array('mssfu30maxuidnumber' => array($newValue))); 3220 logNewMessage(LOG_DEBUG, 'Updated domain info ' . $dn . ' with UID number ' . $newValue . ' and LDAP code ' . ldap_errno($ldapHandle)); 3221 if (ldap_errno($ldapHandle) != 0) { 3222 logNewMessage(LOG_NOTICE, 'Updating domain info ' . $dn . ' with UID number ' . $newValue . ' failed. ' . ldap_error($ldapHandle)); 3223 return null; 3224 } 3225 $result = array(); 3226 for ($i = 0; $i < $count; $i++) { 3227 $result[] = $attrs['mssfu30maxuidnumber'][0] + $i; 3228 } 3229 return $result; 3230 } 3231 return null; 3232 } 3233 3234 /** 3235 * Returns the meta HTML code for each input field. 3236 * format: array(<field1> => array(<META HTML>), ...) 3237 * It is not possible to display help links. 3238 * 3239 * @param array $fields list of active fields 3240 * @param array $attributes attributes of LDAP account 3241 * @param boolean $passwordChangeOnly indicates that the user is only allowed to change his password and no LDAP content is readable 3242 * @param array $readOnlyFields list of read-only fields 3243 * @return array list of meta HTML elements (field name => htmlResponsiveRow) 3244 */ 3245 function getSelfServiceOptions($fields, $attributes, $passwordChangeOnly, $readOnlyFields) { 3246 $return = array(); 3247 if (in_array('password', $fields)) { 3248 $row = new htmlResponsiveRow(); 3249 if (!empty($this->selfServiceSettings->moduleSettings['posixAccount_useOldPwd']) && ($this->selfServiceSettings->moduleSettings['posixAccount_useOldPwd'][0] == 'true')) { 3250 $pwd0 = new htmlResponsiveInputField(_('Old password'), 'posixAccount_passwordOld'); 3251 $pwd0->setIsPassword(true, true); 3252 $row->add($pwd0, 12); 3253 } 3254 $pwd1 = new htmlResponsiveInputField($this->getSelfServiceLabel('password', _('New password')), 'posixAccount_password'); 3255 $pwd1->setIsPassword(true, true); 3256 $row->add($pwd1, 12); 3257 $pwd2 = new htmlResponsiveInputField(_('Reenter password'), 'posixAccount_password2'); 3258 $pwd2->setIsPassword(true); 3259 $pwd2->setSameValueFieldID('posixAccount_password'); 3260 $row->add($pwd2, 12); 3261 $return['password'] = $row; 3262 } 3263 if ($passwordChangeOnly) { 3264 return $return; // only password fields as long no LDAP content can be read 3265 } 3266 if (in_array('cn', $fields)) { 3267 $cn = ''; 3268 if (isset($attributes['cn'][0])) { 3269 $cn = $attributes['cn'][0]; 3270 } 3271 $cnField = new htmlInputField('posixAccount_cn', $cn); 3272 if (in_array('cn', $readOnlyFields)) { 3273 $cnField = new htmlOutputText($cn); 3274 } 3275 $row = new htmlResponsiveRow(); 3276 $row->addLabel(new htmlOutputText($this->getSelfServiceLabel('cn', _('Common name')))); 3277 $row->addField($cnField); 3278 $return['cn'] = $row; 3279 } 3280 if (in_array('loginShell', $fields)) { 3281 $shelllist = $this->getShells(); // list of all valid shells 3282 $loginShell = ''; 3283 if (isset($attributes['loginShell'][0])) { 3284 $loginShell = $attributes['loginShell'][0]; 3285 } 3286 $loginShellField = new htmlSelect('posixAccount_loginShell', $shelllist, array($loginShell)); 3287 if (in_array('loginShell', $readOnlyFields)) { 3288 $loginShellField = new htmlOutputText($loginShell); 3289 } 3290 $row = new htmlResponsiveRow(); 3291 $row->addLabel(new htmlOutputText($this->getSelfServiceLabel('loginShell', _('Login shell')))); 3292 $row->addField($loginShellField); 3293 $return['loginShell'] = $row; 3294 } 3295 if (in_array('unixgroups', $fields) && !empty($this->selfServiceSettings->moduleSettings['posixAccount_groupDn'][0])) { 3296 $groupDn = $this->selfServiceSettings->moduleSettings['posixAccount_groupDn'][0]; 3297 $gidNumber = $attributes['gidNumber'][0]; 3298 $userName = $attributes['uid'][0]; 3299 if (!empty($userName)) { 3300 $filter = '(&(objectClass=posixGroup)(|(gidNumber=' . $gidNumber . ')(memberUid=' . $userName . ')))'; 3301 $results = searchLDAP($groupDn, $filter, array('cn')); 3302 $groups = array(); 3303 foreach ($results as $result) { 3304 $groups[] = $result['cn'][0]; 3305 } 3306 $groups = array_unique($groups); 3307 natcasesort($groups); 3308 $row = new htmlResponsiveRow(); 3309 $row->addLabel(new htmlOutputText($this->getSelfServiceLabel('unixgroups', _('Groups')))); 3310 $row->addField(new htmlOutputText(implode(', ', $groups))); 3311 $return['unixgroups'] = $row; 3312 } 3313 } 3314 return $return; 3315 } 3316 3317 /** 3318 * Checks if all input values are correct and returns the LDAP attributes which should be changed. 3319 * <br>Return values: 3320 * <br>messages: array of parameters to create status messages 3321 * <br>add: array of attributes to add 3322 * <br>del: array of attributes to remove 3323 * <br>mod: array of attributes to modify 3324 * <br>"info" are values with informational value (e.g. to be used later by pre/postModify actions) 3325 * 3326 * Calling this method does not require the existence of an enclosing {@link accountContainer}. 3327 * 3328 * @param string $fields input fields 3329 * @param array $attributes LDAP attributes 3330 * @param boolean $passwordChangeOnly indicates that the user is only allowed to change his password and no LDAP content is readable 3331 * @param array $readOnlyFields list of read-only fields 3332 * @return array messages and attributes (array('messages' => array(), 'add' => array('mail' => array('test@test.com')), 'del' => array(), 'mod' => array(), 'info' => array())) 3333 */ 3334 function checkSelfServiceOptions($fields, $attributes, $passwordChangeOnly, $readOnlyFields) { 3335 $return = array('messages' => array(), 'add' => array(), 'del' => array(), 'mod' => array(), 'info' => array()); 3336 if (in_array('password', $fields)) { 3337 if (isset($_POST['posixAccount_password']) && ($_POST['posixAccount_password'] != '')) { 3338 if ($_POST['posixAccount_password'] != $_POST['posixAccount_password2']) { 3339 $return['messages'][] = $this->messages['userPassword'][0]; 3340 } 3341 else { 3342 if (!get_preg($_POST['posixAccount_password'], 'password')) { 3343 $return['messages'][] = $this->messages['userPassword'][1]; 3344 } 3345 else { 3346 $userName = empty($attributes['uid'][0]) ? null : $attributes['uid'][0]; 3347 $additionalAttrs = array(); 3348 if (!empty($attributes['sn'][0])) { 3349 $additionalAttrs[] = $attributes['sn'][0]; 3350 } 3351 if (!empty($attributes['givenName'][0])) { 3352 $additionalAttrs[] = $attributes['givenName'][0]; 3353 } 3354 $pwdPolicyResult = checkPasswordStrength($_POST['posixAccount_password'], $userName, $additionalAttrs); 3355 if ($pwdPolicyResult === true) { 3356 $passwordHash = $this->selfServiceSettings->moduleSettings['posixAccount_pwdHash'][0]; 3357 if (empty($this->selfServiceSettings->moduleSettings['posixAccount_useOldPwd']) || ($this->selfServiceSettings->moduleSettings['posixAccount_useOldPwd'][0] != 'true')) { 3358 // set SASL password 3359 if (!empty($attributes['uid'][0]) && ($passwordHash === 'SASL')) { 3360 $return['mod']['userPassword'][0] = '{SASL}' . $attributes['uid'][0]; 3361 } 3362 elseif ($passwordHash === 'LDAP_EXOP') { 3363 // no LDAP modify action, use ldap_exop_passwd 3364 $return['info']['userPasswordModify'][0] = 'exop'; 3365 } 3366 // set other password hashes 3367 else { 3368 $return['mod']['userPassword'][0] = pwd_hash($_POST['posixAccount_password'], true, $passwordHash); 3369 } 3370 3371 } 3372 else { 3373 $return['add']['userPassword'][0] = pwd_hash($_POST['posixAccount_password'], true, $passwordHash); 3374 $return['del']['userPassword'][0] = $_POST['posixAccount_passwordOld']; 3375 } 3376 $return['info']['userPasswordClearText'][0] = $_POST['posixAccount_password']; 3377 if (isset($attributes['shadowLastChange'][0])) { 3378 $return['mod']['shadowLastChange'][0] = intval(time()/3600/24); 3379 } 3380 $_SESSION['selfService_clientPasswordNew'] = $_POST['posixAccount_password']; 3381 } 3382 else { 3383 $return['messages'][] = array('ERROR', $pwdPolicyResult); 3384 } 3385 } 3386 } 3387 } 3388 } 3389 // stop processing if only a password change is done 3390 if ($passwordChangeOnly) { 3391 return $return; 3392 } 3393 // sync from Windows password 3394 if (in_array('syncWindowsPassword', $fields) && !empty($_POST['windowsUser_unicodePwd'])) { 3395 $password = $_POST['windowsUser_unicodePwd']; 3396 $return['mod']['unixUserPassword'][0] = pwd_hash($password, true, $this->selfServiceSettings->moduleSettings['posixAccount_pwdHash'][0]); 3397 if (isset($attributes['shadowLastChange'][0])) { 3398 $return['mod']['shadowLastChange'][0] = intval(time()/3600/24); 3399 } 3400 } 3401 // cn 3402 if (in_array('cn', $fields) && !in_array('cn', $readOnlyFields)) { 3403 if (isset($_POST['posixAccount_cn']) && ($_POST['posixAccount_cn'] != '')) { 3404 if (!get_preg($_POST['posixAccount_cn'], 'cn')) { 3405 $return['messages'][] = $this->messages['cn'][0]; 3406 } 3407 else if (!isset($attributes['cn']) || ($attributes['cn'][0] != $_POST['posixAccount_cn'])) { 3408 $return['mod']['cn'][0] = $_POST['posixAccount_cn']; 3409 } 3410 } 3411 else { 3412 $return['messages'][] = $this->messages['cn'][0]; 3413 } 3414 } 3415 // shell 3416 if (in_array('loginShell', $fields) && !in_array('loginShell', $readOnlyFields)) { 3417 $shelllist = $this->getShells(); // list of all valid shells 3418 if (in_array($_POST['posixAccount_loginShell'], $shelllist) 3419 && (!isset($attributes['loginShell']) || ($attributes['loginShell'][0] != $_POST['posixAccount_loginShell']))) { 3420 $return['mod']['loginShell'][0] = $_POST['posixAccount_loginShell']; 3421 } 3422 } 3423 return $return; 3424 } 3425 3426 /** 3427 * {@inheritDoc} 3428 * @see baseModule::postModifySelfService() 3429 */ 3430 public function postModifySelfService($newAccount, $attributes) { 3431 if (isset($attributes['INFO.userPasswordModify'][0]) 3432 && ($attributes['INFO.userPasswordModify'][0] === 'exop')) { 3433 $password = $attributes['INFO.userPasswordClearText'][0]; 3434 $dn = $attributes['dn'][0]; 3435 $success = ldap_exop_passwd($_SESSION['ldapHandle'], $dn, null, $password); 3436 if (!$success) { 3437 StatusMessage('ERROR', _('Unable to set password'), getExtendedLDAPErrorMessage($_SESSION['ldapHandle'])); 3438 } 3439 else { 3440 StatusMessage('INFO', _('Password changed.')); 3441 } 3442 return $success; 3443 } 3444 return true; 3445 } 3446 3447 /** 3448 * Returns if the module manages the password attribute. 3449 * 3450 * @param string $typeId account type id 3451 * @return boolean manages password 3452 */ 3453 private function isPasswordManaged($typeId = null) { 3454 if ($typeId === null) { 3455 $typeId = $this->getAccountContainer()->get_type()->getId(); 3456 } 3457 return !$this->isBooleanConfigOptionSet('posixAccount_' . $typeId . '_hidepassword'); 3458 } 3459 3460 /** 3461 * This method specifies if a module manages password attributes. 3462 * @see passwordService::managesPasswordAttributes 3463 * 3464 * @return boolean true if this module manages password attributes 3465 */ 3466 public function managesPasswordAttributes() { 3467 return $this->isPasswordManaged(); 3468 } 3469 3470 /** 3471 * Specifies if this module supports to force that a user must change his password on next login. 3472 * 3473 * @return boolean force password change supported 3474 */ 3475 public function supportsForcePasswordChange() { 3476 return false; 3477 } 3478 3479 /** 3480 * This function is called whenever the password should be changed. Account modules 3481 * must change their password attributes only if the modules list contains their module name. 3482 * 3483 * @param String $password new password 3484 * @param $modules list of modules for which the password should be changed 3485 * @param boolean $forcePasswordChange force the user to change his password at next login 3486 * @return array list of error messages if any as parameter array for StatusMessage 3487 * e.g. return array(array('ERROR', 'Password change failed.')) 3488 * @see passwordService::passwordChangeRequested 3489 */ 3490 public function passwordChangeRequested($password, $modules, $forcePasswordChange) { 3491 if (!in_array(get_class($this), $modules)) { 3492 return array(); 3493 } 3494 $accountModules = $this->getAccountContainer()->get_type()->getModules(); 3495 // check password strength 3496 $user = empty($this->attributes['uid'][0]) ? null : $this->attributes['uid'][0]; 3497 $additionalAttrs = array(); 3498 if ($this->getAccountContainer()->getAccountModule('inetOrgPerson') != null) { 3499 $attrs = $this->getAccountContainer()->getAccountModule('inetOrgPerson')->getAttributes(); 3500 if (!empty($attrs['sn'][0])) { 3501 $additionalAttrs[] = $attrs['sn'][0]; 3502 } 3503 if (!empty($attrs['givenName'][0])) { 3504 $additionalAttrs[] = $attrs['givenName'][0]; 3505 } 3506 } 3507 $checkResult = checkPasswordStrength($password, $user, $additionalAttrs); 3508 if ($checkResult !== true) { 3509 return array(array('ERROR', $checkResult)); 3510 } 3511 // set new password 3512 $this->clearTextPassword = $password; 3513 // set SASL password 3514 if (!empty($this->attributes['uid'][0]) && !empty($this->moduleSettings['posixAccount_pwdHash'][0]) 3515 && ($this->moduleSettings['posixAccount_pwdHash'][0] === 'SASL')) { 3516 $this->attributes[$this->getPasswordAttrName($accountModules)][0] = '{SASL}' . $this->attributes['uid'][0]; 3517 } 3518 // delay on ldap_exop 3519 elseif (!empty($this->moduleSettings['posixAccount_pwdHash'][0]) && ($this->moduleSettings['posixAccount_pwdHash'][0] === 'LDAP_EXOP')) { 3520 logNewMessage(LOG_DEBUG, 'Setting password in post action, exop'); 3521 } 3522 // set normal password 3523 else { 3524 $this->attributes[$this->getPasswordAttrName($accountModules)][0] = pwd_hash($password, true, $this->moduleSettings['posixAccount_pwdHash'][0]); 3525 } 3526 return array(); 3527 } 3528 3529 /** 3530 * Returns the group ID of the given group. 3531 * 3532 * @param String $groupname group name 3533 * @return String GID 3534 */ 3535 private function getGID($groupname) { 3536 $results = searchLDAPByAttribute('cn', $groupname, 'posixGroup', array('gidnumber'), array('group')); 3537 if ((sizeof($results) > 0) && isset($results[0]['gidnumber'][0])) { 3538 return $results[0]['gidnumber'][0]; 3539 } 3540 return null; 3541 } 3542 3543 /** 3544 * Returns the group name of the group with the given group ID. 3545 * 3546 * @param String $groupID group ID 3547 * @return String group name 3548 */ 3549 private function getGroupName($groupID) { 3550 $results = searchLDAPByAttribute('gidNumber', $groupID, 'posixGroup', array('cn'), array('group')); 3551 if ((sizeof($results) > 0) && isset($results[0]['cn'][0])) { 3552 return $results[0]['cn'][0]; 3553 } 3554 return null; 3555 } 3556 3557 /** 3558 * Finds all existing LDAP groups. 3559 * 3560 * @return array groups array(array(gidnumber, cn), array(gidnumber, cn), ...) 3561 */ 3562 private function findGroups(&$modules) { 3563 if ($this->groupCache != null) { 3564 return $this->groupCache; 3565 } 3566 $this->groupCache = array(); 3567 $typeManager = new TypeManager(); 3568 foreach ($typeManager->getConfiguredTypesForScope('group') as $type) { 3569 $filter = '(objectClass=posixGroup)'; 3570 if ($this->isWindows($modules)) { 3571 $filter = '(&(objectClass=group)(gidNumber=*))'; 3572 } 3573 $typeFilter = $type->getAdditionalLdapFilter(); 3574 if (!empty($typeFilter)) { 3575 if (strpos($typeFilter, '(') !== 0) { 3576 $typeFilter = '(' . $typeFilter . ')'; 3577 } 3578 $filter = '(&' . $filter . $typeFilter . ')'; 3579 } 3580 $results = searchLDAP($type->getSuffix(), $filter, array('cn', 'gidnumber')); 3581 for ($i = 0; $i < sizeof($results); $i++) { 3582 if (isset($results[$i]['cn'][0]) && isset($results[$i]['gidnumber'][0])) { 3583 $this->groupCache[] = array($results[$i]['gidnumber'][0], $results[$i]['cn'][0]); 3584 } 3585 } 3586 } 3587 return $this->groupCache; 3588 } 3589 3590 /** 3591 * Finds all existing LDAP group of names. 3592 * 3593 * @return array groups array(dn => array('cn' => array('groupName'), 'objectclass' => array('top', 'groupOfNames'))) 3594 */ 3595 public function findGroupOfNames() { 3596 if ($this->gonCache != null) { 3597 return $this->gonCache; 3598 } 3599 $return = array(); 3600 $typeManager = new TypeManager(); 3601 $types = $typeManager->getConfiguredTypesForScopes(array('gon', 'group')); 3602 foreach ($types as $type) { 3603 $typeFilter = get_ldap_filter($type->getId()); 3604 $results = searchLDAP($type->getSuffix(), $typeFilter, array('cn', 'dn', 'objectClass')); 3605 for ($i = 0; $i < sizeof($results); $i++) { 3606 if ((in_array_ignore_case('groupOfNames', $results[$i]['objectclass']) 3607 || in_array_ignore_case('groupOfMembers', $results[$i]['objectclass']) 3608 || in_array_ignore_case('groupOfUniqueNames', $results[$i]['objectclass'])) 3609 && isset($results[$i]['cn'][0])) { 3610 $return[$results[$i]['dn']] = $results[$i]; 3611 } 3612 } 3613 } 3614 $this->gonCache = $return; 3615 return $return; 3616 } 3617 3618 /** 3619 * Returns a list of existing UID numbers. 3620 * 3621 * @param string $typeId type id (e.g. user) 3622 * @return array list of UID numbers 3623 */ 3624 private function getUIDs($typeId) { 3625 if ($this->cachedUIDList != null) { 3626 return $this->cachedUIDList; 3627 } 3628 $this->cachedUIDList = array(); 3629 $attrs = array('uidNumber'); 3630 $filter = '(&(objectClass=posixAccount)(uidNumber=*))'; 3631 if ($this->skipObjectClass()) { 3632 $filter = '(uidNumber=*)'; 3633 } 3634 $typeManager = new TypeManager(); 3635 $typesUser = $typeManager->getConfiguredTypesForScope('user'); 3636 $typesHost = $typeManager->getConfiguredTypesForScope('host'); 3637 $suffixes = array(); 3638 if (!empty($typesUser)) { 3639 if (!empty($this->moduleSettings['posixAccount_' . $typeId . '_uidCheckSuffixUser'][0])) { 3640 $suffixes[] = $this->moduleSettings['posixAccount_' . $typeId . '_uidCheckSuffixUser'][0]; 3641 } 3642 else { 3643 foreach ($typesUser as $type) { 3644 $suffixes[] = $type->getSuffix(); 3645 } 3646 } 3647 } 3648 if (!empty($typesHost)) { 3649 if (!empty($this->moduleSettings['posixAccount_' . $typeId . '_uidCheckSuffixHost'][0])) { 3650 $suffixes[] = $this->moduleSettings['posixAccount_' . $typeId . '_uidCheckSuffixHost'][0]; 3651 } 3652 else { 3653 foreach ($typesHost as $type) { 3654 $suffixes[] = $type->getSuffix(); 3655 } 3656 } 3657 } 3658 $suffixes = array_unique($suffixes); 3659 foreach ($suffixes as $suffix) { 3660 $result = searchLDAP($suffix, $filter, $attrs); 3661 foreach ($result as $resultEntry) { 3662 $this->cachedUIDList[] = $resultEntry['uidnumber'][0]; 3663 } 3664 } 3665 $this->cachedUIDList = array_values(array_unique($this->cachedUIDList)); 3666 sort($this->cachedUIDList, SORT_NUMERIC); 3667 return $this->cachedUIDList; 3668 } 3669 3670 /** 3671 * Checks if the given user name already exists in LDAP. 3672 * 3673 * @param String $userName user name 3674 * @param string $typeId type id (e.g. user) 3675 * @return boolean true if already exists 3676 */ 3677 private function userNameExists($userName, $typeId) { 3678 return array_key_exists($userName, $this->getUserNames($typeId)); 3679 } 3680 3681 /** 3682 * Returns a list of all user names in LDAP. 3683 * 3684 * @param string $typeId type id (e.g. user) 3685 * @return array user names 3686 */ 3687 private function getUserNames($typeId) { 3688 if ($this->cachedUserNameList != null) { 3689 return $this->cachedUserNameList; 3690 } 3691 $this->cachedUserNameList = array(); 3692 $attrs = array('uid'); 3693 $filter = '(&(objectClass=posixAccount)(uid=*))'; 3694 if ($this->skipObjectClass()) { 3695 $filter = '(uid=*)'; 3696 } 3697 $typeManager = new TypeManager(); 3698 $typesUser = $typeManager->getConfiguredTypesForScope('user'); 3699 $typesHost = $typeManager->getConfiguredTypesForScope('host'); 3700 $suffixes = array(); 3701 if (!empty($typesUser)) { 3702 if (!empty($this->moduleSettings['posixAccount_' . $typeId . '_uidCheckSuffixUser'][0])) { 3703 $suffixes[] = $this->moduleSettings['posixAccount_' . $typeId . '_uidCheckSuffixUser'][0]; 3704 } 3705 else { 3706 foreach ($typesUser as $type) { 3707 $suffixes[] = $type->getSuffix(); 3708 } 3709 } 3710 } 3711 if (!empty($typesHost)) { 3712 if (!empty($this->moduleSettings['posixAccount_' . $typeId . '_uidCheckSuffixHost'][0])) { 3713 $suffixes[] = $this->moduleSettings['posixAccount_' . $typeId . '_uidCheckSuffixHost'][0]; 3714 } 3715 else { 3716 foreach ($typesHost as $type) { 3717 $suffixes[] = $type->getSuffix(); 3718 } 3719 } 3720 } 3721 $suffixes = array_unique($suffixes); 3722 foreach ($suffixes as $suffix) { 3723 $result = searchLDAP($suffix, $filter, $attrs); 3724 foreach ($result as $resultEntry) { 3725 $this->cachedUserNameList[$resultEntry['uid'][0]] = $resultEntry['dn']; 3726 } 3727 } 3728 return $this->cachedUserNameList; 3729 } 3730 3731 /** 3732 * Returns if LAM manages group of names entries. 3733 * 3734 * @return boolean group of names are active 3735 */ 3736 public static function areGroupOfNamesActive() { 3737 if (!isset($_SESSION['config'])) { 3738 return false; 3739 } 3740 $typeManager = new TypeManager(); 3741 $types = $typeManager->getConfiguredTypesForScopes(array('group', 'gon')); 3742 foreach ($types as $type) { 3743 $modules = $type->getModules(); 3744 if (in_array('groupOfNames', $modules) 3745 || in_array('groupOfMembers', $modules) 3746 || in_array('groupOfUniqueNames', $modules)) { 3747 return true; 3748 } 3749 } 3750 return false; 3751 } 3752 3753 /** 3754 * Returns a suggestion for the user name. 3755 * By default this will be the first character of the first name plus the last name. 3756 * 3757 * @param array $attrs LDAP attributes 3758 * @param string $typeId type id (e.g. user) 3759 * @return String user name 3760 */ 3761 protected function getUserNameSuggestion($attrs, $typeId) { 3762 $attributes = array_change_key_case($attrs, CASE_LOWER); 3763 $format = '@givenname@%sn%'; 3764 if (isset($this->moduleSettings['posixAccount_' . $typeId . '_userNameSuggestion'][0])) { 3765 $format = strtolower($this->moduleSettings['posixAccount_' . $typeId . '_userNameSuggestion'][0]); 3766 } 3767 // search for @key@ wildcards in format string and replace with first character of attribute 3768 $wildcards = array(); 3769 if (preg_match_all('/@([^@]|[a-zA-Z_-])+@/', $format, $wildcards) > 0) { 3770 for ($i = 0; $i < sizeof($wildcards[0]); $i++) { 3771 $wc = substr($wildcards[0][$i], 1, strlen($wildcards[0][$i]) - 2); 3772 $value = ''; 3773 if (isset($attributes[$wc][0]) && !empty($attributes[$wc][0])) { 3774 $value = $this->cleanSuggestionPart($attributes[$wc][0][0]); 3775 } 3776 $format = str_replace('@' . $wc . '@', $value, $format); 3777 } 3778 } 3779 // search for %key% wildcards in format string and replace with attribute 3780 $wildcards = array(); 3781 if (preg_match_all('/%([^%]|[a-zA-Z_-])+%/', $format, $wildcards) > 0) { 3782 for ($i = 0; $i < sizeof($wildcards[0]); $i++) { 3783 $wc = substr($wildcards[0][$i], 1, strlen($wildcards[0][$i]) - 2); 3784 $value = ''; 3785 if (isset($attributes[$wc][0])) { 3786 $value = $this->cleanSuggestionPart($attributes[$wc][0]); 3787 } 3788 $format = str_replace('%' . $wc . '%', $value, $format); 3789 } 3790 } 3791 return $format; 3792 } 3793 3794 /** 3795 * Cleans a string that is injected in user name suggestion. 3796 * 3797 * @param string $part injected part 3798 * @return string cleaned by removing umlauts, spaces, dashes and underscores 3799 */ 3800 private function cleanSuggestionPart($part) { 3801 $result = str_replace(array_keys($this->umlautReplacements), array_values($this->umlautReplacements), strtolower($part)); 3802 return str_replace(array(' ', '_', '-'), array('', '', ''), $result); 3803 } 3804 3805 /** 3806 * Returns if this account can be locked. 3807 * This is the case if a hashed password is set ("{" at the beginning). 3808 * 3809 * @param string[] $modules account modules 3810 * @return boolean lockable 3811 */ 3812 public function isLockable(&$modules) { 3813 if (isset($this->attributes[$this->getPasswordAttrName($modules)][0]) 3814 && pwd_is_lockable($this->attributes[$this->getPasswordAttrName($modules)][0])) { 3815 return true; 3816 } 3817 return false; 3818 } 3819 3820 /** 3821 * Returns if the Unix part of the current account is locked. 3822 * 3823 * @param string[] $modules account modules 3824 * @return boolean password is locked 3825 */ 3826 public function isLocked(&$modules) { 3827 return isset($this->attributes[$this->getPasswordAttrName($modules)][0]) 3828 && !pwd_is_enabled($this->attributes[$this->getPasswordAttrName($modules)][0]); 3829 } 3830 3831 /** 3832 * Locks the user password of this account. 3833 * 3834 * @param string[] $modules account modules 3835 */ 3836 public function lock(&$modules) { 3837 $pwdAttrName = $this->getPasswordAttrName($modules); 3838 if (isset($this->attributes[$pwdAttrName][0])) { 3839 $this->attributes[$pwdAttrName][0] = pwd_disable($this->attributes[$pwdAttrName][0]); 3840 } 3841 } 3842 3843 /** 3844 * Unlocks the user password of this account. 3845 * 3846 * @param string[] $modules account modules 3847 */ 3848 public function unlock(&$modules) { 3849 $pwdAttrName = $this->getPasswordAttrName($modules); 3850 if (isset($this->attributes[$pwdAttrName][0])) { 3851 $this->attributes[$pwdAttrName][0] = pwd_enable($this->attributes[$pwdAttrName][0]); 3852 } 3853 } 3854 3855 /** 3856 * Removes all Unix group memberships from this user. 3857 */ 3858 public function removeFromUnixGroups() { 3859 $this->groups = array(); 3860 } 3861 3862 /** 3863 * Removes all group of names memberships from this user. 3864 */ 3865 public function removeFromGONGroups() { 3866 $this->gonList = array(); 3867 } 3868 3869 /** 3870 * Returns the next possible user name based on the given one. 3871 * If the user name does not end with a number then a "2" is added. 3872 * User names with numbers at the end are simply increased by one. 3873 * <br> 3874 * <br>Attention: This user name might still be in use. This needs to be checked separately. 3875 * 3876 * @param String $userName user name 3877 * @param string[] $moduleNames list of account module names 3878 * @return String new user name 3879 */ 3880 protected function getNextUserName($userName, $moduleNames) { 3881 if ($this->get_scope() == 'host' && in_array('sambaSamAccount', $moduleNames)) { 3882 $userName = substr($userName, 0, -1); 3883 } 3884 // get last character of username 3885 $lastchar = substr($userName, strlen($userName) - 1, 1); 3886 $suffix = ''; 3887 if (($this->get_scope() == 'host') && in_array('sambaSamAccount', $moduleNames)) { 3888 $suffix = '$'; 3889 } 3890 // Last character is no number 3891 if ( !preg_match('/^([0-9])+$/', $lastchar)) { 3892 // Last character is no number. Therefore we only have to add "2" to it. 3893 $userName = $userName . '2' . $suffix; 3894 } 3895 else { 3896 /* Last character is a number -> we have to increase the number until we've 3897 * found a groupname with trailing number which is not in use. 3898 * 3899 * $i will show us were we have to split groupname so we get a part 3900 * with the groupname and a part with the trailing number 3901 */ 3902 $i = strlen($userName) - 1; 3903 $mark = false; 3904 // Set $i to the last character which is a number in $account_new->general_username 3905 while (!$mark) { 3906 if (preg_match('/^([0-9])+$/', substr($userName, $i, strlen($userName) - $i))) { 3907 $i--; 3908 } 3909 else { 3910 $mark=true; 3911 } 3912 } 3913 // increase last number with one 3914 $firstchars = substr($userName, 0, $i + 1); 3915 $lastchars = substr($userName, $i + 1, strlen($userName) - $i); 3916 // Put username together 3917 $userName = $firstchars . (intval($lastchars) + 1) . $suffix; 3918 } 3919 return $userName; 3920 } 3921 3922 /** 3923 * Returns the list of possible login shells. 3924 * 3925 * @return array login shells 3926 */ 3927 private function getShells() { 3928 // self service 3929 if (!isLoggedIn() && isset($this->selfServiceSettings) && isset($this->selfServiceSettings->moduleSettings['posixAccount_shells']) 3930 && (sizeof($this->selfServiceSettings->moduleSettings['posixAccount_shells'])) > 0) { 3931 return $this->selfServiceSettings->moduleSettings['posixAccount_shells']; 3932 } 3933 // server profile 3934 if (!isset($this->selfServiceSettings) && isset($this->moduleSettings) && isset($this->moduleSettings['posixAccount_shells']) 3935 && (sizeof($this->moduleSettings['posixAccount_shells'])) > 0) { 3936 return $this->moduleSettings['posixAccount_shells']; 3937 } 3938 // fall back to default 3939 return array( 3940 '/bin/bash', 3941 '/bin/csh', 3942 '/bin/dash', 3943 '/bin/false', 3944 '/bin/ksh', 3945 '/bin/sh' 3946 ); 3947 } 3948 3949 /** 3950 * Returns if the cn attribute should be managed. 3951 * If Windows modules are active then cn will not be managed. 3952 * 3953 * @param string[] $modules account modules 3954 * @return boolean manage cn attribute 3955 */ 3956 private function manageCn(&$modules) { 3957 return !$this->isWindows($modules); 3958 } 3959 3960 /** 3961 * Returns if the Unix part can be added and removed. 3962 * 3963 * @param string[] $modules account modules 3964 * @return boolean is optional 3965 */ 3966 private function isOptional(&$modules) { 3967 return !$this->manageCn($modules); 3968 } 3969 3970 /** 3971 * Returns if the Windows module is active. 3972 * 3973 * @param string[] $modules account modules 3974 * @return boolean is Windows 3975 */ 3976 private function isWindows(&$modules) { 3977 return in_array('windowsUser', $modules); 3978 } 3979 3980 /** 3981 * Returns the password attribute. 3982 * Usually, this is userPassword. If Windows modules are active this is unixUserPassword. 3983 * 3984 * @param string[] $modules account modules 3985 * @return boolean attribute name 3986 */ 3987 private function getPasswordAttrName(&$modules) { 3988 if ($this->isWindows($modules)) { 3989 return 'unixUserPassword'; 3990 } 3991 return 'userPassword'; 3992 } 3993 3994 /** 3995 * Returns the home directory attribute. 3996 * Usually, this is homeDirectory. If Windows modules are active this is unixHomeDirectory. 3997 * 3998 * @param string[] $modules account modules 3999 * @return boolean attribute name 4000 */ 4001 private function getHomedirAttrName(&$modules) { 4002 if ($this->isWindows($modules)) { 4003 return 'unixHomeDirectory'; 4004 } 4005 return 'homeDirectory'; 4006 } 4007 4008 /** 4009 * Syncs the group of names with groups. 4010 */ 4011 private function syncGonToGroups() { 4012 $this->groups = array(); 4013 $allGons = $this->findGroupOfNames(); 4014 foreach ($this->gonList as $dn) { 4015 if (!isset($allGons[$dn])) { 4016 continue; 4017 } 4018 $gon = $this->gonCache[$dn]; 4019 if (in_array_ignore_case('posixGroup', $gon['objectclass']) && !empty($gon['cn'])) { 4020 $this->groups[] = $gon['cn'][0]; 4021 } 4022 } 4023 } 4024 4025 /** 4026 * Returns if the object class should not be added. 4027 * 4028 * @return do not add 4029 */ 4030 private function skipObjectClass() { 4031 return $this->isBooleanConfigOptionSet('posixAccount_noObjectClass'); 4032 } 4033 4034 /** 4035 * {@inheritdoc} 4036 */ 4037 public function getWildCardReplacements() { 4038 $replacements = array(); 4039 // user name 4040 if (!empty($_POST['uid'])) { 4041 $replacements['user'] = $_POST['uid']; 4042 } 4043 elseif (!empty($this->attributes['uid'][0])) { 4044 $replacements['user'] = $this->attributes['uid'][0]; 4045 } 4046 // group name 4047 if (!empty($_POST['gidNumber'])) { 4048 $replacements['group'] = $this->getGroupName($_POST['gidNumber']); 4049 } 4050 elseif (!empty($this->attributes['gidNumber'][0])) { 4051 $replacements['group'] = $this->getGroupName($this->attributes['gidNumber'][0]); 4052 } 4053 return $replacements; 4054 } 4055 4056 /** 4057 * Returns the current group names. 4058 * 4059 * @return string[] group names 4060 */ 4061 public function getGroups() { 4062 return $this->groups; 4063 } 4064 4065 /** 4066 * Returns the list of group of names where this user is member. 4067 * 4068 * @return string[] list of DNs 4069 */ 4070 public function getGroupOfNames() { 4071 return $this->gonList; 4072 } 4073 4074} 4075 4076?> 4077