From 9fc9a2a43f3a168a96798c7e65af4c4dae8050f3 Mon Sep 17 00:00:00 2001 From: Roman Hergenreder Date: Sat, 20 Jun 2020 20:13:51 +0200 Subject: [PATCH] new API structure --- core/Api/ApiKey/Create.class.php | 44 --- core/Api/ApiKey/Fetch.class.php | 53 --- core/Api/ApiKey/Refresh.class.php | 66 ---- core/Api/ApiKey/Revoke.class.php | 61 ---- core/Api/ApiKeyAPI.class.php | 191 ++++++++++ core/Api/GetLanguages.class.php | 37 -- core/Api/Groups/Fetch.class.php | 84 ----- core/Api/GroupsAPI.class.php | 94 +++++ core/Api/LanguageAPI.class.php | 128 +++++++ core/Api/Notifications/Create.class.php | 134 ------- core/Api/Notifications/Fetch.class.php | 93 ----- core/Api/NotificationsAPI.class.php | 231 +++++++++++++ core/Api/Routes/Fetch.class.php | 51 --- core/Api/Routes/Find.class.php | 61 ---- core/Api/Routes/Save.class.php | 99 ------ core/Api/RoutesAPI.class.php | 208 +++++++++++ core/Api/SetLanguage.class.php | 83 ----- core/Api/User/Create.class.php | 78 ----- core/Api/User/Fetch.class.php | 95 ----- core/Api/User/Info.class.php | 28 -- core/Api/User/Invite.class.php | 84 ----- core/Api/User/Login.class.php | 81 ----- core/Api/User/Logout.class.php | 25 -- core/Api/User/Register.php | 46 --- core/Api/UserAPI.class.php | 442 ++++++++++++++++++++++++ core/Objects/User.class.php | 3 +- core/Views/LanguageFlags.class.php | 3 +- core/core.php | 9 +- index.php | 42 ++- 29 files changed, 1332 insertions(+), 1322 deletions(-) delete mode 100644 core/Api/ApiKey/Create.class.php delete mode 100644 core/Api/ApiKey/Fetch.class.php delete mode 100644 core/Api/ApiKey/Refresh.class.php delete mode 100644 core/Api/ApiKey/Revoke.class.php create mode 100644 core/Api/ApiKeyAPI.class.php delete mode 100644 core/Api/GetLanguages.class.php delete mode 100644 core/Api/Groups/Fetch.class.php create mode 100644 core/Api/GroupsAPI.class.php create mode 100644 core/Api/LanguageAPI.class.php delete mode 100644 core/Api/Notifications/Create.class.php delete mode 100644 core/Api/Notifications/Fetch.class.php create mode 100644 core/Api/NotificationsAPI.class.php delete mode 100644 core/Api/Routes/Fetch.class.php delete mode 100644 core/Api/Routes/Find.class.php delete mode 100644 core/Api/Routes/Save.class.php create mode 100644 core/Api/RoutesAPI.class.php delete mode 100644 core/Api/SetLanguage.class.php delete mode 100644 core/Api/User/Create.class.php delete mode 100644 core/Api/User/Fetch.class.php delete mode 100644 core/Api/User/Info.class.php delete mode 100644 core/Api/User/Invite.class.php delete mode 100644 core/Api/User/Login.class.php delete mode 100644 core/Api/User/Logout.class.php delete mode 100644 core/Api/User/Register.php create mode 100644 core/Api/UserAPI.class.php diff --git a/core/Api/ApiKey/Create.class.php b/core/Api/ApiKey/Create.class.php deleted file mode 100644 index c5da1dd..0000000 --- a/core/Api/ApiKey/Create.class.php +++ /dev/null @@ -1,44 +0,0 @@ -apiKeyAllowed = false; - $this->csrfTokenRequired = true; - $this->loginRequired = true; - } - - public function execute($values = array()) { - - if(!parent::execute($values)) { - return false; - } - - $apiKey = generateRandomString(64); - $sql = $this->user->getSQL(); - $validUntil = (new \DateTime())->modify("+30 DAY"); - - $this->success = $sql->insert("ApiKey", array("user_id", "api_key", "valid_until")) - ->addRow($this->user->getId(), $apiKey, $validUntil) - ->returning("uid") - ->execute(); - - $this->lastError = $sql->getLastError(); - - if ($this->success) { - $this->result["api_key"] = array( - "api_key" => $apiKey, - "valid_until" => $validUntil->getTimestamp(), - "uid" => $sql->getLastInsertId(), - ); - } else { - $this->result["api_key"] = null; - } - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/ApiKey/Fetch.class.php b/core/Api/ApiKey/Fetch.class.php deleted file mode 100644 index df231f2..0000000 --- a/core/Api/ApiKey/Fetch.class.php +++ /dev/null @@ -1,53 +0,0 @@ -loginRequired = true; - $this->csrfTokenRequired = true; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $sql = $this->user->getSQL(); - $res = $sql->select("uid", "api_key", "valid_until") - ->from("ApiKey") - ->where(new Compare("user_id", $this->user->getId())) - ->where(new Compare("valid_until", $sql->currentTimestamp(), ">")) - ->where(new Compare("active", true)) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if($this->success) { - $this->result["api_keys"] = array(); - foreach($res as $row) { - try { - $validUntil = (new DateTime($row["valid_until"]))->getTimestamp(); - } catch (Exception $e) { - $validUntil = $row["valid_until"]; - } - - $this->result["api_keys"][] = array( - "uid" => intval($row["uid"]), - "api_key" => $row["api_key"], - "valid_until" => $validUntil, - ); - } - } - - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/ApiKey/Refresh.class.php b/core/Api/ApiKey/Refresh.class.php deleted file mode 100644 index 945c930..0000000 --- a/core/Api/ApiKey/Refresh.class.php +++ /dev/null @@ -1,66 +0,0 @@ - new Parameter("id", Parameter::TYPE_INT), - )); - $this->loginRequired = true; - $this->csrfTokenRequired = true; - } - - private function apiKeyExists() { - $id = $this->getParam("id"); - - $sql = $this->user->getSQL(); - $res = $sql->select($sql->count()) - ->from("ApiKey") - ->where(new Compare("uid", $id)) - ->where(new Compare("user_id", $this->user->getId())) - ->where(new Compare("valid_until", $sql->currentTimestamp(), ">")) - ->where(new Compare("active", 1)) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if($this->success && $res[0]["count"] === 0) { - $this->success = false; - $this->lastError = "This API-Key does not exist."; - } - - return $this->success; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $id = $this->getParam("id"); - if(!$this->apiKeyExists()) - return false; - - $validUntil = (new \DateTime)->modify("+30 DAY"); - $sql = $this->user->getSQL(); - $this->success = $sql->update("ApiKey") - ->set("valid_until", $validUntil) - ->where(new Compare("uid", $id)) - ->where(new Compare("user_id", $this->user->getId())) - ->execute(); - $this->lastError = $sql->getLastError(); - - if ($this->success) { - $this->result["valid_until"] = $validUntil->getTimestamp(); - } - - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/ApiKey/Revoke.class.php b/core/Api/ApiKey/Revoke.class.php deleted file mode 100644 index 2cb15bb..0000000 --- a/core/Api/ApiKey/Revoke.class.php +++ /dev/null @@ -1,61 +0,0 @@ - new Parameter("id", Parameter::TYPE_INT), - )); - $this->loginRequired = true; - $this->csrfTokenRequired = true; - } - - private function apiKeyExists() { - $id = $this->getParam("id"); - - $sql = $this->user->getSQL(); - $res = $sql->select($sql->count()) - ->from("ApiKey") - ->where(new Compare("uid", $id)) - ->where(new Compare("user_id", $this->user->getId())) - ->where(new Compare("valid_until", $sql->currentTimestamp(), ">")) - ->where(new Compare("active", 1)) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if($this->success && $res[0]["count"] === 0) { - $this->success = false; - $this->lastError = "This API-Key does not exist."; - } - - return $this->success; - } - - public function execute($aValues = array()) { - if(!parent::execute($aValues)) { - return false; - } - - $id = $this->getParam("id"); - if(!$this->apiKeyExists()) - return false; - - $sql = $this->user->getSQL(); - $this->success = $sql->update("ApiKey") - ->set("active", false) - ->where(new Compare("uid", $id)) - ->where(new Compare("user_id", $this->user->getId())) - ->execute(); - $this->lastError = $sql->getLastError(); - - return $this->success; - } -} diff --git a/core/Api/ApiKeyAPI.class.php b/core/Api/ApiKeyAPI.class.php new file mode 100644 index 0000000..45b3893 --- /dev/null +++ b/core/Api/ApiKeyAPI.class.php @@ -0,0 +1,191 @@ +user->getSQL(); + $res = $sql->select($sql->count()) + ->from("ApiKey") + ->where(new Compare("uid", $id)) + ->where(new Compare("user_id", $this->user->getId())) + ->where(new Compare("valid_until", $sql->currentTimestamp(), ">")) + ->where(new Compare("active", 1)) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if($this->success && $res[0]["count"] === 0) { + return $this->createError("This API-Key does not exist."); + } + + return $this->success; + } + } +} + +namespace Api\ApiKey { + + use Api\ApiKeyAPI; + use Api\Parameter\Parameter; + use DateTime; + use Driver\SQL\Condition\Compare; + use Exception; + + class Create extends ApiKeyAPI { + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array()); + $this->apiKeyAllowed = false; + $this->csrfTokenRequired = true; + $this->loginRequired = true; + } + + public function execute($values = array()) { + + if(!parent::execute($values)) { + return false; + } + + $apiKey = generateRandomString(64); + $sql = $this->user->getSQL(); + $validUntil = (new \DateTime())->modify("+30 DAY"); + + $this->success = $sql->insert("ApiKey", array("user_id", "api_key", "valid_until")) + ->addRow($this->user->getId(), $apiKey, $validUntil) + ->returning("uid") + ->execute(); + + $this->lastError = $sql->getLastError(); + + if ($this->success) { + $this->result["api_key"] = array( + "api_key" => $apiKey, + "valid_until" => $validUntil->getTimestamp(), + "uid" => $sql->getLastInsertId(), + ); + } else { + $this->result["api_key"] = null; + } + return $this->success; + } + } + + class Fetch extends ApiKeyAPI { + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array()); + $this->loginRequired = true; + $this->csrfTokenRequired = true; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $sql = $this->user->getSQL(); + $res = $sql->select("uid", "api_key", "valid_until") + ->from("ApiKey") + ->where(new Compare("user_id", $this->user->getId())) + ->where(new Compare("valid_until", $sql->currentTimestamp(), ">")) + ->where(new Compare("active", true)) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if($this->success) { + $this->result["api_keys"] = array(); + foreach($res as $row) { + try { + $validUntil = (new DateTime($row["valid_until"]))->getTimestamp(); + } catch (Exception $e) { + $validUntil = $row["valid_until"]; + } + + $this->result["api_keys"][] = array( + "uid" => intval($row["uid"]), + "api_key" => $row["api_key"], + "valid_until" => $validUntil, + ); + } + } + + return $this->success; + } + } + + class Refresh extends ApiKeyAPI { + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array( + "id" => new Parameter("id", Parameter::TYPE_INT), + )); + $this->loginRequired = true; + $this->csrfTokenRequired = true; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $id = $this->getParam("id"); + if(!$this->apiKeyExists($id)) + return false; + + $validUntil = (new \DateTime)->modify("+30 DAY"); + $sql = $this->user->getSQL(); + $this->success = $sql->update("ApiKey") + ->set("valid_until", $validUntil) + ->where(new Compare("uid", $id)) + ->where(new Compare("user_id", $this->user->getId())) + ->execute(); + $this->lastError = $sql->getLastError(); + + if ($this->success) { + $this->result["valid_until"] = $validUntil->getTimestamp(); + } + + return $this->success; + } + } + + class Revoke extends ApiKeyAPI { + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array( + "id" => new Parameter("id", Parameter::TYPE_INT), + )); + $this->loginRequired = true; + $this->csrfTokenRequired = true; + } + + public function execute($aValues = array()) { + if(!parent::execute($aValues)) { + return false; + } + + $id = $this->getParam("id"); + if(!$this->apiKeyExists($id)) + return false; + + $sql = $this->user->getSQL(); + $this->success = $sql->update("ApiKey") + ->set("active", false) + ->where(new Compare("uid", $id)) + ->where(new Compare("user_id", $this->user->getId())) + ->execute(); + $this->lastError = $sql->getLastError(); + + return $this->success; + } + } + + +} \ No newline at end of file diff --git a/core/Api/GetLanguages.class.php b/core/Api/GetLanguages.class.php deleted file mode 100644 index d4298c6..0000000 --- a/core/Api/GetLanguages.class.php +++ /dev/null @@ -1,37 +0,0 @@ -user->getSQL(); - $res = $sql->select("uid", "code", "name") - ->from("Language") - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if($this->success) { - $this->result['languages'] = array(); - if(empty($res) === 0) { - $this->lastError = L("No languages found"); - } else { - foreach($res as $row) { - $this->result['languages'][$row['uid']] = $row; - } - } - } - - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/Groups/Fetch.class.php b/core/Api/Groups/Fetch.class.php deleted file mode 100644 index 6b7e41e..0000000 --- a/core/Api/Groups/Fetch.class.php +++ /dev/null @@ -1,84 +0,0 @@ - new Parameter('page', Parameter::TYPE_INT, true, 1) - )); - - $this->loginRequired = true; - $this->requiredGroup = USER_GROUP_ADMIN; - $this->csrfTokenRequired = true; - $this->groupCount = 0; - } - - private function getGroupCount() { - - $sql = $this->user->getSQL(); - $res = $sql->select($sql->count())->from("Group")->execute(); - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if ($this->success) { - $this->groupCount = $res[0]["count"]; - } - - return $this->success; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $page = $this->getParam("page"); - if($page < 1) { - return $this->createError("Invalid page count"); - } - - if (!$this->getGroupCount()) { - return false; - } - - $sql = $this->user->getSQL(); - $res = $sql->select("Group.uid as groupId", "Group.name as groupName", $sql->count("UserGroup.user_id")) - ->from("Group") - ->innerJoin("UserGroup", "UserGroup.group_id", "Group.uid") - ->groupBy("Group.uid") - ->orderBy("Group.uid") - ->ascending() - ->limit(Fetch::SELECT_SIZE) - ->offset(($page - 1) * Fetch::SELECT_SIZE) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if($this->success) { - $this->result["groups"] = array(); - foreach($res as $row) { - $groupId = intval($row["groupId"]); - $groupName = $row["groupName"]; - $memberCount = $row["usergroup_user_id_count"]; - $this->result["groups"][$groupId] = array( - "name" => $groupName, - "memberCount" => $memberCount - ); - } - $this->result["pageCount"] = intval(ceil($this->groupCount / Fetch::SELECT_SIZE)); - $this->result["totalCount"] = $this->groupCount; - } - - return $this->success; - } -} diff --git a/core/Api/GroupsAPI.class.php b/core/Api/GroupsAPI.class.php new file mode 100644 index 0000000..d524243 --- /dev/null +++ b/core/Api/GroupsAPI.class.php @@ -0,0 +1,94 @@ + new Parameter('page', Parameter::TYPE_INT, true, 1) + )); + + $this->loginRequired = true; + $this->requiredGroup = USER_GROUP_ADMIN; + $this->csrfTokenRequired = true; + $this->groupCount = 0; + } + + private function getGroupCount() { + + $sql = $this->user->getSQL(); + $res = $sql->select($sql->count())->from("Group")->execute(); + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if ($this->success) { + $this->groupCount = $res[0]["count"]; + } + + return $this->success; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $page = $this->getParam("page"); + if($page < 1) { + return $this->createError("Invalid page count"); + } + + if (!$this->getGroupCount()) { + return false; + } + + $sql = $this->user->getSQL(); + $res = $sql->select("Group.uid as groupId", "Group.name as groupName", $sql->count("UserGroup.user_id")) + ->from("Group") + ->innerJoin("UserGroup", "UserGroup.group_id", "Group.uid") + ->groupBy("Group.uid") + ->orderBy("Group.uid") + ->ascending() + ->limit(Fetch::SELECT_SIZE) + ->offset(($page - 1) * Fetch::SELECT_SIZE) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if($this->success) { + $this->result["groups"] = array(); + foreach($res as $row) { + $groupId = intval($row["groupId"]); + $groupName = $row["groupName"]; + $memberCount = $row["usergroup_user_id_count"]; + $this->result["groups"][$groupId] = array( + "name" => $groupName, + "memberCount" => $memberCount + ); + } + $this->result["pageCount"] = intval(ceil($this->groupCount / Fetch::SELECT_SIZE)); + $this->result["totalCount"] = $this->groupCount; + } + + return $this->success; + } + } + +} \ No newline at end of file diff --git a/core/Api/LanguageAPI.class.php b/core/Api/LanguageAPI.class.php new file mode 100644 index 0000000..1ddce94 --- /dev/null +++ b/core/Api/LanguageAPI.class.php @@ -0,0 +1,128 @@ +user->getSQL(); + $res = $sql->select("uid", "code", "name") + ->from("Language") + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if($this->success) { + $this->result['languages'] = array(); + if(empty($res) === 0) { + $this->lastError = L("No languages found"); + } else { + foreach($res as $row) { + $this->result['languages'][$row['uid']] = $row; + } + } + } + + return $this->success; + } + } + + class Set extends LanguageAPI { + + private Language $language; + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array( + 'langId' => new Parameter('langId', Parameter::TYPE_INT, true, NULL), + 'langCode' => new StringType('langCode', 5, true, NULL), + )); + $this->csrfTokenRequired = true; + } + + private function checkLanguage() { + $langId = $this->getParam("langId"); + $langCode = $this->getParam("langCode"); + + if(is_null($langId) && is_null($langCode)) { + return $this->createError(L("Either langId or langCode must be given")); + } + + $res = $this->user->getSQL() + ->select("uid", "code", "name") + ->from("Language") + ->where(new CondOr(new Compare("uid", $langId), new Compare("code", $langCode))) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $this->user->getSQL()->getLastError(); + + if ($this->success) { + if(count($res) == 0) { + return $this->createError(L("This Language does not exist")); + } else { + $row = $res[0]; + $this->language = Language::newInstance($row['uid'], $row['code'], $row['name']); + if(!$this->language) { + return $this->createError(L("Error while loading language")); + } + } + } + + return $this->success; + } + + private function updateLanguage() { + $languageId = $this->language->getId(); + $userId = $this->user->getId(); + $sql = $this->user->getSQL(); + + $this->success = $sql->update("User") + ->set("language_id", $languageId) + ->where(new Compare("uid", $userId)) + ->execute(); + $this->lastError = $sql->getLastError(); + return $this->success; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + if(!$this->checkLanguage()) + return false; + + if($this->user->isLoggedIn()) { + $this->updateLanguage(); + } + + $this->user->setLanguage($this->language); + return $this->success; + } + } + +} \ No newline at end of file diff --git a/core/Api/Notifications/Create.class.php b/core/Api/Notifications/Create.class.php deleted file mode 100644 index 3a84338..0000000 --- a/core/Api/Notifications/Create.class.php +++ /dev/null @@ -1,134 +0,0 @@ - new Parameter('groupId', Parameter::TYPE_INT, true), - 'userId' => new Parameter('userId', Parameter::TYPE_INT, true), - 'title' => new StringType('title', 32), - 'message' => new StringType('message', 256), - )); - $this->isPublic = false; - $this->csrfTokenRequired = true; - } - - private function checkUser($userId) { - $sql = $this->user->getSQL(); - $res = $sql->select($sql->count()) - ->from("User") - ->where(new Compare("uid", $userId)) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if ($this->success) { - if ($res[0]["count"] == 0) { - $this->success = false; - $this->lastError = "User not found"; - } - } - - return $this->success; - } - - private function insertUserNotification($userId, $notificationId) { - $sql = $this->user->getSQL(); - $res = $sql->insert("UserNotification", array("user_id", "notification_id")) - ->addRow($userId, $notificationId) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - return $this->success; - } - - private function checkGroup($groupId) { - $sql = $this->user->getSQL(); - $res = $sql->select($sql->count()) - ->from("Group") - ->where(new Compare("uid", $groupId)) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if ($this->success) { - if ($res[0]["count"] == 0) { - $this->success = false; - $this->lastError = "Group not found"; - } - } - - return $this->success; - } - - private function insertGroupNotification($groupId, $notificationId) { - $sql = $this->user->getSQL(); - $res = $sql->insert("GroupNotification", array("group_id", "notification_id")) - ->addRow($groupId, $notificationId) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - return $this->success; - } - - private function createNotification($title, $message) { - $sql = $this->user->getSQL(); - $res = $sql->insert("Notification", array("title", "message")) - ->addRow($title, $message) - ->returning("uid") - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if ($this->success) { - return $sql->getLastInsertId(); - } - - return $this->success; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $userId = $this->getParam("userId"); - $groupId = $this->getParam("groupId"); - $title = $this->getParam("title"); - $message = $this->getParam("message"); - - if (is_null($userId) && is_null($groupId)) { - return $this->createError("Either userId or groupId must be specified."); - } else if(!is_null($userId) && !is_null($groupId)) { - return $this->createError("Only one of userId and groupId must be specified."); - } else if(!is_null($userId)) { - if ($this->checkUser($userId)) { - $id = $this->createNotification($title, $message); - if ($this->success) { - return $this->insertUserNotification($userId, $id); - } - } - } else if(!is_null($groupId)) { - if ($this->checkGroup($groupId)) { - $id = $this->createNotification($title, $message); - if ($this->success) { - return $this->insertGroupNotification($groupId, $id); - } - } - } - - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/Notifications/Fetch.class.php b/core/Api/Notifications/Fetch.class.php deleted file mode 100644 index 78836fe..0000000 --- a/core/Api/Notifications/Fetch.class.php +++ /dev/null @@ -1,93 +0,0 @@ -loginRequired = true; - $this->csrfTokenRequired = true; - } - - private function fetchUserNotifications() { - $userId = $this->user->getId(); - $sql = $this->user->getSQL(); - $res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message") - ->from("Notification") - ->innerJoin("UserNotification", "UserNotification.notification_id", "Notification.uid") - ->where(new Compare("UserNotification.user_id", $userId)) - ->where(new Compare("UserNotification.seen", false)) - ->orderBy("created_at")->descending() - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if ($this->success) { - foreach($res as $row) { - $id = $row["uid"]; - if (!isset($this->notifications[$id])) { - $this->notifications[$id] = array( - "uid" => $id, - "title" => $row["title"], - "message" => $row["message"], - "created_at" => $row["created_at"], - ); - } - } - } - - return $this->success; - } - - private function fetchGroupNotifications() { - $userId = $this->user->getId(); - $sql = $this->user->getSQL(); - $res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message") - ->from("Notification") - ->innerJoin("GroupNotification", "GroupNotification.notification_id", "Notification.uid") - ->innerJoin("UserGroup", "GroupNotification.group_id", "UserGroup.group_id") - ->where(new Compare("UserGroup.user_id", $userId)) - ->where(new Compare("GroupNotification.seen", false)) - ->orderBy("created_at")->descending() - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if ($this->success) { - foreach($res as $row) { - $id = $row["uid"]; - if (!isset($this->notifications[$id])) { - $this->notifications[] = array( - "uid" => $id, - "title" => $row["title"], - "message" => $row["message"], - "created_at" => $row["created_at"], - ); - } - } - } - - return $this->success; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $this->notifications = array(); - if ($this->fetchUserNotifications() && $this->fetchGroupNotifications()) { - $this->result["notifications"] = $this->notifications; - } - - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/NotificationsAPI.class.php b/core/Api/NotificationsAPI.class.php new file mode 100644 index 0000000..909eed5 --- /dev/null +++ b/core/Api/NotificationsAPI.class.php @@ -0,0 +1,231 @@ + new Parameter('groupId', Parameter::TYPE_INT, true), + 'userId' => new Parameter('userId', Parameter::TYPE_INT, true), + 'title' => new StringType('title', 32), + 'message' => new StringType('message', 256), + )); + $this->isPublic = false; + $this->csrfTokenRequired = true; + } + + private function checkUser($userId) { + $sql = $this->user->getSQL(); + $res = $sql->select($sql->count()) + ->from("User") + ->where(new Compare("uid", $userId)) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if ($this->success) { + if ($res[0]["count"] == 0) { + $this->success = false; + $this->lastError = "User not found"; + } + } + + return $this->success; + } + + private function insertUserNotification($userId, $notificationId) { + $sql = $this->user->getSQL(); + $res = $sql->insert("UserNotification", array("user_id", "notification_id")) + ->addRow($userId, $notificationId) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + return $this->success; + } + + private function checkGroup($groupId) { + $sql = $this->user->getSQL(); + $res = $sql->select($sql->count()) + ->from("Group") + ->where(new Compare("uid", $groupId)) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if ($this->success) { + if ($res[0]["count"] == 0) { + $this->success = false; + $this->lastError = "Group not found"; + } + } + + return $this->success; + } + + private function insertGroupNotification($groupId, $notificationId) { + $sql = $this->user->getSQL(); + $res = $sql->insert("GroupNotification", array("group_id", "notification_id")) + ->addRow($groupId, $notificationId) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + return $this->success; + } + + private function createNotification($title, $message) { + $sql = $this->user->getSQL(); + $res = $sql->insert("Notification", array("title", "message")) + ->addRow($title, $message) + ->returning("uid") + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if ($this->success) { + return $sql->getLastInsertId(); + } + + return $this->success; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $userId = $this->getParam("userId"); + $groupId = $this->getParam("groupId"); + $title = $this->getParam("title"); + $message = $this->getParam("message"); + + if (is_null($userId) && is_null($groupId)) { + return $this->createError("Either userId or groupId must be specified."); + } else if(!is_null($userId) && !is_null($groupId)) { + return $this->createError("Only one of userId and groupId must be specified."); + } else if(!is_null($userId)) { + if ($this->checkUser($userId)) { + $id = $this->createNotification($title, $message); + if ($this->success) { + return $this->insertUserNotification($userId, $id); + } + } + } else if(!is_null($groupId)) { + if ($this->checkGroup($groupId)) { + $id = $this->createNotification($title, $message); + if ($this->success) { + return $this->insertGroupNotification($groupId, $id); + } + } + } + + return $this->success; + } + } + + class Fetch extends NotificationsAPI { + + private array $notifications; + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array()); + $this->loginRequired = true; + $this->csrfTokenRequired = true; + } + + private function fetchUserNotifications() { + $userId = $this->user->getId(); + $sql = $this->user->getSQL(); + $res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message") + ->from("Notification") + ->innerJoin("UserNotification", "UserNotification.notification_id", "Notification.uid") + ->where(new Compare("UserNotification.user_id", $userId)) + ->where(new Compare("UserNotification.seen", false)) + ->orderBy("created_at")->descending() + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if ($this->success) { + foreach($res as $row) { + $id = $row["uid"]; + if (!isset($this->notifications[$id])) { + $this->notifications[$id] = array( + "uid" => $id, + "title" => $row["title"], + "message" => $row["message"], + "created_at" => $row["created_at"], + ); + } + } + } + + return $this->success; + } + + private function fetchGroupNotifications() { + $userId = $this->user->getId(); + $sql = $this->user->getSQL(); + $res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message") + ->from("Notification") + ->innerJoin("GroupNotification", "GroupNotification.notification_id", "Notification.uid") + ->innerJoin("UserGroup", "GroupNotification.group_id", "UserGroup.group_id") + ->where(new Compare("UserGroup.user_id", $userId)) + ->where(new Compare("GroupNotification.seen", false)) + ->orderBy("created_at")->descending() + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if ($this->success) { + foreach($res as $row) { + $id = $row["uid"]; + if (!isset($this->notifications[$id])) { + $this->notifications[] = array( + "uid" => $id, + "title" => $row["title"], + "message" => $row["message"], + "created_at" => $row["created_at"], + ); + } + } + } + + return $this->success; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $this->notifications = array(); + if ($this->fetchUserNotifications() && $this->fetchGroupNotifications()) { + $this->result["notifications"] = $this->notifications; + } + + return $this->success; + } + } + +} \ No newline at end of file diff --git a/core/Api/Routes/Fetch.class.php b/core/Api/Routes/Fetch.class.php deleted file mode 100644 index 3462263..0000000 --- a/core/Api/Routes/Fetch.class.php +++ /dev/null @@ -1,51 +0,0 @@ -loginRequired = true; - $this->csrfTokenRequired = true; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $sql = $this->user->getSQL(); - - $res = $sql - ->select("uid", "request", "action", "target", "extra", "active") - ->from("Route") - ->orderBy("uid") - ->ascending() - ->execute(); - - $this->lastError = $sql->getLastError(); - $this->success = ($res !== FALSE); - - if ($this->success) { - $routes = array(); - foreach($res as $row) { - $routes[] = array( - "uid" => intval($row["uid"]), - "request" => $row["request"], - "action" => $row["action"], - "target" => $row["target"], - "extra" => $row["extra"] ?? "", - "active" => intval($row["active"]), - ); - } - - $this->result["routes"] = $routes; - } - - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/Routes/Find.class.php b/core/Api/Routes/Find.class.php deleted file mode 100644 index 249a246..0000000 --- a/core/Api/Routes/Find.class.php +++ /dev/null @@ -1,61 +0,0 @@ - new StringType('request', 128, true, '/') - )); - - $this->isPublic = false; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $request = $this->getParam('request'); - if (!startsWith($request, '/')) { - $request = "/$request"; - } - - $sql = $this->user->getSQL(); - - $res = $sql - ->select("uid", "request", "action", "target", "extra") - ->from("Route") - ->where(new CondBool("active")) - ->where(new Regex($request, new Column("request"))) - ->limit(1) - ->execute(); - - $this->lastError = $sql->getLastError(); - $this->success = ($res !== FALSE); - - if ($this->success) { - if (!empty($res)) { - $row = $res[0]; - $this->result["route"] = array( - "uid" => intval($row["uid"]), - "request" => $row["request"], - "action" => $row["action"], - "target" => $row["target"], - "extra" => $row["extra"] - ); - } else { - $this->result["route"] = NULL; - } - } - - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/Routes/Save.class.php b/core/Api/Routes/Save.class.php deleted file mode 100644 index f32616b..0000000 --- a/core/Api/Routes/Save.class.php +++ /dev/null @@ -1,99 +0,0 @@ - new Parameter('routes',Parameter::TYPE_ARRAY, false) - )); - - $this->loginRequired = true; - $this->csrfTokenRequired = true; - $this->requiredGroup = USER_GROUP_ADMIN; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - if (!$this->validateRoutes()) { - return false; - } - - - $sql = $this->user->getSQL(); - - // DELETE old rules - $this->success = ($sql->truncate("Route")->execute() !== FALSE); - $this->lastError = $sql->getLastError(); - - // INSERT new routes - if ($this->success) { - $stmt = $sql->insert("Route", array("request", "action", "target", "extra", "active")); - foreach($this->routes as $route) { - $stmt->addRow($route["request"], $route["action"], $route["target"], $route["extra"], $route["active"]); - } - $this->success = ($stmt->execute() !== FALSE); - $this->lastError = $sql->getLastError(); - } - - return $this->success; - } - - private function validateRoutes() { - - $this->routes = array(); - $keys = array( - "request" => Parameter::TYPE_STRING, - "action" => Parameter::TYPE_STRING, - "target" => Parameter::TYPE_STRING, - "extra" => Parameter::TYPE_STRING, - "active" => Parameter::TYPE_BOOLEAN - ); - - $actions = array( - "redirect_temporary", "redirect_permanently", "static", "dynamic" - ); - - foreach($this->getParam("routes") as $index => $route) { - foreach($keys as $key => $expectedType) { - if (!array_key_exists($key, $route)) { - return $this->createError("Route $index missing key: $key"); - } - - $value = $route[$key]; - $type = Parameter::parseType($value); - if ($type !== $expectedType && ($key !== "active" || !is_null($value))) { - $expectedTypeName = Parameter::names[$expectedType]; - $gotTypeName = Parameter::names[$type]; - return $this->createError("Route $index has invalid value for key: $key, expected: $expectedTypeName, got: $gotTypeName"); - } - } - - $action = $route["action"]; - if (!in_array($action, $actions)) { - return $this->createError("Invalid action: $action"); - } - - if(empty($route["request"])) { - return $this->createError("Request cannot be empty."); - } - - if(empty($route["target"])) { - return $this->createError("Target cannot be empty."); - } - - $this->routes[] = $route; - } - - return true; - } -} \ No newline at end of file diff --git a/core/Api/RoutesAPI.class.php b/core/Api/RoutesAPI.class.php new file mode 100644 index 0000000..bf00240 --- /dev/null +++ b/core/Api/RoutesAPI.class.php @@ -0,0 +1,208 @@ +loginRequired = true; + $this->csrfTokenRequired = true; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $sql = $this->user->getSQL(); + + $res = $sql + ->select("uid", "request", "action", "target", "extra", "active") + ->from("Route") + ->orderBy("uid") + ->ascending() + ->execute(); + + $this->lastError = $sql->getLastError(); + $this->success = ($res !== FALSE); + + if ($this->success) { + $routes = array(); + foreach($res as $row) { + $routes[] = array( + "uid" => intval($row["uid"]), + "request" => $row["request"], + "action" => $row["action"], + "target" => $row["target"], + "extra" => $row["extra"] ?? "", + "active" => intval($row["active"]), + ); + } + + $this->result["routes"] = $routes; + } + + return $this->success; + } +} + + class Find extends RoutesAPI { + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array( + 'request' => new StringType('request', 128, true, '/') + )); + + $this->isPublic = false; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $request = $this->getParam('request'); + if (!startsWith($request, '/')) { + $request = "/$request"; + } + + $sql = $this->user->getSQL(); + + $res = $sql + ->select("uid", "request", "action", "target", "extra") + ->from("Route") + ->where(new CondBool("active")) + ->where(new Regex($request, new Column("request"))) + ->limit(1) + ->execute(); + + $this->lastError = $sql->getLastError(); + $this->success = ($res !== FALSE); + + if ($this->success) { + if (!empty($res)) { + $row = $res[0]; + $this->result["route"] = array( + "uid" => intval($row["uid"]), + "request" => $row["request"], + "action" => $row["action"], + "target" => $row["target"], + "extra" => $row["extra"] + ); + } else { + $this->result["route"] = NULL; + } + } + + return $this->success; + } + } + + class Save extends RoutesAPI { + + private array $routes; + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array( + 'routes' => new Parameter('routes',Parameter::TYPE_ARRAY, false) + )); + + $this->loginRequired = true; + $this->csrfTokenRequired = true; + $this->requiredGroup = USER_GROUP_ADMIN; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + if (!$this->validateRoutes()) { + return false; + } + + + $sql = $this->user->getSQL(); + + // DELETE old rules + $this->success = ($sql->truncate("Route")->execute() !== FALSE); + $this->lastError = $sql->getLastError(); + + // INSERT new routes + if ($this->success) { + $stmt = $sql->insert("Route", array("request", "action", "target", "extra", "active")); + foreach($this->routes as $route) { + $stmt->addRow($route["request"], $route["action"], $route["target"], $route["extra"], $route["active"]); + } + $this->success = ($stmt->execute() !== FALSE); + $this->lastError = $sql->getLastError(); + } + + return $this->success; + } + + private function validateRoutes() { + + $this->routes = array(); + $keys = array( + "request" => Parameter::TYPE_STRING, + "action" => Parameter::TYPE_STRING, + "target" => Parameter::TYPE_STRING, + "extra" => Parameter::TYPE_STRING, + "active" => Parameter::TYPE_BOOLEAN + ); + + $actions = array( + "redirect_temporary", "redirect_permanently", "static", "dynamic" + ); + + foreach($this->getParam("routes") as $index => $route) { + foreach($keys as $key => $expectedType) { + if (!array_key_exists($key, $route)) { + return $this->createError("Route $index missing key: $key"); + } + + $value = $route[$key]; + $type = Parameter::parseType($value); + if ($type !== $expectedType && ($key !== "active" || !is_null($value))) { + $expectedTypeName = Parameter::names[$expectedType]; + $gotTypeName = Parameter::names[$type]; + return $this->createError("Route $index has invalid value for key: $key, expected: $expectedTypeName, got: $gotTypeName"); + } + } + + $action = $route["action"]; + if (!in_array($action, $actions)) { + return $this->createError("Invalid action: $action"); + } + + if(empty($route["request"])) { + return $this->createError("Request cannot be empty."); + } + + if(empty($route["target"])) { + return $this->createError("Target cannot be empty."); + } + + $this->routes[] = $route; + } + + return true; + } + } + +} + diff --git a/core/Api/SetLanguage.class.php b/core/Api/SetLanguage.class.php deleted file mode 100644 index f956f0c..0000000 --- a/core/Api/SetLanguage.class.php +++ /dev/null @@ -1,83 +0,0 @@ - new Parameter('langId', Parameter::TYPE_INT, true, NULL), - 'langCode' => new StringType('langCode', 5, true, NULL), - )); - $this->csrfTokenRequired = true; - } - - private function checkLanguage() { - $langId = $this->getParam("langId"); - $langCode = $this->getParam("langCode"); - - if(is_null($langId) && is_null($langCode)) { - return $this->createError(L("Either langId or langCode must be given")); - } - - $res = $this->user->getSQL() - ->select("uid", "code", "name") - ->from("Language") - ->where(new CondOr(new Compare("uid", $langId), new Compare("code", $langCode))) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $this->user->getSQL()->getLastError(); - - if ($this->success) { - if(count($res) == 0) { - return $this->createError(L("This Language does not exist")); - } else { - $row = $res[0]; - $this->language = Language::newInstance($row['uid'], $row['code'], $row['name']); - if(!$this->language) { - return $this->createError(L("Error while loading language")); - } - } - } - - return $this->success; - } - - private function updateLanguage() { - $languageId = $this->language->getId(); - $userId = $this->user->getId(); - $sql = $this->user->getSQL(); - - $this->success = $sql->update("User") - ->set("language_id", $languageId) - ->where(new Compare("uid", $userId)) - ->execute(); - $this->lastError = $sql->getLastError(); - return $this->success; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - if(!$this->checkLanguage()) - return false; - - if($this->user->isLoggedIn()) { - $this->updateLanguage(); - } - - $this->user->setLanguage($this->language); - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/User/Create.class.php b/core/Api/User/Create.class.php deleted file mode 100644 index 94175e6..0000000 --- a/core/Api/User/Create.class.php +++ /dev/null @@ -1,78 +0,0 @@ - new StringType('username', 32), - 'email' => new StringType('email', 64, true), - 'password' => new StringType('password'), - 'confirmPassword' => new StringType('confirmPassword'), - )); - $this->csrfTokenRequired = true; - $this->loginRequired = true; - $this->requiredGroup = USER_GROUP_ADMIN; - } - - public function execute($values = array()) { - if (!parent::execute($values)) { - return false; - } - - $username = $this->getParam('username'); - $email = $this->getParam('email'); - if (!$this->userExists($username, $email) || !$this->success) { - return false; - } - - $password = $this->getParam('password'); - $confirmPassword = $this->getParam('confirmPassword'); - if ($password !== $confirmPassword) { - return $this->createError("The given passwords do not match."); - } - - $this->success = $this->createUser($username, $email, $password); - return $this->success; - } - - private function userExists($username, $email) { - $sql = $this->user->getSQL(); - $res = $sql->select("User.name", "User.email") - ->from("User") - ->where(new Compare("User.name", $username), new Compare("User.email", $email)) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if ($this->success && !empty($res)) { - $row = $res[0]; - if (strcasecmp($username, $row['name']) === 0) { - return $this->createError("This username is already taken."); - } else if (strcasecmp($username, $row['email']) === 0) { - return $this->createError("This email address is already in use."); - } - } - - return $this->success; - } - - private function createUser($username, $email, $password) { - $sql = $this->user->getSQL(); - $salt = generateRandomString(16); - $hash = hash('sha256', $password . $salt); - $res = $sql->insert("User", array("name", "password", "salt", "email")) - ->addRow($username, $hash, $salt, $email) - ->execute(); - - $this->lastError = $sql->getLastError(); - $this->success = ($res !== FALSE); - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/User/Fetch.class.php b/core/Api/User/Fetch.class.php deleted file mode 100644 index 6d886f0..0000000 --- a/core/Api/User/Fetch.class.php +++ /dev/null @@ -1,95 +0,0 @@ - new Parameter('page', Parameter::TYPE_INT, true, 1) - )); - - $this->loginRequired = true; - $this->requiredGroup = USER_GROUP_ADMIN; - $this->userCount = 0; - $this->csrfTokenRequired = true; - } - - private function getUserCount() { - - $sql = $this->user->getSQL(); - $res = $sql->select($sql->count())->from("User")->execute(); - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if ($this->success) { - $this->userCount = $res[0]["count"]; - } - - return $this->success; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $page = $this->getParam("page"); - if($page < 1) { - return $this->createError("Invalid page count"); - } - - if (!$this->getUserCount()) { - return false; - } - - $sql = $this->user->getSQL(); - $res = $sql->select("User.uid as userId", "User.name", "User.email", "User.registered_at", - "Group.uid as groupId", "Group.name as groupName") - ->from("User") - ->leftJoin("UserGroup", "User.uid", "UserGroup.user_id") - ->leftJoin("Group", "Group.uid", "UserGroup.group_id") - ->orderBy("User.uid") - ->ascending() - ->limit(Fetch::SELECT_SIZE) - ->offset(($page - 1) * Fetch::SELECT_SIZE) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if($this->success) { - $this->result["users"] = array(); - foreach($res as $row) { - $userId = intval($row["userId"]); - $groupId = intval($row["groupId"]); - $groupName = $row["groupName"]; - if (!isset($this->result["users"][$userId])) { - $this->result["users"][$userId] = array( - "uid" => $userId, - "name" => $row["name"], - "email" => $row["email"], - "registered_at" => $row["registered_at"], - "groups" => array(), - ); - } - - if(!is_null($groupId)) { - $this->result["users"][$userId]["groups"][$groupId] = $groupName; - } - } - $this->result["pageCount"] = intval(ceil($this->userCount / Fetch::SELECT_SIZE)); - $this->result["totalCount"] = $this->userCount; - } - - return $this->success; - } -} diff --git a/core/Api/User/Info.class.php b/core/Api/User/Info.class.php deleted file mode 100644 index 0ea8eba..0000000 --- a/core/Api/User/Info.class.php +++ /dev/null @@ -1,28 +0,0 @@ -csrfTokenRequired = true; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - if (!$this->user->isLoggedIn()) { - $this->result["loggedIn"] = false; - } else { - $this->result["loggedIn"] = true; - } - - $this->result["user"] = $this->user->jsonSerialize(); - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/User/Invite.class.php b/core/Api/User/Invite.class.php deleted file mode 100644 index c65da11..0000000 --- a/core/Api/User/Invite.class.php +++ /dev/null @@ -1,84 +0,0 @@ - new StringType('username', 32), - 'email' => new StringType('email', 64), - )); - $this->csrfTokenRequired = true; - $this->loginRequired = true; - $this->requiredGroup = USER_GROUP_ADMIN; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $username = $this->getParam('username'); - $email = $this->getParam('email'); - if (!$this->userExists($username, $email) || !$this->success) { - return false; - } - - //add to DB - $token = generateRandomString(36); - $valid_until = (new DateTime())->modify("+48 hour"); - $sql = $this->user->getSQL(); - $res = $sql->insert("UserInvitation", array("username", "email", "token", "valid_until")) - ->addRow($username, $email, $token, $valid_until) - ->execute(); - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - //send validation mail - if($this->success) { - $request = new SendMail($this->user); - $link = "http://localhost/acceptInvitation?token=$token"; - $this->success = $request->execute(array( - "from" => "webmaster@romanh.de", - "to" => $email, - "subject" => "Account Invitation for web-base@localhost", - "body" => -"Hello,
-you were invited to create an account on web-base@localhost. Click on the following link to confirm the registration, it is 48h valid from now. -If the invitation was not intended, you can simply ignore this email.

