* User profile for editing own settings.
* Collection management has the option to update entry rights with the collection ones.
* Entry rights can now be managed. More info about user and rights can be found in documentation.
+ * User management: Honor rights from current logged in user
+ * Group management now available. But no relation check yet.
1.0 - Castle 20210106
* First usable version
-* rights example setup and also check if it matches the menu and actions
- + admin
- + Management
- + User (only its data)
* sort by filter for collection display
* Mass edit of entries
+* User and groupmanagement: Check where a user or group is used!
* Better error handling and display while adding / update and delete
* responsive and breakpoints
-Currently there is only a user management. Groups or default users can not be changed.
+Groups or default users can not be changed.
Default users are:
UPDATE `bib_menu` SET `group` = '2' WHERE `bib_menu`.`id` = 10;
INSERT INTO `bib_menu` (`id`, `text`, `action`, `icon`, `owner`, `group`, `rights`, `position`, `category`) VALUES (NULL, 'Profile', 'profile', 'user', '1', '2', 'rw-rw----', '5', 'manage');
DELETE FROM `bib_menu` WHERE `bib_menu`.`id` = 13;
+INSERT INTO `bib_menu` (`id`, `text`, `action`, `icon`, `owner`, `group`, `rights`, `position`, `category`) VALUES (NULL, 'Groups', 'managegroups', 'users', '1', '1', 'rw-------', '5', 'manage');
+UPDATE `bib_menu` SET `position` = '6' WHERE `bib_menu`.`id` = 16;
$queryOrder = " ORDER BY";
if (!empty($this->_queryOptions['sort'])) {
$queryOrder .= ' t.' . $this->_queryOptions['sort'];
- } else {
+ }
+ else {
$queryOrder .= " t.created";
}
if (!empty($this->_queryOptions['sortDirection'])) {
$queryOrder .= ' ' . $this->_queryOptions['sortDirection'];
- } else {
+ }
+ else {
$queryOrder .= " DESC";
}
}
/**
* Class Possessed
- * User management
- * There is no group management yet. It uses the defined groups
- * from the initial setup. Don't change em, could break something.
+ * User and group management
+ * Some groups are protected and should not be removed.
*
* passwords used here: password_hash("somePassword", PASSWORD_DEFAULT);
*
*/
private $_DB;
+ /**
+ * The user object to query with
+ *
+ * @var Doomguy
+ */
+ private $_User;
+
/**
* Possessed constructor.
- * @param mysqli $db
+ *
+ * @param mysqli $databaseConnectionObject
+ * @param Doomguy $userObj
*/
- public function __construct($db) {
- $this->_DB = $db;
+ public function __construct($databaseConnectionObject, $userObj) {
+ $this->_DB = $databaseConnectionObject;
+ $this->_User = $userObj;
}
/**
public function getGroups() {
$ret = array();
- $queryStr = "SELECT `id`, `name`, `description` FROM `".DB_PREFIX."_group` ORDER BY `name`";
+ $queryStr = "SELECT `id`, `name`, `description`, `created`, `protected`
+ FROM `".DB_PREFIX."_group`
+ WHERE ".$this->_User->getSQLRightsString("delete")."
+ ORDER BY `name`";
if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
try {
$query = $this->_DB->query($queryStr);
$ret = array();
$queryStr = "SELECT `id`, `login`, `name`, `active`, `baseGroupId`, `protected`, `created`
- FROM `".DB_PREFIX."_user`";
+ FROM `".DB_PREFIX."_user`
+ WHERE ".$this->_User->getSQLRightsString("delete")."";
if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
try {
$query = $this->_DB->query($queryStr);
public function createUser($username, $login, $password, $group, $active=false) {
$ret = false;
- if(!empty($login) === true
- && $this->_validNewLogin($login) == true
- && $this->_validUsergroup($group) == true
- &&(!empty($password))
- ) {
+ if($this->_validNewLogin($login) && $this->_validUsergroup($group)) {
if ($active === true) {
$active = "1";
} else {
public function updateUser($id, $username, $login, $password, $group, $active=false, $refreshApiToken=false) {
$ret = false;
- if(!empty($login) === true
- && $this->_validUpdateLogin($login,$id) == true
- && $this->_validUsergroup($group) == true
- && !empty($id)
- ) {
+ if($this->_validUpdateLogin($login,$id) && $this->_validUsergroup($group)) {
if ($active === true) {
$active = "1";
} else {
$queryStr .= ", `apiTokenValidDate` = CURRENT_TIMESTAMP() + INTERVAL 1 DAY";
}
$queryStr .= " WHERE `id` = '".$this->_DB->real_escape_string($id)."'
- AND `protected` = '0'";
+ AND ".$this->_User->getSQLRightsString("delete")."";
if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
try {
$this->_DB->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
$ret = array();
if(Summoner::validate($userId,'digit')) {
- $queryStr = "SELECT `id`, `login`, `name`, `active`, `baseGroupId`, `created`,`apiToken`,`apiTokenValidDate`
+ $queryStr = "SELECT `id`, `login`, `name`, `active`, `baseGroupId`,
+ `created`,`apiToken`,`apiTokenValidDate`, `protected`
FROM `".DB_PREFIX."_user`
- WHERE `protected` = '0'
+ WHERE ".$this->_User->getSQLRightsString("delete")."
AND `id` = '".$this->_DB->real_escape_string($userId)."'";
if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
try {
public function deleteUser($id) {
$ret = false;
- if(!empty($id)) {
+ if(Summoner::validate($id,'digit')) {
try {
$this->_DB->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
$d1 = $this->_DB->query("DELETE FROM `".DB_PREFIX."_user`
WHERE `id` = '".$this->_DB->real_escape_string($id)."'
+ AND ".$this->_User->getSQLRightsString("delete")."
AND `protected` = '0'");
$d2 = $this->_DB->query("DELETE FROM `".DB_PREFIX."_user2group` WHERE `fk_user_id` = '".$this->_DB->real_escape_string($id)."'");
$d3 = $this->_DB->query("DELETE FROM `".DB_PREFIX."_userSession` WHERE `fk_user_id` = '".$this->_DB->real_escape_string($id)."'");
return $ret;
}
+ /**
+ * Create group with given data. Validates duplicates based on name
+ *
+ * @param string $name
+ * @param string $description
+ * @return bool
+ */
+ public function createGroup($name, $description) {
+ $ret = false;
+
+ if($this->_validNewGroup($name)) {
+ $queryStr = "INSERT INTO `".DB_PREFIX."_group` SET
+ `name` = '".$this->_DB->real_escape_string($name)."',
+ `description` = '".$this->_DB->real_escape_string($description)."',
+ `modificationuser` = '".$this->_DB->real_escape_string($this->_User->param('id'))."',
+ `owner` = '".$this->_DB->real_escape_string($this->_User->param('id'))."',
+ `group` = '".ADMIN_GROUP_ID."',
+ `rights` = 'rwxr--r--'";
+ if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
+ try {
+ $this->_DB->query($queryStr);
+ $ret = true;
+ }
+ catch (Exception $e) {
+ error_log("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Update given group identified by id with given name and description
+ * Checks for duplicate
+ *
+ * @param string $id Number
+ * @param string $name
+ * @param string $description
+ * @return bool
+ */
+ public function updateGroup($id, $name, $description) {
+ $ret = false;
+
+ if($this->_validUpdateGroup($name, $id)) {
+ $queryStr = "UPDATE `".DB_PREFIX."_group` SET
+ `name` = '".$this->_DB->real_escape_string($name)."',
+ `description` = '".$this->_DB->real_escape_string($description)."',
+ `modificationuser` = '".$this->_DB->real_escape_string($this->_User->param('id'))."'
+ WHERE `id` = '".$this->_DB->real_escape_string($id)."'
+ AND ".$this->_User->getSQLRightsString("delete")."";
+ if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
+ try {
+ $this->_DB->query($queryStr);
+ $ret = true;
+ }
+ catch (Exception $e) {
+ error_log("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Delete given group identified by id from group table. No relation check yet.
+ *
+ * @param string $id Number
+ * @return bool
+ */
+ public function deleteGroup($id) {
+ $ret = false;
+
+ if(Summoner::validate($id,'digit')) {
+ $queryStr = "DELETE FROM `".DB_PREFIX."_group`
+ WHERE ".$this->_User->getSQLRightsString("delete")."
+ AND `protected` = '0'
+ AND `id` = '".$this->_DB->real_escape_string($id)."'";
+ if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
+ try {
+ $this->_DB->query($queryStr);
+ $ret = true;
+ }
+ catch (Exception $e) {
+ error_log("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Load groupd data from group table fo edit
+ *
+ * @param string $id Number
+ * @return array
+ */
+ public function getEditGroupData($id) {
+ $ret = array();
+
+ if(Summoner::validate($id,'digit')) {
+ $queryStr = "SELECT `id`, `name`, `description`, `created`, `protected`
+ FROM `".DB_PREFIX."_group`
+ WHERE ".$this->_User->getSQLRightsString("delete")."
+ AND `id` = '".$this->_DB->real_escape_string($id)."'";
+ if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
+ try {
+ $query = $this->_DB->query($queryStr);
+ if($query !== false && $query->num_rows > 0) {
+ $ret = $query->fetch_assoc();
+ }
+ }
+ catch (Exception $e) {
+ error_log("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Check if given group name can be used as a new one
+ *
+ * @param string $name
+ * @return bool
+ */
+ private function _validNewGroup($name) {
+ $ret = false;
+
+ if (Summoner::validate($name, 'nospace')) {
+ $queryStr = "SELECT `id` FROM `".DB_PREFIX."_group`
+ WHERE `name` = '".$this->_DB->real_escape_string($name)."'";
+ if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
+ try {
+ $query = $this->_DB->query($queryStr);
+ if ($query !== false && $query->num_rows < 1) {
+ $ret = true;
+ }
+ }
+ catch (Exception $e) {
+ error_log("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Check if given group name can be used as an update to given group id
+ *
+ * @param string $name
+ * @param string $id Number
+ * @return bool
+ */
+ private function _validUpdateGroup($name,$id) {
+ $ret = false;
+
+ if (Summoner::validate($name, 'nospace') && Summoner::validate($id,"digit")) {
+ $queryStr = "SELECT `id` FROM `" . DB_PREFIX . "_group`
+ WHERE `name` = '".$this->_DB->real_escape_string($name)."'
+ AND `id` != '".$this->_DB->real_escape_string($id)."'";
+ if(QUERY_DEBUG) error_log("[QUERY] ".__METHOD__." query: ".var_export($queryStr,true));
+ try {
+ $query = $this->_DB->query($queryStr);
+ if ($query !== false && $query->num_rows < 1) {
+ $ret = true;
+ }
+ }
+ catch (Exception $e) {
+ error_log("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
+ }
+ }
+
+ return $ret;
+ }
+
/**
* Check if given login can be used as a new one
*
*/
private function _validNewLogin($login) {
$ret = false;
+
if (Summoner::validate($login, 'nospace')) {
$queryStr = "SELECT `id` FROM `".DB_PREFIX."_user`
WHERE `login` = '".$this->_DB->real_escape_string($login)."'";
*/
private function _validUpdateLogin($login,$id) {
$ret = false;
- if (Summoner::validate($login, 'nospace')) {
+
+ if (Summoner::validate($login, 'nospace') && Summoner::validate($id,"digit")) {
$queryStr = "SELECT `id` FROM `" . DB_PREFIX . "_user`
WHERE `login` = '".$this->_DB->real_escape_string($login)."'
AND `id` != '".$this->_DB->real_escape_string($id)."'";
/**
* based on self::ifset check also the value
*
- * @param array $array
- * @param string $key
- * @param string $value
+ * @param array $array The array to use
+ * @param string $key The key to check
+ * @param string $value The value to compare
* @return bool
*/
static function ifsetValue($array,$key,$value) {
- if(self::ifset($array,$key)) {
+ if(self::ifset($array,$key) !== false) {
return $array[$key] == $value;
}
return false;
--- /dev/null
+<h3 class="uk-h3">Group management</h3>
+<div class="uk-grid-small uk-grid-row-small uk-grid-row-small" uk-grid>
+ <div class="uk-width-1-2">
+ <h4 class="uk-h4">Add or modify a group</h4>
+ <form class="uk-form-horizontal uk-margin-small" method="post">
+ <div class="uk-margin">
+ <label class="uk-form-label" for="name">Name</label>
+ <div class="uk-form-controls">
+ <input class="uk-input" id="name" type="text" autocomplete="off" name="fdata[name]"
+ value="<?php echo Summoner::ifset($TemplateData['editData'], 'name'); ?>">
+ </div>
+ </div>
+ <div class="uk-margin">
+ <label class="uk-form-label" for="description">Description</label>
+ <div class="uk-form-controls">
+ <input class="uk-input" id="description" type="text" autocomplete="off" name="fdata[description]"
+ value="<?php echo Summoner::ifset($TemplateData['editData'], 'description'); ?>">
+ </div>
+ </div>
+
+ <?php if(Summoner::ifset($TemplateData['editData'], 'name') && Summoner::ifsetValue($TemplateData['editData'], 'protected', '0')) { ?>
+ <div class="uk-margin">
+ <div class="uk-form-label">Delete <span uk-icon="warning"></span></div>
+ <div class="uk-form-controls uk-form-controls-text">
+ <label>
+ <input class="uk-checkbox" type="checkbox" name="fdata[doDelete]" value="1">
+ Warning: Content owned by this group will not be deleted and thus only manageable by admin!<br />
+ Right now don't do this. Only if you are sure there is no usage of this group.
+ </label>
+ </div>
+ </div>
+ <?php } ?>
+ <div class="uk-margin">
+ <button class="uk-button uk-button-primary" type="submit" name="submitForm">
+ Save
+ </button>
+ </div>
+ </form>
+ </div>
+ <div class="uk-width-1-2">
+ <h4 class="uk-h4">Available groups</h4>
+ <table class="uk-table">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Description</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach($TemplateData['existingGroups'] as $k=>$v) { ?>
+ <tr>
+ <td>
+ <?php echo $v['name']; ?><br/>
+ <small><?php echo $v['created']; ?></small>
+ </td>
+ <td><?php echo $v['description']; ?></td>
+ <td>
+ <a href="index.php?p=managegroups&m=edit&id=<?php echo $k; ?>" uk-icon="pencil"></a>
+ </td>
+ </tr>
+ <?php } ?>
+ </tbody>
+ </table>
+
+
+ </div>
+</div>
--- /dev/null
+<?php
+/**
+ * Bibliotheca
+ *
+ * Copyright 2018-2021 Johannes Keßler
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+require_once 'lib/possessed.class.php';
+$Possessed = new Possessed($DB, $Doomguy);
+$TemplateData['existingGroups'] = $Possessed->getGroups();
+$TemplateData['editData'] = false;
+
+$_id = false;
+if(isset($_GET['id']) && !empty($_GET['id'])) {
+ $_id = trim($_GET['id']);
+ $_id = Summoner::validate($_id,'digit') ? $_id : false;
+}
+
+if(!empty($_id)) {
+ $TemplateData['editData'] = $Possessed->getEditGroupData($_id);
+ if(!isset($TemplateData['editData']['name'])) {
+ $TemplateData['refresh'] = 'index.php?p=managegroups';
+ }
+}
+
+if(isset($_POST['submitForm'])) {
+ $fdata = $_POST['fdata'];
+ if(!empty($fdata)) {
+ $_name = trim($fdata['name']);
+ $_description = trim($fdata['description']);
+
+ if(!empty($TemplateData['editData'])) {
+ if(isset($fdata['doDelete'])) {
+ $do = $Possessed->deleteGroup($_id);
+ if ($do === true) {
+ $TemplateData['refresh'] = 'index.php?p=managegroups';
+ }
+ else {
+ $TemplateData['message']['content'] = "Group could not be deleted.";
+ $TemplateData['message']['status'] = "error";
+ }
+ }
+ elseif (Summoner::validate($_name,'nospace') && Summoner::validate($_description)) {
+ $do = $Possessed->updateGroup($_id, $_name, $_description);
+ if ($do === true) {
+ $TemplateData['refresh'] = 'index.php?p=managegroups';
+ }
+ else {
+ $TemplateData['message']['content'] = "Group could not be updated. Either wrong input or duplicate group name";
+ $TemplateData['message']['status'] = "error";
+ }
+ }
+ else {
+ $TemplateData['message']['content'] = "Provide name and description.";
+ $TemplateData['message']['status'] = "error";
+ }
+ }
+ else { // adding mode
+ if (Summoner::validate($_name,'nospace') && Summoner::validate($_description)) {
+ $do = $Possessed->createGroup($_name, $_description);
+ if ($do === true) {
+ $TemplateData['refresh'] = 'index.php?p=managegroups';
+ }
+ else {
+ $TemplateData['message']['content'] = "Group could not be created.";
+ $TemplateData['message']['status'] = "error";
+ }
+ }
+ else {
+ $TemplateData['message']['content'] = "Provide name and description.";
+ $TemplateData['message']['status'] = "error";
+ }
+ }
+
+ }
+}
</label>
</div>
</div>
- <?php if(Summoner::ifset($TemplateData['editData'], 'name')) { ?>
+ <?php if(Summoner::ifset($TemplateData['editData'], 'name') && Summoner::ifsetValue($TemplateData['editData'], 'protected', '0')) { ?>
<div class="uk-margin">
<div class="uk-form-label">Delete <span uk-icon="warning"></span></div>
<div class="uk-form-controls uk-form-controls-text">
</td>
<td><?php echo $v['active']; ?></td>
<td>
- <?php if($v['protected'] == "0") { ?>
- <a href="index.php?p=manageusers&m=edit&id=<?php echo $k; ?>" uk-icon="pencil"></a>
- <?php } ?>
+ <a href="index.php?p=manageusers&id=<?php echo $k; ?>" uk-icon="pencil"></a>
</td>
</tr>
<?php } ?>
* limitations under the License.
*/
require_once 'lib/possessed.class.php';
-$Possessed = new Possessed($DB);
+$Possessed = new Possessed($DB, $Doomguy);
$TemplateData['existingGroups'] = $Possessed->getGroups();
$TemplateData['existingUsers'] = $Possessed->getUsers();
$TemplateData['editData'] = false;
-$_editMode = false;
-if(isset($_GET['m']) && !empty($_GET['m'])) {
- if($_GET['m'] == "edit") {
- $_editMode = true;
- }
-}
-
$_id = false;
if(isset($_GET['id']) && !empty($_GET['id'])) {
$_id = trim($_GET['id']);
$_id = Summoner::validate($_id,'digit') ? $_id : false;
}
-if($_editMode === true && !empty($_id)) {
+if(!empty($_id)) {
$TemplateData['editData'] = $Possessed->getEditData($_id);
if(!isset($TemplateData['editData']['name'])) {
$TemplateData['refresh'] = 'index.php?p=manageusers';
$do = $Possessed->deleteUser($_id);
if ($do === true) {
$TemplateData['refresh'] = 'index.php?p=manageusers';
- } else {
+ }
+ else {
$TemplateData['message']['content'] = "User could not be deleted.";
$TemplateData['message']['status'] = "error";
}
}
elseif (!empty($_username) && !empty($_group) && !empty($_login)) {
- if (Summoner::validate($_username, 'text') === true
+ if (Summoner::validate($_username) === true
&& Summoner::validate($_login, 'nospace') === true
&& isset($TemplateData['existingGroups'][$_group])
) {
$do = $Possessed->updateUser($_id, $_username, $_login, $_password, $_group, $_active, $refreshApi);
if ($do === true) {
$TemplateData['refresh'] = 'index.php?p=manageusers';
- } else {
- $TemplateData['message']['content'] = "User could not be updated.";
+ }
+ else {
+ $TemplateData['message']['content'] = "User could not be updated. Either wrong input or duplicate user name";
$TemplateData['message']['status'] = "error";
}
- } else {
+ }
+ else {
$TemplateData['message']['content'] = "Provide username, login and a valid user group.";
$TemplateData['message']['status'] = "error";
}
$do = $Possessed->createUser($_username, $_login, $_password, $_group, $_active);
if ($do === true) {
$TemplateData['refresh'] = 'index.php?p=manageusers';
- } else {
+ }
+ else {
$TemplateData['message']['content'] = "User could not be created.";
$TemplateData['message']['status'] = "error";
}
- } else {
+ }
+ else {
$TemplateData['message']['content'] = "Provide username, login, password and a valid user group.";
$TemplateData['message']['status'] = "error";
}
* limitations under the License.
*/
require_once 'lib/possessed.class.php';
-$Possessed = new Possessed($DB);
+$Possessed = new Possessed($DB, $Doomguy);
$TemplateData['editData'] = $Doomguy->getAllUserData();