auth requests won't timeout.
[spidio.git] / www / includes / auth / Spidio / Framework.php
blob8de160e101151e79ff164ee5495b15311d065c15
1 <?php
3 abstract class SpidioAuth extends AuthBase implements DatabaseSession, DatabaseKey {
4 /**
5 * Session representation within Auth system.
7 * @var Session
8 */
9 protected static $objSession;
11 /**
12 * SessionKey representation within Auth system.
14 * @var SessionKey
16 protected static $objSessionKey;
18 /**
19 * Constructor.
22 public function __construct() {
23 // Set authentication scheme.
24 $this->AuthenticationType = AuthenticationType::Sha1;
26 // Load Session.
27 $this->SessionLoad();
29 // Start Session.
30 $this->SessionStart();
32 // Collect garbage for Sessions.
33 $this->SessionGarbageCollect();
35 // Lookup SessionKey.
36 $this->KeyLookup();
38 // Cleanup SessionKey table.
39 $this->KeyGarbageCollect();
42 /**
43 * Check if user is logged in.
45 * @return bool
47 public function IsLoggedIn() {
48 return !is_null(self::$objSession->User);
51 /**
52 * Reset current Session.
55 public function ResetSession() {
56 $this->AddLogEntry(LogTypes::SESSIONRESET);
57 if ($this->IsLoggedIn()) {
58 $this->AddLogEntry(LogTypes::USERLOGOUTFORCE);
61 $this->SessionDestroy();
64 /**
65 * Check if user $strName exists.
67 * @param string $strName
68 * @return bool
70 public function UserExists($strName) {
71 return !is_null(User::LoadByName(ucfirst(strtolower($strName))));
74 /**
75 * Add Log entry.
77 * @param int $intLogType constant of LogTypes
78 * @param string $strItemData data of item where the log entry is about
80 protected function AddLogEntry($intLogType, $strItemData = null) {
81 $objLog = new Log();
83 if ($intLogType != LogTypes::SESSIONCREATED && $intLogType != LogTypes::SESSIONTIMEOUT) {
84 $objLog->UserId = self::$objSession->UserId;
85 } else {
86 $objLog->UserId = null;
89 $objLog->LogTypeId = $intLogType;
90 $objLog->LogActionTypeId = LogActionTypes::NUL;
91 $objLog->Date = new QDateTime(QDateTime::Now);
92 $objLog->ItemId = null;
93 $objLog->ItemData = $strItemData;
94 $objLog->SessionName = $this->SessionGetName();
95 $objLog->IpAddress = (string) new IpAddress($this->UserIpAddress());
96 $objLog->Save();
99 abstract public function SessionLoad();
102 * Register Session or update Session.
105 public function SessionStart() {
106 if (method_exists($this, 'SessionStartHookBegin')) {
107 $this->SessionStartHookBegin();
110 // Register when no session is registered, otherwise update.
111 if (is_null(self::$objSession)) {
112 $this->SessionRegister();
113 } else {
114 $this->SessionUpdate();
117 if (method_exists($this, 'SessionStartHookEnd')) {
118 $this->SessionStartHookEnd();
123 * Register Session for first time.
126 protected function SessionRegister() {
127 if (method_exists($this, 'SessionRegisterHookBegin')) {
128 $this->SessionRegisterHookBegin();
131 // Create new Session.
132 self::$objSession = new Session();
134 // Fill fields with data.
135 self::$objSession->Id = $this->UUID();
136 self::$objSession->UserId = null;
137 self::$objSession->IpAddress = (string) new IpAddress($this->UserIpAddress());
138 self::$objSession->AutoLogin = false;
140 // Save the new Session.
141 self::$objSession->Save();
143 // Reload to load self::$objSession->Added value.
144 self::$objSession->Reload();
146 // Save Cookie
147 $this->SetCookie('sid', self::$objSession->Id);
149 // Add Log entry.
150 $this->AddLogEntry(LogTypes::SESSIONCREATED);
152 if (method_exists($this, 'SessionRegisterHookEnd')) {
153 $this->SessionRegisterHookEnd();
158 * Update Session with fresh data.
161 protected function SessionUpdate() {
162 if (method_exists($this, 'SessionUpdateHookBegin')) {
163 $this->SessionUpdateHookBegin();
166 // Set current IP address.
167 self::$objSession->IpAddress = (string) new IpAddress($this->UserIpAddress());
169 // save update.
170 self::$objSession->Save();
172 if (method_exists($this, 'SessionUpdateHookEnd')) {
173 $this->SessionUpdateHookEnd();
178 * Register User to Session.
180 * @param User $objUser
182 public function SessionRegisterUser(User $objUser) {
183 if (method_exists($this, 'SessionRegisterUserHookBegin')) {
184 $this->SessionRegisterUserHookBegin();
187 // Assign User object to Session.
188 self::$objSession->User = $objUser;
190 // Save Session.
191 self::$objSession->Save();
193 if (method_exists($this, 'SessionRegisterUserHookEnd')) {
194 $this->SessionRegisterUserHookEnd();
199 * Unregister User from Session.
202 public function SessionUnRegisterUser() {
203 if (method_exists($this, 'SessionUnRegisterUserHookBegin')) {
204 $this->SessionUnRegisterUserHookBegin();
207 // Kill SessionKey related to this Session.
208 $this->KeyDestroy();
210 // Nullify User object this Session.
211 self::$objSession->User = null;
213 // Save Session.
214 self::$objSession->Save();
216 if (method_exists($this, 'SessionUnRegisterUserHookEnd')) {
217 $this->SessionUnRegisterUserHookEnd();
222 * Delete Session.
225 public function SessionDestroy() {
226 if (method_exists($this, 'SessionDestroyHookBegin')) {
227 $this->SessionDestroyHookBegin();
230 if (!is_null(self::$objSession->UserId)) {
231 // Logout User.
232 $this->SessionUnRegisterUser();
235 // Delete Session.
236 self::$objSession->Delete();
238 if (method_exists($this, 'SessionDestroyHookEnd')) {
239 $this->SessionDestroyHookEnd();
244 * Delete old sessions from database.
247 public function SessionGarbageCollect() {
248 if (method_exists($this, 'SessionGarbageCollectHookBegin')) {
249 $this->SessionGarbageCollectHookBegin();
252 // define timeout time.
253 $dttExpire = new QDateTime(QDateTime::Now);
254 $dttExpire->AddSeconds(-$this->SessionGetTimeout());
256 // Query table and delete the first 10 timed out sessions, delete only when there is no SessionKey assigned (checked via reverse relationship).
257 foreach (Session::QueryArray(
258 QQ::AndCondition(
259 QQ::LessThan(QQN::Session()->Changed, $dttExpire),
260 QQ::IsNull(QQN::Session()->SessionKeyAsSession->Id)
262 QQ::Clause(
263 QQ::LimitInfo(10)
265 ) as $objSession) {
266 $objSession->Delete();
267 $this->AddLogEntry(LogTypes::SESSIONTIMEOUT);
270 if (method_exists($this, 'SessionGarbageCollectHookEnd')) {
271 $this->SessionGarbageCollectHookEnd();
276 * Get Session timeout time in seconds.
278 * @return int
280 protected function SessionGetTimeout() {
281 return $this->GetConfig('session_timeout', QType::Integer);
285 * Return Session Id.
287 * @return string
289 public function SessionGetName() {
290 return self::$objSession->Id;
294 * Check if Session exists.
296 * @param string $strSessionId
297 * @return bool
299 public function SessionExists($strSessionId) {
300 return !is_null(Session::Load($strSessionId));
304 * Check if Session exists and an User is logged.
306 * @param string $strSessionId
307 * @return bool
309 public function SessionIsValid($strSessionId) {
310 if (is_null($strSessionId)) {
311 return false;
312 } else {
313 return !is_null(Session::Load($strSessionId)->UserId);
318 * Lookup key for Session.
321 public function KeyLookup() {
322 if (method_exists($this, 'KeyLookupHookBegin')) {
323 $this->KeyLookupHookBegin();
326 // Load SessionKey from database
327 if ($this->KeyExists()) {
328 // Update SessionKey.
329 self::$objSessionKey = SessionKey::Load($this->GetCookie('kid'));
330 $this->KeyUpdate();
333 if (method_exists($this, 'KeyLookupHookEnd')) {
334 $this->KeyLookupHookEnd();
339 * Register SessionKey for Session.
342 public function KeyRegister() {
343 if (method_exists($this, 'KeyRegisterHookBegin')) {
344 $this->KeyRegisterHookBegin();
347 if (!SessionKey::LoadBySessionId(self::$objSession->Id)) {
348 // Fill fields with data
349 self::$objSessionKey = new SessionKey();
350 self::$objSessionKey->Id = $this->UUID();
351 self::$objSessionKey->User = self::$objSession->User;
352 self::$objSessionKey->Session = self::$objSession;
353 self::$objSessionKey->IpAddress = self::$objSession->IpAddress;
355 // Save new SessionKey
356 self::$objSessionKey->Save();
358 // Update AutoLogin flag.
359 self::$objSession->AutoLogin = true;
361 // Save Session changed session fields.
362 self::$objSession->Save();
364 // Save Cookie.
365 $dttExpire = new QDateTime(QDateTime::Now);
366 $dttExpire->AddSeconds($this->GetConfig('sessionkey_timeout', QType::Integer));
367 $this->SetCookie('kid', self::$objSessionKey->Id, $dttExpire);
370 if (method_exists($this, 'KeyDeleteHookEnd')) {
371 $this->KeyDeleteHookEnd();
376 * Update SessionKey with fresh data.
379 protected function KeyUpdate() {
380 if (method_exists($this, 'KeyUpdateHookBegin')) {
381 $this->KeyUpdateHookBegin();
384 $dttExpire = new QDateTime(QDateTime::Now);
385 $dttExpire->AddSeconds(-$this->KeyGetChange());
387 // Update key id when item is longer than config value for session id changing.
388 if ($dttExpire->IsLaterThan(self::$objSessionKey->Changed)) {
389 // Generate SessionKey id.
390 self::$objSessionKey->Id = $this->UUID();
392 // Save Cookie.
393 $dttExpire = new QDateTime(QDateTime::Now);
394 $dttExpire->AddSeconds($this->GetConfig('sessionkey_timeout', QType::Integer));
395 $this->SetCookie('kid', self::$objSessionKey->Id, $dttExpire);
398 // Update Session in SessionKey when there is another Session for that SessionKey based on the database.
399 if (self::$objSessionKey->Session->Id != $this->SessionGetName()) {
400 self::$objSessionKey->Session = self::$objSession;
402 self::$objSession->UserId = self::$objSessionKey->UserId;
403 self::$objSession->AutoLogin = true;
404 self::$objSession->Save();
406 $this->AddLogEntry(LogTypes::USERLOGIN, sprintf('Key: %s', $this->KeyGetName()));
409 // Update IP address of SessionKey.
410 self::$objSessionKey->IpAddress = self::$objSession->IpAddress;
412 // Force update SessionKey because it is possible that the row is modified by another request from the same browser.
413 self::$objSessionKey->Save(false, true);
415 // Log in Session with user from SessionKey
416 $this->SessionRegisterUser(self::$objSessionKey->User);
418 if (method_exists($this, 'KeyUpdateHookEnd')) {
419 $this->KeyUpdateHookEnd();
424 * Delete SessionKey.
427 public function KeyDestroy() {
428 if (method_exists($this, 'KeyDeleteHookBegin')) {
429 $this->KeyDeleteHookBegin();
432 // Delete SessionKey.
433 if (!is_null(self::$objSessionKey)) {
434 self::$objSessionKey->Delete();
437 if (method_exists($this, 'KeyDeleteHookEnd')) {
438 $this->KeyDeleteHookEnd();
443 * Delete all sessionkeys for this User.
446 public function KeyDestroyAll() {
447 if (method_exists($this, 'KeyDeleteAllHookBegin')) {
448 $this->KeyDeleteAllHookBegin();
451 $this->KeyDestroy();
453 foreach (SessionKey::LoadArrayByUserId(self::$objSession->UserId) as $objSessionKey) {
454 $objSessionKey->Delete();
457 if (method_exists($this, 'KeyDestroyAllHookEnd')) {
458 $this->KeyDestroyAllHookEnd();
463 * Collect garbage of SessionKeyytable.
466 public function KeyGarbageCollect() {
467 if (method_exists($this, 'KeyGarbageCollectHookBegin')) {
468 $this->KeyGarbageCollectHookBegin();
471 $dttExpire = new QDateTime(QDateTime::Now);
472 $dttExpire->AddSeconds(-$this->KeyGetTimeout());
474 // Query table and delete the first 10 timed out sessionkeys.
475 foreach (SessionKey::QueryArray(
476 QQ::LessThan(QQN::SessionKey()->Changed, $dttExpire),
477 QQ::Clause(
478 QQ::LimitInfo(10)
480 ) as $objSessionKey) {
481 $objSessionKey->Delete();
484 if (method_exists($this, 'KeyGarbageCollectHookEnd')) {
485 $this->KeyGarbageCollectHookEnd();
490 * Return SessionKey Id.
492 * @return string
494 public function KeyGetName() {
495 return self::$objSessionKey->Id;
499 * Check if SessionKey exists.
501 * @return bool
503 public function KeyExists() {
504 if ($this->ExistCookie('kid')) {
505 return (SessionKey::Load($this->GetCookie('kid')));
506 } else {
507 return false;
512 * Get SessionKey timeout time in seconds.
514 * @return int
516 protected function KeyGetChange() {
517 return $this->GetConfig('sessionkey_change', QType::Integer);
521 * Get SessionKey timeout time in seconds.
523 * @return int
525 protected function KeyGetTimeout() {
526 return $this->GetConfig('sessionkey_timeout', QType::Integer);
530 * Get Cookie value.
532 * @param string $strName
533 * @return string
535 protected function GetCookie($strName) {
536 $objCookie = CookieStore::UpdateCookie(sprintf('%s_%s', strtolower($this->GetConfig('software_coresystem_name')), $strName));
537 return $objCookie->Value;
541 * Check if Cookie exists.
543 * @param string $strName
544 * @return bool
546 protected function ExistCookie($strName) {
547 return CookieStore::ExistCookie(sprintf('%s_%s', strtolower($this->GetConfig('software_coresystem_name')), $strName));
551 * Set or update cookie.
553 * @param string $strName
554 * @param string $strValue
555 * @param null|QDateTime $dttExpire
557 protected function SetCookie($strName, $strValue, $dttExpire = null) {
558 $objCookie = CookieStore::UpdateCookie(sprintf('%s_%s', strtolower($this->GetConfig('software_coresystem_name')), $strName));
559 $objCookie->Value = $strValue;
560 $objCookie->Path = sprintf('%s%s/', __VIRTUAL_DIRECTORY__, __SUBDIRECTORY__);
562 if (!is_null($dttExpire)) {
563 $objCookie->Expire = $dttExpire;
566 if (method_exists($this, 'SetCookieHook')) {
567 $this->SetCookieHook($objCookie);
571 public function __get($strName) {
572 switch ($strName) {
573 case 'Session': return self::$objSession;
574 case 'SessionKey': return self::$objSessionKey;
576 default:
577 try {
578 return parent::__get($strName);
579 } catch (QCallerException $objExc) {
580 $objExc->IncrementOffset();
581 throw $objExc;
586 public function __set($strName, $mixValue) {
587 switch ($strName) {
588 case 'Session':
589 try {
590 return (self::$objSession = QType::Cast($mixValue, 'Session'));
591 } catch (QCallerException $objExc) {
592 $objExc->IncrementOffset();
593 throw $objExc;
595 case 'SessionKey':
596 try {
597 return (self::$objSessionKey = QType::Cast($mixValue, 'SessionKey'));
598 } catch (QCallerException $objExc) {
599 $objExc->IncrementOffset();
600 throw $objExc;
603 default:
604 try {
605 return parent::__set($strName, $mixValue);
606 } catch (QCallerException $objExc) {
607 $objExc->IncrementOffset();
608 throw $objExc;