$link" - ) - ); - $this->lastError = $request->getLastError(); - } - return $this->success; - } - - private function userExists($username, $email) { - $sql = $this->user->getSQL(); - $res = $sql->select("User.name", "User.email") - ->from("User") - ->where(new Compare("User.name", $username), new Compare("User.email", $email)) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if ($this->success && !empty($res)) { - $row = $res[0]; - if (strcasecmp($username, $row['name']) === 0) { - return $this->createError("This username is already taken."); - } else if (strcasecmp($username, $row['email']) === 0) { - return $this->createError("This email address is already in use."); - } - } - - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/User/Login.class.php b/core/Api/User/Login.class.php deleted file mode 100644 index 9fb3c8f..0000000 --- a/core/Api/User/Login.class.php +++ /dev/null @@ -1,81 +0,0 @@ - new StringType('username', 32), - 'password' => new StringType('password'), - 'stayLoggedIn' => new Parameter('stayLoggedIn', Parameter::TYPE_BOOLEAN, true, true) - )); - $this->forbidMethod("GET"); - } - - private function wrongCredentials() { - $runtime = microtime(true) - $this->startedAt; - $sleepTime = round(3e6 - $runtime); - if($sleepTime > 0) usleep($sleepTime); - return $this->createError(L('Wrong username or password')); - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - if($this->user->isLoggedIn()) { - $this->lastError = L('You are already logged in'); - $this->success = true; - return true; - } - - $this->startedAt = microtime(true); - $this->success = false; - $username = $this->getParam('username'); - $password = $this->getParam('password'); - $stayLoggedIn = $this->getParam('stayLoggedIn'); - - $sql = $this->user->getSQL(); - $res = $sql->select("User.uid", "User.password", "User.salt") - ->from("User") - ->where(new Compare("User.name", $username)) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); - - if($this->success) { - if(count($res) === 0) { - return $this->wrongCredentials(); - } else { - $row = $res[0]; - $salt = $row['salt']; - $uid = $row['uid']; - $hash = hash('sha256', $password . $salt); - if($hash === $row['password']) { - if(!($this->success = $this->user->createSession($uid, $stayLoggedIn))) { - return $this->createError("Error creating Session: " . $sql->getLastError()); - } else { - $this->result["loggedIn"] = true; - $this->result['logoutIn'] = $this->user->getSession()->getExpiresSeconds(); - $this->success = true; - } - } - else { - return $this->wrongCredentials(); - } - } - } - - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/User/Logout.class.php b/core/Api/User/Logout.class.php deleted file mode 100644 index f7c52d5..0000000 --- a/core/Api/User/Logout.class.php +++ /dev/null @@ -1,25 +0,0 @@ -loginRequired = true; - $this->apiKeyAllowed = false; - $this->csrfTokenRequired = true; - } - - public function execute($values = array()) { - if(!parent::execute($values)) { - return false; - } - - $this->success = $this->user->logout(); - $this->lastError = $this->user->getSQL()->getLastError(); - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/User/Register.php b/core/Api/User/Register.php deleted file mode 100644 index b8cfad2..0000000 --- a/core/Api/User/Register.php +++ /dev/null @@ -1,46 +0,0 @@ -user->isLoggedIn()) { - $this->lastError = L('You are already logged in'); - $this->success = false; - return false; - } - - if (!parent::execute($values)) { - return false; - } - - if($this->success) { - $email = $this->getParam('email'); - $token = generateRandomString(36); - $request = new SendMail($this->user); - $link = "http://localhost/acceptInvitation?token=$token"; - $this->success = $request->execute(array( - "from" => "webmaster@romanh.de", - "to" => $email, - "subject" => "Account Invitation for web-base@localhost", - "body" => - "Hello,
-you were invited to create an account on web-base@localhost. Click on the following link to confirm the registration, it is 48h valid from now. -If the invitation was not intended, you can simply ignore this email.

