commit fa7daba41edb81958aae0e3cac9b0b5d2ecacef6 Author: Dorian Zedler Date: Sun Jul 11 09:12:51 2021 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe107c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/data.json \ No newline at end of file diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..f4a3cef --- /dev/null +++ b/.htaccess @@ -0,0 +1,30 @@ + + +RewriteEngine On + +## Begin - Index +# If the requested path and file is not /index.php and the request +# has not already been internally rewritten to the index.php script +RewriteCond %{REQUEST_URI} !^/index\.php +# and the requested path and file doesn't directly match a physical file +RewriteCond %{REQUEST_FILENAME} !-f +# and the requested path and file doesn't directly match a physical folder +RewriteCond %{REQUEST_FILENAME} !-d +# internally rewrite the request to the index.php script +RewriteRule .* index.php [L] +## End - Index + +## Begin - Security +# Block all direct access for these folders +RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F] +# Block all direct access to files and folders beginning with a dot +RewriteRule (^|/)\.(?!well-known) - [F] +# Block access to specific files in the root folder +RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess|README\.md|data\.json)$ error [F] +## End - Security + + + +# Begin - Prevent Browsing and Set Default Resources +Options -Indexes +DirectoryIndex index.php \ No newline at end of file diff --git a/IconSmallSquareOutline.png b/IconSmallSquareOutline.png new file mode 100644 index 0000000..74c348d Binary files /dev/null and b/IconSmallSquareOutline.png differ diff --git a/index.php b/index.php new file mode 100644 index 0000000..2187f27 --- /dev/null +++ b/index.php @@ -0,0 +1,250 @@ + true + ); + private $_themeConfig = array(); + private $_translations; + private $_basepath; + private $_resourcePath; + private $_path; + private $_dataFile = "./data.json"; + private $_storageHelper; + + public function __construct($translations) + { + session_start(); + $this->_translations = $translations; + + $this->_storageHelper = new StorageHelper($this->_dataFile); + + $this->_calculateBasepath(); + $this->_themeConfig["basePath"] = $this->_basepath; + $this->_themeConfig["mainIcon"] = $this->_resourcePath . "IconSmallSquareOutline.png"; + $this->_theme = new LandingpageTheme($this->_themeConfig, $this->_storageHelper, $this->_translations); + + $this->_processRequest(); + } + + private function _processRequest() + { + $this->_updatePermissions(); + $this->_checkPagePermissions(); + + if ($this->_stringEndsWith($this->_path, "submit")) { + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + $this->_redirect('/'); + } + + switch ($this->_path) { + case '/login/submit': + $this->_handleLoginSubmit(); + break; + + case '/logout/submit': + unset($_SESSION["auth"]); + $this->_redirect('/'); + break; + + case '/manage/submit': + $this->_handleManageSubmit(); + $this->_redirect('/manage'); + break; + + default: + $this->_redirect("/"); + break; + } + } else { + if ($this->_path === '/logout') { + $this->_redirect('/'); + } else { + $paramList = explode('/', ltrim($this->_path, '/'), 2); + $endpoint = $paramList[0]; + $username = $paramList[1]; + + if ($endpoint === "api" && isset($username)) + $this->_printJson($username); + else + $this->_theme->printPage($endpoint, $username); + } + } + + unset($_SESSION['lastResult']); + } + + private function _calculateBasepath() + { + if ($this->_serverConfig['hideIndexPhp']) { + $this->_basepath = str_replace(basename($_SERVER["SCRIPT_NAME"]), '', $_SERVER['SCRIPT_NAME']); + $this->_resourcePath = $this->_basepath; + } else { + $this->_basepath = $_SERVER["SCRIPT_NAME"]; + $this->_resourcePath = str_replace(basename($_SERVER["SCRIPT_NAME"]), '', $_SERVER['SCRIPT_NAME']); + } + + $this->_basepath = rtrim($this->_basepath, "/ "); + + if (($this->_basepath !== '' && strpos($_SERVER['REQUEST_URI'], $this->_basepath) === false) || $_SERVER['REQUEST_URI'] === $this->_basepath) + $this->_path = "/"; + else + $this->_path = str_replace($this->_basepath, "", $_SERVER['REQUEST_URI']); + } + + private function _redirect($path) + { + header('Location: ' . $this->_basepath . $path); + die(); + } + + // + // - Page printer (theme) + // + + private function _printJson($username) + { + header('Content-Type: application/json'); + header('Access-Control-Allow-Origin: *'); + $allUserdata = $this->_storageHelper->loadUserdata(); + if (array_key_exists($username, $allUserdata)) { + $userdata = $allUserdata[$username]; + die(json_encode(array( + "status" => 200, + "data" => array( + "time" => $userdata["time"], + "startedAt" => $userdata["startedAt"], + "header" => $userdata["header"] + ) + ))); + } else { + http_response_code(404); + die(json_encode(array( + "status" => 404, + "data" => null + ))); + } + } + + // ----------------------- + // - Permission handlers - + // ----------------------- + + private function _updatePermissions() + { + $_SESSION['permissions'][''] = false; + $_SESSION['permissions']['t'] = true; + $_SESSION['permissions']['api'] = true; + $_SESSION['permissions']['login'] = !$this->_isUserAuthenticated(); + $_SESSION['permissions']['logout'] = $this->_isUserAuthenticated(); + $_SESSION['permissions']['manage'] = $this->_isUserAuthenticated(); + } + + private function _checkPagePermissions() + { + $pageRedirectOnInsufficientPermissionsPriority = [ + 0 => "manage", + 1 => "login" + ]; + + $page = explode("/", $this->_path)[1]; + + if (!isset($_SESSION['permissions'][$page])) { + $this->_redirect('/'); + } else if ($_SESSION['permissions'][$page] === false) { + if (!$this->_isUserAuthenticated()) { + $_SESSION['lastResult'] = 'loginRequired'; + } + + if ($this->_path === '/' || $this->_path === '') { + // if the root is opened, do not throw an error! + unset($_SESSION['lastResult']); + } + + // redirect to the first page the user has access to + foreach ($pageRedirectOnInsufficientPermissionsPriority as $page) { + if ($_SESSION['permissions'][$page]) + $this->_redirect("/" . $page); + } + + die($this->_translations['results']['noPermissionToAnyPage']); + } + } + + // ------------------- + // - Submit handlers - + // ------------------- + + private function _handleLoginSubmit() + { + $username = $_POST["username"]; + $password = $_POST["password"]; + $userData = $this->_storageHelper->loadUserdata(); + if (array_key_exists($username, $userData) && password_verify($password, $userData[$username]['password'])) { + $_SESSION["auth"]["loggedIn"] = true; + $_SESSION["auth"]["username"] = $username; + $_SESSION['lastResult'] = "loginSuccess"; + $this->_redirect("/manage"); + } else { + $_SESSION['lastResult'] = "loginFailed"; + $this->_redirect("/login"); + } + } + + private function _handleManageSubmit() + { + $time = $_POST["time"]; + $header = $_POST["header"]; + $startedAt = time(); + $newData = array( + "time" => intval($time), + "header" => $header, + "startedAt" => $startedAt + ); + $this->_storageHelper->writeUserdata($_SESSION["auth"]["username"], $newData); + $_SESSION['lastResult'] = "timerSetSuccessfully"; + } + + // ---------------------------- + // - General helper functions - + // ---------------------------- + + private function _isUserAuthenticated() + { + $authenticated = + isset($_SESSION['auth']) + && isset($_SESSION['auth']['loggedIn']) + && $_SESSION['auth']['loggedIn'] === true + && isset($_SESSION['auth']['username']); + + if (!$authenticated && isset($_SESSION['auth'])) { + unset($_SESSION['auth']); + } + + return $authenticated; + } + + private function _stringStartsWith($haystack, $needle) + { + $length = strlen($needle); + return substr($haystack, 0, $length) === $needle; + } + + private function _stringEndsWith($haystack, $needle) + { + $length = strlen($needle); + if (!$length) { + return true; + } + return substr($haystack, -$length) === $needle; + } +} + +new SyncedTimer($translations); diff --git a/storageHelper.php b/storageHelper.php new file mode 100644 index 0000000..4e07038 --- /dev/null +++ b/storageHelper.php @@ -0,0 +1,31 @@ +_dataFile = $dataFile; + } + + function loadUserdata() + { + $data = json_decode(file_get_contents($this->_dataFile), true); + return $data; + } + + function writeUserdata($username, $newUserdata) + { + $allUserdata = $this->loadUserdata(); + $currentUserdata = $allUserdata[$username]; + $newUserdata = array_replace_recursive($currentUserdata, $newUserdata); + $dataFile = fopen($this->_dataFile, "w") or die("Unable to open file!"); + $allUserdata[$username] = $newUserdata; + + fwrite($dataFile, json_encode($allUserdata)); + fclose($dataFile) or die("Unable to write to data file!"); + } + +} \ No newline at end of file diff --git a/strings.php b/strings.php new file mode 100644 index 0000000..3095344 --- /dev/null +++ b/strings.php @@ -0,0 +1,51 @@ +{$customizationConfig['supportEmailAddress']} (%ERR%)"; +$translations['results']['permissionDenied'] = "Zugriff verweigert"; +$translations['results']['noPermissionToAnyPage'] = str_replace("%ERR%", "0005", $translations['results']['internalError']); +$translations['results']['loginSuccess'] = "Erfolgreich angemeldet"; +$translations['results']['loginFailed'] = "Ungültige Zugangsdaten"; +$translations['results']['loginRequired'] = "Bitte zuerst anmelden!"; +$translations['results']['oldPasswordIsWrong'] = "Das aktulle Passwort ist falsch"; +$translations['results']['newPasswordMustNotBeEqualToOldPassword'] = "Das neue Passwort darf nicht mit dem Alten übereinstimmen"; +$translations['results']['newPasswordAndRepeatDidNotMatch'] = "Die Passwörter stimmen nicht überein"; +$translations['results']['passwordIsTooShort'] = $translations['passwordRules'][0]; +$translations['results']['newPasswordMustNotBeOldPassword'] = "Das neue Passwort darf nicht mit dem Alten übereinstimmen."; +$translations['results']['passwordChangedSuccessfully'] = "Dein Passwort wurde erfolgreich geändert."; +$translations['results']['timerSetSuccessfully'] = "The timer was started successfully"; + +$translations['globals']['title'] = "{$customizationConfig['organizationName']} Account"; +$translations['globals']['usernameLabel'] = "Benutzername"; +$translations['globals']['passwordLabel'] = "Passwort"; +$translations['globals']['emailAddress'] = "E-Mail-Adresse"; + +$translations['login']['title'] = "Bitte anmelden"; +$translations['login']['submitLabel'] = "Anmelden"; +$translations['login']['footnote'] = "Login für Mitglieder des {$customizationConfig['fullOrganizationName']}"; + +$translations['changePassword']['passwordRulesHeader'] = "Regeln für Passwörter:"; +$translations['changePassword']['currentPasswordLabel'] = "Aktuelles Passwort"; +$translations['changePassword']['newPasswordLabel'] = "Neues Passwort"; +$translations['changePassword']['repeatNewPasswordLabel'] = "Neues Password Wiederholen"; +$translations['changePassword']['submitLabel'] = "Passwort ändern"; \ No newline at end of file diff --git a/theme.php b/theme.php new file mode 100644 index 0000000..f2d77b2 --- /dev/null +++ b/theme.php @@ -0,0 +1,473 @@ +_globalConfig = $config; + $this->_storageHelper = $storageHelper; + $this->_translations = $translations; + + $this->_resultLevels['loginSuccess'] = "success"; + $this->_resultLevels['loginFailed'] = "danger"; + $this->_resultLevels['ldapConnectFailed'] = "danger"; + $this->_resultLevels['ldapSearchFailed'] = "danger"; + $this->_resultLevels['ldapTlsInitializationFailed'] = "danger"; + $this->_resultLevels['bindingToLdapAdminFailed'] = "danger"; + $this->_resultLevels['loginRequired'] = "warning"; + $this->_resultLevels['oldPasswordIsWrong'] = "danger"; + $this->_resultLevels['newPasswordMustNotBeEqualToOldPassword'] = "danger"; + $this->_resultLevels['newPasswordAndRepeatDidNotMatch'] = "danger"; + $this->_resultLevels['passwordIsTooShort'] = "danger"; + $this->_resultLevels['passwordDoesNotContainANumberOrSpecialCharacter'] = "danger"; + $this->_resultLevels['passwordDoesNotContainALetter'] = "danger"; + $this->_resultLevels['passwordDoesNotContainAnUppercaseLetter'] = "danger"; + $this->_resultLevels['passwordDoesNotContainALowercaseLetter'] = "danger"; + $this->_resultLevels['passwordChangeLdapError'] = "danger"; + $this->_resultLevels['newPasswordMustNotBeOldPassword'] = "danger"; + $this->_resultLevels['passwordChangedSuccessfully'] = 'success'; + $this->_resultLevels['emailChangedSuccessfully'] = 'success'; + $this->_resultLevels['emailChangeLdapError'] = 'danger'; + $this->_resultLevels['invalidEmailError'] = 'danger'; + $this->_resultLevels['permissionDenied'] = 'danger'; + $this->_resultLevels['generateJitsiLinkRoomMustNotBeEmpty'] = 'danger'; + $this->_resultLevels['generateJitsiLinkSuccessfull'] = 'success'; + $this->_resultLevels['timerSetSuccessfully'] = 'success'; + } + + public function printPage($page, $parameters) + { + switch ($page) { + case 'login': + $this->_printLogin(); + break; + case "manage": + $this->_printManagePage(); + break; + case "t": + $this->_printTimerPage($parameters); + } + } + + private function _printLogin() + { + $this->_printHeader(true); +?> + + + + + +
+
+ +

