diff --git a/zmsapi/src/Zmsapi/WorkstationLogin.php b/zmsapi/src/Zmsapi/WorkstationLogin.php index 6611bf89c..7b24af794 100644 --- a/zmsapi/src/Zmsapi/WorkstationLogin.php +++ b/zmsapi/src/Zmsapi/WorkstationLogin.php @@ -58,6 +58,7 @@ public static function getLoggedInWorkstation($request, $entity, $resolveReferen $useraccount->id, $useraccount->password, \App::getNow(), + (new \DateTime())->setTimestamp(time() + \App::SESSION_DURATION), $resolveReferences ); diff --git a/zmsapi/src/Zmsapi/WorkstationOAuth.php b/zmsapi/src/Zmsapi/WorkstationOAuth.php index d1d8363fc..c4b74e9be 100644 --- a/zmsapi/src/Zmsapi/WorkstationOAuth.php +++ b/zmsapi/src/Zmsapi/WorkstationOAuth.php @@ -60,6 +60,7 @@ protected function getLoggedInWorkstationByOidc($request, $entity, $resolveRefer $entity->id, $request->getHeaderLine('X-Authkey'), \App::getNow(), + (new \DateTime())->setTimestamp(time() + \App::SESSION_DURATION), $resolveReferences ); return $workstation; @@ -73,12 +74,14 @@ protected function writeOAuthWorkstation(UseraccountEntity $entity, $state, $res $useraccount->getId(), $entity->password, \App::getNow(), + (new \DateTime())->setTimestamp(time() + \App::SESSION_DURATION), $resolveReferences ); $workstation = $query->updateEntityAuthkey( $useraccount->getId(), $entity->password, $state, + (new \DateTime())->setTimestamp(time() + \App::SESSION_DURATION), $resolveReferences ); return $workstation; diff --git a/zmsapi/tests/Zmsapi/WorkstationGetTest.php b/zmsapi/tests/Zmsapi/WorkstationGetTest.php index e0f80a888..294bb31bc 100644 --- a/zmsapi/tests/Zmsapi/WorkstationGetTest.php +++ b/zmsapi/tests/Zmsapi/WorkstationGetTest.php @@ -36,7 +36,7 @@ public function testRendering() public function testReadWorkstationByXAuthKey() { $workstation = (new \BO\Zmsdb\Workstation) - ->writeEntityLoginByName(static::$loginName, md5(static::$authKey), \App::getNow(), 1); + ->writeEntityLoginByName(static::$loginName, md5(static::$authKey), \App::getNow(), (new \DateTime())->setTimestamp(time() + \App::SESSION_DURATION), 1); $logInHash = (new \BO\Zmsdb\Workstation)->readLoggedInHashByName($workstation->getUseraccount()->id); $response = $this->render([], [ '__header' => [ diff --git a/zmsapi/tests/Zmsapi/WorkstationUpdateTest.php b/zmsapi/tests/Zmsapi/WorkstationUpdateTest.php index 6b1c04364..6c502f4d6 100644 --- a/zmsapi/tests/Zmsapi/WorkstationUpdateTest.php +++ b/zmsapi/tests/Zmsapi/WorkstationUpdateTest.php @@ -63,7 +63,7 @@ public function testOveragedLogin() $this->expectExceptionCode(200); $entity = (new \BO\Zmsdb\Workstation) - ->writeEntityLoginByName(static::$loginName, static::$authKey, \App::getNow(), 2); + ->writeEntityLoginByName(static::$loginName, static::$authKey, \App::getNow(), (new \DateTime())->setTimestamp(time() + \App::SESSION_DURATION), 2); $entity->scope['id'] = self::SCOPEID; $entity->name = self::PLACE; (new \BO\Zmsdb\Workstation)->updateEntity($entity, 0); diff --git a/zmsdb/migrations/91729672394-add-session-duration-to-nutzer-table.sql b/zmsdb/migrations/91729672394-add-session-duration-to-nutzer-table.sql new file mode 100644 index 000000000..7c31bae3f --- /dev/null +++ b/zmsdb/migrations/91729672394-add-session-duration-to-nutzer-table.sql @@ -0,0 +1,2 @@ +ALTER TABLE `nutzer` + ADD COLUMN `sessionExpiry` DATETIME DEFAULT NULL; diff --git a/zmsdb/src/Zmsdb/Query/Workstation.php b/zmsdb/src/Zmsdb/Query/Workstation.php index e7514bbff..549c781a8 100644 --- a/zmsdb/src/Zmsdb/Query/Workstation.php +++ b/zmsdb/src/Zmsdb/Query/Workstation.php @@ -18,6 +18,7 @@ class Workstation extends Base implements MappingInterface '. self::TABLE .' SET `SessionID`=?, + `sessionExpiry`=?, `Datum`=?, `lastUpdate`=?, `Arbeitsplatznr`="", @@ -33,6 +34,7 @@ class Workstation extends Base implements MappingInterface '. self::TABLE .' SET `SessionID`=?, + `sessionExpiry`=?, `Datum`=?, `Arbeitsplatznr`="", `aufrufzusatz`="", @@ -56,6 +58,7 @@ class Workstation extends Base implements MappingInterface '. self::TABLE .' SET `SessionID`="", + `sessionExpiry`=NULL, `StandortID`=0, `Datum`="0000-00-00", `Arbeitsplatznr`="", @@ -78,7 +81,8 @@ class Workstation extends Base implements MappingInterface UPDATE '. self::TABLE .' SET - `SessionID`=? + `SessionID`=?, + `sessionExpiry`=? WHERE `Name`= ? AND `Passworthash` = ? diff --git a/zmsdb/src/Zmsdb/Status.php b/zmsdb/src/Zmsdb/Status.php index f932be3cb..05e560706 100644 --- a/zmsdb/src/Zmsdb/Status.php +++ b/zmsdb/src/Zmsdb/Status.php @@ -17,6 +17,7 @@ public function readEntity(\DateTimeImmutable $now, $includeProcessStats = true) $configVariables = $this->readConfigVariables(); $statusVariables = $this->readStatusVariables(); $nodeConnections = round($statusVariables['Threads_connected'] / $configVariables['max_connections'], 2); + $entity['database']['problems'] = $this->getConfigProblems($configVariables); $entity['database']['locks'] = $statusVariables['Innodb_row_lock_current_waits']; $entity['database']['threads'] = $statusVariables['Threads_connected']; @@ -25,22 +26,81 @@ public function readEntity(\DateTimeImmutable $now, $includeProcessStats = true) array_key_exists('wsrep_ready', $statusVariables) ? $statusVariables['wsrep_ready'] : 'OFF'; $entity['database']['logbin'] = array_key_exists('log_bin', $configVariables) ? $configVariables['log_bin'] : 'OFF'; - $entity['processes'] = []; - if ($includeProcessStats) { - $entity['processes'] = $this->readProcessStats($now); - $outdated = $this->readOutdatedSlots(); - $entity['processes']['outdated'] = $outdated['cnt']; - $entity['processes']['outdatedOldest'] = $outdated['oldest']; - $freeSlots = $this->readFreeSlots(); - $entity['processes']['freeSlots'] = $freeSlots['cnt']; - } - $entity['mail'] = $this->readMailStats(); - $entity['notification'] = $this->readNotificationStats(); - $entity['sources']['dldb']['last'] = $this->readDdldUpdateStats(); - $entity['processes']['lastCalculate'] = $this->readLastCalculateSlots(); + + $entity['processes'] = $includeProcessStats ? $this->readProcessStats($now) : []; + $entity['useraccounts']['activeSessions'] = $this->getTotalActiveSessions(); + $entity['useraccounts']['departments'] = $this->getActiveSessionsByBehoerdenWithScopes(); + return $entity; } + /** + * Get total active sessions where SessionID is not null or empty and sessionExpiry is in the future + * + * @return int + */ + protected function getTotalActiveSessions() + { + $result = $this->getReader()->fetchOne( + 'SELECT COUNT(n.SessionID) as totalActiveSessions + FROM nutzer n + WHERE n.SessionID IS NOT NULL + AND n.SessionID != "" + AND n.sessionExpiry > UNIX_TIMESTAMP()' + ); + + return (int) $result['totalActiveSessions']; + } + + /** + * Get active sessions grouped by BehoerdenID with scopes, excluding expired sessions + * + * @return array + */ + protected function getActiveSessionsByBehoerdenWithScopes() + { + $result = $this->getReader()->fetchAll( + 'SELECT b.BehoerdenID, b.Name as BehoerdeName, + s.StandortID, s.Bezeichnung as StandortName, + COALESCE(SUM(CASE + WHEN n.sessionExpiry > UNIX_TIMESTAMP() + THEN 1 ELSE 0 END), 0) as activeSessions + FROM behoerde b + LEFT JOIN standort s ON b.BehoerdenID = s.BehoerdenID + LEFT JOIN nutzer n ON s.StandortID = n.StandortID + GROUP BY b.BehoerdenID, b.Name, s.StandortID, s.Bezeichnung' + ); + + $activeSessionsByBehoerden = []; + + foreach ($result as $row) { + $behoerdenID = $row['BehoerdenID']; + $standortID = $row['StandortID']; + + if (!isset($activeSessionsByBehoerden[$behoerdenID])) { + $activeSessionsByBehoerden[$behoerdenID] = [ + 'activeSessions' => 0, + 'name' => $row['BehoerdeName'], + 'scopes' => [] + ]; + } + + $activeSessionsByBehoerden[$behoerdenID]['scopes'][$standortID] = [ + 'activeSessions' => (int) $row['activeSessions'], + 'name' => $row['StandortName'] + ]; + + $activeSessionsByBehoerden[$behoerdenID]['activeSessions'] += (int) $row['activeSessions']; + } + + return $activeSessionsByBehoerden; + } + + /** + * Get the configuration problems + * + * @return string + */ public function getConfigProblems($configVariables) { $problems = []; @@ -50,182 +110,140 @@ public function getConfigProblems($configVariables) if ($configVariables['max_heap_table_size'] < 32000000) { $problems[] = 'max_heap_table_size should be at least 32MB'; } - $problems = implode('; ', $problems); - return $problems; + return implode('; ', $problems); } /** - * Get the information on dldb update status + * Get the dldb update status * - * @return Array + * @return array */ protected function readDdldUpdateStats() { $stats = $this->getReader()->fetchOne( - 'SELECT - value - FROM config - WHERE name = "sources_dldb_last" - ' + 'SELECT value FROM config WHERE name = "sources_dldb_last"' ); return $stats['value']; } /** - * Hint: "cancelled" slots might be older, but do not get updated, if not bookable in availability any longer - * @return Array + * Get outdated slots + * + * @return array */ protected function readOutdatedSlots() { $stats = $this->getReader()->fetchOne( - 'SELECT - COUNT(*) cnt, MIN(a.updateTimestamp) oldest - FROM slot s LEFT JOIN oeffnungszeit a ON s.availabilityID = a.OeffnungszeitID - WHERE s.updateTimestamp < a.updateTimestamp AND s.status = "free" - ' + 'SELECT COUNT(*) cnt, MIN(a.updateTimestamp) oldest + FROM slot s + LEFT JOIN oeffnungszeit a ON s.availabilityID = a.OeffnungszeitID + WHERE s.updateTimestamp < a.updateTimestamp AND s.status = "free"' ); return $stats; } /** - * @return Array + * Get free slots count + * + * @return array */ protected function readFreeSlots() { $stats = $this->getReader()->fetchOne( - 'SELECT - SUM(intern) cnt - FROM slot s - WHERE s.status = "free" - ' + 'SELECT SUM(intern) cnt FROM slot s WHERE s.status = "free"' ); return $stats; } /** - * Get the information on dldb update status + * Get last calculate slots information * - * @return Array + * @return array */ protected function readLastCalculateSlots() { $stats = $this->getReader()->fetchOne( - 'SELECT - value - FROM config - WHERE name = "status__calculateSlotsLastRun" - ' + 'SELECT value FROM config WHERE name = "status__calculateSlotsLastRun"' ); return $stats['value']; } /** - * Get the information on processes + * Get mail stats * - * @return Array + * @return array */ protected function readMailStats() { $stats = $this->getReader()->fetchOne( - 'SELECT - COUNT(id) as queueCount, - UNIX_TIMESTAMP() - MIN(createTimestamp) as oldestSeconds, + 'SELECT COUNT(id) as queueCount, UNIX_TIMESTAMP() - MIN(createTimestamp) as oldestSeconds, UNIX_TIMESTAMP() - MAX(createTimestamp) as newestSeconds - FROM mailqueue - ' + FROM mailqueue' ); return $stats; } /** - * Get the information on processes + * Get notification stats * - * @return Array + * @return array */ protected function readNotificationStats() { $stats = $this->getReader()->fetchOne( - 'SELECT - COUNT(id) as queueCount, - UNIX_TIMESTAMP() - MIN(createTimestamp) as oldestSeconds, + 'SELECT COUNT(id) as queueCount, UNIX_TIMESTAMP() - MIN(createTimestamp) as oldestSeconds, UNIX_TIMESTAMP() - MAX(createTimestamp) as newestSeconds - FROM notificationqueue - ' + FROM notificationqueue' ); return $stats; } /** - * Get the information on processes + * Get process statistics * - * @return Array + * @return array */ protected function readProcessStats(\DateTimeImmutable $now) { $midnight = $now->modify('00:00:00')->getTimestamp(); - $last7days = $now->modify('-7days 00:00:00')->getTimestamp(); + $last7days = $now->modify('-7 days 00:00:00')->getTimestamp(); + $processStats = $this->getReader()->fetchOne( 'SELECT - SUM(CASE name WHEN "dereferenced" THEN 1 ELSE NULL END) as blocked, - SUM(CASE - WHEN b.StandortID != 0 AND vorlaeufigeBuchung = 0 AND Abholer = 0 - THEN 1 ELSE NULL END) as confirmed, - SUM(CASE - WHEN (b.StandortID != 0 OR AbholortID != 0) AND vorlaeufigeBuchung = 0 AND Abholer = 1 - THEN 1 ELSE NULL END) as pending, + SUM(CASE WHEN name = "dereferenced" THEN 1 ELSE NULL END) as blocked, + SUM(CASE WHEN b.StandortID != 0 AND vorlaeufigeBuchung = 0 AND Abholer = 0 THEN 1 ELSE NULL END) as confirmed, + SUM(CASE WHEN (b.StandortID != 0 OR AbholortID != 0) AND vorlaeufigeBuchung = 0 AND Abholer = 1 THEN 1 ELSE NULL END) as pending, SUM(CASE WHEN name = "(abgesagt)" THEN 1 ELSE NULL END) as deleted, SUM(CASE WHEN nicht_erschienen > 0 AND b.StandortID != 0 THEN 1 ELSE NULL END) as missed, SUM(CASE WHEN vorlaeufigeBuchung = 1 AND b.StandortID != 0 THEN 1 ELSE NULL END) as reserved, - SUM( - CASE - WHEN IPTimeStamp > '.intval($midnight).' AND b.StandortID != 0 - AND vorlaeufigeBuchung = 0 AND Abholer = 0 - THEN - 1 - ELSE - NULL - END) - as sincemidnight, - SUM( - CASE - WHEN - IPTimeStamp > '.intval($last7days).' AND b.StandortID != 0 - AND vorlaeufigeBuchung = 0 AND Abholer = 0 - THEN - 1 - ELSE - NULL - END) - as last7days, + SUM(CASE WHEN IPTimeStamp > '.intval($midnight).' AND b.StandortID != 0 + AND vorlaeufigeBuchung = 0 AND Abholer = 0 THEN 1 ELSE NULL END) as sincemidnight, + SUM(CASE WHEN IPTimeStamp > '.intval($last7days).' AND b.StandortID != 0 + AND vorlaeufigeBuchung = 0 AND Abholer = 0 THEN 1 ELSE NULL END) as last7days, FROM_UNIXTIME(MAX(IPTimeStamp)) as lastInsert - FROM buerger AS b - WHERE - b.istFolgeterminvon IS NULL - OR b.istFolgeterminvon = 0 - ' + FROM buerger AS b + WHERE b.istFolgeterminvon IS NULL OR b.istFolgeterminvon = 0' ); return $processStats; } /** - * Fetch mysql config variables + * Fetch MySQL config variables * - * @return Array + * @return array */ protected function readConfigVariables() { - $configVariables = $this->getReader()->fetchPairs('SHOW VARIABLES'); - return $configVariables; + return $this->getReader()->fetchPairs('SHOW VARIABLES'); } /** - * Fetch mysql status variables + * Fetch MySQL status variables * - * @return Array + * @return array */ protected function readStatusVariables() { - $statusVariables = $this->getReader()->fetchPairs('SHOW STATUS'); - return $statusVariables; + return $this->getReader()->fetchPairs('SHOW STATUS'); } } diff --git a/zmsdb/src/Zmsdb/Workstation.php b/zmsdb/src/Zmsdb/Workstation.php index 15793e5f7..00b178336 100644 --- a/zmsdb/src/Zmsdb/Workstation.php +++ b/zmsdb/src/Zmsdb/Workstation.php @@ -129,7 +129,7 @@ public function readCollectionByDepartmentId($departmentId, $resolveReferences = return $collection; } - public function writeEntityLoginByOidc($loginName, $authKey, \DateTimeInterface $dateTime, $resolveReferences = 0) + public function writeEntityLoginByOidc($loginName, $authKey, \DateTimeInterface $dateTime, \DateTimeInterface $sessionExpiry, $resolveReferences = 0) { $workstation = new Entity(); $query = Query\Workstation::QUERY_LOGIN_OIDC; @@ -137,6 +137,7 @@ public function writeEntityLoginByOidc($loginName, $authKey, \DateTimeInterface $query, array( $authKey, + $sessionExpiry->format('Y-m-d H:i:s'), $dateTime->format('Y-m-d'), $loginName ) @@ -150,7 +151,7 @@ public function writeEntityLoginByOidc($loginName, $authKey, \DateTimeInterface return $workstation; } - public function writeEntityLoginByName($loginName, $password, \DateTimeInterface $dateTime, $resolveReferences = 0) + public function writeEntityLoginByName($loginName, $password, \DateTimeInterface $dateTime, \DateTimeInterface $sessionExpiry, $resolveReferences = 0) { $useraccount = new Useraccount(); $workstation = new Entity(); @@ -161,6 +162,7 @@ public function writeEntityLoginByName($loginName, $password, \DateTimeInterface $query, array( $authKey, + $sessionExpiry->format('Y-m-d H:i:s'), $dateTime->format('Y-m-d'), $dateTime->format('Y-m-d H:i:s'), $loginName, @@ -285,13 +287,14 @@ public function updateEntity(\BO\Zmsentities\Workstation $entity, $resolveRefere * * @return Entity */ - public function updateEntityAuthkey($loginName, $password, $authKey, $resolveReferences) + public function updateEntityAuthkey($loginName, $password, $authKey, \DateTimeInterface $sessionExpiry, $resolveReferences) { $query = Query\Workstation::QUERY_UPDATE_AUTHKEY; $result = $this->perform( $query, array( $authKey, + $sessionExpiry->format('Y-m-d H:i:s'), $loginName, $password ) diff --git a/zmsdb/tests/Zmsdb/LoginTest.php b/zmsdb/tests/Zmsdb/LoginTest.php index f1938804e..d0fa51ce7 100644 --- a/zmsdb/tests/Zmsdb/LoginTest.php +++ b/zmsdb/tests/Zmsdb/LoginTest.php @@ -17,7 +17,7 @@ public function testBasic() 'id' => static::$username )); - $workstation = $query->writeEntityLoginByName($userAccount->id, md5(static::$password), $now); + $workstation = $query->writeEntityLoginByName($userAccount->id, md5(static::$password), $now, (new \DateTime())->setTimestamp(time() + 28800)); $this->assertEquals(true, $workstation->hasAuthKey()); $workstation->scope['id'] = 141; //Bürgeramt Heerstraße diff --git a/zmsdb/tests/Zmsdb/LogoutTest.php b/zmsdb/tests/Zmsdb/LogoutTest.php index 73718a548..619dffac6 100644 --- a/zmsdb/tests/Zmsdb/LogoutTest.php +++ b/zmsdb/tests/Zmsdb/LogoutTest.php @@ -18,7 +18,7 @@ public function testBasic() 'password' => md5(static::$password) )); - $workstation = $query->writeEntityLoginByName($userAccount->id, $userAccount->password, $now, 2); + $workstation = $query->writeEntityLoginByName($userAccount->id, $userAccount->password, $now, (new \DateTime())->setTimestamp(time() + 28800), 2); $this->assertEquals(true, $workstation->hasAuthKey()); $workstation = $query->writeEntityLogoutByName($userAccount->id, 2); $this->assertEntity("\\BO\\Zmsentities\\Workstation", $workstation); diff --git a/zmsdb/tests/Zmsdb/ProcessTest.php b/zmsdb/tests/Zmsdb/ProcessTest.php index 517a98bbd..1d934b810 100644 --- a/zmsdb/tests/Zmsdb/ProcessTest.php +++ b/zmsdb/tests/Zmsdb/ProcessTest.php @@ -40,7 +40,7 @@ public function testReadByWorkstation() { $now = static::$now; $workstation = (new \BO\Zmsdb\Workstation) - ->writeEntityLoginByName('testadmin', md5(static::$password), $now, 2); + ->writeEntityLoginByName('testadmin', md5(static::$password), $now, (new \DateTime())->setTimestamp(time() + 28800), 2); $process =(new Query)->readEntity(10029, '1c56'); $workstation->process = (new \BO\Zmsdb\Workstation)->writeAssignedProcess($workstation, $process, $now); $process = (new Query)->readByWorkstation($workstation, 1); diff --git a/zmsdb/tests/Zmsdb/UserAccountTest.php b/zmsdb/tests/Zmsdb/UserAccountTest.php index f39a048a8..934ef793b 100644 --- a/zmsdb/tests/Zmsdb/UserAccountTest.php +++ b/zmsdb/tests/Zmsdb/UserAccountTest.php @@ -41,7 +41,7 @@ public function testReadByAuthKey() $userAccount = $query->writeUpdatedEntity($userAccount->id, $userAccount, 2); $workstation = (new Workstation()) - ->writeEntityLoginByName($userAccount->id, $input->password, $this->dateTime, 2); + ->writeEntityLoginByName($userAccount->id, $input->password, $this->dateTime, (new \DateTime())->setTimestamp(time() + 28800), 2); $this->assertEquals(true, $workstation->hasAuthKey()); $userAccount = $query->readEntityByAuthKey($workstation->authkey, 1); @@ -172,7 +172,7 @@ public function testLoginFailed() { $now = static::$now; $this->expectException('\BO\Zmsdb\Exception\Useraccount\InvalidCredentials'); - (new Workstation())->writeEntityLoginByName('johndoe', 'secret', $now); + (new Workstation())->writeEntityLoginByName('johndoe', 'secret',$now, (new \DateTime())->setTimestamp($now->getTimestamp() + 28800)); } public function testWriteHintByName() @@ -195,7 +195,7 @@ protected function writeTestLogin() //first write userAccount example in Database $userAccount = $query->writeEntity($input); //login workstation by useraccount - $workstation = (new Workstation())->writeEntityLoginByName($userAccount->id, $input->password, $this->dateTime); + $workstation = (new Workstation())->writeEntityLoginByName($userAccount->id, $input->password, $this->dateTime, (new \DateTime())->setTimestamp(time() + 28800)); //get example workstation account with scope etc and give id from logged in workstation for update $workstationInput = (new WorkstationEntity())->getExample(); $workstationInput->id = $workstation->id;