Added access rights for more detailed user restrictions

This commit is contained in:
markseu 2019-12-17 11:46:09 +01:00
parent 45e32c5ed4
commit d577145cd5
3 changed files with 85 additions and 88 deletions

View file

@ -256,7 +256,7 @@ yellow.edit = {
elementDiv.innerHTML =
"<form method=\"post\">"+
"<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
"<div class=\"yellow-title\"><h1 id=\"yellow-pane-update-title\">"+yellow.toolbox.encodeHtml(yellow.system.serverVersion)+"</h1></div>"+
"<div class=\"yellow-title\"><h1 id=\"yellow-pane-update-title\">"+yellow.toolbox.encodeHtml(yellow.system.coreVersion)+"</h1></div>"+
"<div class=\"yellow-status\"><p id=\"yellow-pane-update-status\" class=\""+paneStatus+"\">"+this.getText("UpdateStatus", "", paneStatus)+"</p></div>"+
"<div class=\"yellow-output\" id=\"yellow-pane-update-output\">"+yellow.page.rawDataOutput+"</div>"+
"<div class=\"yellow-buttons\" id=\"yellow-pane-update-buttons\">"+
@ -376,12 +376,12 @@ yellow.edit = {
}
break;
case "yellow-pane-update":
if (paneStatus=="none" && this.isUserAdministrator()) {
if (paneStatus=="none") {
document.getElementById("yellow-pane-update-status").innerHTML = this.getText("UpdateStatusCheck");
document.getElementById("yellow-pane-update-output").innerHTML = "";
setTimeout("yellow.action('submit', '', 'action:update/option:check/');", 500);
}
if (paneStatus=="updates" && this.isUserAdministrator()) {
if (paneStatus=="updates") {
document.getElementById("yellow-pane-update-status").innerHTML = "<a href=\"#\" data-action=\"submit\" data-args=\"action:update\">"+this.getText("UpdateStatusUpdates")+"</a>";
}
break;
@ -401,7 +401,7 @@ yellow.edit = {
yellow.toolbox.setVisible(document.getElementById(paneId+"-toolbar-title"), false);
this.updateToolbar(0, "yellow-toolbar-checked");
}
if (yellow.system.userRestriction || (yellow.page.rawDataReadonly && paneId!="yellow-pane-create")) {
if (!this.isUserAccess(paneAction, yellow.page.location) || (yellow.page.rawDataReadonly && paneId!="yellow-pane-create")) {
yellow.toolbox.setVisible(document.getElementById(paneId+"-submit"), false);
document.getElementById(paneId+"-text").readOnly = true;
}
@ -813,7 +813,7 @@ yellow.edit = {
uploadFile: function(elementText, file) {
var extension = (file.name.lastIndexOf(".")!=-1 ? file.name.substring(file.name.lastIndexOf("."), file.name.length) : "").toLowerCase();
var extensions = yellow.system.editUploadExtensions.split(/\s*,\s*/);
if (file.size<=yellow.system.serverFileSizeMax && extensions.indexOf(extension)!=-1) {
if (file.size<=yellow.system.coreFileSizeMax && extensions.indexOf(extension)!=-1) {
var text = this.getText("UploadProgress")+"\u200b";
yellow.editor.setMarkdown(elementText, text, "insert");
var thisObject = this;
@ -834,8 +834,8 @@ yellow.edit = {
if (result) {
var textOld = this.getText("UploadProgress")+"\u200b";
var textNew;
if (result.location.substring(0, yellow.system.imageLocation.length)==yellow.system.imageLocation) {
textNew = "[image "+result.location.substring(yellow.system.imageLocation.length)+"]";
if (result.location.substring(0, yellow.system.coreImageLocation.length)==yellow.system.coreImageLocation) {
textNew = "[image "+result.location.substring(yellow.system.coreImageLocation.length)+"]";
} else {
textNew = "[link]("+result.location+")";
}
@ -889,10 +889,10 @@ yellow.edit = {
// Return raw data for languages
getRawDataLanguages: function(paneId) {
var rawDataLanguages = "";
if (yellow.system.serverLanguages && Object.keys(yellow.system.serverLanguages).length>1) {
for (var language in yellow.system.serverLanguages) {
if (yellow.system.coreLanguages && Object.keys(yellow.system.coreLanguages).length>1) {
for (var language in yellow.system.coreLanguages) {
var checked = language==this.getRequest("language") ? " checked=\"checked\"" : "";
rawDataLanguages += "<label for=\""+paneId+"-"+language+"\"><input type=\"radio\" name=\"language\" id=\""+paneId+"-"+language+"\" value=\""+language+"\""+checked+"> "+yellow.toolbox.encodeHtml(yellow.system.serverLanguages[language])+"</label><br />";
rawDataLanguages += "<label for=\""+paneId+"-"+language+"\"><input type=\"radio\" name=\"language\" id=\""+paneId+"-"+language+"\" value=\""+language+"\""+checked+"> "+yellow.toolbox.encodeHtml(yellow.system.coreLanguages[language])+"</label><br />";
}
}
return rawDataLanguages
@ -959,9 +959,10 @@ yellow.edit = {
return yellow.toolbox.getCookie(name);
},
// Check if user is administrator
isUserAdministrator: function() {
return yellow.system.userGroup=="administrator";
// Check if user with access
isUserAccess: function(action, location) {
var tokens = yellow.system.userAccess.split(/\s*,\s*/);
return tokens.indexOf(action)!=-1 && (!location || location.substring(0, yellow.system.userHome.length)==yellow.system.userHome);
},
// Check if element is expandable
@ -971,7 +972,7 @@ yellow.edit = {
// Check if extension exists
isExtension: function(name) {
return name in yellow.system.serverExtensions;
return name in yellow.system.coreExtensions;
}
};

View file

@ -4,7 +4,7 @@
// This file may be used and distributed under the terms of the public license.
class YellowEdit {
const VERSION = "0.8.13";
const VERSION = "0.8.14";
const TYPE = "feature";
public $yellow; //access to API
public $response; //web response
@ -28,8 +28,8 @@ class YellowEdit {
$this->yellow->system->setDefault("editUserPasswordMinLength", "8");
$this->yellow->system->setDefault("editUserHashAlgorithm", "bcrypt");
$this->yellow->system->setDefault("editUserHashCost", "10");
$this->yellow->system->setDefault("editUserGroup", "user");
$this->yellow->system->setDefault("editUserHome", "/");
$this->yellow->system->setDefault("editUserAccess", "create, edit, delete, upload");
$this->yellow->system->setDefault("editLoginSessionTimeout", "2592000");
$this->yellow->system->setDefault("editLoginRestriction", "0");
$this->yellow->system->setDefault("editBruteForceProtection", "25");
@ -102,16 +102,12 @@ class YellowEdit {
$fileData = $this->yellow->toolbox->readFile($fileNameUser);
foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) {
preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches);
if (!empty($matches[1]) && !empty($matches[2]) && $matches[1][0]!="#" && preg_match("/@/", $matches[1])) {
if (lcfirst($matches[1])=="group") {
$fileDataNew .= "Access: create, edit, delete, upload".($matches[2]=="administrator" ? ", system, update" : "")."\n";
} elseif (!empty($matches[1]) && !empty($matches[2]) && $matches[1][0]!="#" && preg_match("/@/", $matches[1])) {
list($hash, $name, $language, $status, $pending, $stamp, $modified, $failed, $group, $home) = explode(",", $matches[2]);
if (!is_numeric($failed)) {
list($hash, $name, $language, $status, $stamp, $modified, $failed, $pending, $group, $home) = explode(",", $matches[2]);
}
if (empty($home)) {
$home = $group;
$group = $matches[1]==$this->yellow->system->get("email") ? "administrator" : "user";
}
$fileDataNew .= "Email: $matches[1]\nName: $name\nLanguage: $language\nGroup: $group\nHome: $home\nStatus: $status\nPending: $pending\nHash: $hash\nStamp: $stamp\nFailed: $failed\nModified: $modified\n\n";
$access = "create, edit, delete, upload".($group=="administrator" ? ", system, update" : "");
$fileDataNew .= "Email: $matches[1]\nName: $name\nLanguage: $language\nHome: $home\nAccess: $access\nStatus: $status\nPending: $pending\nHash: $hash\nStamp: $stamp\nFailed: $failed\nModified: $modified\n\n";
} else {
$fileDataNew .= $line;
}
@ -183,8 +179,8 @@ class YellowEdit {
$settings = array(
"name" => $name,
"language" => $this->yellow->system->get("language"),
"group" => $this->yellow->system->get("editUserGroup"),
"home" => $this->yellow->system->get("editUserHome"),
"access" => $this->yellow->system->get("editUserAccess"),
"status" => "active",
"pending" => "none",
"hash" => $this->users->createHash($password),
@ -306,7 +302,7 @@ class YellowEdit {
$location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location);
$statusCode = $this->yellow->sendStatus(301, $location);
} else {
$this->yellow->page->error($this->response->isUserRestriction() ? 404 : 434);
$this->yellow->page->error($this->response->isUserAccess("edit", $location) ? 434 : 404);
$statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false);
}
}
@ -363,8 +359,8 @@ class YellowEdit {
$settings = array(
"name" => $name,
"language" => $this->yellow->lookup->findLanguageFromFile($fileName, $this->yellow->system->get("language")),
"group" => $this->yellow->system->get("editUserGroup"),
"home" => $this->yellow->system->get("editUserHome"),
"access" => $this->yellow->system->get("editUserAccess"),
"status" => "unconfirmed",
"pending" => "none",
"hash" => $this->users->createHash($password),
@ -607,8 +603,8 @@ class YellowEdit {
$settings = array(
"name" => $name,
"language" => $language,
"group" => $this->users->getUser($emailSource, "group"),
"home" => $this->users->getUser($emailSource, "home"),
"access" => $this->users->getUser($emailSource, "access"),
"status" => "unverified",
"pending" => $emailSource,
"hash" => $this->users->createHash("none"),
@ -654,7 +650,7 @@ class YellowEdit {
// Process request to change system settings
public function processRequestSystem($scheme, $address, $base, $location, $fileName) {
$statusCode = 0;
if ($this->response->isUserAdministrator()) {
if ($this->response->isUserAccess("system")) {
$this->response->action = "system";
$this->response->status = "ok";
$sitename = trim($_REQUEST["sitename"]);
@ -666,7 +662,8 @@ class YellowEdit {
if ($this->response->status=="ok") {
$fileName = $this->yellow->system->get("coreSettingDir").$this->yellow->system->get("coreSystemFile");
$settings = array("sitename" => $sitename, "author" => $author, "email" => $email);
$this->response->status = $this->yellow->system->save($fileName, $settings) ? "done" : "error";
$file = $this->response->getFileSystem($scheme, $address, $base, $location, $fileName, $settings);
$this->response->status = (!$file->isError() && $this->yellow->system->save($fileName, $settings)) ? "done" : "error";
if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileName'!");
}
if ($this->response->status=="done") {
@ -682,7 +679,7 @@ class YellowEdit {
// Process request to update website
public function processRequestUpdate($scheme, $address, $base, $location, $fileName) {
$statusCode = 0;
if ($this->response->isUserAdministrator()) {
if ($this->response->isUserAccess("update")) {
$this->response->action = "update";
$this->response->status = "ok";
$extension = trim($_REQUEST["extension"]);
@ -711,7 +708,7 @@ class YellowEdit {
// Process request to create page
public function processRequestCreate($scheme, $address, $base, $location, $fileName) {
$statusCode = 0;
if (!$this->response->isUserRestriction() && !empty($_REQUEST["rawdataedit"])) {
if ($this->response->isUserAccess("create", $location) && !empty($_REQUEST["rawdataedit"])) {
$this->response->rawDataSource = $_REQUEST["rawdatasource"];
$this->response->rawDataEdit = $_REQUEST["rawdatasource"];
$this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"];
@ -737,7 +734,7 @@ class YellowEdit {
// Process request to edit page
public function processRequestEdit($scheme, $address, $base, $location, $fileName) {
$statusCode = 0;
if (!$this->response->isUserRestriction() && !empty($_REQUEST["rawdataedit"])) {
if ($this->response->isUserAccess("edit", $location) && !empty($_REQUEST["rawdataedit"])) {
$this->response->rawDataSource = $_REQUEST["rawdatasource"];
$this->response->rawDataEdit = $_REQUEST["rawdataedit"];
$this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"];
@ -775,7 +772,7 @@ class YellowEdit {
// Process request to delete page
public function processRequestDelete($scheme, $address, $base, $location, $fileName) {
$statusCode = 0;
if (!$this->response->isUserRestriction() && is_file($fileName)) {
if ($this->response->isUserAccess("delete", $location) && is_file($fileName)) {
$this->response->rawDataSource = $_REQUEST["rawdatasource"];
$this->response->rawDataEdit = $_REQUEST["rawdatasource"];
$this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"];
@ -828,8 +825,8 @@ class YellowEdit {
$fileSizeMax = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize"));
$extension = strtoloweru(($pos = strrposu($fileNameShort, ".")) ? substru($fileNameShort, $pos) : "");
$extensions = preg_split("/\s*,\s*/", $this->yellow->system->get("editUploadExtensions"));
if (!$this->response->isUserRestriction() && is_uploaded_file($fileNameTemp) &&
filesize($fileNameTemp)<=$fileSizeMax && in_array($extension, $extensions)) {
if ($this->response->isUserAccess("upload", $location) && is_uploaded_file($fileNameTemp) &&
filesize($fileNameTemp)<=$fileSizeMax && in_array($extension, $extensions)) {
$file = $this->response->getFileUpload($scheme, $address, $base, $location, $fileNameTemp, $fileNameShort);
if (!$file->isError() && $this->yellow->toolbox->copyFile($fileNameTemp, $file->fileName, true)) {
$data["location"] = $file->getLocation();
@ -859,7 +856,6 @@ class YellowEdit {
if ($this->users->checkAuthLogin($email, $password)) {
$this->response->createCookies($scheme, $address, $base, $email);
$this->response->userEmail = $email;
$this->response->userRestriction = $this->getUserRestriction($email, $location, $fileName);
$this->response->language = $this->getUserLanguage($email);
} else {
$this->response->userFailedError = "login";
@ -869,7 +865,6 @@ class YellowEdit {
} elseif (isset($_COOKIE["authtoken"]) && isset($_COOKIE["csrftoken"])) {
if ($this->users->checkAuthToken($_COOKIE["authtoken"], $_COOKIE["csrftoken"], $_POST["csrftoken"], $_REQUEST["action"]=="")) {
$this->response->userEmail = $email = $this->users->getAuthEmail($_COOKIE["authtoken"]);
$this->response->userRestriction = $this->getUserRestriction($email, $location, $fileName);
$this->response->language = $this->getUserLanguage($email);
} else {
$this->response->userFailedError = "auth";
@ -964,22 +959,6 @@ class YellowEdit {
return $status;
}
// Return user restriction
public function getUserRestriction($email, $location, $fileName) {
$userRestriction = null;
foreach ($this->yellow->extensions->extensions as $key=>$value) {
if (method_exists($value["obj"], "onEditUserRestriction")) {
$userRestriction = $value["obj"]->onEditUserRestriction($email, $location, $fileName, $this->users);
if (!is_null($userRestriction)) break;
}
}
if (is_null($userRestriction)) {
$userRestriction = substru($location, 0, strlenu($this->users->getUser($email, "home")))!=$this->users->getUser($email, "home");
$userRestriction |= empty($fileName) || strlenu(dirname($fileName))>128 || strlenu(basename($fileName))>128;
}
return $userRestriction;
}
// Return user language
public function getUserLanguage($email) {
$language = $this->users->getUser($email, "language");
@ -1006,7 +985,6 @@ class YellowEditResponse {
public $extension; //access to extension
public $active; //location is active? (boolean)
public $userEmail; //user email
public $userRestriction; //user with restriction? (boolean)
public $userFailedError; //error of failed authentication
public $userFailedEmail; //email of failed authentication
public $userFailedExpire; //expiration time of failed authentication
@ -1064,7 +1042,7 @@ class YellowEditResponse {
} else {
$page->fileName = $this->getPageNewFile($page->location);
}
if ($this->extension->getUserRestriction($this->userEmail, $page->location, $page->fileName)) {
if (!$this->isUserAccess("create", $page->location)) {
$page->error(500, "Page '".$page->get("title")."' is restricted!");
}
return $page;
@ -1091,8 +1069,8 @@ class YellowEditResponse {
}
}
if (empty($page->rawData)) $page->error(500, "Page has been modified by someone else!");
if ($this->extension->getUserRestriction($this->userEmail, $page->location, $page->fileName) ||
$this->extension->getUserRestriction($this->userEmail, $pageSource->location, $pageSource->fileName)) {
if (!$this->isUserAccess("edit", $page->location) ||
!$this->isUserAccess("edit", $pageSource->location)) {
$page->error(500, "Page '".$page->get("title")."' is restricted!");
}
return $page;
@ -1104,7 +1082,7 @@ class YellowEditResponse {
$page->setRequestInformation($scheme, $address, $base, $location, $fileName);
$page->parseData($this->normaliseLines($rawData, $endOfLine), false, 0);
$this->editContentFile($page, "delete");
if ($this->extension->getUserRestriction($this->userEmail, $page->location, $page->fileName)) {
if (!$this->isUserAccess("delete", $page->location)) {
$page->error(500, "Page '".$page->get("title")."' is restricted!");
}
return $page;
@ -1144,6 +1122,16 @@ class YellowEditResponse {
return $file;
}
// Return system file
public function getFileSystem($scheme, $address, $base, $pageLocation, $fileName, $settings) {
$file = new YellowPage($this->yellow);
$file->setRequestInformation($scheme, $address, $base, "/".$fileName, $fileName);
$file->parseData(null, false, 0);
foreach ($settings as $key=>$value) $file->set($key, $value);
$this->editSystemFile($file, "system");
return $file;
}
// Return page data including status information
public function getPageData($page) {
$data = array();
@ -1176,26 +1164,20 @@ class YellowEditResponse {
$data["userName"] = $this->extension->users->getUser($this->userEmail, "name");
$data["userLanguage"] = $this->extension->users->getUser($this->userEmail, "language");
$data["userStatus"] = $this->extension->users->getUser($this->userEmail, "status");
$data["userGroup"] = $this->extension->users->getUser($this->userEmail, "group");
$data["userHome"] = $this->extension->users->getUser($this->userEmail, "home");
$data["userRestriction"] = intval($this->isUserRestriction());
if ($this->isUserAdministrator()) {
$data["sitename"] = $this->yellow->system->get("sitename");
$data["author"] = $this->yellow->system->get("author");
$data["email"] = $this->yellow->system->get("email");
}
$data["serverScheme"] = $this->yellow->system->get("coreServerScheme");
$data["serverAddress"] = $this->yellow->system->get("coreServerAddress");
$data["serverBase"] = $this->yellow->system->get("coreServerBase");
$data["serverFileSizeMax"] = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize"));
$data["serverVersion"] = "Datenstrom Yellow ".YellowCore::VERSION;
$data["serverExtensions"] = array();
$data["userAccess"] = $this->extension->users->getUser($this->userEmail, "access");
$data["coreServerScheme"] = $this->yellow->system->get("coreServerScheme");
$data["coreServerAddress"] = $this->yellow->system->get("coreServerAddress");
$data["coreServerBase"] = $this->yellow->system->get("coreServerBase");
$data["coreFileSizeMax"] = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize"));
$data["coreVersion"] = "Datenstrom Yellow ".YellowCore::VERSION;
$data["coreExtensions"] = array();
foreach ($this->yellow->extensions->extensions as $key=>$value) {
$data["serverExtensions"][$key] = $value["type"];
$data["coreExtensions"][$key] = $value["type"];
}
$data["serverLanguages"] = array();
$data["coreLanguages"] = array();
foreach ($this->yellow->text->getLanguages() as $language) {
$data["serverLanguages"][$language] = $this->yellow->text->getTextHtml("languageDescription", $language);
$data["coreLanguages"][$language] = $this->yellow->text->getTextHtml("languageDescription", $language);
}
$data["editSettingsActions"] = $this->getSettingsActions();
$data["editUploadExtensions"] = $this->yellow->system->get("editUploadExtensions");
@ -1203,6 +1185,11 @@ class YellowEditResponse {
$data["editToolbarButtons"] = $this->getToolbarButtons("edit");
$data["emojiawesomeToolbarButtons"] = $this->getToolbarButtons("emojiawesome");
$data["fontawesomeToolbarButtons"] = $this->getToolbarButtons("fontawesome");
if ($this->isUserAccess("system")) {
$data["sitename"] = $this->yellow->system->get("sitename");
$data["author"] = $this->yellow->system->get("author");
$data["email"] = $this->yellow->system->get("email");
}
} else {
$data["editLoginEmail"] = $this->yellow->page->get("editLoginEmail");
$data["editLoginPassword"] = $this->yellow->page->get("editLoginPassword");
@ -1232,7 +1219,10 @@ class YellowEditResponse {
// Return settings actions
public function getSettingsActions() {
return $this->isUserAdministrator() ? "account, system, update" : "none";
$settingsActions = "account";
if ($this->isUserAccess("system")) $settingsActions .= ", system";
if ($this->isUserAccess("update")) $settingsActions .= ", update";
return $settingsActions=="account" ? "" : $settingsActions;
}
// Return toolbar buttons
@ -1557,6 +1547,15 @@ class YellowEditResponse {
}
}
// Change system file
public function editSystemFile($file, $action) {
if (!$file->isError()) {
foreach ($this->yellow->extensions->extensions as $key=>$value) {
if (method_exists($value["obj"], "onEditSystemFile")) $value["obj"]->onEditSystemFile($file, $action);
}
}
}
// Check if meta data has been modified
public function isMetaModified($pageSource, $pageOther) {
return substrb($pageSource->rawData, 0, $pageSource->metaDataOffsetBytes) !=
@ -1573,14 +1572,11 @@ class YellowEditResponse {
return !empty($this->userEmail);
}
// Check if user is administrator
public function isUserAdministrator() {
return !empty($this->userEmail) && $this->extension->users->getUser($this->userEmail, "group")=="administrator";
}
// Check if user with restriction
public function isUserRestriction() {
return empty($this->userEmail) || $this->userRestriction;
// Check if user with access
public function isUserAccess($action, $location = "") {
$userHome = $this->extension->users->getUser($this->userEmail, "home");
$userAccess = preg_split("/\s*,\s*/", $this->extension->users->getUser($this->userEmail, "access"));
return in_array($action, $userAccess) && (empty($location) || substru($location, 0, strlenu($userHome))==$userHome);
}
// Check if login with restriction
@ -1780,7 +1776,7 @@ class YellowEditUsers {
foreach ($this->users as $key=>$value) {
$name = $value["name"];
if (preg_match("/\s/", $name)) $name = "\"$name\"";
$data[$key] = "$value[email] $name $value[group] $value[status]";
$data[$key] = "$value[email] $name $value[status]";
}
uksort($data, "strnatcasecmp");
return $data;

View file

@ -4,7 +4,7 @@
// This file may be used and distributed under the terms of the public license.
class YellowInstall {
const VERSION = "0.8.11";
const VERSION = "0.8.12";
const TYPE = "feature";
const PRIORITY = "1";
public $yellow; //access to API
@ -179,8 +179,8 @@ class YellowInstall {
$settings = array(
"name" => $name,
"language" => $language,
"group" => "administrator",
"home" => "/",
"access" => "create, edit, delete, upload, system, update",
"status" => "active",
"pending" => "none",
"hash" => $this->yellow->extensions->get("edit")->users->createHash($password),