_trId("login.title"); ?>

+ _printResultAlert(); ?> + + " name="username" required autofocus> + + " name="password" required> + +

_trId("login.footnote"); ?>

+
+
+ + _printFooter(); + } + + private function _printManagePage() + { + $userData = $this->_storageHelper->loadUserdata()[$_SESSION["auth"]["username"]]; + $this->_printHeader(); + ?> + + + +
+ + +
+

Curent timer

+ +

00:00:00

+

Manage timer

+ _printResultAlert(); ?> +
+
+ +
+ +
+ + +
+
+ + " required> +
+ +
+
+
+ + _printTimerJs($_SESSION["auth"]["username"]); + $this->_printFooter(); + } + + private function _printTimerPage($username) + { + $userData = $this->_storageHelper->loadUserdata(); + if (!array_key_exists($username, $userData)) { + $this->_printNotFoundPage(); + } + + ?> + + + + + + + + + + + + +
+
+ +
+

00:00:00

+
+
+ + +
+ + + + + + +

Not found!

+ + + + + + + + + + + + + + + + <?= $this->_trId("globals.title"); ?> + + + _printAlert($this->_resultToMessage($_SESSION['lastResult']), $this->_resultLevels[$_SESSION['lastResult']]); + } + + private function _printAlert($content, $level = 'waring', $dismissible = true) + { + ?> +
fade show" role="alert"> + + + + +
+ + + + +_translations['results'][$result]; + } + + private function _trId($id) + { + $result = $this->_translations; + foreach (explode(".", $id) as $sub) { + $result = $result[$sub]; + } + return $result; + } + }