mikuli.cz

:)
git clone https://git.sr.ht/~ashymad/mikuli.cz
Log | Files | Refs

commit d577145cd5d2181b295916c18a424030e9dfe676
parent 45e32c5ed434cd414433e4f93fae384e5c8fddfa
Author: markseu <mark2011@mayberg.se>
Date:   Tue, 17 Dec 2019 11:46:09 +0100

Added access rights for more detailed user restrictions

Diffstat:
Msystem/extensions/edit.js | 29+++++++++++++++--------------
Msystem/extensions/edit.php | 140++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msystem/extensions/install.php | 4++--
3 files changed, 85 insertions(+), 88 deletions(-)

diff --git a/system/extensions/edit.js b/system/extensions/edit.js @@ -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; } }; diff --git a/system/extensions/edit.php b/system/extensions/edit.php @@ -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; diff --git a/system/extensions/install.php b/system/extensions/install.php @@ -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),