Updated editor, improved token handling and brute force protection
This commit is contained in:
parent
6299c44a89
commit
f7e00cd7e7
3 changed files with 198 additions and 137 deletions
|
@ -171,11 +171,18 @@
|
|||
#yellow-pane-signup-fields { width:15em; text-align:left; margin:0 auto; }
|
||||
#yellow-pane-signup-buttons { margin-top:-0.5em; }
|
||||
|
||||
#yellow-pane-forgot { text-align:center; white-space:nowrap; }
|
||||
#yellow-pane-forgot .yellow-form-control { width:15em; box-sizing:border-box; }
|
||||
#yellow-pane-forgot .yellow-btn { width:15em; margin:1em 1em 0.5em 0; }
|
||||
#yellow-pane-forgot-status { margin:0.5em 0; display:inline-block; }
|
||||
#yellow-pane-forgot-fields { width:15em; text-align:left; margin:0 auto; }
|
||||
#yellow-pane-forgot-buttons { margin-top:-0.5em; }
|
||||
|
||||
#yellow-pane-recover { text-align:center; white-space:nowrap; }
|
||||
#yellow-pane-recover .yellow-form-control { width:15em; box-sizing:border-box; }
|
||||
#yellow-pane-recover .yellow-btn { width:15em; margin:1em 1em 0.5em 0; }
|
||||
#yellow-pane-recover-status { margin:0.5em 0; display:inline-block; }
|
||||
#yellow-pane-recover-fields-first, #yellow-pane-recover-fields-second { width:15em; text-align:left; margin:0 auto; }
|
||||
#yellow-pane-recover-fields { width:15em; text-align:left; margin:0 auto; }
|
||||
#yellow-pane-recover-buttons { margin-top:-0.5em; }
|
||||
|
||||
#yellow-pane-settings { text-align:center; white-space:nowrap; }
|
||||
|
|
|
@ -49,10 +49,11 @@ yellow.edit =
|
|||
case "signup": this.showPane("yellow-pane-signup", action, status); break;
|
||||
case "confirm": this.showPane("yellow-pane-signup", action, status); break;
|
||||
case "approve": this.showPane("yellow-pane-signup", action, status); break;
|
||||
case "reactivate": this.showPane("yellow-pane-settings", action, status); break;
|
||||
case "forgot": this.showPane("yellow-pane-forgot", action, status); break;
|
||||
case "recover": this.showPane("yellow-pane-recover", action, status); break;
|
||||
case "reactivate": this.showPane("yellow-pane-settings", action, status); break;
|
||||
case "settings": this.showPane("yellow-pane-settings", action, status); break;
|
||||
case "reconfirm": this.showPane("yellow-pane-settings", action, status); break;
|
||||
case "verify": this.showPane("yellow-pane-settings", action, status); break;
|
||||
case "change": this.showPane("yellow-pane-settings", action, status); break;
|
||||
case "version": this.showPane("yellow-pane-version", action, status); break;
|
||||
case "update": this.sendPane("yellow-pane-update", action, status, args); break;
|
||||
|
@ -182,7 +183,7 @@ yellow.edit =
|
|||
"<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("LoginButton")+"\" /></p>"+
|
||||
"</div>"+
|
||||
"<div id=\"yellow-pane-login-buttons\">"+
|
||||
"<p><a href=\"#\" id=\"yellow-pane-login-recover\" data-action=\"recover\">"+this.getText("LoginRecover")+"</a><p>"+
|
||||
"<p><a href=\"#\" id=\"yellow-pane-login-forgot\" data-action=\"forgot\">"+this.getText("LoginForgot")+"</a><p>"+
|
||||
"<p><a href=\"#\" id=\"yellow-pane-login-signup\" data-action=\"signup\">"+this.getText("LoginSignup")+"</a><p>"+
|
||||
"</div>"+
|
||||
"</form>";
|
||||
|
@ -205,18 +206,29 @@ yellow.edit =
|
|||
"</div>"+
|
||||
"</form>";
|
||||
break;
|
||||
case "yellow-pane-forgot":
|
||||
elementDiv.innerHTML =
|
||||
"<form method=\"post\">"+
|
||||
"<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
|
||||
"<h1>"+this.getText("ForgotTitle")+"</h1>"+
|
||||
"<div id=\"yellow-pane-forgot-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+
|
||||
"<div id=\"yellow-pane-forgot-fields\">"+
|
||||
"<input type=\"hidden\" name=\"action\" value=\"forgot\" />"+
|
||||
"<p><label for=\"yellow-pane-forgot-email\">"+this.getText("ForgotEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-forgot-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+
|
||||
"<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+
|
||||
"</div>"+
|
||||
"<div id=\"yellow-pane-forgot-buttons\">"+
|
||||
"<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+
|
||||
"</div>"+
|
||||
"</form>";
|
||||
break;
|
||||
case "yellow-pane-recover":
|
||||
elementDiv.innerHTML =
|
||||
"<form method=\"post\">"+
|
||||
"<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+
|
||||
"<h1>"+this.getText("RecoverTitle")+"</h1>"+
|
||||
"<div id=\"yellow-pane-recover-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+
|
||||
"<div id=\"yellow-pane-recover-fields-first\">"+
|
||||
"<input type=\"hidden\" name=\"action\" value=\"recover\" />"+
|
||||
"<p><label for=\"yellow-pane-recover-email\">"+this.getText("RecoverEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-recover-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+
|
||||
"<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+
|
||||
"</div>"+
|
||||
"<div id=\"yellow-pane-recover-fields-second\">"+
|
||||
"<div id=\"yellow-pane-recover-fields\">"+
|
||||
"<p><label for=\"yellow-pane-recover-password\">"+this.getText("RecoverPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-recover-password\" maxlength=\"64\" value=\"\" /></p>"+
|
||||
"<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+
|
||||
"</div>"+
|
||||
|
@ -318,7 +330,7 @@ yellow.edit =
|
|||
updatePane: function(paneId, paneAction, paneStatus, init)
|
||||
{
|
||||
if(yellow.config.debug) console.log("yellow.edit.updatePane id:"+paneId);
|
||||
var showFields = paneStatus!="next" && paneStatus!="done" && paneStatus!="expired";
|
||||
var showFields = paneStatus!="next" && paneStatus!="done";
|
||||
switch(paneId)
|
||||
{
|
||||
case "yellow-pane-login":
|
||||
|
@ -331,19 +343,13 @@ yellow.edit =
|
|||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-signup-fields"), showFields);
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-signup-buttons"), !showFields);
|
||||
break;
|
||||
case "yellow-pane-forgot":
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-forgot-fields"), showFields);
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-forgot-buttons"), !showFields);
|
||||
break;
|
||||
case "yellow-pane-recover":
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-first"), showFields);
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-second"), showFields);
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields"), showFields);
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-buttons"), !showFields);
|
||||
if(showFields)
|
||||
{
|
||||
if(this.getRequest("id"))
|
||||
{
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-first"), false);
|
||||
} else {
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-second"), false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "yellow-pane-settings":
|
||||
yellow.toolbox.setVisible(document.getElementById("yellow-pane-settings-fields"), showFields);
|
||||
|
@ -423,6 +429,7 @@ yellow.edit =
|
|||
{
|
||||
case "yellow-pane-login":
|
||||
case "yellow-pane-signup":
|
||||
case "yellow-pane-forgot":
|
||||
case "yellow-pane-recover":
|
||||
case "yellow-pane-settings":
|
||||
case "yellow-pane-version":
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
class YellowEdit
|
||||
{
|
||||
const VERSION = "0.7.15";
|
||||
const VERSION = "0.7.16";
|
||||
var $yellow; //access to API
|
||||
var $response; //web response
|
||||
var $users; //user accounts
|
||||
|
@ -187,7 +187,7 @@ class YellowEdit
|
|||
function processRequest($scheme, $address, $base, $location, $fileName)
|
||||
{
|
||||
$statusCode = 0;
|
||||
if($this->checkUser($scheme, $address, $base, $location, $fileName))
|
||||
if($this->checkUserAuth($scheme, $address, $base, $location, $fileName))
|
||||
{
|
||||
switch($_REQUEST["action"])
|
||||
{
|
||||
|
@ -203,21 +203,22 @@ class YellowEdit
|
|||
case "preview": $statusCode = $this->processRequestPreview($scheme, $address, $base, $location, $fileName); break;
|
||||
case "upload": $statusCode = $this->processRequestUpload($scheme, $address, $base, $location, $fileName); break;
|
||||
}
|
||||
} else {
|
||||
} else if($this->checkUserUnauth($scheme, $address, $base, $location, $fileName)) {
|
||||
$this->yellow->lookup->requestHandler = "core";
|
||||
switch($_REQUEST["action"])
|
||||
{
|
||||
case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break;
|
||||
case "signup": $statusCode = $this->processRequestSignup($scheme, $address, $base, $location, $fileName); break;
|
||||
case "forgot": $statusCode = $this->processRequestForgot($scheme, $address, $base, $location, $fileName); break;
|
||||
case "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break;
|
||||
case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break;
|
||||
case "reactivate": $statusCode = $this->processRequestReactivate($scheme, $address, $base, $location, $fileName); break;
|
||||
case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break;
|
||||
case "reconfirm": $statusCode = $this->processRequestReconfirm($scheme, $address, $base, $location, $fileName); break;
|
||||
case "reactivate": $statusCode = $this->processRequestReactivate($scheme, $address, $base, $location, $fileName); break;
|
||||
case "verify": $statusCode = $this->processRequestVerify($scheme, $address, $base, $location, $fileName); break;
|
||||
case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break;
|
||||
}
|
||||
$this->checkUserFailed($scheme, $address, $base, $location, $fileName);
|
||||
}
|
||||
$this->checkUserFailed($scheme, $address, $base, $location, $fileName);
|
||||
return $statusCode;
|
||||
}
|
||||
|
||||
|
@ -310,7 +311,7 @@ class YellowEdit
|
|||
$this->response->action = "confirm";
|
||||
$this->response->status = "ok";
|
||||
$email = $_REQUEST["email"];
|
||||
$this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]);
|
||||
$this->response->status = $this->getUserStatus($email, $_REQUEST["action"]);
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
|
||||
|
@ -332,7 +333,7 @@ class YellowEdit
|
|||
$this->response->action = "approve";
|
||||
$this->response->status = "ok";
|
||||
$email = $_REQUEST["email"];
|
||||
$this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]);
|
||||
$this->response->status = $this->getUserStatus($email, $_REQUEST["action"]);
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
|
||||
|
@ -348,18 +349,18 @@ class YellowEdit
|
|||
return $statusCode;
|
||||
}
|
||||
|
||||
// Process request to reactivate account
|
||||
function processRequestReactivate($scheme, $address, $base, $location, $fileName)
|
||||
// Process request for forgotten password
|
||||
function processRequestForgot($scheme, $address, $base, $location, $fileName)
|
||||
{
|
||||
$this->response->action = "reactivate";
|
||||
$this->response->action = "forgot";
|
||||
$this->response->status = "ok";
|
||||
$email = $_REQUEST["email"];
|
||||
$this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]);
|
||||
$email = trim($_REQUEST["email"]);
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) $this->response->status = "invalid";
|
||||
if($this->response->status=="ok" && !$this->users->isExisting($email)) $this->response->status = "next";
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
|
||||
$this->response->status = $this->users->update($fileNameUser, $email, "", "", "", "active") ? "done" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
|
||||
$this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "recover") ? "next" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!");
|
||||
}
|
||||
$statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false);
|
||||
return $statusCode;
|
||||
|
@ -372,35 +373,41 @@ class YellowEdit
|
|||
$this->response->status = "ok";
|
||||
$email = trim($_REQUEST["email"]);
|
||||
$password = trim($_REQUEST["password"]);
|
||||
if(empty($_REQUEST["id"]))
|
||||
$this->response->status = $this->getUserStatus($email, $_REQUEST["action"]);
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) $this->response->status = "invalid";
|
||||
if($this->response->status=="ok" && !$this->users->isExisting($email)) $this->response->status = "next";
|
||||
if(empty($password)) $this->response->status = "password";
|
||||
if($this->response->status=="ok") $this->response->status = $this->getUserAccount($email, $password, $this->response->action);
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "recover") ? "next" : "error";
|
||||
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
|
||||
$this->response->status = $this->users->update($fileNameUser, $email, $password) ? "ok" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
|
||||
}
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$this->response->userEmail = "";
|
||||
$this->response->destroyCookies($scheme, $address, $base);
|
||||
$this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!");
|
||||
}
|
||||
} else {
|
||||
$this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]);
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
if(empty($password)) $this->response->status = "password";
|
||||
if($this->response->status=="ok") $this->response->status = $this->getUserAccount($email, $password, $this->response->action);
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
|
||||
$this->response->status = $this->users->update($fileNameUser, $email, $password) ? "ok" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
|
||||
}
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$this->response->userEmail = "";
|
||||
$this->response->destroyCookies($scheme, $address, $base);
|
||||
$this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!");
|
||||
}
|
||||
}
|
||||
}
|
||||
$statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false);
|
||||
return $statusCode;
|
||||
}
|
||||
|
||||
// Process request to reactivate account
|
||||
function processRequestReactivate($scheme, $address, $base, $location, $fileName)
|
||||
{
|
||||
$this->response->action = "reactivate";
|
||||
$this->response->status = "ok";
|
||||
$email = $_REQUEST["email"];
|
||||
$this->response->status = $this->getUserStatus($email, $_REQUEST["action"]);
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
|
||||
$this->response->status = $this->users->update($fileNameUser, $email, "", "", "", "active") ? "done" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
|
||||
}
|
||||
$statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false);
|
||||
return $statusCode;
|
||||
|
@ -426,7 +433,7 @@ class YellowEdit
|
|||
$pending = $emailSource;
|
||||
$home = $this->users->getHome($emailSource);
|
||||
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
|
||||
$this->response->status = $this->users->update($fileNameUser, $email, "no", $name, $language, "unconfirmed", "", "", "", $pending, $home) ? "ok" : "error";
|
||||
$this->response->status = $this->users->update($fileNameUser, $email, "no", $name, $language, "unverified", "", "", "", $pending, $home) ? "ok" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
|
||||
}
|
||||
if($this->response->status=="ok")
|
||||
|
@ -438,7 +445,7 @@ class YellowEdit
|
|||
}
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$action = $email!=$emailSource ? "reconfirm" : "change";
|
||||
$action = $email!=$emailSource ? "verify" : "change";
|
||||
$this->response->status = $this->response->sendMail($scheme, $address, $base, $email, $action) ? "next" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!");
|
||||
}
|
||||
|
@ -460,13 +467,13 @@ class YellowEdit
|
|||
return $statusCode;
|
||||
}
|
||||
|
||||
// Process request to reconfirm email
|
||||
function processRequestReconfirm($scheme, $address, $base, $location, $fileName)
|
||||
// Process request to verify email
|
||||
function processRequestVerify($scheme, $address, $base, $location, $fileName)
|
||||
{
|
||||
$this->response->action = "reconfirm";
|
||||
$this->response->action = "verify";
|
||||
$this->response->status = "ok";
|
||||
$email = $emailSource = $_REQUEST["email"];
|
||||
$this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]);
|
||||
$this->response->status = $this->getUserStatus($email, $_REQUEST["action"]);
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
$emailSource = $this->users->getPending($email);
|
||||
|
@ -487,13 +494,13 @@ class YellowEdit
|
|||
return $statusCode;
|
||||
}
|
||||
|
||||
// Process request to change account
|
||||
// Process request to change email or password
|
||||
function processRequestChange($scheme, $address, $base, $location, $fileName)
|
||||
{
|
||||
$this->response->action = "change";
|
||||
$this->response->status = "ok";
|
||||
$email = $emailSource = trim($_REQUEST["email"]);
|
||||
$this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]);
|
||||
$this->response->status = $this->getUserStatus($email, $_REQUEST["action"]);
|
||||
if($this->response->status=="ok")
|
||||
{
|
||||
list($email, $hash) = explode(':', $this->users->getPending($email), 2);
|
||||
|
@ -503,7 +510,7 @@ class YellowEdit
|
|||
{
|
||||
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
|
||||
$this->users->users[$emailSource]["pending"] = "none";
|
||||
$this->response->status = $this->users->update($fileNameUser, $emailSource, "", "", "", "inactive") ? "ok" : "error";
|
||||
$this->response->status = $this->users->update($fileNameUser, $emailSource, "", "", "", "removed") ? "ok" : "error";
|
||||
if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
|
||||
}
|
||||
if($this->response->status=="ok")
|
||||
|
@ -741,8 +748,8 @@ class YellowEdit
|
|||
return $this->response->isActive();
|
||||
}
|
||||
|
||||
// Check user
|
||||
function checkUser($scheme, $address, $base, $location, $fileName)
|
||||
// Check user authentication
|
||||
function checkUserAuth($scheme, $address, $base, $location, $fileName)
|
||||
{
|
||||
if($this->isRequestSameSite("POST", $scheme, $address) || $_REQUEST["action"]=="")
|
||||
{
|
||||
|
@ -757,8 +764,9 @@ class YellowEdit
|
|||
$this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName);
|
||||
$this->response->language = $this->getUserLanguage($email);
|
||||
} else {
|
||||
$this->response->userFailed = true;
|
||||
$this->response->userFailedError = "login";
|
||||
$this->response->userFailedEmail = $email;
|
||||
$this->response->userFailedExpire = PHP_INT_MAX;
|
||||
}
|
||||
} else if(isset($_COOKIE["authtoken"]) && isset($_COOKIE["csrftoken"])) {
|
||||
if($this->users->checkAuthToken($_COOKIE["authtoken"], $_COOKIE["csrftoken"], $_POST["csrftoken"], $_REQUEST["action"]==""))
|
||||
|
@ -767,22 +775,43 @@ class YellowEdit
|
|||
$this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName);
|
||||
$this->response->language = $this->getUserLanguage($email);
|
||||
} else {
|
||||
$this->response->userFailed = true;
|
||||
$this->response->userFailedError = "auth";
|
||||
$this->response->userFailedEmail = $this->users->getAuthEmail($_COOKIE["authtoken"]);
|
||||
$this->response->userFailedExpire = $this->users->getAuthExpire($_COOKIE["authtoken"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->response->isUser();
|
||||
}
|
||||
|
||||
|
||||
// Check user without authentication
|
||||
function checkUserUnauth($scheme, $address, $base, $location, $fileName)
|
||||
{
|
||||
$ok = false;
|
||||
if($_REQUEST["action"]=="" || $_REQUEST["action"]=="signup" || $_REQUEST["action"]=="forgot")
|
||||
{
|
||||
$ok = true;
|
||||
} else if(isset($_REQUEST["actiontoken"])) {
|
||||
if($this->users->checkActionToken($_REQUEST["actiontoken"], $_REQUEST["email"], $_REQUEST["action"], $_REQUEST["expire"]))
|
||||
{
|
||||
$ok = true;
|
||||
} else {
|
||||
$this->response->userFailedError = "action";
|
||||
$this->response->userFailedEmail = $_REQUEST["email"];
|
||||
$this->response->userFailedExpire = $_REQUEST["expire"];
|
||||
}
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
|
||||
// Check user failed
|
||||
function checkUserFailed($scheme, $address, $base, $location, $fileName)
|
||||
{
|
||||
if($this->response->userFailed)
|
||||
if(!empty($this->response->userFailedError))
|
||||
{
|
||||
$email = $this->response->userFailedEmail;
|
||||
if($this->users->isExisting($email))
|
||||
if($this->response->userFailedExpire>time() && $this->users->isExisting($this->response->userFailedEmail))
|
||||
{
|
||||
$email = $this->response->userFailedEmail;
|
||||
$modified = $this->users->getModified($email);
|
||||
$errors = $this->users->getErrors($email)+1;
|
||||
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
|
||||
|
@ -790,24 +819,47 @@ class YellowEdit
|
|||
if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
|
||||
if($errors==$this->yellow->config->get("editBruteForceProtection"))
|
||||
{
|
||||
$statusBeforeProtection = $this->users->getStatus($email);
|
||||
$statusAfterProtection = ($statusBeforeProtection=="active" || $statusBeforeProtection=="inactive") ? "inactive" : "removed";
|
||||
if($status=="ok")
|
||||
{
|
||||
$status = $this->users->update($fileNameUser, $email, "", "", "", "inactive", "", $modified, $errors) ? "ok" : "error";
|
||||
$status = $this->users->update($fileNameUser, $email, "", "", "", $statusAfterProtection, "", $modified, $errors) ? "ok" : "error";
|
||||
if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
|
||||
}
|
||||
if($status=="ok")
|
||||
if($status=="ok" && $statusBeforeProtection=="active")
|
||||
{
|
||||
$status = $this->response->sendMail($scheme, $address, $base, $email, "reactivate") ? "done" : "error";
|
||||
if($status=="error") $this->yellow->page->error(500, "Can't send email on this server!");
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->response->destroyCookies($scheme, $address, $base);
|
||||
$this->response->status = "error";
|
||||
$this->yellow->page->error(430);
|
||||
if($this->response->userFailedError=="login" || $this->response->userFailedError=="auth")
|
||||
{
|
||||
$this->response->destroyCookies($scheme, $address, $base);
|
||||
$this->response->status = "error";
|
||||
$this->yellow->page->error(430);
|
||||
} else {
|
||||
$this->response->status = "error";
|
||||
$this->yellow->page->error(500, "Link has expired!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return user status changes
|
||||
function getUserStatus($email, $action)
|
||||
{
|
||||
switch($action)
|
||||
{
|
||||
case "confirm": $statusExpected = "unconfirmed"; break;
|
||||
case "approve": $statusExpected = "unapproved"; break;
|
||||
case "recover": $statusExpected = "active"; break;
|
||||
case "reactivate": $statusExpected = "inactive"; break;
|
||||
case "verify": $statusExpected = "unverified"; break;
|
||||
case "change": $statusExpected = "active"; break;
|
||||
}
|
||||
return $this->users->getStatus($email)==$statusExpected ? "ok" : "done";
|
||||
}
|
||||
|
||||
// Return user account changes
|
||||
function getUserAccount($email, $password, $action)
|
||||
{
|
||||
|
@ -873,8 +925,9 @@ class YellowResponse
|
|||
var $active; //location is active? (boolean)
|
||||
var $userEmail; //user email
|
||||
var $userRestrictions; //user can change page? (boolean)
|
||||
var $userFailed; //user failed authentication? (boolean)
|
||||
var $userFailedError; //error of failed authentication
|
||||
var $userFailedEmail; //email of failed authentication
|
||||
var $userFailedExpire; //expiration time of failed authentication
|
||||
var $rawDataSource; //raw data of page for comparison
|
||||
var $rawDataEdit; //raw data of page for editing
|
||||
var $rawDataOutput; //raw data of dynamic output
|
||||
|
@ -1090,7 +1143,7 @@ class YellowResponse
|
|||
$data = array();
|
||||
foreach($_REQUEST as $key=>$value)
|
||||
{
|
||||
if($key=="password" || $key=="authtoken" || $key=="csrftoken" || substru($key, 0, 7)=="rawdata") continue;
|
||||
if($key=="password" || $key=="authtoken" || $key=="csrftoken" || $key=="actiontoken" || substru($key, 0, 7)=="rawdata") continue;
|
||||
$data["request".ucfirst($key)] = trim($value);
|
||||
}
|
||||
return $data;
|
||||
|
@ -1267,11 +1320,11 @@ class YellowResponse
|
|||
// Create browser cookies
|
||||
function createCookies($scheme, $address, $base, $email)
|
||||
{
|
||||
$authToken = $this->plugin->users->createAuthToken($email);
|
||||
$expire = time() + $this->yellow->config->get("editLoginSessionTimeout");
|
||||
$authToken = $this->plugin->users->createAuthToken($email, $expire);
|
||||
$csrfToken = $this->plugin->users->createCsrfToken();
|
||||
$timeout = $this->yellow->config->get("editLoginSessionTimeout");
|
||||
setcookie("authtoken", $authToken, $timeout ? time()+$timeout : 0, "$base/", "", $scheme=="https", true);
|
||||
setcookie("csrftoken", $csrfToken, $timeout ? time()+$timeout : 0, "$base/", "", $scheme=="https", false);
|
||||
setcookie("authtoken", $authToken, $expire, "$base/", "", $scheme=="https", true);
|
||||
setcookie("csrftoken", $csrfToken, $expire, "$base/", "", $scheme=="https", false);
|
||||
}
|
||||
|
||||
// Destroy browser cookies
|
||||
|
@ -1288,9 +1341,9 @@ class YellowResponse
|
|||
{
|
||||
$url = "$scheme://$address$base/";
|
||||
} else {
|
||||
$expire = time()+60*60*24;
|
||||
$id = $this->plugin->users->createRequestId($email, $action, $expire);
|
||||
$url = "$scheme://$address$base"."/action:$action/email:$email/expire:$expire/id:$id/";
|
||||
$expire = time() + 60*60*24;
|
||||
$actionToken = $this->plugin->users->createActionToken($email, $action, $expire);
|
||||
$url = "$scheme://$address$base"."/action:$action/email:$email/expire:$expire/actiontoken:$actionToken/";
|
||||
}
|
||||
if($action=="approve")
|
||||
{
|
||||
|
@ -1417,7 +1470,7 @@ class YellowUsers
|
|||
{
|
||||
list($hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) = explode(',', $matches[2]);
|
||||
if($errors=="none") { $home=$pending; $pending=$errors; $errors=$modified; $modified=$stamp; $stamp=$this->createStamp(); } //TODO: remove later
|
||||
if(strlenu($stamp)!=20) $stamp=$this->createStamp(); //TODO: remove later, converts old file format
|
||||
if(strlenb($stamp)!=20) $stamp=$this->createStamp(); //TODO: remove later, converts old file format
|
||||
if($status=="active" || $status=="inactive")
|
||||
{
|
||||
$pending = "none";
|
||||
|
@ -1502,19 +1555,36 @@ class YellowUsers
|
|||
// Check user authentication from tokens
|
||||
function checkAuthToken($authToken, $csrfTokenExpected, $csrfTokenReceived, $ignoreCsrfToken)
|
||||
{
|
||||
$session = "$5y$".substru($authToken, 0, 96);
|
||||
$signature = "$5y$".substrb($authToken, 0, 96);
|
||||
$email = $this->getAuthEmail($authToken);
|
||||
return $this->isExisting($email) && $this->users[$email]["status"]=="active" &&
|
||||
$this->yellow->toolbox->verifyHash($this->users[$email]["hash"], "sha256", $session) &&
|
||||
$expire = $this->getAuthExpire($authToken);
|
||||
return $expire>time() && $this->isExisting($email) && $this->users[$email]["status"]=="active" &&
|
||||
$this->yellow->toolbox->verifyHash($this->users[$email]["hash"]."auth".$expire, "sha256", $signature) &&
|
||||
($this->verifyToken($csrfTokenExpected, $csrfTokenReceived) || $ignoreCsrfToken);
|
||||
}
|
||||
|
||||
// Create authentication token
|
||||
function createAuthToken($email)
|
||||
// Check action token
|
||||
function checkActionToken($actionToken, $email, $action, $expire)
|
||||
{
|
||||
$session = $this->yellow->toolbox->createHash($this->users[$email]["hash"], "sha256");
|
||||
if(empty($session)) $session = "padd"."error-hash-algorithm-sha256";
|
||||
return substru($session, 4).$this->getStamp($email);
|
||||
$signature = "$5y$".$actionToken;
|
||||
return $expire>time() && $this->isExisting($email) &&
|
||||
$this->yellow->toolbox->verifyHash($this->users[$email]["hash"].$action.$expire, "sha256", $signature);
|
||||
}
|
||||
|
||||
// Create authentication token
|
||||
function createAuthToken($email, $expire)
|
||||
{
|
||||
$signature = $this->yellow->toolbox->createHash($this->users[$email]["hash"]."auth".$expire, "sha256");
|
||||
if(empty($signature)) $signature = "padd"."error-hash-algorithm-sha256";
|
||||
return substrb($signature, 4).$this->getStamp($email).dechex($expire);
|
||||
}
|
||||
|
||||
// Create action token
|
||||
function createActionToken($email, $action, $expire)
|
||||
{
|
||||
$signature = $this->yellow->toolbox->createHash($this->users[$email]["hash"].$action.$expire, "sha256");
|
||||
if(empty($signature)) $signature = "padd"."error-hash-algorithm-sha256";
|
||||
return substrb($signature, 4);
|
||||
}
|
||||
|
||||
// Create CSRF token
|
||||
|
@ -1523,14 +1593,6 @@ class YellowUsers
|
|||
return $this->yellow->toolbox->createSalt(64);
|
||||
}
|
||||
|
||||
// Create user stamp
|
||||
function createStamp()
|
||||
{
|
||||
$stamp = $this->yellow->toolbox->createSalt(20);
|
||||
while($this->getAuthEmail("none", $stamp)) $stamp = $this->yellow->toolbox->createSalt(20);
|
||||
return $stamp;
|
||||
}
|
||||
|
||||
// Create password hash
|
||||
function createHash($password)
|
||||
{
|
||||
|
@ -1541,39 +1603,18 @@ class YellowUsers
|
|||
return $hash;
|
||||
}
|
||||
|
||||
// Create request ID for action
|
||||
function createRequestId($email, $action, $expire)
|
||||
// Create user stamp
|
||||
function createStamp()
|
||||
{
|
||||
$id = $this->yellow->toolbox->createHash($this->users[$email]["hash"].$action.$expire, "sha256");
|
||||
if(empty($id)) $hash = "error-hash-algorithm-sha256";
|
||||
return $id;
|
||||
$stamp = $this->yellow->toolbox->createSalt(20);
|
||||
while($this->getAuthEmail("none", $stamp)) $stamp = $this->yellow->toolbox->createSalt(20);
|
||||
return $stamp;
|
||||
}
|
||||
|
||||
// Return response status for action
|
||||
function getResponseStatus($email, $action, $expire, $id)
|
||||
{
|
||||
$status = "done";
|
||||
switch($action)
|
||||
{
|
||||
case "confirm": $statusExpected = "unconfirmed"; break;
|
||||
case "reconfirm": $statusExpected = "unconfirmed"; break;
|
||||
case "approve": $statusExpected = "unapproved"; break;
|
||||
case "reactivate": $statusExpected = "inactive"; break;
|
||||
default: $statusExpected = "active"; break;
|
||||
}
|
||||
if($this->isExisting($email) && $this->users[$email]["status"]==$statusExpected &&
|
||||
$this->yellow->toolbox->verifyHash($this->users[$email]["hash"].$action.$expire, "sha256", $id))
|
||||
{
|
||||
$status = "ok";
|
||||
}
|
||||
if($expire<=time()) $status = "expired";
|
||||
return $status;
|
||||
}
|
||||
|
||||
// Return user email from authentication, timing attack safe email lookup
|
||||
function getAuthEmail($authToken, $stamp = "")
|
||||
{
|
||||
if(empty($stamp)) $stamp = substru($authToken, 96);
|
||||
if(empty($stamp)) $stamp = substrb($authToken, 96, 20);
|
||||
foreach($this->users as $key=>$value)
|
||||
{
|
||||
if($this->verifyToken($value["stamp"], $stamp)) $email = $key;
|
||||
|
@ -1581,6 +1622,12 @@ class YellowUsers
|
|||
return $email;
|
||||
}
|
||||
|
||||
// Return expiration time from authentication
|
||||
function getAuthExpire($authToken)
|
||||
{
|
||||
return hexdec(substrb($authToken, 96+20));
|
||||
}
|
||||
|
||||
// Return user hash
|
||||
function getHash($email)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue