UserStorageMacOSX.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. <?php
  2. Lib::loadClass('UserStorageBase');
  3. Lib::loadClass('ObjectUserLdap');
  4. Lib::loadClass('ObjectGroupLdap');
  5. Lib::loadClass('UsersLdapHelper');
  6. Lib::loadClass('LDAP');
  7. /**
  8. * Test remove user:
  9. * $ dscl /Local/Default -list /Groups GroupMembership | grep test13
  10. * ... for all groups:
  11. * $ dscl -u diradmin -p /Local/Default -delete /Groups/workgroup GroupMembership test13
  12. * $ dscl /LDAPv3/127.0.0.1 -list /Groups GroupMembership | grep test13
  13. * ... for all groups:
  14. * $ dscl -u diradmin -p /LDAPv3/127.0.0.1 -delete /Groups/workgroup GroupMembership test13
  15. * $ dscl -u diradmin -p /Local/Default -delete /Users/test13
  16. */
  17. class UserStorageMacOSX extends UserStorageBase {
  18. private $_rootUser;
  19. private $_rootPass;
  20. private $_host;
  21. private $_ldapRoot;
  22. public function __construct($rootUser, $rootPass, $host) {
  23. $this->_rootUser = $rootUser;
  24. $this->_rootPass = $rootPass;
  25. $this->_host = $host;
  26. }
  27. /**
  28. * @return ObjectUserLdap
  29. */
  30. public function getUser($usrLogin) {
  31. $usrLdap = $this->getRawLdapUser($usrLogin);
  32. if (empty($usrLdap)) return null;
  33. $user = $this->_buildUserFromLdap($usrLdap);
  34. return $user;
  35. }
  36. private function getRawLdapUser($usrLogin) {
  37. $usrLdap = UsersLdapHelper::getUser($usrLogin, $allAttrs = true, $onyFirstAttr = false);
  38. if (empty($usrLdap[0])) return null;
  39. $usrLdap = $usrLdap[0];
  40. DBG::_('DBG_SU', '>2', 'usrLdapRaw', $usrLdap, __CLASS__, __FUNCTION__, __LINE__);
  41. if (!empty($usrLdap->uid) && is_array($usrLdap->uid)) {
  42. $usrLdap->uid = $usrLdap->uid[0];
  43. }
  44. $fldPassPolicy = "apple-user-passwordpolicy";
  45. if (isset($usrLdap->$fldPassPolicy)) unset($usrLdap->$fldPassPolicy);
  46. if (isset($usrLdap->authAuthority)) unset($usrLdap->authAuthority);
  47. if (isset($usrLdap->altSecurityIdentities)) unset($usrLdap->altSecurityIdentities);
  48. if (!empty($usrLdap->mail)) {
  49. $usrLdap->aliasesList = array();
  50. {
  51. $aliasesEx = (is_array($usrLdap->mail))? $usrLdap->mail : array($usrLdap->mail);
  52. foreach ($aliasesEx as $emailAlias) {
  53. $emailAlias = trim($emailAlias);
  54. if (!empty($emailAlias) && filter_var($emailAlias, FILTER_VALIDATE_EMAIL)) {
  55. $usrLdap->aliasesList[] = $emailAlias;
  56. }
  57. }
  58. }
  59. $usrLdap->mail = reset($usrLdap->aliasesList);
  60. }
  61. // join all fiels by ' ', skip aliasesList
  62. foreach ($usrLdap as $fldName => $fdlVal) {
  63. if ('aliasesList' != $fldName && !empty($fdlVal) && is_array($fdlVal)) {
  64. $usrLdap->$fldName = implode(' ', $fdlVal);
  65. }
  66. }
  67. DBG::_('DBG_SU', true, 'usrLdap', $usrLdap, __CLASS__, __FUNCTION__, __LINE__);
  68. return $usrLdap;
  69. }
  70. private function _buildUserFromLdap($usrLdap) {
  71. $user = new ObjectUserLdap($this);
  72. $user->primaryKey = V::get('uidNumber', '', $usrLdap);
  73. $user->login = V::get('uid', '', $usrLdap);
  74. $user->password = '';
  75. $user->name = V::get('cn', '', $usrLdap);
  76. $user->email = V::get('mail', '', $usrLdap);
  77. $user->aliasesList = V::get('aliasesList', array(), $usrLdap, 'array');
  78. $user->phone = V::get('telephoneNumber', '', $usrLdap);
  79. $user->homeEmail = V::get('carLicense', '', $usrLdap);
  80. $user->homePhone = V::get('homePhone', '', $usrLdap);
  81. $user->employeeType = V::get('employeeType', '', $usrLdap);
  82. $user->firstName = V::get('givenName', '', $usrLdap);
  83. $user->lastName = V::get('sn', '', $usrLdap);
  84. return $user;
  85. }
  86. /**
  87. * @return ObjectGroupLdap $group
  88. */
  89. public function getGroup($groupID) {
  90. return $this->_getGroup($groupID, $fetchNested = true);
  91. }
  92. private function _getGroup($groupID, $fetchNested = false) {
  93. if ($groupID <= 0) return false;
  94. $group = null;
  95. $groups = UsersLdapHelper::getGroupsByID($groupID);
  96. if (count($groups) == 1) {
  97. $group = reset($groups);
  98. DBG::_('DBG_SU', '>2', "groupLdap", $group, __CLASS__, __FUNCTION__, __LINE__);
  99. $group = $this->_buildGroupFromLdap($group, $fetchNested);
  100. DBG::_('DBG_SU', '>2', "group", $group, __CLASS__, __FUNCTION__, __LINE__);
  101. } else if (count($groups) > 1) {
  102. DBG::_('DBG_SU', '>2', "Too much groups in ldap by ID {$groupID}", $groups, __CLASS__, __FUNCTION__, __LINE__);
  103. throw new Exception("Za dużo grup w bazie Ldap pasujących do grupy nr {$groupID}!");
  104. }
  105. return $group;
  106. }
  107. public function getParentGroups(ObjectGroup $group) {
  108. $parentGroups = array();
  109. $parentGroupsLdap = UsersLdapHelper::getParentGroupsByAppleUID($group->getLdapUID());
  110. foreach ($parentGroupsLdap as $groupLdap) {
  111. $group = $this->_buildGroupFromLdap($groupLdap);
  112. if ($group->zasobID > 0) {
  113. $parentGroups[$group->zasobID] = $group;
  114. }
  115. }
  116. return $parentGroups;
  117. }
  118. /**
  119. * @return bool
  120. */
  121. public function isDisabled($usr) {
  122. if (!$usr) throw new Exception("Błąd podczas sprawdzania statusu blokady - nie podano użytkownika!");
  123. if (null === $usr->isDisabled) {
  124. $allGroups = $this->_fetchAllUserGroups($usr->login);
  125. $usr->isDisabled = in_array('com.apple.access_disabled', $allGroups);
  126. DBG::_('DBG_SU', '>1', "usr->isDisabled(" . (($usr->isDisabled)? 'true' : 'false') . ") ", null, __CLASS__, __FUNCTION__, __LINE__);
  127. }
  128. return $usr->isDisabled;
  129. }
  130. /**
  131. * @return bool
  132. */
  133. public function setDisabled($usrLogin, $isDisabled) {
  134. // pwpolicy -a diradmin -u t1 -disableuser
  135. // pwpolicy -a diradmin -u t1 -enableuser
  136. if (empty($usrLogin) || null === $isDisabled) {
  137. return false;
  138. }
  139. $cmdDisabled = ($isDisabled)? ' -disableuser' : ' -enableuser';
  140. $cmd = "pwpolicy -a {$this->_rootUser} -p {$this->_rootPass} -u {$usrLogin} {$cmdDisabled} 2>&1 ";
  141. $cmdOut = null; $cmdRet = null;
  142. exec($cmd, $cmdOut, $cmdRet);
  143. DBG::_('DBG_SU', '>1', "cmd(" . str_replace($this->_rootPass, '***', $cmd) . ") ret({$cmdRet}) ", $cmdOut, __CLASS__, __FUNCTION__, __LINE__);
  144. if ($cmdRet !== 0) {
  145. return false;
  146. }
  147. return true;
  148. }
  149. /**
  150. * @param $usr - user object @see UserStorageBase::getUser()
  151. * $usr->employeeType: Pracownik, Kandydat, Partner, Anonymous
  152. * Pracownik - all access
  153. * Kandydat - no access
  154. * Partner - access: smb/afp, TODO: calendar?, addressbook?
  155. * Anonymous - no access
  156. */
  157. public function createUser($usr) {
  158. DBG::_('DBG_SU', '>1', 'usr', $usr, __CLASS__, __FUNCTION__, __LINE__);
  159. $cmdDsclAuth = "dscl -u {$this->_rootUser} -P {$this->_rootPass} /LDAPv3/127.0.0.1 ";
  160. $login = $this->_cleanUid($usr->login);
  161. $name = $this->_cleanText($usr->name);
  162. $type = $usr->employeeType;
  163. $email = $usr->email;
  164. $pass = $usr->password;
  165. $uniqueID = 0;
  166. // test user login and pass by searching for $uniqueID for new user
  167. $cmd = "{$cmdDsclAuth} -list /Users > /dev/null && {$cmdDsclAuth} -list /Users UniqueID|awk '{print \$2}'|sort -n|tail -1 ";
  168. $cmdOut = null; $cmdRet = null;
  169. exec($cmd, $cmdOut, $cmdRet);
  170. if ($cmdRet == 0 && !empty($cmdOut[0])) {
  171. $uniqueID = intval($cmdOut[0]);
  172. if ($uniqueID > 0) {
  173. $uniqueID += 1;
  174. }
  175. }
  176. if ($uniqueID <= 0) {
  177. throw new Exception("Error: dscl auth - check login and password in ldap config");
  178. }
  179. if (empty($name)) {
  180. $name = $login;
  181. } else {
  182. // TODO: replace bad signs str_replace($_SESSION['CONFIG']['BAD_FILE_SIGNS_LETTERS'],$_SESSION['CONFIG']['OK_FILE_SIGNS_LETTERS'],$ADM_NAME)
  183. }
  184. if (empty($pass)) {
  185. $pass = $login;
  186. }
  187. $cmds = array();
  188. //$cmds[] = "{$cmdDsclAuth} -create /Users/{$login} HomeDirectory \"<home_dir><url>afp://{$this->_host}/Users</url><path>{$login}</path></home_dir>\" ";
  189. //$cmds[] = "{$cmdDsclAuth} -create /Users/{$login} NFSHomeDirectory /Network/Servers/{$this->_host}/Users/{$login} ";
  190. $cmds[] = "{$cmdDsclAuth} -create /Users/{$login} NFSHomeDirectory /Users/{$login} ";
  191. $cmds[] = "{$cmdDsclAuth} -create /Users/{$login} UserShell /bin/bash ";// TODO: bash?
  192. $cmds[] = "{$cmdDsclAuth} -create /Users/{$login} UniqueID {$uniqueID} ";
  193. $cmds[] = "{$cmdDsclAuth} -create /Users/{$login} PrimaryGroupID 20 ";// TODO: 20 maja domyslnie inne konta?
  194. $cmds[] = "{$cmdDsclAuth} -create /Users/{$login} RealName \"{$name}\" ";
  195. if (!empty($email)) $cmds[] = "{$cmdDsclAuth} -create /Users/{$login} EMailAddress {$email} ";
  196. $cmds[] = "{$cmdDsclAuth} -passwd /Users/{$login} \"{$pass}\" ";
  197. $cmds[] = "sudo /usr/sbin/createhomedir -c -u {$login} 2>&1 ";// TODO:INSTALATOR: add to sudoers _www ALL = NOPASSWD: /usr/sbin/createhomedir
  198. foreach ($cmds as $cmd) {
  199. $cmdOut = null; $cmdRet = null;
  200. exec($cmd, $cmdOut, $cmdRet);
  201. if ($cmdRet != 0) {
  202. DBG::_('DBG_SU', '>1', "cmd failed: " . str_replace($cmdDsclAuth, "dscl __auth__ ", $cmd), $cmdOut, __CLASS__, __FUNCTION__, __LINE__);
  203. throw new Exception("Wystąpił błąd podczas tworzenia użytwkonika '{$usrLogin}' w bazie Ldap");
  204. }
  205. }
  206. }
  207. private function _getAdminLdap() {
  208. if (!$this->_ldapRoot) {
  209. $this->_ldapRoot = LDAP::getInstance();
  210. if (!$this->_ldapRoot->bindDiradmin($errorMsg)) {
  211. // $errorMsg?
  212. $this->setError(1, "cant bind as diradmin", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
  213. return null;
  214. }
  215. }
  216. return $this->_ldapRoot;
  217. }
  218. public function updateUser($userName, $updateData) {
  219. if (empty($updateData)) return true;
  220. foreach ($updateData as $fldName => $val) {
  221. if (is_scalar($val)) $val = trim($val);
  222. switch ($fldName) {
  223. case 'aliasesList':
  224. $updateAliasesList = $val;
  225. if (!empty($updateAliasesList) && is_array($updateAliasesList) && is_array($updateAliasesList)) {
  226. $ldap = $this->_getAdminLdap();
  227. if ($ldap) {
  228. if (!empty($updateAliasesList['add'])) $this->_addMailAliases($userName, $updateAliasesList['add']);
  229. if (!empty($updateAliasesList['remove'])) $this->_removeMailAliases($userName, $updateAliasesList['remove']);
  230. }
  231. }
  232. break;
  233. case 'name':
  234. $ldap = $this->_getAdminLdap();
  235. if ($ldap) {
  236. $attr = array();
  237. $attr['cn'] = $val;
  238. $ldap->mod_replace($userName, $attr);
  239. }
  240. break;
  241. case 'firstName':
  242. $ldap = $this->_getAdminLdap();
  243. if ($ldap) {
  244. $attr = array();
  245. $attr['givenName'] = $val;
  246. $ldap->mod_replace($userName, $attr);
  247. }
  248. break;
  249. case 'lastName':
  250. $ldap = $this->_getAdminLdap();
  251. if ($ldap) {
  252. $attr = array();
  253. $attr['sn'] = $val;
  254. $ldap->mod_replace($userName, $attr);
  255. }
  256. break;
  257. case 'phone':
  258. $ldap = $this->_getAdminLdap();
  259. if ($ldap) {
  260. $attr = array();
  261. $attr['telephoneNumber'] = $val;
  262. $ldap->mod_replace($userName, $attr);
  263. }
  264. break;
  265. case 'employeeType':
  266. $ldap = $this->_getAdminLdap();
  267. if ($ldap) {
  268. $attr = array();
  269. $attr['employeeType'] = $val;
  270. $ldap->mod_replace($userName, $attr);
  271. }
  272. break;
  273. case 'password':
  274. if (!empty($val) && !$this->changePassword($userName, $val)) {
  275. $this->setError(1, "Nie udało się zmienić hasła dla usera '{$userName}'", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
  276. }
  277. break;
  278. case 'homeEmail':
  279. $ldap = $this->_getAdminLdap();
  280. if ($ldap) {
  281. $attr = array();
  282. $attr['carLicense'] = $val;
  283. if (!$ldap->mod_replace($userName, $attr)) {
  284. if (!$ldap->mod_add($userName, $attr)) {
  285. //$this->setError(1, "TODO: update homeEmail failed " . $ldap->error(), '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
  286. }
  287. }
  288. }
  289. break;
  290. default:
  291. $this->setError(1, "TODO: update user {$userName} field {$fldName} to value '{$val}'", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
  292. }
  293. }
  294. if ($this->hasErrors()) {
  295. $this->setError(1, "Nie udało się zaktualizować danych usera '{$userName}'", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
  296. return false;
  297. }
  298. return true;
  299. }
  300. public function updateGroup($group, $updateData) {
  301. if (!$group) return false;
  302. if (empty($updateData)) return true;
  303. foreach ($updateData as $fldName => $val) {
  304. switch ($fldName) {
  305. case 'realName':
  306. $ldap = $this->_getAdminLdap();
  307. if ($ldap) {
  308. $attr = array();
  309. $attr['apple-group-realname'] = $val;
  310. $ldap->groupAttrUpdate($group->primaryKey, $attr);
  311. }
  312. break;
  313. default:
  314. $this->setError(1, "Błąd podczas aktulizacji grupy '{$group->primaryKey}' - pole '{$fldName}' watość '{$val}'", '#L' . __LINE__);
  315. }
  316. }
  317. return true;
  318. }
  319. public function getGroupIdFromUid($groupUid) {
  320. if (empty($groupUid)) return null;
  321. if (!is_numeric(substr($groupUid, 0, 1))) return null;
  322. $tmp = str_replace(array('-', '_'), '_', $groupUid);
  323. $tmp = explode('_', $tmp);
  324. $tmp = reset($tmp);
  325. if (!empty($tmp) && is_numeric($tmp)) {
  326. return $tmp;
  327. }
  328. return null;
  329. }
  330. /**
  331. * User group list by id.
  332. *
  333. * @param bool $fetchNested - contain all groups below connected groups and group PODMIOT from above.
  334. *
  335. * @return array with group objects @see getGroup
  336. */
  337. public function getUserGroups($usrLogin, $fetchNested = false) {
  338. $groups = array();
  339. $groupsNetwork = $this->_getUserGroupsNetwork($usrLogin);
  340. $groupsLocal = $this->_getUserGroupsLocal($usrLogin);
  341. foreach ($groupsLocal as $kGroupUid => $vGroup) {
  342. $groups[$kGroupUid] = $vGroup;
  343. }
  344. foreach ($groupsNetwork as $kGroupUid => $vGroupNetwork) {
  345. if ($vGroupNetwork->primaryKey == 'workgroup') {
  346. $groups[$vGroupNetwork->primaryKey] = $vGroupNetwork;
  347. }
  348. else if ($vGroupNetwork->zasobID > 0) {
  349. $groups[$vGroupNetwork->zasobID] = $vGroupNetwork;
  350. }
  351. }
  352. DBG::_('DBG_SU', '>2', "groupsNetwork", array_keys($groupsNetwork), __CLASS__, __FUNCTION__, __LINE__);
  353. DBG::_('DBG_SU', '>2', "groupsLocal", array_keys($groupsLocal), __CLASS__, __FUNCTION__, __LINE__);
  354. return $groups;
  355. }
  356. /**
  357. * Build network group object.
  358. *
  359. * @param object $groupDB {ID, DESC} @see _getUserGroupsAll
  360. * @return object $group @see getGroup
  361. *
  362. * Example: _buildGroupFromLdap($groupLdap) => {@see getGroup}
  363. */
  364. private function _buildGroupFromLdap($groupLdap, $fetchNested = false) {
  365. $group = new ObjectGroupLdap('MacOSX');
  366. $group->primaryKey = $groupLdap->cn;
  367. $group->realName = V::get('realName', '', $groupLdap);
  368. $group->zasobID = $this->getGroupIdFromUid($groupLdap->cn);
  369. $group->type = 'unknown';// TODO: try to fetch from name or from ldap attribute
  370. if ($groupLdap->cn == 'workgroup') $group->type = 'network';
  371. if ($fetchNested && !empty($groupLdap->nestedGroups)) {
  372. $group->nestedGroups = $this->_fetchNestedGroupsByAppleUids($groupLdap->nestedGroups);
  373. }
  374. $group->setLdapUID($groupLdap->appleUID);
  375. return $group;
  376. }
  377. public function getGroupsByUserUid($usrLogin) {
  378. $groups = array();
  379. $rawUsrLdap = $this->getRawLdapUser($usrLogin);
  380. if (!$rawUsrLdap) return $groups;
  381. $usrAppleUid = V::get('apple-generateduid', '', $rawUsrLdap);
  382. DBG::_('DBG_SU', '>0', "CleanupAppleMemberUidTodoList user apple-generateduid({$usrAppleUid})", $rawUsrLdap, __CLASS__, __FUNCTION__, __LINE__);
  383. if (empty($usrAppleUid)) return $groups;
  384. $groupsLdap = UsersLdapHelper::getUserGroupsByAppleUid($usrAppleUid, 0);
  385. foreach ($groupsLdap as $groupLdap) {
  386. $group = $this->_buildGroupFromLdap($groupLdap);
  387. if ($group->zasobID > 0) {
  388. $groups[$group->zasobID] = $group;
  389. }
  390. }
  391. return $groups;
  392. }
  393. private function _fetchNestedGroupsByAppleUids($appleUids) {
  394. $groups = array();
  395. if (!is_array($appleUids)) $appleUids = array($appleUids);
  396. $groupsLdap = UsersLdapHelper::getGroupsByAppleUids($appleUids);
  397. foreach ($groupsLdap as $vGroupLdap) {
  398. $group = $this->_buildGroupFromLdap($vGroupLdap, $fetchNested = false);
  399. if ($group && $group->zasobID > 0) {
  400. $groups[$group->zasobID] = $group;
  401. }
  402. }
  403. return $groups;
  404. }
  405. /**
  406. * @param string $usrLogin - user login
  407. * @return array of group objects @see getGroup
  408. */
  409. private function _getUserGroupsNetwork($usrLogin) {
  410. $groups = array();
  411. $groupsNetwork = UsersLdapHelper::getUserGroups($usrLogin, 0);
  412. foreach ($groupsNetwork as $vGroupNetwork) {
  413. $groups[$vGroupNetwork->cn] = $this->_buildGroupFromLdap($vGroupNetwork);
  414. }
  415. return $groups;
  416. }
  417. /**
  418. * @param string $usrLogin - user login
  419. * @return array of group objects @see getGroup
  420. */
  421. private function _getUserGroupsLocal($usrLogin) {
  422. $groups = array();
  423. $allGroups = $this->_fetchAllUserGroups($usrLogin);
  424. foreach ($allGroups as $groupName) {
  425. if ($this->_isGroupLocal($groupName)) {
  426. $groups[$groupName] = $this->_buildGroupLocal($groupName);
  427. }
  428. }
  429. DBG::_('DBG_SU', '>1', "User '{$usrLogin}' GroupsLocal:", $groups, __CLASS__, __FUNCTION__, __LINE__);
  430. return $groups;
  431. }
  432. private function _fetchAllUserGroups($usrLogin) {
  433. $groups = array();
  434. $cmd = "groups {$usrLogin}";
  435. $cmdOut = null; $cmdRet = null;
  436. exec($cmd, $cmdOut, $cmdRet);
  437. if ($cmdRet == 0 && !empty($cmdOut[0])) {
  438. $groupsCmd = explode(' ', $cmdOut[0]);
  439. foreach ($groupsCmd as $groupName) {
  440. if (!empty($groupName)) {
  441. $groups[] = $groupName;
  442. }
  443. }
  444. }
  445. DBG::_('DBG_SU', '>1', "User '{$usrLogin}' all groups:", $groups, __CLASS__, __FUNCTION__, __LINE__);
  446. return $groups;
  447. }
  448. public function getUserGroupsWithNested($usrLogin) {// TODO: NOT USED
  449. $groups = array();
  450. $groupsAll = array();
  451. $cmd = "groups {$usrLogin}";
  452. $cmdOut = null; $cmdRet = null;
  453. exec($cmd, $cmdOut, $cmdRet);
  454. if ($cmdRet == 0 && !empty($cmdOut[0])) {
  455. $pominGrupy = array('staff','everyone','netaccounts');
  456. $groupsCmd = explode(' ', $cmdOut[0]);
  457. foreach ($groupsCmd as $group) {
  458. $groupsAll[] = $group;
  459. $groupID = $this->getGroupIdFromUid($group);
  460. if (!empty($groupID)) {
  461. $groups[$groupID] = $group;
  462. }
  463. else if ('workgroup' == $group) {
  464. $groups[$group] = $group;
  465. }
  466. else if (substr($group, 0, strlen('com.apple.access_')) == 'com.apple.access_') {
  467. $groups[$group] = $group;
  468. }
  469. }
  470. }
  471. DBG::_('DBG_SU', '>1', "groupsAll", $groupsAll, __CLASS__, __FUNCTION__, __LINE__);
  472. DBG::_('DBG_SU', '>1', "groups", $groups, __CLASS__, __FUNCTION__, __LINE__);
  473. return $groups;
  474. }
  475. private function _groupNameRemoveID($groupName) {
  476. if (substr($groupName, 0, 1) == '[' && strpos($groupName, ']')) {
  477. $groupName = substr($groupName, strpos($groupName, ']') + 1);
  478. $groupName = trim($groupName);
  479. }
  480. return $groupName;
  481. }
  482. private function _generateGroupName($id, $groupName) {
  483. $groupNameShort = $groupName;
  484. $groupNameShort = $this->_groupNameRemoveID($groupNameShort);
  485. // TODO: polish chars - replace to ascii?
  486. $groupNameShort = preg_replace('/[^a-zA-Z0-9_-]+/', '_', $groupNameShort);
  487. // TODO: skrócić nazwę bo nie widać w aplikacji Server, np.
  488. // RealName: [5] Typowe_stanowisko_obs_uguj_ce_Obieg_Dokument_w_do_implementacji_po_instalacji_systemu
  489. // w apliakcji Server pokauje tylko "[5] ", tak samo w edycji
  490. return "[{$id}] {$groupNameShort}";
  491. }
  492. private function _generateGroupUid($id, $groupName) {
  493. $groupNameShort = $groupName;
  494. $groupNameShort = $this->_groupNameRemoveID($groupNameShort);
  495. $groupNameShort = str_replace(' ', '_', $groupNameShort);
  496. $groupNameShort = preg_replace('/[^a-zA-Z0-9_-]+/', '_', $groupNameShort);
  497. if (strlen($groupNameShort) > 30) {
  498. $groupNameShort = substr($groupNameShort, 0, 30);
  499. }
  500. return "{$id}_{$groupNameShort}";
  501. }
  502. /**
  503. * Create group.
  504. *
  505. * @param object $group @see getGroup
  506. * @return bool
  507. *
  508. * @require $group->zasobID - Allowed only network group based on Zasob.
  509. */
  510. public function createGroup(ObjectGroup $group) {
  511. // TEST: $ dscl /LDAPv3/127.0.0.1 -list /Groups PrimaryGroupID
  512. if ($group->zasobID <= 0) {
  513. throw new Exception("Nie udało się utworzyć grupy sieciowej '{$group->primaryKey}' '{$group->realName}' - brak numery zasobu");
  514. }
  515. $groupName = $this->_generateGroupName($group->zasobID, $group->realName);
  516. $groupUidGenerated = $this->_generateGroupUid($group->zasobID, $group->realName);
  517. /*
  518. * dseditgroup -o create -n /LDAPv3/ldap.company.com -u {$this->_rootUser} -P {$this->_rootPass} -r "Extra Group" -c "a nice comment" -k "some keyword" extragroup
  519. * The group extragroup is created from the node /LDAPv3/ldap.company.com with the realname, comment,
  520. * timetolive (instead of default of 14400 = 4 hours), and keyword atttribute values given above if the user
  521. * myusername has supplied a correct password and has write access.
  522. *
  523. * -r realname
  524. * This is a simple text string.
  525. *
  526. * -t recordtype
  527. * The type of the record to be added to or deleted from the group specified by groupname. Valid values are user, computer, group, or computergroup.
  528. *
  529. */
  530. $cmd = "dseditgroup -o create -n /LDAPv3/127.0.0.1 -u {$this->_rootUser} -P {$this->_rootPass} -r \"{$groupName}\" {$groupUidGenerated}";
  531. $cmdOut = null; $cmdRet = null;
  532. exec($cmd, $cmdOut, $cmdRet);
  533. DBG::_('DBG_SU', '>1', "create group cmd(" . str_replace($this->_rootPass, '***', $cmd) . ") ret({$cmdRet})", $cmdOut, __CLASS__, __FUNCTION__, __LINE__);
  534. if ($cmdRet !== 0) {
  535. throw new Exception("Nie udało się utworzyć grupy sieciowej '{$group->primaryKey}' '{$group->realName}'");
  536. }
  537. //$command8 = "dscl -u {$user} -P {$pass} /LDAPv3/127.0.0.1 -append /Groups/{$groupUid} GroupMembership {$ACCOUNT} ";
  538. //$command8 = "dscl -u {$user} -P {$pass} /LDAPv3/127.0.0.1 -delete /Groups/{$groupUid} GroupMembership {$ACCOUNT} ";
  539. //$command1 = "dscl -u {$user} -P {$pass} /LDAPv3/127.0.0.1 -create /Groups/{$groupUid} PrimaryGroupID {$PrimaryGroupID} ";
  540. //$command2 = "dscl -u {$user} -P {$pass} /LDAPv3/127.0.0.1 -create /Groups/{$groupUid} RealName \"{$groupName}\" ";
  541. DB::getPDO()->exec("update `CRM_LISTA_ZASOBOW` set `A_SYNC_LDAP_DATE`=NOW() where ID={$group->zasobID}");
  542. }
  543. private function _isGroupLocal($groupUid) {
  544. $localGroups = array();
  545. $localGroups[] = 'com.apple.access_mail';
  546. $localGroups[] = 'com.apple.access_addressbook';
  547. $localGroups[] = 'com.apple.access_calendar';
  548. $localGroups[] = 'com.apple.access_smb';
  549. $localGroups[] = 'com.apple.access_afp';
  550. $localGroups[] = 'com.apple.access_vpn';
  551. $localGroups[] = 'com.apple.access_chat';
  552. //$localGroups[] = 'workgroup'; - Network Group
  553. return in_array($groupUid, $localGroups);
  554. }
  555. /**
  556. * Add local group member.
  557. *
  558. * @param string $usrLogin - user login
  559. * @param object $group - @see getGroup
  560. * @return bool
  561. *
  562. * @require sudoers dla _www
  563. *
  564. * cat /etc/sudoers |grep "'.$ADMIN_USERNAME.' ALL = NOPASSWD: /usr/bin/su" || echo "'.$ADMIN_USERNAME.' ALL = NOPASSWD: /usr/bin/su " >> /etc/sudoers;
  565. * cat /etc/sudoers |grep "'.$ADMIN_USERNAME.' ALL = NOPASSWD: /usr/bin/su"
  566. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /Applications/Server.app/Contents/ServerRoot/usr/sbin/calendarserver_manage_principals" || echo "_www ALL = NOPASSWD: /Applications/Server.app/Contents/ServerRoot/usr/sbin/calendarserver_manage_principals " >> /etc/sudoers;
  567. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /Applications/Server.app/Contents/ServerRoot/usr/sbin/calendarserver_manage_principals"';
  568. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /usr/bin/dscl" || echo "_www ALL = NOPASSWD: /usr/bin/dscl " >> /etc/sudoers;
  569. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /usr/bin/dscl";
  570. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /usr/bin/pwpolicy" || echo "_www ALL = NOPASSWD: /usr/bin/pwpolicy" >> /etc/sudoers;
  571. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /usr/bin/pwpolicy";
  572. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /usr/sbin/createhomedir" || echo "_www ALL = NOPASSWD: /usr/sbin/createhomedir" >> /etc/sudoers;
  573. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /usr/sbin/createhomedir";
  574. *
  575. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /usr/sbin/dseditgroup" || echo "_www ALL = NOPASSWD: /usr/sbin/dseditgroup" >> /etc/sudoers;
  576. * cat /etc/sudoers |grep "_www ALL = NOPASSWD: /usr/sbin/dseditgroup";
  577. */
  578. private function _addUserGroupLocal($usrLogin, $group) {
  579. if (!$group || empty($group->primaryKey) || empty($usrLogin)) return false;
  580. $groupUid = $group->primaryKey;
  581. $cmd = "sudo dscl /Local/Default -append /Groups/{$groupUid} GroupMembership {$usrLogin} ";
  582. $cmdOut = null; $cmdRet = null;
  583. exec($cmd, $cmdOut, $cmdRet);
  584. if ($cmdRet != 0) {
  585. // group may not exists - fixed in instalator for 'El Capitan'
  586. throw new Exception("Nie udało się dodać usera '{$usrLogin}' do grupy lokalnej '{$groupUid}'");
  587. }
  588. }
  589. /**
  590. * Remove local group member.
  591. *
  592. * @param string $usrLogin - user login
  593. * @param object $group - @see getGroup
  594. * @return bool
  595. */
  596. private function _removeUserGroupLocal($usrLogin, $group) {
  597. if (!$group || empty($group->primaryKey) || empty($usrLogin)) return false;
  598. $groupUid = $group->primaryKey;
  599. //$cmd = "sudo dscl /Local/Default -delete /Groups/{$groupUid} GroupMembership {$usrLogin} 2>&1 ";
  600. //$cmd = "dseditgroup -o edit -n /Local/Default -u diradmin -p ... -d username -t user {$groupUid} ";
  601. $cmd = "sudo dseditgroup -o edit -n /Local/Default -d {$usrLogin} -t user {$groupUid} 2>&1 ";
  602. // The group extragroup from the node /LDAPv3/ldap.company.com will have the username deleted if the correct
  603. // password is presented interactively for the user myusername which also need to have write access.
  604. // -t recordtype type of the record to add or delete
  605. // -d recordname name of the record to delete
  606. $cmdOut = null; $cmdRet = null;
  607. exec($cmd, $cmdOut, $cmdRet);
  608. DBG::_('DBG_SU', '>1', "cmd({$cmd}) ret({$cmdRet})", $cmdOut, __CLASS__, __FUNCTION__, __LINE__);
  609. if ($cmdRet != 0) {
  610. //throw new Exception("Nie udało się usunąć usera '{$usrLogin}' z grupy lokalnej '{$groupUid}'");
  611. // TODO: user may not be in this local group
  612. }
  613. }
  614. public function findGroupUidDscl($groupUid) {// not used @see findGroupUid
  615. $groupRealUid = null;
  616. $cmd = "dscl /LDAPv3/127.0.0.1 -list /Groups | grep '^{$groupUid}' ";
  617. $cmdOut = null; $cmdRet = null;
  618. exec($cmd, $cmdOut, $cmdRet);
  619. if ($cmdRet != 0) {
  620. $this->setError(1, "cmd failed - search for group by uid", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
  621. return false;
  622. }
  623. if (!empty($cmdOut)) {
  624. foreach ($cmdOut as $vGroupUid) {
  625. $vGroupID = $this->getGroupIdFromUid($vGroupUid);
  626. if (!empty($vGroupID) && $vGroupID == $groupUid) {
  627. $groupRealUid = $vGroupUid;
  628. break;
  629. }
  630. }
  631. }
  632. return $groupRealUid;
  633. }
  634. public function findGroupUidLdap($groupUid) {
  635. $groupRealUid = null;
  636. $groups = UsersLdapHelper::getGroupsByID($groupUid);
  637. if (count($groups) == 1) {
  638. $groupRealUid = reset($groups)->cn;
  639. }
  640. return $groupRealUid;
  641. }
  642. public function findGroupUid($groupUid) {
  643. return $this->findGroupUidLdap($groupUid);
  644. }
  645. /**
  646. * Add network group member.
  647. *
  648. * @param string $usrLogin - user login
  649. * @param object $group - @see getGroup
  650. * @return bool
  651. */
  652. private function _addUserGroupNetwork($usrLogin, $group) {
  653. if (!$group || empty($group->primaryKey) || empty($usrLogin)) return false;
  654. $groupUid = $group->primaryKey;
  655. $groupName = $group->realName;
  656. $groupRealUid = '';
  657. if ($group->type == 'network') {
  658. $groupRealUid = $group->primaryKey;// workgroup
  659. }
  660. else if (is_numeric($groupUid)) {
  661. $groupRealUid = $this->findGroupUid($groupUid);
  662. }
  663. if (!$groupRealUid) {
  664. if ($group->type == 'network') {
  665. throw new Exception("Brak dostępu do utworzenia grupy sieciowej '{$group->primaryKey}'");
  666. } else if ($group->type == 'local') {
  667. throw new Exception("Brak dostępu do utworzenia grupy lokalnej '{$group->primaryKey}'");
  668. }
  669. $this->createGroup($group);
  670. $groupRealUid = $this->findGroupUid($groupUid);
  671. if (!$groupRealUid) throw new Exception("Nie udało się utworzyć grupy sieciowej '{$group->primaryKey}'");
  672. }
  673. $cmdDsclAuth = "dscl -u {$this->_rootUser} -P {$this->_rootPass} /LDAPv3/127.0.0.1 ";
  674. $cmd = "{$cmdDsclAuth} -append /Groups/{$groupRealUid} GroupMembership {$usrLogin} ";
  675. $cmdOut = null; $cmdRet = null;
  676. exec($cmd, $cmdOut, $cmdRet);
  677. if ($cmdRet != 0) {// TODO: may return 62 - user already in this group
  678. throw new Exception("Nie udało się dodać usera '{$usrLogin}' do grupy sieciowej '{$groupUid}'");
  679. }
  680. }
  681. /**
  682. * Remove network group member.
  683. *
  684. * @param string $usrLogin - user login
  685. * @param object $group - @see getGroup
  686. * @return bool
  687. */
  688. private function _removeUserGroupNetwork($usrLogin, $group) {
  689. if (!$group || empty($group->primaryKey) || empty($usrLogin)) return false;
  690. $groupUid = $group->primaryKey;
  691. $cmdDsclAuth = "dscl -u {$this->_rootUser} -P {$this->_rootPass} /LDAPv3/127.0.0.1 ";
  692. $cmd = "{$cmdDsclAuth} -delete /Groups/{$groupUid} GroupMembership {$usrLogin} ";
  693. $cmdOut = null; $cmdRet = null;
  694. exec($cmd, $cmdOut, $cmdRet);
  695. DBG::_('DBG_SU', '>1', "cmd({$cmd}) ret({$cmdRet})", $cmdOut, __CLASS__, __FUNCTION__, __LINE__);
  696. if ($cmdRet != 0) {
  697. throw new Exception("Nie udało się usunąć usera '{$usrLogin}' z grupy sieciowej '{$groupUid}'");
  698. }
  699. }
  700. private function _removeUserUidFromGroupNetwork($usrAppleUid, $group) {
  701. if (!$group || empty($group->primaryKey) || empty($usrAppleUid)) return false;
  702. $groupUid = $group->primaryKey;
  703. $cmdDsclAuth = "dscl -u {$this->_rootUser} -P {$this->_rootPass} /LDAPv3/127.0.0.1 ";
  704. $cmd = "{$cmdDsclAuth} -delete /Groups/{$groupUid} GroupMembers {$usrAppleUid} ";
  705. $cmdOut = null; $cmdRet = null;
  706. exec($cmd, $cmdOut, $cmdRet);
  707. DBG::_('DBG_SU', '>1', "cmd({$cmd}) ret({$cmdRet})", $cmdOut, __CLASS__, __FUNCTION__, __LINE__);
  708. if ($cmdRet != 0) {
  709. throw new Exception("Nie udało się usunąć uid '{$usrAppleUid}' z grupy sieciowej '{$groupUid}'");
  710. }
  711. }
  712. /**
  713. * Add group member.
  714. *
  715. * @param string $usrLogin - user login
  716. * @param object $group - @see getGroup
  717. * @return bool
  718. */
  719. public function addUserGroup($usrLogin, $group) {
  720. // $groupUid, $groupName
  721. if ($group->type == 'local') {
  722. return $this->_addUserGroupLocal($usrLogin, $group);
  723. } else {
  724. return $this->_addUserGroupNetwork($usrLogin, $group);
  725. }
  726. }
  727. /**
  728. * Remove group member.
  729. *
  730. * @param string $usrLogin - user login
  731. * @param object $group - @see getGroup
  732. * @return bool
  733. */
  734. public function removeUserGroup($usrLogin, $group) {
  735. if ($group->type == 'local') {
  736. return $this->_removeUserGroupLocal($usrLogin, $group);
  737. } else {
  738. return $this->_removeUserGroupNetwork($usrLogin, $group);
  739. }
  740. }
  741. public function removeUserUidFromGroup($usrLogin, $group) {
  742. $rawUsrLdap = $this->getRawLdapUser($usrLogin);
  743. if (!$rawUsrLdap) throw new Exception("Cannot find user '{$usrLogin}'");
  744. $usrAppleUid = V::get('apple-generateduid', '', $rawUsrLdap);
  745. if (empty($usrAppleUid)) throw new Exception("Cannot find uid for user '{$usrLogin}'");
  746. DBG::_('DBG_SU', '>0', "CleanupAppleMemberUidTodoList user apple-generateduid({$usrAppleUid})", $group, __CLASS__, __FUNCTION__, __LINE__);
  747. if ($group->type == 'local') {
  748. //return $this->_removeUserGroupLocal($usrLogin, $group);
  749. } else {
  750. return $this->_removeUserUidFromGroupNetwork($usrAppleUid, $group);
  751. }
  752. }
  753. public function addNestedGroup($groupID, $nestedGroupID) {
  754. if ($groupID <= 0) return false;
  755. if ($nestedGroupID <= 0) return false;
  756. $group = $this->_getGroup($groupID);
  757. $groupNested = $this->_getGroup($nestedGroupID);
  758. if (!$group || !$groupNested) {
  759. return false;
  760. }
  761. $groupToAdd = $groupNested->primaryKey;
  762. $groupName = $group->primaryKey;
  763. // put a group called {$groupToAdd} into the {$groupName} group
  764. $cmd = "dseditgroup -o edit -n /LDAPv3/127.0.0.1 -u {$this->_rootUser} -P {$this->_rootPass} -a {$groupToAdd} -t group {$groupName}";
  765. $cmdOut = null; $cmdRet = null;
  766. exec($cmd, $cmdOut, $cmdRet);
  767. if ($cmdRet != 0) {
  768. DBG::_('DBG_SU', '>1', "cmd(" . str_replace($this->_rootPass, '***', $cmd) . ") ret({$cmdRet})", $cmdOut, __CLASS__, __FUNCTION__, __LINE__);
  769. $this->setError(1, "Nie udało się dodać grupy nadrzędnej '{$groupToAdd}' do grupy '{$groupName}' ", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
  770. return false;
  771. }
  772. return true;
  773. }
  774. public function removeNestedGroup($groupID, $nestedGroupID) {
  775. if ($groupID <= 0) return false;
  776. if ($nestedGroupID <= 0) return false;
  777. $group = $this->_getGroup($groupID);
  778. $groupNested = $this->_getGroup($nestedGroupID);
  779. if (!$group || !$groupNested) {
  780. return false;
  781. }
  782. $groupToRemove = $groupNested->primaryKey;
  783. $groupName = $group->primaryKey;
  784. // put a group called {$groupToAdd} into the {$groupName} group
  785. $cmd = "dseditgroup -o edit -n /LDAPv3/127.0.0.1 -u {$this->_rootUser} -P {$this->_rootPass} -d {$groupToRemove} -t group {$groupName}";
  786. $cmdOut = null; $cmdRet = null;
  787. exec($cmd, $cmdOut, $cmdRet);
  788. if ($cmdRet != 0) {
  789. DBG::_('DBG_SU', '>1', "cmd(" . str_replace($this->_rootPass, '***', $cmd) . ") ret({$cmdRet})", $cmdOut, __CLASS__, __FUNCTION__, __LINE__);
  790. $this->setError(1, "Nie udało się usunąć grupy podrzędnej '{$groupToRemove}' z grupy '{$groupName}' ", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
  791. return false;
  792. }
  793. return true;
  794. }
  795. public function changePassword($usrLogin, $passwd) {
  796. $cmdDsclAuth = "dscl -u {$this->_rootUser} -P {$this->_rootPass} /LDAPv3/127.0.0.1 ";
  797. $cmd = "{$cmdDsclAuth} -passwd /Users/{$usrLogin} \"{$passwd}\" ";
  798. $cmdOut = null; $cmdRet = null;
  799. exec($cmd, $cmdOut, $cmdRet);
  800. if ($cmdRet != 0) {
  801. return false;
  802. }
  803. return true;
  804. }
  805. public function _addMailAliases($usrLogin, $aliasList) {
  806. if (empty($usrLogin)) throw new Exception("Cannot add mail alias list: Unknown user login!");
  807. if (empty($aliasList)) throw new Exception("Cannot add mail alias list: Empty alias list!");
  808. $aliasListFlat = implode(' ', $aliasList);
  809. $cmdDsclAuth = "dscl -u {$this->_rootUser} -P {$this->_rootPass} /LDAPv3/127.0.0.1 ";
  810. $cmd = "{$cmdDsclAuth} -append /Users/{$usrLogin} EMailAddress {$aliasListFlat} ";
  811. $cmdOut = null; $cmdRet = null;
  812. exec($cmd, $cmdOut, $cmdRet);
  813. if ($cmdRet != 0) throw new Exception("Nie udało się dodać aliasów: {$aliasListFlat}!");
  814. }
  815. public function _removeMailAliases($usrLogin, $aliasList) {
  816. if (empty($usrLogin)) throw new Exception("Cannot remove mail alias list: Unknown user login!");
  817. if (empty($aliasList)) throw new Exception("Cannot remove mail alias list: Empty alias list!");
  818. $aliasListFlat = implode(' ', $aliasList);
  819. $cmdDsclAuth = "dscl -u {$this->_rootUser} -P {$this->_rootPass} /LDAPv3/127.0.0.1 ";
  820. $cmd = "{$cmdDsclAuth} -delete /Users/{$usrLogin} EMailAddress {$aliasListFlat} ";
  821. $cmdOut = null; $cmdRet = null;
  822. exec($cmd, $cmdOut, $cmdRet);
  823. if ($cmdRet != 0) throw new Exception("Nie udało się usunąć aliasów: {$aliasListFlat}");
  824. }
  825. public function forceReplaceAliasList($usrLogin, $aliasList) {
  826. if (empty($usrLogin)) throw new Exception("Cannot replace mail alias list: Unknown user login!");
  827. $aliasListFlat = implode(' ', $aliasList);
  828. $cmdDsclAuth = "dscl -u {$this->_rootUser} -P {$this->_rootPass} /LDAPv3/127.0.0.1 ";
  829. $cmd = "{$cmdDsclAuth} -delete /Users/{$usrLogin} EMailAddress";
  830. $cmdOut = null; $cmdRet = null;
  831. exec($cmd, $cmdOut, $cmdRet);
  832. if ($cmdRet != 0) throw new Exception("Nie udało się usunąć wszystkich aliasów");
  833. if (!empty($aliasList)) {
  834. sleep(1);
  835. $cmd = "{$cmdDsclAuth} -append /Users/{$usrLogin} EMailAddress {$aliasListFlat}";
  836. $cmdOut = null; $cmdRet = null;
  837. exec($cmd, $cmdOut, $cmdRet);
  838. if ($cmdRet != 0) throw new Exception("Nie udało się dodać aliasów: {$aliasListFlat}");
  839. }
  840. }
  841. }