diff --git a/Core/API/Request.class.php b/Core/API/Request.class.php index a404567..8d445a1 100644 --- a/Core/API/Request.class.php +++ b/Core/API/Request.class.php @@ -278,6 +278,7 @@ abstract class Request { $this->success = $success; } } catch (\Error $err) { + http_response_code(500); $this->createError($err->getMessage()); $this->logger->error($err->getMessage()); } diff --git a/Core/API/Stats.class.php b/Core/API/Stats.class.php index e8be77a..589941d 100644 --- a/Core/API/Stats.class.php +++ b/Core/API/Stats.class.php @@ -57,6 +57,7 @@ class Stats extends Request { $sql = $this->context->getSQL(); $userCount = User::count($sql); $pageCount = Route::count($sql, new CondBool("active")); + $groupCount = Group::count($sql); $req = new \Core\API\Visitors\Stats($this->context); $this->success = $req->execute(array("type"=>"monthly")); $this->lastError = $req->getLastError(); @@ -82,6 +83,7 @@ class Stats extends Request { $this->result["data"] = [ "userCount" => $userCount, "pageCount" => $pageCount, + "groupCount" => $groupCount, "visitors" => $visitorStatistics, "visitorsTotal" => $visitorCount, "server" => [ diff --git a/Core/API/UserAPI.class.php b/Core/API/UserAPI.class.php index a08883f..d8ea8c9 100644 --- a/Core/API/UserAPI.class.php +++ b/Core/API/UserAPI.class.php @@ -576,26 +576,26 @@ namespace Core\API\User { if ($user !== false) { if ($user === null) { return $this->wrongCredentials(); - } else { - if (password_verify($password, $user->password)) { - if (!$user->confirmed) { - $this->result["emailConfirmed"] = false; - return $this->createError("Your email address has not been confirmed yet."); - } else if (!($session = $this->context->createSession($user, $stayLoggedIn))) { - return $this->createError("Error creating Session: " . $sql->getLastError()); - } else { - $tfaToken = $user->getTwoFactorToken(); - - $this->result["loggedIn"] = true; - $this->result["user"] = $user->jsonSerialize(); - $this->result["session"] = $session->jsonSerialize(); - $this->result["logoutIn"] = $session->getExpiresSeconds(); - $this->check2FA($tfaToken); - $this->success = true; - } + } else if (!$user->isActive()) { + return $this->createError("This user is currently disabled. Contact the server administrator, if you believe this is a mistake."); + } else if (password_verify($password, $user->password)) { + if (!$user->confirmed) { + $this->result["emailConfirmed"] = false; + return $this->createError("Your email address has not been confirmed yet."); + } else if (!($session = $this->context->createSession($user, $stayLoggedIn))) { + return $this->createError("Error creating Session: " . $sql->getLastError()); } else { - return $this->wrongCredentials(); + $tfaToken = $user->getTwoFactorToken(); + + $this->result["loggedIn"] = true; + $this->result["user"] = $user->jsonSerialize(); + $this->result["session"] = $session->jsonSerialize(); + $this->result["logoutIn"] = $session->getExpiresSeconds(); + $this->check2FA($tfaToken); + $this->success = true; } + } else { + return $this->wrongCredentials(); } } else { return $this->createError("Error fetching user details: " . $sql->getLastError()); @@ -934,43 +934,47 @@ namespace Core\API\User { if ($user === false) { return $this->createError("Could not fetch user details: " . $sql->getLastError()); } else if ($user !== null) { - $validHours = 1; - $token = generateRandomString(36); - $userToken = new UserToken($user, $token, UserToken::TYPE_PASSWORD_RESET, $validHours); - if (!$userToken->save($sql)) { - return $this->createError("Could not create user token: " . $sql->getLastError()); - } + if (!$user->isActive()) { + return $this->createError("This user is currently disabled. Contact the server administrator, if you believe this is a mistake."); + } else { + $validHours = 1; + $token = generateRandomString(36); + $userToken = new UserToken($user, $token, UserToken::TYPE_PASSWORD_RESET, $validHours); + if (!$userToken->save($sql)) { + return $this->createError("Could not create user token: " . $sql->getLastError()); + } - $baseUrl = $settings->getBaseUrl(); - $siteName = $settings->getSiteName(); + $baseUrl = $settings->getBaseUrl(); + $siteName = $settings->getSiteName(); - $req = new Render($this->context); - $this->success = $req->execute([ - "file" => "mail/reset_password.twig", - "parameters" => [ - "link" => "$baseUrl/resetPassword?token=$token", - "site_name" => $siteName, - "base_url" => $baseUrl, - "username" => $user->name, - "valid_time" => $this->formatDuration($validHours, "hour") - ] - ]); - $this->lastError = $req->getLastError(); + $req = new Render($this->context); + $this->success = $req->execute([ + "file" => "mail/reset_password.twig", + "parameters" => [ + "link" => "$baseUrl/resetPassword?token=$token", + "site_name" => $siteName, + "base_url" => $baseUrl, + "username" => $user->name, + "valid_time" => $this->formatDuration($validHours, "hour") + ] + ]); + $this->lastError = $req->getLastError(); - if ($this->success) { - $messageBody = $req->getResult()["html"]; + if ($this->success) { + $messageBody = $req->getResult()["html"]; - $gpgKey = $user->getGPG(); - $gpgFingerprint = ($gpgKey && $gpgKey->isConfirmed()) ? $gpgKey->getFingerprint() : null; - $request = new \Core\API\Mail\Send($this->context); - $this->success = $request->execute(array( - "to" => $email, - "subject" => "[$siteName] Password Reset", - "body" => $messageBody, - "gpgFingerprint" => $gpgFingerprint - )); - $this->lastError = $request->getLastError(); - $this->logger->info("Requested password reset for user id=" . $user->getId() . " by ip_address=" . $_SERVER["REMOTE_ADDR"]); + $gpgKey = $user->getGPG(); + $gpgFingerprint = ($gpgKey && $gpgKey->isConfirmed()) ? $gpgKey->getFingerprint() : null; + $request = new \Core\API\Mail\Send($this->context); + $this->success = $request->execute(array( + "to" => $email, + "subject" => "[$siteName] Password Reset", + "body" => $messageBody, + "gpgFingerprint" => $gpgFingerprint + )); + $this->lastError = $request->getLastError(); + $this->logger->info("Requested password reset for user id=" . $user->getId() . " by ip_address=" . $_SERVER["REMOTE_ADDR"]); + } } } diff --git a/Core/Localization/de_DE/account.php b/Core/Localization/de_DE/account.php index c012567..efe2a38 100644 --- a/Core/Localization/de_DE/account.php +++ b/Core/Localization/de_DE/account.php @@ -22,6 +22,7 @@ return [ "reset_password_form_title" => "Ein neues Passwort wählen", "reset_password_request_form_title" => "Geben Sie Ihre E-Mail Adresse ein um ein Passwort-Reset Token zu erhalten", "form_title" => "Bitte geben Sie ihre Daten ein", + "name" => "Name", "username" => "Benutzername", "username_or_email" => "Benutzername oder E-Mail", "email" => "E-Mail Adresse", @@ -60,5 +61,11 @@ return [ "registered_at" => "Registriert am", "last_online" => "Zuletzt online", "groups" => "Gruppen", + "group_name" => "Gruppenname", + "new_group" => "Neue Gruppe", + "members" => "Mitglieder", + "member_count" => "Mitgliederanzahl", + "color" => "Farbe", "logged_in_as" => "Eingeloggt als", + "active" => "Aktiv", ]; \ No newline at end of file diff --git a/Core/Localization/de_DE/admin.php b/Core/Localization/de_DE/admin.php index d1af028..ea9bcd3 100644 --- a/Core/Localization/de_DE/admin.php +++ b/Core/Localization/de_DE/admin.php @@ -12,4 +12,12 @@ return [ "acl" => "Zugriffsberechtigung", "logs" => "Logs", "help" => "Hilfe", + + # Dashboard + "users_registered" => "Benutzer registriert", + "available_groups" => "verfügbare Gruppen", + "routes_defined" => "Routen definiert", + + # Dialogs + "fetch_stats_error" => "Fehler beim Holen der Stats", ]; \ No newline at end of file diff --git a/Core/Localization/de_DE/logs.php b/Core/Localization/de_DE/logs.php index 05666aa..d6df6c5 100644 --- a/Core/Localization/de_DE/logs.php +++ b/Core/Localization/de_DE/logs.php @@ -10,6 +10,8 @@ return [ "search_query" => "Suchanfrage", "no_entries_placeholder" => "Keine Log-Einträge zum Anzeigen", "timestamp_placeholder" => "Datum und Zeitpunk Auswählen zum Filtern", + "hide_details" => "Details verstecken", + "show_details" => "Details zeigen", // dialog "fetch_log_error" => "Fehler beim Holen der Log-Einträge", diff --git a/Core/Localization/en_US/account.php b/Core/Localization/en_US/account.php index 58a689e..d08617d 100644 --- a/Core/Localization/en_US/account.php +++ b/Core/Localization/en_US/account.php @@ -22,6 +22,7 @@ return [ "reset_password_form_title" => "Choose a new password", "reset_password_request_form_title" => "Enter your E-Mail address, to receive a password reset token.", "form_title" => "Please fill with your details", + "name" => "Name", "username" => "Username", "username_or_email" => "Username or E-Mail", "email" => "E-Mail Address", @@ -60,5 +61,11 @@ return [ "registered_at" => "Registered At", "last_online" => "Last Online", "groups" => "Groups", + "group_name" => "Group Name", + "new_group" => "New Group", + "members" => "Members", + "member_count" => "Member Count", + "color" => "Color", "logged_in_as" => "Logged in as", + "active" => "Active", ]; \ No newline at end of file diff --git a/Core/Localization/en_US/admin.php b/Core/Localization/en_US/admin.php index 68a12e9..14269fd 100644 --- a/Core/Localization/en_US/admin.php +++ b/Core/Localization/en_US/admin.php @@ -12,4 +12,12 @@ return [ "acl" => "Access Control", "logs" => "Logs", "help" => "Help", + + # Dashboard + "users_registered" => "Users registered", + "available_groups" => "available Groups", + "routes_defined" => "Routes defined", + + # Dialogs + "fetch_stats_error" => "Error fetching stats", ]; \ No newline at end of file diff --git a/Core/Localization/en_US/general.php b/Core/Localization/en_US/general.php index 3da41ec..a45ab35 100644 --- a/Core/Localization/en_US/general.php +++ b/Core/Localization/en_US/general.php @@ -59,7 +59,6 @@ return [ "move" => "Move", "overwrite" => "Overwrite", - # data table "showing_x_of_y_entries" => "Showing %d of %d entries", "controls" => "Controls", diff --git a/Core/Localization/en_US/logs.php b/Core/Localization/en_US/logs.php index b9c23f8..6990b02 100644 --- a/Core/Localization/en_US/logs.php +++ b/Core/Localization/en_US/logs.php @@ -10,6 +10,8 @@ return [ "search_query" => "Search query", "no_entries_placeholder" => "No log entries to display", "timestamp_placeholder" => "Select date and time to filter", + "hide_details" => "Hide details", + "show_details" => "Show details", // dialog "fetch_log_error" => "Error fetching log entries", diff --git a/Core/Objects/Context.class.php b/Core/Objects/Context.class.php index 346196a..94778cf 100644 --- a/Core/Objects/Context.class.php +++ b/Core/Objects/Context.class.php @@ -176,8 +176,9 @@ class Context { ->addJoin(new InnerJoin("ApiKey", "ApiKey.user_id", "User.id")) ->whereEq("ApiKey.api_key", $apiKey) ->whereGt("valid_until", $this->sql->currentTimestamp()) - ->whereTrue("ApiKey.active", true) - ->whereTrue("User.confirmed", true) + ->whereTrue("ApiKey.active") + ->whereTrue("User.confirmed") + ->whereTrue("User.active") ->fetchEntities()); return $this->user !== null; diff --git a/Core/Objects/DatabaseEntity/Session.class.php b/Core/Objects/DatabaseEntity/Session.class.php index d40eeb9..e553361 100644 --- a/Core/Objects/DatabaseEntity/Session.class.php +++ b/Core/Objects/DatabaseEntity/Session.class.php @@ -46,10 +46,16 @@ class Session extends DatabaseEntity { ->whereEq("Session.uuid", $sessionUUID) ->whereTrue("Session.active") ->whereGt("Session.expires", $sql->now())); + if (!$session) { return null; } + $user = $session->getUser(); + if (!$user->isActive() || !$user->isConfirmed()) { + return null; + } + if (is_array($session->data)) { foreach ($session->data as $key => $value) { $_SESSION[$key] = $value; diff --git a/Core/Objects/DatabaseEntity/User.class.php b/Core/Objects/DatabaseEntity/User.class.php index 37899a3..ed499d0 100644 --- a/Core/Objects/DatabaseEntity/User.class.php +++ b/Core/Objects/DatabaseEntity/User.class.php @@ -45,6 +45,10 @@ class User extends DatabaseEntity { #[DefaultValue(false)] public bool $confirmed; + #[Visibility(Visibility::BY_GROUP, Group::ADMIN, Group::SUPPORT)] + #[DefaultValue(true)] + public bool $active; + #[DefaultValue(Language::AMERICAN_ENGLISH)] public Language $language; #[Visibility(Visibility::BY_GROUP, Group::ADMIN, Group::SUPPORT)] @@ -92,6 +96,14 @@ class User extends DatabaseEntity { return $this->profilePicture; } + public function isActive():bool { + return $this->active; + } + + public function isConfirmed():bool { + return $this->confirmed; + } + public function __debugInfo(): array { return [ 'id' => $this->getId(), diff --git a/index.php b/index.php index 2f2505c..170e37d 100644 --- a/index.php +++ b/index.php @@ -65,7 +65,13 @@ if ($installation) { is_string($_GET["error"]) && preg_match("/^\d+$/", $_GET["error"])) { $response = $router->returnStatusCode(intval($_GET["error"])); } else { - $response = $router->run($requestedUri); + try { + $response = $router->run($requestedUri); + } catch (\Error $e) { + http_response_code(500); + $router->getLogger()->error($e->getMessage()); + $router->returnStatusCode(500); + } } } diff --git a/react/admin-panel/src/views/group/group-edit.js b/react/admin-panel/src/views/group/group-edit.js index a185098..7386f47 100644 --- a/react/admin-panel/src/views/group/group-edit.js +++ b/react/admin-panel/src/views/group/group-edit.js @@ -56,7 +56,7 @@ export default function EditGroupView(props) {
{entry.message}+ return
+ {entry.showDetails ? entry.message : entry.message.split("\n")[0]} ++
{props.text}
+ > :Users registered
- > :