8bd0b1efd3dcd30249513fcdd3791e969f343b53
[tine20] / tine20 / Tinebase / Controller.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Tinebase
6  * @subpackage  Server
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
9  * @author      Lars Kneschke <l.kneschke@metaways.de>
10  * 
11  */
12
13 /**
14  * the class provides functions to handle applications
15  * 
16  * @package     Tinebase
17  * @subpackage  Server
18  */
19 class Tinebase_Controller extends Tinebase_Controller_Event
20 {
21     /**
22      * holds the instance of the singleton
23      *
24      * @var Tinebase_Controller
25      */
26     private static $_instance = NULL;
27     
28     /**
29      * application name
30      *
31      * @var string
32      */
33     protected $_applicationName = 'Tinebase';
34     
35     protected $_writeAccessLog;
36     
37     /**
38      * the constructor
39      *
40      */
41     private function __construct()
42     {
43         $this->_writeAccessLog = Setup_Controller::getInstance()->isInstalled('Tinebase')
44             && (Tinebase_Core::get('serverclassname') !== 'ActiveSync_Server_Http' 
45                 || (Setup_Controller::getInstance()->isInstalled('ActiveSync')
46                         && !(ActiveSync_Config::getInstance()->get(ActiveSync_Config::DISABLE_ACCESS_LOG))));
47     }
48
49     /**
50      * don't clone. Use the singleton.
51      *
52      */
53     private function __clone() {}
54
55     /**
56      * the singleton pattern
57      *
58      * @return Tinebase_Controller
59      */
60     public static function getInstance()
61     {
62         if (self::$_instance === NULL) {
63             self::$_instance = new Tinebase_Controller;
64         }
65         
66         return self::$_instance;
67     }
68     
69     /**
70      * create new user session
71      *
72      * @param   string                           $loginName
73      * @param   string                           $password
74      * @param   Zend_Controller_Request_Abstract $request
75      * @param   string                           $clientIdString
76      *
77      * @return  bool
78      *
79      * TODO what happened to the $securitycode parameter?
80      *  ->  @param   string                           $securitycode   the security code(captcha)
81      */
82     public function login($loginName, $password, \Zend\Http\Request $request, $clientIdString = NULL)
83     {
84         $authResult = Tinebase_Auth::getInstance()->authenticate($loginName, $password);
85         
86         $accessLog = $this->_getAccessLogEntry($loginName, $authResult, $request, $clientIdString);
87         
88         $user = $this->_validateAuthResult($authResult, $accessLog);
89         
90         if (!($user instanceof Tinebase_Model_FullUser)) {
91             return false;
92         }
93         
94         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
95             __METHOD__ . '::' . __LINE__ . " Login with username {$accessLog->login_name} from {$accessLog->ip} succeeded.");
96         
97         $this->_setSessionId($accessLog);
98         
99         $this->initUser($user);
100         
101         $this->_updateCredentialCache($user->accountLoginName, $password);
102         
103         $this->_updateAccessLog($user, $accessLog);
104         
105         return true;
106     }
107     
108     /**
109      * get login user
110      * 
111      * @param string $_username
112      * @param Tinebase_Model_AccessLog $_accessLog
113      * @return Tinebase_Model_FullUser|NULL
114      */
115     protected function _getLoginUser($_username, Tinebase_Model_AccessLog $_accessLog)
116     {
117         $accountsController = Tinebase_User::getInstance();
118         $user = NULL;
119         
120         try {
121             // does the user exist in the user database?
122             if ($accountsController instanceof Tinebase_User_Interface_SyncAble) {
123                 /**
124                  * catch all exceptions during user data sync
125                  * either it's the first sync and no user data get synchronized or
126                  * we can work with the data synced during previous login
127                  */
128                 try {
129                     Tinebase_User::syncUser($_username,array('syncContactData' => TRUE));
130                 } catch (Exception $e) {
131                     Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' Failed to sync user data for: ' . $_username . ' reason: ' . $e->getMessage());
132                     Tinebase_Exception::log($e);
133                 }
134             }
135             
136             $user = $accountsController->getFullUserByLoginName($_username);
137             
138             $_accessLog->account_id = $user->getId();
139             $_accessLog->login_name = $user->accountLoginName;
140             
141         } catch (Tinebase_Exception_NotFound $e) {
142             if (Tinebase_Core::isLogLevel(Zend_Log::CRIT)) Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' Account ' . $_username . ' not found in account storage.');
143             $_accessLog->result = Tinebase_Auth::FAILURE_IDENTITY_NOT_FOUND;
144         } catch (Zend_Db_Adapter_Exception $zdae) {
145             if (Tinebase_Core::isLogLevel(Zend_Log::CRIT)) Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' Some database connection failed: ' . $zdae->getMessage());
146             $_accessLog->result = Tinebase_Auth::FAILURE_DATABASE_CONNECTION;
147         }
148         
149         return $user;
150     }
151     
152     /**
153      * check user status
154      * 
155      * @param Tinebase_Model_FullUser $_user
156      * @param Tinebase_Model_AccessLog $_accessLog
157      */
158     protected function _checkUserStatus(Tinebase_Model_FullUser $_user, Tinebase_Model_AccessLog $_accessLog)
159     {
160         // is the user enabled?
161         if ($_accessLog->result == Tinebase_Auth::SUCCESS && $_user->accountStatus !== Tinebase_User::STATUS_ENABLED) {
162             // is the account enabled?
163             if ($_user->accountStatus == Tinebase_User::STATUS_DISABLED) {
164                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::'
165                     . __LINE__ . ' Account: '. $_user->accountLoginName . ' is disabled');
166                 $_accessLog->result = Tinebase_Auth::FAILURE_DISABLED;
167             }
168             
169             // is the account expired?
170             else if ($_user->accountStatus == Tinebase_User::STATUS_EXPIRED) {
171                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::'
172                     . __LINE__ . ' Account: '. $_user->accountLoginName . ' password is expired');
173                 $_accessLog->result = Tinebase_Auth::FAILURE_PASSWORD_EXPIRED;
174             }
175             
176             // too many login failures?
177             else if ($_user->accountStatus == Tinebase_User::STATUS_BLOCKED) {
178                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::'
179                     . __LINE__ . ' Account: '. $_user->accountLoginName . ' is blocked');
180                 $_accessLog->result = Tinebase_Auth::FAILURE_BLOCKED;
181             }
182
183             // Tinebase run permission
184             else if (! $_user->hasRight('Tinebase', Tinebase_Acl_Rights_Abstract::RUN)) {
185                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::'
186                     . __LINE__ . ' Account: '. $_user->accountLoginName . ' has not permissions for Tinebase');
187                 $_accessLog->result = Tinebase_Auth::FAILURE_DISABLED;
188             }
189         }
190     }
191     
192     /**
193      * initialize user (session, locale, tz)
194      * 
195      * @param Tinebase_Model_FullUser $_user
196      * @param boolean $fixCookieHeader
197      */
198     public function initUser(Tinebase_Model_FullUser $_user, $fixCookieHeader = true)
199     {
200         Tinebase_Core::set(Tinebase_Core::USER, $_user);
201         
202         if (Tinebase_Session_Abstract::getSessionEnabled()) {
203             $this->_initUserSession($fixCookieHeader);
204         }
205         
206         // need to set locale again and because locale might not be set correctly during loginFromPost
207         // use 'auto' setting because it is fetched from cookie or preference then
208         Tinebase_Core::setupUserLocale('auto');
209         
210         // need to set userTimeZone again
211         $userTimezone = Tinebase_Core::getPreference()->getValue(Tinebase_Preference::TIMEZONE);
212         Tinebase_Core::setupUserTimezone($userTimezone);
213     }
214     
215     /**
216      * init session after successful login
217      * 
218      * @param Tinebase_Model_FullUser $user
219      * @param boolean $fixCookieHeader
220      */
221     protected function _initUserSession($fixCookieHeader = true)
222     {
223         // FIXME 0010508: Session_Validator_AccountStatus causes problems
224         //Tinebase_Session::registerValidatorAccountStatus();
225         
226         if (Tinebase_Config::getInstance()->get(Tinebase_Config::SESSIONUSERAGENTVALIDATION, TRUE)) {
227             Tinebase_Session::registerValidatorHttpUserAgent();
228         } else {
229             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' User agent validation disabled.');
230         }
231         
232         // we only need to activate ip session validation for non-encrypted connections
233         $ipSessionValidationDefault = Tinebase_Core::isHttpsRequest() ? FALSE : TRUE;
234         if (Tinebase_Config::getInstance()->get(Tinebase_Config::SESSIONIPVALIDATION, $ipSessionValidationDefault)) {
235             Tinebase_Session::registerValidatorIpAddress();
236         } else {
237             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Session ip validation disabled.');
238         }
239         
240         if ($fixCookieHeader && Zend_Session::getOptions('use_cookies')) {
241             /** 
242              * fix php session header handling http://forge.tine20.org/mantisbt/view.php?id=4918 
243              * -> search all Set-Cookie: headers and replace them with the last one!
244              **/
245             $cookieHeaders = array();
246             foreach (headers_list() as $headerString) {
247                 if (strpos($headerString, 'Set-Cookie: TINE20SESSID=') === 0) {
248                     array_push($cookieHeaders, $headerString);
249                 }
250             }
251             header(array_pop($cookieHeaders), true);
252             /** end of fix **/
253         }
254         
255         Tinebase_Session::getSessionNamespace()->currentAccount = Tinebase_Core::getUser();
256     }
257     
258     /**
259      * login failed
260      * 
261      * @param  string                    $loginName
262      * @param  Tinebase_Model_AccessLog  $accessLog
263      */
264     protected function _loginFailed($authResult, Tinebase_Model_AccessLog $accessLog)
265     {
266         if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) Tinebase_Core::getLogger()->warn(
267             __METHOD__ . '::' . __LINE__ . " Login with username {$accessLog->login_name} from {$accessLog->ip} failed ({$accessLog->result})!");
268         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
269             __METHOD__ . '::' . __LINE__ . ' Failure messages: ' . print_r($authResult->getMessages(), TRUE));
270         
271         // @todo update sql schema to allow empty sessionid column
272         $accessLog->sessionid = Tinebase_Record_Abstract::generateUID();
273         $accessLog->lo = $accessLog->li;
274         
275         Tinebase_User::getInstance()->setLastLoginFailure($accessLog->login_name);
276         Tinebase_AccessLog::getInstance()->create($accessLog);
277         
278         sleep(mt_rand(2,5));
279     }
280     
281      /**
282      * renders and send to browser one captcha image
283      *
284      * @return array
285      */
286     public function makeCaptcha()
287     {
288         return $this->_makeImage();
289     }
290
291     /**
292      * renders and send to browser one captcha image
293      *
294      * @return array
295      */
296     protected function _makeImage()
297     {
298         $result = array();
299         $width='170';
300         $height='40';
301         $characters= mt_rand(5,7);
302         $possible = '123456789aAbBcCdDeEfFgGhHIijJKLmMnNpPqQrRstTuUvVwWxXyYZz';
303         $code = '';
304         $i = 0;
305         while ($i < $characters) {
306             $code .= substr($possible, mt_rand(0, strlen($possible)-1), 1);
307             $i++;
308         }
309         $font = './fonts/Milonga-Regular.ttf';
310         /* font size will be 70% of the image height */
311         $font_size = $height * 0.67;
312         try {
313             $image = @imagecreate($width, $height);
314             /* set the colours */
315             $text_color = imagecolorallocate($image, 20, 40, 100);
316             $noise_color = imagecolorallocate($image, 100, 120, 180);
317             /* generate random dots in background */
318             for( $i=0; $i<($width*$height)/3; $i++ ) {
319                 imagefilledellipse($image, mt_rand(0,$width), mt_rand(0,$height), 1, 1, $noise_color);
320             }
321             /* generate random lines in background */
322             for( $i=0; $i<($width*$height)/150; $i++ ) {
323                 imageline($image, mt_rand(0,$width), mt_rand(0,$height), mt_rand(0,$width), mt_rand(0,$height), $noise_color);
324             }
325             /* create textbox and add text */
326             $textbox = imagettfbbox($font_size, 0, $font, $code);
327             $x = ($width - $textbox[4])/2;
328             $y = ($height - $textbox[5])/2;
329             imagettftext($image, $font_size, 0, $x, $y, $text_color, $font , $code);
330             ob_start();
331             imagejpeg($image);
332             $image_code = ob_get_contents ();
333             ob_end_clean();
334             imagedestroy($image);
335             $result = array();
336             $result['1'] = base64_encode($image_code);
337             Tinebase_Session::getSessionNamespace()->captcha['code'] = $code;
338         } catch (Exception $e) {
339             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' ' . $e->getMessage());
340         }
341         return $result;
342     }
343
344     /**
345      * authenticate user but don't log in
346      *
347      * @param   string $loginName
348      * @param   string $password
349      * @param   array  $remoteInfo
350      * @param   string $clientIdString
351      * @return  bool
352      */
353     public function authenticate($loginName, $password, $remoteInfo, $clientIdString = NULL)
354     {
355         $result = $this->login($loginName, $password, $remoteInfo, $clientIdString);
356         
357         /**
358          * we unset the Zend_Auth session variable. This way we keep the session,
359          * but the user is not logged into Tine 2.0
360          * we use this to validate passwords for OpenId for example
361          */
362         $coreSession = Tinebase_Session::getSessionNamespace();
363         unset($coreSession->Zend_Auth);
364         unset($coreSession->currentAccount);
365         
366         return $result;
367     }
368     
369     /**
370      * change user password
371      *
372      * @param string $_oldPassword
373      * @param string $_newPassword
374      * @throws  Tinebase_Exception_AccessDenied
375      * @throws  Tinebase_Exception_InvalidArgument
376      */
377     public function changePassword($_oldPassword, $_newPassword)
378     {
379         if (! Tinebase_Config::getInstance()->get(Tinebase_Config::PASSWORD_CHANGE, TRUE)) {
380             throw new Tinebase_Exception_AccessDenied('Password change not allowed.');
381         }
382         
383         $loginName = Tinebase_Core::getUser()->accountLoginName;
384         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " change password for $loginName");
385         
386         if (!Tinebase_Auth::getInstance()->isValidPassword($loginName, $_oldPassword)) {
387             throw new Tinebase_Exception_InvalidArgument('Old password is wrong.');
388         }
389         
390         Tinebase_User::getInstance()->setPassword(Tinebase_Core::getUser(), $_newPassword, true, false);
391     }
392     
393     /**
394      * switch to another user's account
395      *
396      * @param string $loginName
397      * @return boolean
398      * @throws Tinebase_Exception_AccessDenied
399      */
400     public function changeUserAccount($loginName)
401     {
402         $allowedRoleChanges = Tinebase_Config::getInstance()->get(Tinebase_Config::ROLE_CHANGE_ALLOWED);
403         
404         if (!$allowedRoleChanges) {
405             throw new Tinebase_Exception_AccessDenied('It is not allowed to switch to this account');
406         }
407         
408         $currentAccountName = Tinebase_Core::getUser()->accountLoginName;
409         
410         $allowedRoleChangesArray = $allowedRoleChanges->toArray();
411         
412         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
413             __METHOD__ . '::' . __LINE__ . ' ROLE_CHANGE_ALLOWED: ' . print_r($allowedRoleChangesArray, true));
414         
415         $user = null;
416         
417         if (isset($allowedRoleChangesArray[$currentAccountName])
418             && in_array($loginName, $allowedRoleChangesArray[$currentAccountName])
419         ) {
420             $user = Tinebase_User::getInstance()->getFullUserByLoginName($loginName);
421             Tinebase_Session::getSessionNamespace()->userAccountChanged = true;
422             Tinebase_Session::getSessionNamespace()->originalAccountName = $currentAccountName;
423             
424         } else if (Tinebase_Session::getSessionNamespace()->userAccountChanged 
425             && isset($allowedRoleChangesArray[Tinebase_Session::getSessionNamespace()->originalAccountName])
426         ) {
427             $user = Tinebase_User::getInstance()->getFullUserByLoginName(Tinebase_Session::getSessionNamespace()->originalAccountName);
428             Tinebase_Session::getSessionNamespace()->userAccountChanged = false;
429             Tinebase_Session::getSessionNamespace()->originalAccountName = null;
430         }
431         
432         if ($user) {
433             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
434                 __METHOD__ . '::' . __LINE__ . ' Switching to user account ' . $user->accountLoginName);
435             
436             $this->initUser($user, /* $fixCookieHeader = */ false);
437             return true;
438         }
439
440         return false;
441     }
442     
443     /**
444      * logout user
445      *
446      * @return void
447      */
448     public function logout()
449     {
450         if ($this->_writeAccessLog) {
451             if (Tinebase_Core::isRegistered(Tinebase_Core::USER) && is_object(Tinebase_Core::getUser())) {
452                 Tinebase_AccessLog::getInstance()->setLogout(Tinebase_Core::get(Tinebase_Core::SESSIONID));
453             }
454         }
455     }
456     
457     /**
458      * gets image info and data
459      * 
460      * @param   string $_application application which manages the image
461      * @param   string $_identifier identifier of image/record
462      * @param   string $_location optional additional identifier
463      * @return  Tinebase_Model_Image
464      * @throws  Tinebase_Exception_NotFound
465      * @throws  Tinebase_Exception_UnexpectedValue
466      */
467     public function getImage($_application, $_identifier, $_location = '')
468     {
469         $appController = Tinebase_Core::getApplicationInstance($_application);
470         if (!method_exists($appController, 'getImage')) {
471             throw new Tinebase_Exception_NotFound("$_application has no getImage function.");
472         }
473         $image = $appController->getImage($_identifier, $_location);
474         
475         if (!$image instanceof Tinebase_Model_Image) {
476             throw new Tinebase_Exception_UnexpectedValue("$_application returned invalid image.");
477         }
478         return $image;
479     }
480     
481     /**
482      * remove obsolete/outdated stuff from cache
483      * notes: CLEANING_MODE_OLD -> removes obsolete cache entries (files for file cache)
484      *        CLEANING_MODE_ALL -> removes complete cache structure (directories for file cache) + cache entries
485      * 
486      * @param string $_mode
487      */
488     public function cleanupCache($_mode = Zend_Cache::CLEANING_MODE_OLD)
489     {
490         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
491             __METHOD__ . '::' . __LINE__ . ' Cleaning up the cache (mode: ' . $_mode . ')');
492         
493         Tinebase_Core::getCache()->clean($_mode);
494     }
495     
496     /**
497      * cleanup old sessions files => needed only for filesystems based sessions
498      */
499     public function cleanupSessions()
500     {
501         $config = Tinebase_Core::getConfig();
502         
503         $backendType = ($config->session && $config->session->backend) ? ucfirst($config->session->backend) : 'File';
504         
505         if (strtolower($backendType) == 'file') {
506             $maxLifeTime = ($config->session && $config->session->lifetime) ? $config->session->lifetime : 86400;
507             $path = ini_get('session.save_path');
508             
509             $unlinked = 0;
510             try {
511                 $dir = new DirectoryIterator($path);
512             } catch (Exception $e) {
513                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(
514                     __METHOD__ . '::' . __LINE__ . " Could not cleanup sessions");
515                 Tinebase_Exception::log($e);
516                 return;
517             }
518             
519             foreach ($dir as $fileinfo) {
520                 if (!$fileinfo->isDot() && !$fileinfo->isLink() && $fileinfo->isFile()) {
521                     if ($fileinfo->getMTime() < Tinebase_DateTime::now()->getTimestamp() - $maxLifeTime) {
522                         unlink($fileinfo->getPathname());
523                         $unlinked++;
524                     }
525                 }
526             }
527             
528             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
529                 __METHOD__ . '::' . __LINE__ . " Deleted $unlinked expired session files");
530             
531             Tinebase_Config::getInstance()->set(Tinebase_Config::LAST_SESSIONS_CLEANUP_RUN, Tinebase_DateTime::now()->toString());
532         }
533     }
534     
535     /**
536      * spy function for unittesting of queue workers
537      * 
538      * this function writes the number of executions of itself in the given 
539      * file and optionally sleeps a given time
540      * 
541      * @param string  $filename
542      * @param int     $sleep
543      * @param int     $fail
544      */
545     public function testSpy($filename=NULL, $sleep=0, $fail=NULL)
546     {
547         $filename = $filename ? $filename : ('/tmp/'.__METHOD__);
548         $counter = file_exists($filename) ? (int) file_get_contents($filename) : 0;
549         
550         file_put_contents($filename, ++$counter);
551         
552         if ($sleep) {
553             sleep($sleep);
554         }
555         
556         if ($fail && (int) $counter <= $fail) {
557             throw new Exception('spy failed on request');
558         }
559         
560         return;
561     }
562     
563     /**
564      * return accessLog instance 
565      * 
566      * @param string $loginName
567      * @param Zend_Auth_Result $authResult
568      * @param Zend_Controller_Request_Abstract $request
569      * @param string $clientIdString
570      * @return Tinebase_Model_AccessLog
571      */
572     protected function _getAccessLogEntry($loginName, Zend_Auth_Result $authResult, \Zend\Http\Request $request, $clientIdString)
573     {
574         if ($header = $request->getHeaders('USER-AGENT')) {
575             $userAgent = substr($header->getFieldValue(), 0, 255);
576         } else {
577             $userAgent = 'unknown';
578         }
579         
580         $accessLog = new Tinebase_Model_AccessLog(array(
581             'ip'         => $request->getServer('REMOTE_ADDR'),
582             'li'         => Tinebase_DateTime::now(),
583             'result'     => $authResult->getCode(),
584             'clienttype' => $clientIdString,
585             'login_name' => $loginName ? $loginName : $authResult->getIdentity(),
586             'user_agent' => $userAgent
587         ), true);
588         
589         return $accessLog;
590     }
591     
592     /**
593      * handle events for Tinebase
594      * 
595      * @param Tinebase_Event_Abstract $_eventObject
596      */
597     protected function _handleEvent(Tinebase_Event_Abstract $_eventObject)
598     {
599         switch (get_class($_eventObject)) {
600             case 'Admin_Event_DeleteGroup':
601                 foreach ($_eventObject->groupIds as $groupId) {
602                     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
603                         . ' Removing role memberships of group ' .$groupId );
604                     
605                     $roleIds = Tinebase_Acl_Roles::getInstance()->getRoleMemberships($groupId, Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP);
606                     foreach ($roleIds as $roleId) {
607                         Tinebase_Acl_Roles::getInstance()->removeRoleMember($roleId, array(
608                             'id'   => $groupId,
609                             'type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP,
610                         ));
611                     }
612                 }
613                 break;
614         }
615     }
616     
617     /**
618      * set session for current request
619      * 
620      * @param Tinebase_Model_AccessLog $accessLog
621      */
622     protected function _setSessionId(Tinebase_Model_AccessLog &$accessLog)
623     {
624         if (in_array($accessLog->clienttype, array(Tinebase_Server_WebDAV::REQUEST_TYPE, ActiveSync_Server_Http::REQUEST_TYPE))) {
625             try {
626                 $accessLog = Tinebase_AccessLog::getInstance()->getPreviousAccessLog($accessLog);
627                 // $accessLog->sessionid is set now
628             } catch (Tinebase_Exception_NotFound $tenf) {
629                 // ignore
630             }
631         }
632         
633         if (!$accessLog->sessionid) {
634             $accessLog->sessionid = Tinebase_Record_Abstract::generateUID();
635         }
636         
637         Tinebase_Core::set(Tinebase_Core::SESSIONID, $accessLog->sessionid);
638     }
639     
640     /**
641      * update access log entry if needed
642      * 
643      * @param Tinebase_Model_FullUser $user
644      * @param Tinebase_Model_AccessLog $accessLog
645      */
646     protected function _updateAccessLog(Tinebase_Model_FullUser $user, Tinebase_Model_AccessLog $accessLog)
647     {
648         if (! $accessLog->getId()) {
649             $user->setLoginTime($accessLog->ip);
650             if ($this->_writeAccessLog) {
651                 $accessLog->setId(Tinebase_Record_Abstract::generateUID());
652                 $accessLog = Tinebase_AccessLog::getInstance()->create($accessLog);
653             }
654         }
655         
656         Tinebase_Core::set(Tinebase_Core::USERACCESSLOG, $accessLog);
657     }
658     
659     /**
660      * update credential cache
661      * 
662      * @param string $loginName
663      * @param string $password
664      */
665     protected function _updateCredentialCache($loginName, $password)
666     {
667         $credentialCache = Tinebase_Auth_CredentialCache::getInstance()->cacheCredentials($loginName, $password);
668         Tinebase_Core::set(Tinebase_Core::USERCREDENTIALCACHE, $credentialCache);
669     }
670     
671     /**
672      * validate is authentication was successful, user object is available and user is not expired
673      * 
674      * @param Zend_Auth_Result $authResult
675      * @param Tinebase_Model_AccessLog $accessLog
676      * @return boolean|Tinebase_Model_FullUser
677      */
678     protected function _validateAuthResult(Zend_Auth_Result $authResult, Tinebase_Model_AccessLog $accessLog)
679     {
680         // authentication failed
681         if ($accessLog->result !== Tinebase_Auth::SUCCESS) {
682             $this->_loginFailed($authResult, $accessLog);
683             
684             return false;
685         }
686         
687         // try to retrieve user from accounts backend
688         $user = $this->_getLoginUser($authResult->getIdentity(), $accessLog);
689         
690         if ($accessLog->result !== Tinebase_Auth::SUCCESS || !$user) {
691             $this->_loginFailed($authResult, $accessLog);
692             
693             return false;
694         }
695         
696         // check if user is expired or blocked
697         $this->_checkUserStatus($user, $accessLog);
698         
699         if ($accessLog->result !== Tinebase_Auth::SUCCESS) {
700             $this->_loginFailed($authResult, $accessLog);
701             
702             return false;
703         }
704         
705         return $user;
706     }
707     
708     /**
709      * returns true if user account has been changed
710      * 
711      * @return boolean
712      */
713     public function userAccountChanged()
714     {
715         try {
716             $session = Tinebase_Session::getSessionNamespace();
717         } catch (Zend_Session_Exception $zse) {
718             $session = null;
719         }
720         
721         return ($session instanceof Zend_Session_Namespace && isset($session->userAccountChanged)) 
722                 ? $session->userAccountChanged
723                 : false;
724     }
725 }