7 * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
8 * @copyright Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de)
9 * @author Lars Kneschke <l.kneschke@metaways.de>
25 const ACTIVEDIRECTORY = 'ActiveDirectory';
28 const TYPO3 = 'Typo3';
31 * user status constants
35 * @todo use constants from model
37 const STATUS_BLOCKED = 'blocked';
38 const STATUS_DISABLED = 'disabled';
39 const STATUS_ENABLED = 'enabled';
40 const STATUS_EXPIRED = 'expired';
43 * Key under which the default user group name setting will be stored/retrieved
46 const DEFAULT_USER_GROUP_NAME_KEY = 'defaultUserGroupName';
49 * Key under which the default admin group name setting will be stored/retrieved
52 const DEFAULT_ADMIN_GROUP_NAME_KEY = 'defaultAdminGroupName';
54 protected static $_contact2UserMapping = array(
55 'n_family' => 'accountLastName',
56 'n_given' => 'accountFirstName',
57 'n_fn' => 'accountFullName',
58 'n_fileas' => 'accountDisplayName',
59 'email' => 'accountEmailAddress',
60 'container_id' => 'container_id',
66 * don't use the constructor. use the singleton
68 private function __construct() {}
71 * don't clone. Use the singleton.
73 private function __clone() {}
76 * holds the instance of the singleton
78 * @var Tinebase_User_Interface
80 private static $_instance = NULL;
83 * Holds the accounts backend type (e.g. Ldap or Sql.
84 * Property is lazy loaded on first access via getter {@see getConfiguredBackend()}
86 * @var array | optional
88 private static $_backendType;
91 * Holds the backend configuration options.
92 * Property is lazy loaded from {@see Tinebase_Config} on first access via
93 * getter {@see getBackendConfiguration()}
95 * @var array | optional
97 private static $_backendConfiguration;
100 * Holds the backend configuration options.
101 * Property is lazy loaded from {@see Tinebase_Config} on first access via
102 * getter {@see getBackendConfiguration()}
104 * @var array | optional
106 private static $_backendConfigurationDefaults = array(
108 self::DEFAULT_USER_GROUP_NAME_KEY => Tinebase_Group::DEFAULT_USER_GROUP,
109 self::DEFAULT_ADMIN_GROUP_NAME_KEY => Tinebase_Group::DEFAULT_ADMIN_GROUP,
112 'host' => 'localhost',
115 'bindRequiresDn' => true,
116 'useStartTls' => false,
117 'useRfc2307bis' => false,
119 'userFilter' => 'objectclass=posixaccount',
120 'userSearchScope' => Zend_Ldap::SEARCH_SCOPE_SUB,
122 'groupFilter' => 'objectclass=posixgroup',
123 'groupSearchScope' => Zend_Ldap::SEARCH_SCOPE_SUB,
124 'pwEncType' => 'SSHA',
125 'minUserId' => '10000',
126 'maxUserId' => '29999',
127 'minGroupId' => '11000',
128 'maxGroupId' => '11099',
129 'groupUUIDAttribute' => 'entryUUID',
130 'userUUIDAttribute' => 'entryUUID',
131 self::DEFAULT_USER_GROUP_NAME_KEY => Tinebase_Group::DEFAULT_USER_GROUP,
132 self::DEFAULT_ADMIN_GROUP_NAME_KEY => Tinebase_Group::DEFAULT_ADMIN_GROUP,
135 self::ACTIVEDIRECTORY => array(
136 'host' => 'localhost',
139 'bindRequiresDn' => true,
140 'useRfc2307' => false,
142 'userFilter' => 'objectclass=user',
143 'userSearchScope' => Zend_Ldap::SEARCH_SCOPE_SUB,
145 'groupFilter' => 'objectclass=group',
146 'groupSearchScope' => Zend_Ldap::SEARCH_SCOPE_SUB,
147 'minUserId' => '10000',
148 'maxUserId' => '29999',
149 'minGroupId' => '11000',
150 'maxGroupId' => '11099',
151 'groupUUIDAttribute' => 'objectGUID',
152 'userUUIDAttribute' => 'objectGUID',
153 self::DEFAULT_USER_GROUP_NAME_KEY => 'Domain Users',
154 self::DEFAULT_ADMIN_GROUP_NAME_KEY => 'Domain Admins',
160 * the singleton pattern
162 * @return Tinebase_User_Abstract
164 public static function getInstance()
166 if (self::$_instance === NULL) {
167 $backendType = self::getConfiguredBackend();
168 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .' accounts backend: ' . $backendType);
170 self::$_instance = self::factory($backendType);
173 return self::$_instance;
177 * return an instance of the current user backend
179 * @param string $backendType name of the user backend
180 * @return Tinebase_User_Abstract
181 * @throws Tinebase_Exception_InvalidArgument
183 public static function factory($backendType)
185 $options = self::getBackendConfiguration();
187 // this is a dangerous TRACE as there might be passwords in here!
188 //if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' '
189 // . print_r($options, TRUE));
191 $options['plugins'] = array();
193 // manage email user settings
194 if (Tinebase_EmailUser::manages(Tinebase_Config::IMAP)) {
196 $options['plugins'][] = Tinebase_EmailUser::getInstance(Tinebase_Config::IMAP);
197 } catch (Exception $e) {
198 if (Tinebase_Core::isLogLevel(Zend_Log::ERR)) Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__
199 . ' Could not add IMAP EmailUser plugin: ' . $e);
202 if (Tinebase_EmailUser::manages(Tinebase_Config::SMTP)) {
204 $options['plugins'][] = Tinebase_EmailUser::getInstance(Tinebase_Config::SMTP);
205 } catch (Exception $e) {
206 if (Tinebase_Core::isLogLevel(Zend_Log::ERR)) Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__
207 . ' Could not add SMTP EmailUser plugin: ' . $e);
211 switch ($backendType) {
212 case self::ACTIVEDIRECTORY:
213 $result = new Tinebase_User_ActiveDirectory($options);
219 if (isset(Tinebase_Core::getConfig()->samba) && Tinebase_Core::getConfig()->samba->get('manageSAM', FALSE) == true) {
220 $options['plugins'][] = new Tinebase_User_Plugin_Samba(Tinebase_Core::getConfig()->samba->toArray());
223 $result = new Tinebase_User_Ldap($options);
228 $result = new Tinebase_User_Sql($options);
233 $result = new Tinebase_User_Typo3($options);
238 throw new Tinebase_Exception_InvalidArgument("User backend type $backendType not implemented.");
241 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
242 . ' Created user backend of type ' . get_class($result));
248 * returns the configured rs backend
252 public static function getConfiguredBackend()
254 if (!isset(self::$_backendType)) {
255 if (Setup_Controller::getInstance()->isInstalled('Tinebase')) {
256 self::setBackendType(Tinebase_Config::getInstance()->get(Tinebase_Config::USERBACKENDTYPE, self::SQL));
258 self::setBackendType(self::SQL);
262 return self::$_backendType;
266 * setter for {@see $_backendType}
268 * @todo persist in db
270 * @param string $backendType
273 public static function setBackendType($backendType)
275 if (empty($backendType)) {
276 throw new Tinebase_Exception_InvalidArgument('Backend type can not be empty!');
279 $newBackendType = ucfirst($backendType);
280 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ .
281 ' Setting backend type to ' . $newBackendType);
283 self::$_backendType = $newBackendType;
287 * Setter for {@see $_backendConfiguration}
290 * Setting will not be written to Database or Filesystem.
291 * To persist the change call {@see saveBackendConfiguration()}
293 * @param mixed $_value
294 * @param string $_key
295 * @param boolean $_applyDefaults
298 * @todo generalize this (see Tinebase_Auth::setBackendConfiguration)
300 public static function setBackendConfiguration($_value, $_key = null, $_applyDefaults = false)
302 $defaultValues = self::$_backendConfigurationDefaults[self::getConfiguredBackend()];
304 if (is_null($_key) && !is_array($_value)) {
305 throw new Tinebase_Exception_InvalidArgument('To set backend configuration either a key and value '
306 . 'parameter are required or the value parameter should be a hash');
307 } elseif (is_null($_key) && is_array($_value)) {
308 $configToSet = $_applyDefaults ? array_merge($defaultValues, $_value) : $_value;
309 foreach ($configToSet as $key => $value) {
310 self::setBackendConfiguration($value, $key);
313 if ( ! (isset($defaultValues[$_key]) || array_key_exists($_key, $defaultValues))) {
314 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ .
315 " Cannot set backend configuration option '$_key' for accounts storage " . self::getConfiguredBackend());
318 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
319 ' Setting backend key ' . $_key . ' to ' . (preg_match('/password|pwd|pass|passwd/i', $_key) ? '********' : $_value));
321 self::$_backendConfiguration[$_key] = $_value;
326 * Delete the given config setting or all config settings if {@param $_key} is not specified
328 * @param string | optional $_key
331 public static function deleteBackendConfiguration($_key = null)
333 if (is_null($_key)) {
334 self::$_backendConfiguration = array();
335 } elseif ((isset(self::$_backendConfiguration[$_key]) || array_key_exists($_key, self::$_backendConfiguration))) {
336 unset(self::$_backendConfiguration[$_key]);
338 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' configuration option does not exist: ' . $_key);
343 * Write backend configuration setting {@see $_backendConfigurationSettings} and {@see $_backendType} to
348 public static function saveBackendConfiguration()
350 Tinebase_Config::getInstance()->set(Tinebase_Config::USERBACKEND, self::getBackendConfiguration());
351 Tinebase_Config::getInstance()->set(Tinebase_Config::USERBACKENDTYPE, self::getConfiguredBackend());
355 * Getter for {@see $_backendConfiguration}
357 * @param String | optional $_key
358 * @return mixed [If {@param $_key} is set then only the specified option is returned, otherwise the whole options hash]
360 public static function getBackendConfiguration($_key = null, $_default = null)
362 //lazy loading for $_backendConfiguration
363 if (!isset(self::$_backendConfiguration)) {
364 if (Setup_Controller::getInstance()->isInstalled('Tinebase')) {
365 $rawBackendConfiguration = Tinebase_Config::getInstance()->get(Tinebase_Config::USERBACKEND, new Tinebase_Config_Struct())->toArray();
367 $rawBackendConfiguration = array();
369 self::$_backendConfiguration = is_array($rawBackendConfiguration) ? $rawBackendConfiguration : Zend_Json::decode($rawBackendConfiguration);
373 return (isset(self::$_backendConfiguration[$_key]) || array_key_exists($_key, self::$_backendConfiguration)) ? self::$_backendConfiguration[$_key] : $_default;
375 return self::$_backendConfiguration;
380 * Returns default configuration for all supported backends
381 * and overrides the defaults with concrete values stored in this configuration
383 * @param boolean $_getConfiguredBackend
384 * @return mixed [If {@param $_key} is set then only the specified option is returned, otherwise the whole options hash]
386 public static function getBackendConfigurationWithDefaults($_getConfiguredBackend = TRUE)
389 $defaultConfig = self::getBackendConfigurationDefaults();
390 foreach ($defaultConfig as $backendType => $backendConfig) {
391 $config[$backendType] = ($_getConfiguredBackend && $backendType == self::getConfiguredBackend() ? self::getBackendConfiguration() : array());
392 if (is_array($config[$backendType])) {
393 foreach ($backendConfig as $key => $value) {
394 if (! (isset($config[$backendType][$key]) || array_key_exists($key, $config[$backendType]))) {
395 $config[$backendType][$key] = $value;
399 $config[$backendType] = $backendConfig;
406 * Getter for {@see $_backendConfigurationDefaults}
407 * @param String | optional $_backendType
410 public static function getBackendConfigurationDefaults($_backendType = null) {
412 if (!(isset(self::$_backendConfigurationDefaults[$_backendType]) || array_key_exists($_backendType, self::$_backendConfigurationDefaults))) {
413 throw new Tinebase_Exception_InvalidArgument("Unknown backend type '$_backendType'");
415 return self::$_backendConfigurationDefaults[$_backendType];
417 return self::$_backendConfigurationDefaults;
422 * syncronize user from syncbackend to local sql backend
424 * @param mixed $username the login id of the user to synchronize
425 * @param array $options
426 * @return Tinebase_Model_FullUser
427 * @throws Tinebase_Exception
429 * @todo make use of dbmail plugin configurable (should be false by default)
430 * @todo switch to new primary group if it could not be found
431 * @todo write a test and refactor this ... :(
433 public static function syncUser($username, $options = array())
435 if ($username instanceof Tinebase_Model_FullUser) {
436 $username = $username->accountLoginName;
439 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " sync user data for: " . $username);
441 $userBackend = Tinebase_User::getInstance();
442 if (isset($options['ldapplugins']) && is_array($options['ldapplugins'])) {
443 foreach ($options['ldapplugins'] as $plugin) {
444 $userBackend->registerLdapPlugin($plugin);
448 $user = $userBackend->getUserByPropertyFromSyncBackend('accountLoginName', $username, 'Tinebase_Model_FullUser');
449 $user->accountPrimaryGroup = Tinebase_Group::getInstance()->resolveGIdNumberToUUId($user->accountPrimaryGroup);
451 $userProperties = method_exists($userBackend, 'getLastUserProperties') ? $userBackend->getLastUserProperties() : array();
453 $hookResult = self::_syncUserHook($user, $userProperties);
458 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' '
459 . print_r($user->toArray(), TRUE));
461 self::getPrimaryGroupForUser($user);
464 $currentUser = $userBackend->getUserByProperty('accountId', $user, 'Tinebase_Model_FullUser');
466 $currentUser->accountLoginName = $user->accountLoginName;
467 $currentUser->accountLastPasswordChange = $user->accountLastPasswordChange;
468 $currentUser->accountExpires = $user->accountExpires;
469 $currentUser->accountPrimaryGroup = $user->accountPrimaryGroup;
470 $currentUser->accountDisplayName = $user->accountDisplayName;
471 $currentUser->accountLastName = $user->accountLastName;
472 $currentUser->accountFirstName = $user->accountFirstName;
473 $currentUser->accountFullName = $user->accountFullName;
474 $currentUser->accountEmailAddress = $user->accountEmailAddress;
475 $currentUser->accountHomeDirectory = $user->accountHomeDirectory;
476 $currentUser->accountLoginShell = $user->accountLoginShell;
477 if (! empty($user->visibility) && $currentUser->visibility !== $user->visibility) {
478 $currentUser->visibility = $user->visibility;
479 if (empty($currentUser->contact_id) && $currentUser->visibility == Tinebase_Model_FullUser::VISIBILITY_DISPLAYED) {
480 self::createContactForSyncedUser($currentUser);
484 Tinebase_Timemachine_ModificationLog::setRecordMetaData($currentUser, 'update');
485 $syncedUser = $userBackend->updateUserInSqlBackend($currentUser);
486 if (! empty($user->container_id)) {
487 $syncedUser->container_id = $user->container_id;
489 $userBackend->updatePluginUser($syncedUser, $user);
491 } catch (Tinebase_Exception_NotFound $ten) {
493 $invalidUser = $userBackend->getUserByPropertyFromSqlBackend('accountLoginName', $username, 'Tinebase_Model_FullUser');
494 if (Tinebase_Core::isLogLevel(Zend_Log::CRIT)) Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__
495 . " Remove invalid user: " . $username);
496 $userBackend->deleteUserInSqlBackend($invalidUser);
497 } catch (Tinebase_Exception_NotFound $ten) {
501 if ($user->visibility !== Tinebase_Model_FullUser::VISIBILITY_HIDDEN) {
502 self::createContactForSyncedUser($user);
504 Tinebase_Timemachine_ModificationLog::setRecordMetaData($user, 'create');
505 $syncedUser = $userBackend->addUserInSqlBackend($user);
506 $userBackend->addPluginUser($syncedUser, $user);
509 self::syncContactData($syncedUser, $options);
511 Tinebase_Group::syncMemberships($syncedUser);
517 * import contact data(phone, address, fax, birthday. photo)
519 * @param Tinebase_Model_FullUser $syncedUser
520 * @param array $options
522 public static function syncContactData($syncedUser, $options)
524 if (! Tinebase_Config::getInstance()->get(Tinebase_Config::SYNC_USER_CONTACT_DATA, true)
525 || ! isset($options['syncContactData'])
526 || ! $options['syncContactData']
527 || ! Tinebase_Application::getInstance()->isInstalled('Addressbook')
528 || $syncedUser->visibility === Tinebase_Model_FullUser::VISIBILITY_HIDDEN
530 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
531 . ' Contact data sync disabled');
535 $addressbook = Addressbook_Backend_Factory::factory(Addressbook_Backend_Factory::SQL);
538 $contact = $addressbook->getByUserId($syncedUser->getId());
539 $originalContact = clone $contact;
541 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
542 . ' user: ' .print_r($syncedUser->toArray(), true));
544 Tinebase_User::getInstance()->updateContactFromSyncBackend($syncedUser, $contact);
545 $contact = self::_user2Contact($syncedUser, $contact);
547 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
548 . ' new contact: ' . print_r($contact->toArray(), true)
549 . ' orig contact:' . print_r($originalContact->toArray(), true));
551 // TODO allow to diff jpegphoto, too / maybe this should only be done when called via CLI/cronjob
552 $diff = $contact->diff($originalContact, array('jpegphoto'));
553 if (! $diff->isEmpty() || ($originalContact->jpegphoto == 0 && ! empty($contact->jpegphoto))) {
555 Tinebase_Timemachine_ModificationLog::setRecordMetaData($contact, 'update');
556 if ($contact->container_id !== null) {
557 Tinebase_Container::getInstance()->increaseContentSequence($contact->container_id);
560 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
561 . ' Updating contact data for user ' . $syncedUser->accountLoginName);
562 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
563 . ' Diff: ' . print_r($diff->toArray(), true));
565 $addressbook->update($contact);
567 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
568 . ' User contact is up to date.');
570 } catch (Addressbook_Exception_NotFound $aenf) {
571 self::createContactForSyncedUser($syncedUser);
572 $syncedUser = Tinebase_User::getInstance()->updateUserInSqlBackend($syncedUser);
574 } catch (Tinebase_Exception_NotFound $tenf) {
575 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
576 . ' Contact information seems to be missing in sync backend');
577 Tinebase_Exception::log($tenf);
582 * get primary group for user and make sure that group exists
584 * @param Tinebase_Model_FullUser $user
585 * @throws Tinebase_Exception
586 * @return Tinebase_Model_Group
588 public static function getPrimaryGroupForUser($user)
590 $groupBackend = Tinebase_Group::getInstance();
593 $group = $groupBackend->getGroupById($user->accountPrimaryGroup);
594 } catch (Tinebase_Exception_Record_NotDefined $tern) {
595 if ($groupBackend->isDisabledBackend()) {
596 // groups are sql only
597 $group = $groupBackend->getDefaultGroup();
598 $user->accountPrimaryGroup = $group->getId();
601 $group = $groupBackend->getGroupByIdFromSyncBackend($user->accountPrimaryGroup);
602 } catch (Tinebase_Exception_Record_NotDefined $ternd) {
603 throw new Tinebase_Exception('Primary group ' . $user->accountPrimaryGroup . ' not found in sync backend.');
606 $groupBackend->getGroupByName($group->name);
607 throw new Tinebase_Exception('Group already exists but it has a different ID: ' . $group->name);
609 } catch (Tinebase_Exception_Record_NotDefined $tern) {
610 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
611 . " Adding group " . $group->name);
612 $group = $groupBackend->addGroupInSqlBackend($group);
621 * call configured hooks for adjusting synced user data
623 * @param Tinebase_Model_FullUser $user
624 * @param array $userProperties
625 * @return boolean if false, user is skipped
627 protected static function _syncUserHook(Tinebase_Model_FullUser $user, $userProperties)
630 $hookClass = Tinebase_Config::getInstance()->get(Tinebase_Config::SYNC_USER_HOOK_CLASS);
631 if ($hookClass && class_exists($hookClass)) {
632 $hook = new $hookClass();
633 if (method_exists($hook, 'syncUser')) {
634 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
635 . ' Calling ' . $hookClass . '::syncUser() ...');
638 $result = call_user_func_array(array($hook, 'syncUser'), array($user, $userProperties));
639 } catch (Tinebase_Exception $te) {
640 Tinebase_Exception::log($te);
650 * create contact in addressbook
652 * @param Tinebase_Model_FullUser $user
654 public static function createContactForSyncedUser($user)
656 if (! Tinebase_Application::getInstance()->isInstalled('Addressbook')) {
660 $contact = self::_user2Contact($user);
663 Tinebase_Timemachine_ModificationLog::setRecordMetaData($contact, 'create');
665 $addressbook = Addressbook_Backend_Factory::factory(Addressbook_Backend_Factory::SQL);
666 $contact = $addressbook->create($contact);
668 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
669 . " Added contact " . $contact->n_given);
671 $user->contact_id = $contact->getId();
675 * sync user data to contact
677 * @param Tinebase_Model_FullUser $user
678 * @param Addressbook_Model_Contact $contact
679 * @return Addressbook_Model_Contact
681 protected static function _user2Contact($user, $contact = null)
683 if ($contact === null) {
684 $contact = new Addressbook_Model_Contact(array(), true);
687 $contact->type = Addressbook_Model_Contact::CONTACTTYPE_USER;
689 foreach (self::$_contact2UserMapping as $contactKey => $userKey) {
690 if (! empty($contact->{$contactKey}) && $contact->{$contactKey} == $user->{$userKey}) {
694 switch ($contactKey) {
696 $contact->container_id = (! empty($user->container_id)) ? $user->container_id : Admin_Controller_User::getInstance()->getDefaultInternalAddressbook();
699 $contact->{$contactKey} = $user->{$userKey};
707 * import users from sync backend
709 * @param array $options
711 public static function syncUsers($options)
713 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
714 .' Start synchronizing users with options ' . print_r($options, true));
716 $users = Tinebase_User::getInstance()->getUsersFromSyncBackend(NULL, NULL, 'ASC', NULL, NULL, 'Tinebase_Model_FullUser');
718 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
719 . ' About to sync ' . count($users) . ' users from sync backend ...');
721 foreach ($users as $user) {
723 self::syncUser($user, $options);
724 } catch (Tinebase_Exception_NotFound $ten) {
725 Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . " User {$user->accountLoginName} not synced: "
726 . $ten->getMessage());
730 if (isset($options['deleteUsers']) && $options['deleteUsers']) {
731 self::_syncDeletedUsers($users);
734 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
735 . ' Finished synchronizing users.');
739 * deletes user in tine20 db that no longer exist in sync backend
741 * @param Tinebase_Record_RecordSet $usersInSyncBackend
743 protected static function _syncDeletedUsers(Tinebase_Record_RecordSet $usersInSyncBackend)
745 $userIdsInSqlBackend = Tinebase_User::getInstance()->getAllUserIdsFromSqlBackend();
746 $deletedInSyncBackend = array_diff($userIdsInSqlBackend, $usersInSyncBackend->getArrayOfIds());
748 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
749 . ' About to delete ' . count($deletedInSyncBackend) . ' users in SQL backend...');
751 foreach ($deletedInSyncBackend as $userToDelete) {
752 $user = Tinebase_User::getInstance()->getUserByPropertyFromSqlBackend('accountId', $userToDelete, 'Tinebase_Model_FullUser');
754 // at first, we expire the user
755 $now = Tinebase_DateTime::now();
756 if (! $user->accountExpires) {
757 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
758 . ' Set expiry date to ' . $now);
759 $user->accountExpires = $now;
760 Tinebase_User::getInstance()->updateUserInSqlBackend($user);
762 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
763 . ' User already expired ' . print_r($user->toArray(), true));
765 // TODO make time span configurable?
766 if ($user->accountExpires->isEarlier($now->subYear(1))) {
767 // if he or she is already expired longer than configured expiry, we remove them!
768 Tinebase_User::getInstance()->deleteUserInSqlBackend($userToDelete);
770 if (Tinebase_Application::getInstance()->isInstalled('Addressbook') === true && ! empty($user->contact_id)) {
771 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
772 . ' Deleting user contact of ' . $user->accountLoginName);
774 $contactsBackend = Addressbook_Backend_Factory::factory(Addressbook_Backend_Factory::SQL);
775 $contactsBackend->delete($user->contact_id);
778 // keep user in expiry state
785 * get all user passwords from ldap
786 * - set pw for user (in sql and sql plugins)
787 * - do not encrypt the pw again as it is encrypted in LDAP
789 * @throws Tinebase_Exception_Backend
791 public static function syncLdapPasswords()
793 $userBackend = Tinebase_User::getInstance();
794 if (! $userBackend instanceof Tinebase_User_Ldap) {
795 throw new Tinebase_Exception_Backend('Needs LDAP accounts backend');
798 $result = $userBackend->getUserAttributes(array('entryUUID', 'userPassword'));
800 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
801 . ' About to sync ' . count($result) . ' user passwords from LDAP to Tine 2.0.');
803 $sqlBackend = Tinebase_User::factory(self::SQL);
804 foreach ($result as $user) {
806 $sqlBackend->setPassword($user['entryUUID'], $user['userPassword'], FALSE);
807 } catch (Tinebase_Exception_NotFound $tenf) {
808 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
809 . ' Could not find user with id ' . $user['entryUUID'] . ' in SQL backend.');
815 * create initial admin account
817 * Method is called during Setup Initialization
819 * $_options may contain the following keys:
822 * 'adminLoginName' => 'admin',
823 * 'adminPassword' => 'lars',
824 * 'adminFirstName' => 'Tine 2.0',
825 * 'adminLastName' => 'Admin Account',
826 * 'adminEmailAddress' => 'admin@tine20domain.org',
827 * 'expires' => Tinebase_DateTime object
831 * @param array $_options [hash that may contain override values for admin user name and password]
833 * @throws Tinebase_Exception_InvalidArgument
835 public static function createInitialAccounts($_options)
837 if (! isset($_options['adminPassword']) || ! isset($_options['adminLoginName'])) {
838 throw new Tinebase_Exception_InvalidArgument('Admin password and login name have to be set when creating initial account.', 503);
841 $adminLoginName = $_options['adminLoginName'];
842 $adminPassword = $_options['adminPassword'];
843 $adminFirstName = isset($_options['adminFirstName']) ? $_options['adminFirstName'] : 'Tine 2.0';
844 $adminLastName = isset($_options['adminLastName']) ? $_options['adminLastName'] : 'Admin Account';
845 $adminEmailAddress = ((isset($_options['adminEmailAddress']) || array_key_exists('adminEmailAddress', $_options))) ? $_options['adminEmailAddress'] : NULL;
847 // get admin & user groups
848 $userBackend = Tinebase_User::getInstance();
849 $groupsBackend = Tinebase_Group::getInstance();
851 $adminGroup = $groupsBackend->getDefaultAdminGroup();
852 $userGroup = $groupsBackend->getDefaultGroup();
854 Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Creating initial admin user (login: ' . $adminLoginName . ' / email: ' . $adminEmailAddress . ')');
856 $user = new Tinebase_Model_FullUser(array(
857 'accountLoginName' => $adminLoginName,
858 'accountStatus' => 'enabled',
859 'accountPrimaryGroup' => $userGroup->getId(),
860 'accountLastName' => $adminLastName,
861 'accountDisplayName' => $adminLastName . ', ' . $adminFirstName,
862 'accountFirstName' => $adminFirstName,
863 'accountExpires' => (isset($_options['expires'])) ? $_options['expires'] : NULL,
864 'accountEmailAddress' => $adminEmailAddress
867 if ($adminEmailAddress !== NULL) {
868 $user->imapUser = new Tinebase_Model_EmailUser(array(
869 'emailPassword' => $adminPassword
871 $user->smtpUser = new Tinebase_Model_EmailUser(array(
872 'emailPassword' => $adminPassword
876 // update or create user in local sql backend
878 $userBackend->getUserByProperty('accountLoginName', $adminLoginName);
879 Tinebase_Timemachine_ModificationLog::setRecordMetaData($user, 'update');
880 $user = $userBackend->updateUserInSqlBackend($user);
881 } catch (Tinebase_Exception_NotFound $ten) {
882 // call addUser here to make sure, sql user plugins (email, ...) are triggered
883 Tinebase_Timemachine_ModificationLog::setRecordMetaData($user, 'create');
884 $user = $userBackend->addUser($user);
887 // set the password for the account
888 // empty password triggers password change dialogue during first login
889 if (!empty($adminPassword)) {
890 Tinebase_User::getInstance()->setPassword($user, $adminPassword);
893 // add the admin account to all groups
894 Tinebase_Group::getInstance()->addGroupMember($adminGroup, $user);
895 Tinebase_Group::getInstance()->addGroupMember($userGroup, $user);