null, 'success' => null, 'notice' => null]; private static $userId = null; /** * Returns the current login status * * The login status is stored in the session cookie. If it is not even set it means the login is invalid. * * @return The login status or false if none is set so far */ public static function isLoginValid() { return ($_SESSION['login'] ?? false); } /** * A little Box with the login status as html entity * * @return string htmlEntity showing the login status */ public static function htmlLoginStatus() { return '
' . 'Datum: ' . date('Y-m-d') . '
' . 'Angemeldet als ' . htmlspecialchars($_SESSION['user']['username']) . '.
' . 'Sitzung beenden' . '
'; } /** * Checks, if there already is a valid login, if not redirect to the login form * @todo rename to authenticate * * @retval void */ public static function authentificate() { session_start(); if (!self::isLoginValid()) { header('Location: login?returnToUrl=' . urlencode($_SERVER['REQUEST_URI'] . ($_POST['fragment'] ?? '')), true, 301); exit(); // should'nt matter } participo::$userId = $_SESSION['user']['userId']; } public static function getMessages() { return self::$message; } public static function addMessage($type, $message) { self::$message[$type] = (self::$message[$type] ?? '') . $message; } /** * check password for user * * @param string $loginName user who wants to get in * @param string $password password for the user * * @retval true $password belongs to $loginName * @retval false otherwise */ public static function checkCredentials($loginName, $password) { sleep(1); // just to discourage brute force attacks // Check for dbConnection if (!dbConnector::getDbConnection()) { self::addMessage('error', '
No DbConnection available
'); return false; } // query *all* users with the entered name // @todo check for e.g., len(user)=1 // @todo getUser? $user = dbConnector::query( 'SELECT `id`, `loginName`, `pwHash`, `config` FROM `wkParticipo_Users` WHERE `loginName` = :loginName', ['loginName' => ['value' => $loginName, 'data_type' => PDO::PARAM_STR]] ); $user = $user[0]; // If there is no such user OR the password isn't valid the login fails if (empty($user) || !password_verify($password, $user['pwHash'])) { sleep(5); // discourage brute force attacks self::addMessage('error', '
Falsches Passwort oder LoginName
'); return false; } session_start(); // case valid login: Set the session data $_SESSION = [ 'login' => true, 'user' => [ 'username' => $user['loginName'], 'userId' => $user['id'], 'userConfig' => json_decode($user['config'], true) ] ]; // Logging Logins logLoginsToJsonFile($_SESSION['user']['username']); self::addMessage('success', '
Anmeldung erfolgreich
'); return true; } /** * Checks, if a user is an admin * * @param [type] $userId id of the user to check * @retval true user with id $userId has attribute "isAdmin" * @retval false otherwise */ public static function isUserAdmin($userId) { return self::hasUserAttribute($userId, 'isAdmin'); } /** * Checks, if a user as a certain attribute * * @param [type] $userId id of the user to check * @param [type] $attributeName string name of the attribute to check * @return boolean */ public static function hasUserAttribute($userId, $attributeName) { // sqlQuery: Select the user if it has the given attribute $query = <<userAttributes`.userId, `wkParticipo_userAttributes`.name FROM `wkParticipo_user<=>userAttributes` LEFT JOIN `wkParticipo_userAttributes` ON `wkParticipo_user<=>userAttributes`.`attributeId` = `wkParticipo_userAttributes`.`id` WHERE `wkParticipo_userAttributes`.name = :attributeName AND userId=:userId; SQL; $params = [ ':userId' => ['value' => $userId, 'data_type' => PDO::PARAM_INT], ':attributeName' => ['value' => $attributeName, 'data_type' => PDO::PARAM_STR] ]; $attributedUsers = dbConnector::query($query, $params); // Since the id should be unique, there should only be one result this is just for dealing with empty arrays foreach ($attributedUsers as $u) { if ($u['userId'] == $userId) { return true; } } return false; } public static function getEventStarter($sinceDate = null) { $userId = $_SESSION['user']['userId']; if (!$sinceDate) { $sinceDate = 'CURDATE()'; } else { $sinceDate = 'DATE("' . $sinceDate . '")'; } $query = <<= $sinceDate AND `vormundschaft`.`userId` = $userId ORDER BY `wkParticipo_Events`.`date` DESC; SQL; $commingStarts = dbConnector::query($query); return $commingStarts; } } /** * Action element of an MaterializeCss (App-)card */ class AppCardAction { private $caption = null; //< Caption for the action private $link = '.'; //< link for the action /** * Constructor for the AppAction * * @param string $caption caption for the action * @param string $link link to the action */ public function __construct($caption, $link = '.') { //! @todo input sanitation $this->link = $link; $this->caption = $caption; } /** * Create htmlCode for the action * * @return string with htmlCode of the action */ public function htmlCode() { return '' . $this->caption . ''; } /** * Create AppCardAction from assoziative array * * @param array $member array with the member values * @return AppCardAction */ public static function fromArray($member) { $caption = $member['caption'] ?? null; $link = $member['link'] ?? '.'; return new AppCardAction($caption, $link); } } /** * MaterializeCss card for an App */ class AppCard { private $title = ''; //< title of the card private $description = ''; //< description of the App private $link = null; //< link for the card-content private $imgUrl = null; //< url for an image right under the title private $actionList = []; //< list of actions for the bottom of the card /** * Constructor for the AppCard * * @param string $title title of the card * @param string $description description of the card * @param string $link link for the card-content * @param string $imgUrl url for an image right under the title * @param array $actionList list of actions at the bottom of the card */ public function __construct($title, $description, $link = null, $imgUrl = null, $actionList = []) { //! @todo input sanitation $this->title = $title; $this->description = $description; $this->link = $link; $this->imgUrl = $imgUrl; $this->actionList = $actionList; } /** * Create htmlCode for the AppCard * * @return string html code for the AppCard */ public function htmlCode($options = []) { $extraClass = $options['extraClass'] ?? ''; $actionListCode = ''; foreach ($this->actionList as $a) { $actionListCode .= $a->htmlCode(); } return '
' . '
' . '
' . (($this->link != null) ? ('') : ('')) . '' . $this->title . '' . (($this->link != null) ? ('') : ('')) . (($this->imgUrl != null) ? ('' . $this->title . '') : ('')) . '

' . $this->description . '

' . '
' . '
' . $actionListCode . '
' . '
' . '
'; } /** * Create AppCard from an associative array * * @param array $member array with member as keys and values as the member values * @return AppCard from array values */ public static function fromArray($member) { $title = $member['title'] ?? ''; $description = $member['description'] ?? ''; $link = $member['link'] ?? null; $imgUrl = $member['imgUrl'] ?? null; $actionList = $member['actions'] ?? []; return new AppCard($title, $description, $link, $imgUrl, $actionList); } } /** * Generate a html table of the last logins of the users * * @param string $jsonFileName path to the json file with the logged logins * @return string Html table of users last logins */ function lastLoginTable($jsonFileName = 'lastLogins.json') { $lastLogins = json_decode(file_get_contents($jsonFileName), true); $lastLoginsTable = '' . '' . ''; foreach ($lastLogins as $userName => $lastLogins) { $lastLoginsTable .= ''; } $lastLoginsTable .= '
userNamelastLogins
' . $userName . '' . $lastLogins['lastLogins'][0] . '
'; return $lastLoginsTable; } /// Eine Fehler/Warnung/Notiz/Erfolgsmeldung als divBox im String zurückgeben function htmlRetMessage($anRetMessage) { $retHtmlString = ''; if (!empty($anRetMessage)) { $retHtmlString .= '
'; if (!empty($anRetMessage['error'])) { $retHtmlString .= '
'; $retHtmlString .= 'ERROR:
'; $retHtmlString .= $anRetMessage['error']; $retHtmlString .= '
'; } if (!empty($anRetMessage['warning'])) { $retHtmlString .= '
'; $retHtmlString .= 'WARNING:
'; $retHtmlString .= $anRetMessage['warning']; $retHtmlString .= '
'; } if (!empty($anRetMessage['notice'])) { $retHtmlString .= '
'; $retHtmlString .= 'Info:
'; $retHtmlString .= $anRetMessage['notice']; $retHtmlString .= '
'; } if (!empty($anRetMessage['success'])) { $retHtmlString .= '
'; $retHtmlString .= 'SUCCESS:
'; $retHtmlString .= $anRetMessage['success']; $retHtmlString .= '
'; } $retHtmlString .= '
'; } return $retHtmlString; } /** * load a MarkdownFile with yaml header * * @param string $fileName filename of the markdown file * @return array assocative array('yaml'=>array(..), 'mdText'=>string) containing the yamlHeader as associative array and the markdown text as string */ function loadMarkdownFile($fileName) { // load the whole file $fileText = file_get_contents($fileName); // split at '---' to get ((),yamls,array) $fileParts = preg_split('/[\n]*[-]{3}[\n]/', $fileText, 3); // not all mdfiles have a yamlHeader, so the mdText can be at different indices $yaml = []; $mdText = ''; switch(count($fileParts)) { case 1:{ $mdText = $fileParts[0]; break; } case 3:{ $yaml = Spyc::YAMLLoadString($fileParts[1]); $mdText = $fileParts[2]; break; } default:{ $mdText = $fileText; } } // get a title, if none is in the markdown if (!array_key_exists('title', $yaml)) { // find the first heading, set it as header and remove it from the markdown if (preg_match('/^#(.*)$/m', $mdText, $matches)) { $yaml['title'] = $matches[1]; $mdText = preg_replace('/^#(.*)$/m', '', $mdText, 1); } else { // fallback for the title, if not even one heading is found $yaml['title'] = ''; } } return [ 'yaml' => $yaml, 'mdText' => $mdText ]; } /** * Log the Login of an user into a logFile * * @param string $userName name of the user * @param string $fileName filename to log to * @return void */ function logLoginsToJsonFile($userName, $fileName = 'lastLogins.json') { try { $lastLogins = json_decode(file_get_contents($fileName), true); if ($lastLogins == null) { return; } if (!array_key_exists($userName, $lastLogins)) { $lastLogins[$userName] = []; } if (!array_key_exists('lastLogins', $lastLogins[$userName])) { $lastLogins[$userName]['lastLogins'] = []; } $lastLogins[$userName]['lastLogins'] = array_merge([date('Y-m-d H:i:s')], $lastLogins[$userName]['lastLogins']); file_put_contents($fileName, json_encode($lastLogins)); } catch (Exception $e) { // silently ignore errors } } /** * interface for connecting and communicating with a database */ class dbConnector { private static $db = null; // connect to the database public static function connect($hostname, $dbName, $user, $password) { return self::setDbConnection(self::connectToPdo($hostname, $dbName, $user, $password)); } public static function getDbConnection() { return self::$db; } /// perform a pdo-query /// /// @param $aQueryString /// @param $aBindArray e.g. array( /// ':userId' => array('value'=>$anUserId, 'data_type'=>PDO::PARAM_INT), /// ':attributeId'=> array('value'=>$anAttributeId, 'data_type'=>PDO::PARAM_INT) ) /// @param $someOption public static function query($aQueryString, $aBindArray = [], $someOptions = []) { // Standardbelegungen if (empty($someOptions['dbCharset'])) { $someOptions['dbCharset'] = 'ISO-8859-1'; } if (empty($someOptions['outCharset'])) { $someOptions['outCharset'] = 'UTF-8'; } if (empty($someOptions['dontFetch'])) { $someOptions['dontFetch'] = false; } /// @toDo: Bisher wird nur die Rückgabe konvertiert. Eigentlich muss /// doch auch die Eingabe konvertiert werden. Aber das jetzt /// umzustellen wird schwer! Die User im Wettkampfplaner sind ja z.B. /// als UTF8 in latin1(?) gespeichert. /// @toDo: Die Standardwerte sollten vielleicht aus einer config /// kommen, nicht hardcoded try { $pdoStatement = self::$db->prepare($aQueryString); foreach ($aBindArray as $bindName => $bind) { if ($bind['data_type'] == PDO::PARAM_STR) { $bind['value'] = iconv( $someOptions['outCharset'], $someOptions['dbCharset'], $bind['value'] ); } $pdoStatement->bindValue( $bindName, $bind['value'], (isset($bind['data_type']) ? $bind['data_type'] : PDO::PARAM_STR) ); } $pdoResult = $pdoStatement->execute(); if (!$pdoResult) { echo("Error during dbQuery!\n"); echo("DB-Error:\n"); var_dump(self::$db->errorInfo()); } if ($someOptions['dontFetch']) { $ret = null; } else { $ret = $pdoStatement->fetchAll(PDO::FETCH_ASSOC); } } catch(PDOException $db_error) { print 'Error!: ' . $db_error->getMessage() . '
'; return null; } // Zeichensatzkonvertierung if (is_array($ret)) { foreach ($ret as &$entry) { array_walk( $entry, function (&$value, $key, $someOptions) { $value = iconv($someOptions['dbCharset'], $someOptions['outCharset'], $value); }, $someOptions ); } } return $ret; } // get a Connection to the database private static function connectToPdo($hostname, $dbName, $user, $password) { $dbConnection = null; try { $dbConnection = new PDO( 'mysql:host=' . $hostname . ';dbname=' . $dbName, $user, $password ); } catch(PDOException $dbError) { echo('Error whilst getting a dbConnection!: ' . $dbError->getMessage()); } return $dbConnection; } // set the dbConnection (just setting, no establishing) private static function setDbConnection($dbConnection) { $success = false; if ($dbConnection instanceof PDO) { self::$db = $dbConnection; $success = true; } else { self::$db = null; } } } /** * User for the Participo system */ class User { private $id; private $loginName; private $name; private $firstName; private $dateOfBirth; private $eMail; public function __construct($id, $loginName, $name, $firstName, $dateOfBirth, $eMail) { $this->id = (int) id; $this->loginName = $loginName; $this->name = $name; $this->firstName = $firstName; $this->dateOfBirth = $dateOfBirth != null ? DateTime::createFromFormat('Y-m-d', $dateOfBirth) : null; $this->eMail = $eMail; } /** * Create a User from an assoziative array like it is returned from db requests * * @param array $member associative array with the UserData from the dbRequest * @return User initialized user */ public static function fromDbArray($member) { return new User( $member['id'] ?? null, $member['loginName'] ?? null, $member['name'] ?? null, $member['vorname'] ?? null, $member['gebDatum'] ?? null, array_key_exist('eMail', $member) ? explode(',', $member['eMail']) : null ); } /** * Export the User data into an associative array */ public function toAssoc() { return [ 'id' => $this->id, 'loginName' => $this->loginName, 'name' => $this->name, 'vorname' => $this->firstName, 'gebDatum' => $this->dateOfBirth, 'eMail' => $this->eMail]; } public function loadFromDb($dbConn, $id) { $this->set( loadUserDataFromDb($dbConn, $id) ); } }