$link" - ) - ); - $this->lastError = $request->getLastError(); - } - return $this->success; - } -} \ No newline at end of file diff --git a/core/Api/UserAPI.class.php b/core/Api/UserAPI.class.php new file mode 100644 index 0000000..cc51998 --- /dev/null +++ b/core/Api/UserAPI.class.php @@ -0,0 +1,442 @@ +user->getSQL(); + $res = $sql->select("User.name", "User.email") + ->from("User") + ->where(new Compare("User.name", $username), new Compare("User.email", $email)) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if ($this->success && !empty($res)) { + $row = $res[0]; + if (strcasecmp($username, $row['name']) === 0) { + return $this->createError("This username is already taken."); + } else if (strcasecmp($username, $row['email']) === 0) { + return $this->createError("This email address is already in use."); + } + } + + return $this->success; + } + + protected function insertUser($username, $email, $password) { + $sql = $this->user->getSQL(); + $salt = generateRandomString(16); + $hash = $this->hashPassword($password, $salt); + $res = $sql->insert("User", array("name", "password", "salt", "email")) + ->addRow($username, $hash, $salt, $email) + ->returning("uid") + ->execute(); + + $this->lastError = $sql->getLastError(); + $this->success = ($res !== FALSE); + + if ($this->success) { + return $sql->getLastInsertId(); + } + + return $this->success; + } + + // TODO: replace this with crypt() in the future + protected function hashPassword($password, $salt) { + return hash('sha256', $password . $salt); + } +} + +} + +namespace Api\User { + + use Api\Parameter\Parameter; + use Api\Parameter\StringType; + use Api\SendMail; + use Api\UserAPI; + use DateTime; + use Driver\SQL\Condition\Compare; + + class Create extends UserAPI { + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array( + 'username' => new StringType('username', 32), + 'email' => new StringType('email', 64, true), + 'password' => new StringType('password'), + 'confirmPassword' => new StringType('confirmPassword'), + )); + $this->csrfTokenRequired = true; + $this->loginRequired = true; + $this->requiredGroup = USER_GROUP_ADMIN; + } + + public function execute($values = array()) { + if (!parent::execute($values)) { + return false; + } + + $username = $this->getParam('username'); + $email = $this->getParam('email'); + if (!$this->userExists($username, $email) || !$this->success) { + return false; + } + + $password = $this->getParam('password'); + $confirmPassword = $this->getParam('confirmPassword'); + if ($password !== $confirmPassword) { + return $this->createError("The given passwords do not match."); + } + + return $this->insertUser($username, $email, $password) !== FALSE; + } +} + +class Fetch extends UserAPI { + + const SELECT_SIZE = 10; + + private int $userCount; + + public function __construct($user, $externalCall = false) { + + parent::__construct($user, $externalCall, array( + 'page' => new Parameter('page', Parameter::TYPE_INT, true, 1) + )); + + $this->loginRequired = true; + $this->requiredGroup = USER_GROUP_ADMIN; + $this->userCount = 0; + $this->csrfTokenRequired = true; + } + + private function getUserCount() { + + $sql = $this->user->getSQL(); + $res = $sql->select($sql->count())->from("User")->execute(); + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if ($this->success) { + $this->userCount = $res[0]["count"]; + } + + return $this->success; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $page = $this->getParam("page"); + if($page < 1) { + return $this->createError("Invalid page count"); + } + + if (!$this->getUserCount()) { + return false; + } + + $sql = $this->user->getSQL(); + $res = $sql->select("User.uid as userId", "User.name", "User.email", "User.registered_at", + "Group.uid as groupId", "Group.name as groupName") + ->from("User") + ->leftJoin("UserGroup", "User.uid", "UserGroup.user_id") + ->leftJoin("Group", "Group.uid", "UserGroup.group_id") + ->orderBy("User.uid") + ->ascending() + ->limit(Fetch::SELECT_SIZE) + ->offset(($page - 1) * Fetch::SELECT_SIZE) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if($this->success) { + $this->result["users"] = array(); + foreach($res as $row) { + $userId = intval($row["userId"]); + $groupId = intval($row["groupId"]); + $groupName = $row["groupName"]; + if (!isset($this->result["users"][$userId])) { + $this->result["users"][$userId] = array( + "uid" => $userId, + "name" => $row["name"], + "email" => $row["email"], + "registered_at" => $row["registered_at"], + "groups" => array(), + ); + } + + if(!is_null($groupId)) { + $this->result["users"][$userId]["groups"][$groupId] = $groupName; + } + } + $this->result["pageCount"] = intval(ceil($this->userCount / Fetch::SELECT_SIZE)); + $this->result["totalCount"] = $this->userCount; + } + + return $this->success; + } +} + +class Info extends UserAPI { + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array()); + $this->csrfTokenRequired = true; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + if (!$this->user->isLoggedIn()) { + $this->result["loggedIn"] = false; + } else { + $this->result["loggedIn"] = true; + } + + $this->result["user"] = $this->user->jsonSerialize(); + return $this->success; + } +} + +class Invite extends UserAPI { + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array( + 'username' => new StringType('username', 32), + 'email' => new StringType('email', 64), + )); + $this->csrfTokenRequired = true; + $this->loginRequired = true; + $this->requiredGroup = USER_GROUP_ADMIN; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $username = $this->getParam('username'); + $email = $this->getParam('email'); + if (!$this->userExists($username, $email)) { + return false; + } + + //add to DB + $token = generateRandomString(36); + $valid_until = (new DateTime())->modify("+48 hour"); + $sql = $this->user->getSQL(); + $res = $sql->insert("UserInvitation", array("username", "email", "token", "valid_until")) + ->addRow($username, $email, $token, $valid_until) + ->execute(); + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + //send validation mail + if($this->success) { + $request = new SendMail($this->user); + $link = "http://localhost/acceptInvitation?token=$token"; + $this->success = $request->execute(array( + "from" => "webmaster@romanh.de", + "to" => $email, + "subject" => "Account Invitation for web-base@localhost", + "body" => + "Hello,
+you were invited to create an account on web-base@localhost. Click on the following link to confirm the registration, it is 48h valid from now. +If the invitation was not intended, you can simply ignore this email.

