diff --git a/core/Api/UserAPI.class.php b/core/Api/UserAPI.class.php index dc4b86f..d9ec54b 100644 --- a/core/Api/UserAPI.class.php +++ b/core/Api/UserAPI.class.php @@ -6,11 +6,25 @@ namespace Api { abstract class UserAPI extends Request { - protected function userExists($username, $email) { + protected function userExists(?string $username, ?string $email) { + + $conditions = array(); + if (!is_null($username) && !empty($username)) { + $conditions[] = new Compare("User.name", $username); + } + + if (!is_null($email) && !empty($email)) { + $conditions[] = new Compare("User.email", $email); + } + + if (empty($conditions)) { + return true; + } + $sql = $this->user->getSQL(); $res = $sql->select("User.name", "User.email") ->from("User") - ->where(new Compare("User.name", $username), new Compare("User.email", $email)) + ->where(...$conditions) ->execute(); $this->success = ($res !== FALSE); @@ -70,6 +84,22 @@ namespace Api { return array(); } + + protected function getUser($id) { + $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", "Group.color as groupColor") + ->from("User") + ->leftJoin("UserGroup", "User.uid", "UserGroup.user_id") + ->leftJoin("Group", "Group.uid", "UserGroup.group_id") + ->where(new Compare("User.uid", $id)) + ->execute(); + + $this->success = ($res !== FALSE); + $this->lastError = $sql->getLastError(); + + return ($this->success && !empty($res) ? $res : array()); + } } } @@ -82,6 +112,7 @@ namespace Api\User { use Api\UserAPI; use DateTime; use Driver\SQL\Condition\Compare; + use Objects\User; class Create extends UserAPI { @@ -104,7 +135,7 @@ namespace Api\User { $username = $this->getParam('username'); $email = $this->getParam('email'); - if (!$this->userExists($username, $email) || !$this->success) { + if (!$this->userExists($username, $email)) { return false; } @@ -232,33 +263,21 @@ namespace Api\User { } $id = $this->getParam("id"); - - $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", "Group.color as groupColor") - ->from("User") - ->leftJoin("UserGroup", "User.uid", "UserGroup.user_id") - ->leftJoin("Group", "Group.uid", "UserGroup.group_id") - ->where(new Compare("User.uid", $id)) - ->execute(); - - $this->success = ($res !== FALSE); - $this->lastError = $sql->getLastError(); + $user = $this->getUser($id); if ($this->success) { - if (empty($res)) { + if (empty($user)) { return $this->createError("User not found"); } else { - $row = $res[0]; $this->result["user"] = array( - "uid" => $row["userId"], - "name" => $row["name"], - "email" => $row["email"], - "registered_at" => $row["registered_at"], + "uid" => $user[0]["userId"], + "name" => $user[0]["name"], + "email" => $user[0]["email"], + "registered_at" => $user[0]["registered_at"], "groups" => array() ); - foreach($res as $row) { + foreach($user as $row) { $this->result["user"]["groups"][] = array( "uid" => $row["groupId"], "name" => $row["groupName"], @@ -548,5 +567,81 @@ If the registration was not intended, you can simply ignore this email.

< } } + class Edit extends UserAPI { + public function __construct(User $user, bool $externalCall) { + parent::__construct($user, $externalCall, array( + 'id' => new Parameter('id', Parameter::TYPE_INT), + 'username' => new StringType('username', 32, true, NULL), + 'email' => new StringType('email', 64, true, NULL), + 'password' => new StringType('password', -1, true, NULL), + 'groups' => new Parameter('groups', Parameter::TYPE_ARRAY, true, NULL), + )); + + $this->requiredGroup = array(USER_GROUP_ADMIN); + $this->loginRequired = true; + } + + public function execute($values = array()) { + if (!parent::execute($values)) { + return false; + } + + $id = $this->getParam("id"); + $user = $this->getUser($id); + + if ($this->success) { + if (empty($user)) { + return $this->createError("User not found"); + } + + $username = $this->getParam("username"); + $email = $this->getParam("email"); + $password = $this->getParam("password"); + $groups = $this->getParam("groups"); + + if (!is_null($groups)) { + if ($id === $this->user->getId() && !in_array(USER_GROUP_ADMIN, $groups)) { + return $this->createError("Cannot remove Administrator group from own user."); + } + } + + // Check for duplicate username, email + $usernameChanged = !is_null($username) ? strcasecmp($username, $this->user->getUsername()) !== 0 : false; + $emailChanged = !is_null($email) ? strcasecmp($email, $this->user->getEmail()) !== 0 : false; + if($usernameChanged || $emailChanged) { + if (!$this->userExists($usernameChanged ? $username : NULL, $emailChanged ? $email : NULL)) { + return false; + } + } + + $sql = $this->user->getSQL(); + $query = $sql->update("User"); + + if ($usernameChanged) $query->set("name", $username); + if ($emailChanged) $query->set("email", $email); + if (!is_null($password)) $query->set("password", $this->hashPassword($password, $user[0]["salt"])); + + $query->where(new Compare("User.uid", $id)); + $res = $query->execute(); + $this->lastError = $sql->getLastError(); + $this->success = ($res !== FALSE); + + if ($this->success && !is_null($groups)) { + + $deleteQuery = $sql->delete("UserGroup")->where(new Compare("user_id", $id)); + $insertQuery = $sql->insert("UserGroup", array("user_id", "group_id")); + + foreach($groups as $groupId) { + $insertQuery->addRow(array($id, $groupId)); + } + + $this->success = ($deleteQuery->execute() !== FALSE) && ($insertQuery->execute() !== FALSE); + $this->lastError = $sql->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 973e948..a2cff62 100644 --- a/core/Objects/User.class.php +++ b/core/Objects/User.class.php @@ -20,6 +20,7 @@ class User extends ApiObject { private ?Session $session; private int $uid; private string $username; + private string $email; private Language $language; private array $groups; @@ -50,6 +51,7 @@ class User extends ApiObject { public function getId() { return $this->uid; } public function isLoggedIn() { return $this->loggedIn; } public function getUsername() { return $this->username; } + public function getEmail() { return $this->email; } public function getSQL() { return $this->sql; } public function getLanguage() { return $this->language; } public function setLanguage(Language $language) { $this->language = $language; $language->load(); } @@ -77,6 +79,7 @@ class User extends ApiObject { return array( 'uid' => $this->uid, 'name' => $this->username, + 'email' => $this->email, 'groups' => $this->groups, 'language' => $this->language->jsonSerialize(), 'session' => $this->session->jsonSerialize(), @@ -91,6 +94,7 @@ class User extends ApiObject { private function reset() { $this->uid = 0; $this->username = ''; + $this->email = ''; $this->loggedIn = false; $this->session = null; } @@ -125,7 +129,8 @@ class User extends ApiObject { public function readData($userId, $sessionId, $sessionUpdate = true) { - $res = $this->sql->select("User.name", "Language.uid as langId", "Language.code as langCode", "Language.name as langName", + $res = $this->sql->select("User.name", "User.email", + "Language.uid as langId", "Language.code as langCode", "Language.name as langName", "Session.data", "Session.stay_logged_in", "Session.csrf_token", "Group.uid as groupId", "Group.name as groupName") ->from("User") ->innerJoin("Session", "Session.user_id", "User.uid") @@ -146,6 +151,7 @@ class User extends ApiObject { $row = $res[0]; $csrfToken = $row["csrf_token"]; $this->username = $row['name']; + $this->email = $row["email"]; $this->uid = $userId; $this->session = new Session($this, $sessionId, $csrfToken); $this->session->setData(json_decode($row["data"] ?? '{}')); @@ -209,7 +215,8 @@ class User extends ApiObject { if($this->loggedIn) return true; - $res = $this->sql->select("ApiKey.user_id as uid", "User.name as username", "Language.uid as langId", "Language.code as langCode", "Language.name as langName") + $res = $this->sql->select("ApiKey.user_id as uid", "User.name", "User.email", + "Language.uid as langId", "Language.code as langCode", "Language.name as langName") ->from("ApiKey") ->innerJoin("User", "ApiKey.user_id", "User.uid") ->leftJoin("Language", "User.language_id", "Language.uid") @@ -225,7 +232,8 @@ class User extends ApiObject { } else { $row = $res[0]; $this->uid = $row['uid']; - $this->username = $row['username']; + $this->username = $row['name']; + $this->email = $row['email']; if(!is_null($row['langId'])) { $this->setLanguage(Language::newInstance($row['langId'], $row['langCode'], $row['langName']));