$link" + ) + ); + $this->lastError = $request->getLastError(); + } + return $this->success; + } +} + +class Login extends UserAPI { + + private int $startedAt; + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array( + 'username' => new StringType('username', 32), + 'password' => new StringType('password'), + 'stayLoggedIn' => new Parameter('stayLoggedIn', Parameter::TYPE_BOOLEAN, true, true) + )); + $this->forbidMethod("GET"); + } + + private function wrongCredentials() { + $runtime = microtime(true) - $this->startedAt; + $sleepTime = round(3e6 - $runtime); + if($sleepTime > 0) usleep($sleepTime); + return $this->createError(L('Wrong username or password')); + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + if($this->user->isLoggedIn()) { + $this->lastError = L('You are already logged in'); + $this->success = true; + return true; + } + + $this->startedAt = microtime(true); + $this->success = false; + $username = $this->getParam('username'); + $password = $this->getParam('password'); + $stayLoggedIn = $this->getParam('stayLoggedIn'); + + $sql = $this->user->getSQL(); + $res = $sql->select("User.uid", "User.password", "User.salt") + ->from("User") + ->where(new Compare("User.name", $username)) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + if($this->success) { + if(count($res) === 0) { + return $this->wrongCredentials(); + } else { + $row = $res[0]; + $salt = $row['salt']; + $uid = $row['uid']; + $hash = $this->hashPassword($password, $salt); + if($hash === $row['password']) { + if(!($this->success = $this->user->createSession($uid, $stayLoggedIn))) { + return $this->createError("Error creating Session: " . $sql->getLastError()); + } else { + $this->result["loggedIn"] = true; + $this->result['logoutIn'] = $this->user->getSession()->getExpiresSeconds(); + $this->success = true; + } + } + else { + return $this->wrongCredentials(); + } + } + } + + return $this->success; + } +} + +class Logout extends UserAPI { + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall); + $this->loginRequired = true; + $this->apiKeyAllowed = false; + $this->csrfTokenRequired = true; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $this->success = $this->user->logout(); + $this->lastError = $this->user->getSQL()->getLastError(); + return $this->success; + } +} + +class Register extends UserAPI { + + private ?int $userId; + private string $token; + + public function __construct($user, $externalCall = false) { + parent::__construct($user, $externalCall, array( + "username" => new StringType("username", 32), + "email" => new StringType("email", 64), + "password" => new StringType("password"), + "confirmPassword" => new StringType("confirmPassword"), + )); + } + + private function insertToken() { + $validUntil = (new DateTime())->modify("+48 hour"); + $sql = $this->user->getSQL(); + $res = $sql->insert("UserToken", array("user_id", "token", "token_type", "valid_until")) + ->addRow(array($this->userId, $this->token, "confirmation", $validUntil)) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + return $this->success; + } + + public function execute($values = array()) { + if (!parent::execute($values)) { + return false; + } + + if ($this->user->isLoggedIn()) { + $this->lastError = L('You are already logged in'); + $this->success = false; + return false; + } + + $username = $this->getParam("username"); + $email = $this->getParam('email'); + if (!$this->userExists($username, $email)) { + return false; + } + + $password = $this->getParam("password"); + $confirmPassword = $this->getParam("confirmPassword"); + if(strcmp($password, $confirmPassword) !== 0) { + return $this->createError("The given passwords don't match"); + } + + $id = $this->insertUser($username, $email, $password); + if ($id === FALSE) { + return false; + } + + $this->userId = $id; + $this->token = generateRandomString(36); + if ($this->insertToken()) { + return false; + } + + $request = new SendMail($this->user); + $link = "http://localhost/confirmEmail?token=$this->token"; + $this->success = $request->execute(array( + "from" => "webmaster@romanh.de", + "to" => $email, + "subject" => "E-Mail Confirmation for web-base@localhost", + "body" => + "Hello,
+you recently registered an account on web-base@localhost. Click on the following link to confirm the registration, it is 48h valid from now. +If the registration was not intended, you can simply ignore this email.

$link" + ) + ); + + if (!$this->success) { + $this->lastError = "Your account was registered but the confirmation email could not be sent. " . + "Please contact the server administration. Reason: " . $request->getLastError(); + } + + return $this->success; + } +} + +} \ No newline at end of file diff --git a/core/Objects/User.class.php b/core/Objects/User.class.php index 1fb423b..973e948 100644 --- a/core/Objects/User.class.php +++ b/core/Objects/User.class.php @@ -2,7 +2,6 @@ namespace Objects; -use Api\SetLanguage; use Configuration\Configuration; use DateTime; use Driver\SQL\Expression\Add; @@ -108,7 +107,7 @@ class User extends ApiObject { public function updateLanguage($lang) { if($this->sql) { - $request = new SetLanguage($this); + $request = new \Api\Language\Set($this); return $request->execute(array("langCode" => $lang)); } else { return false; diff --git a/core/Views/LanguageFlags.class.php b/core/Views/LanguageFlags.class.php index c4f0283..555857d 100644 --- a/core/Views/LanguageFlags.class.php +++ b/core/Views/LanguageFlags.class.php @@ -2,7 +2,6 @@ namespace Views; -use Api\GetLanguages; use Elements\View; class LanguageFlags extends View { @@ -17,7 +16,7 @@ class LanguageFlags extends View { public function loadView() { parent::loadView(); - $request = new GetLanguages($this->getDocument()->getUser()); + $request = new \Api\Language\Get($this->getDocument()->getUser()); if($request->execute()) { $requestUri = $_SERVER["REQUEST_URI"]; diff --git a/core/core.php b/core/core.php index 7063ae5..26af7e6 100644 --- a/core/core.php +++ b/core/core.php @@ -87,7 +87,14 @@ function getClassPath($class, $suffix=true) { $path = str_replace('\\', '/', $class); - if (startsWith($path, "/")) $path = substr($path, 1); + $path = array_values(array_filter(explode("/", $path))); + + if (strcasecmp($path[0], "api") === 0 && count($path) > 2 && strcasecmp($path[1], "Parameter") !== 0) { + $path = "Api/" . $path[1] . "API"; + } else { + $path = implode("/", $path); + } + $suffix = ($suffix ? ".class" : ""); return "core/$path$suffix.php"; } diff --git a/index.php b/index.php index 9b2cd1b..7ead9e1 100644 --- a/index.php +++ b/index.php @@ -41,21 +41,35 @@ if(isset($_GET["api"]) && is_string($_GET["api"])) { header("400 Bad Request"); $response = createError("Invalid Method"); } else { - $apiFunction = implode("\\", array_map('ucfirst', explode("/", $apiFunction))); - if($apiFunction[0] !== "\\") $apiFunction = "\\$apiFunction"; - $class = "\\Api$apiFunction"; - $file = getClassPath($class); - if(!file_exists($file)) { - header("404 Not Found"); - $response = createError("Not found"); - } else if(!is_subclass_of($class, Request::class)) { - header("400 Bad Request"); - $response = createError("Invalid Method"); + $apiFunction = array_filter(array_map('ucfirst', explode("/", $apiFunction))); + if (count($apiFunction) > 1) { + $parentClass = "\\Api\\" . reset($apiFunction) . "API"; + $apiClass = "\\Api\\" . implode("\\", $apiFunction); } else { - $request = new $class($user, true); - $success = $request->execute(); - $msg = $request->getLastError(); - $response = $request->getJsonResult(); + $apiClass = "\\Api\\" . implode("\\", $apiFunction); + $parentClass = $apiClass; + } + + try { + $file = getClassPath($parentClass); + if(!file_exists($file)) { + header("404 Not Found"); + $response = createError("Not found"); + } else { + $parentClass = new ReflectionClass($parentClass); + $apiClass = new ReflectionClass($apiClass); + if(!$apiClass->isSubclassOf(Request::class) || !$apiClass->isInstantiable()) { + header("400 Bad Request"); + $response = createError("Invalid Method"); + } else { + $request = $apiClass->newInstanceArgs(array($user, true)); + $success = $request->execute(); + $msg = $request->getLastError(); + $response = $request->getJsonResult(); + } + } + } catch (ReflectionException $e) { + $response = createError("Error instantiating class: $e"); } } }