add account deletion and password changing
This commit is contained in:
parent
902e561a09
commit
22c337f8aa
8 changed files with 179 additions and 36 deletions
|
@ -5,4 +5,4 @@ it's like pronouns.page but i made it
|
||||||
|
|
||||||
### todo
|
### todo
|
||||||
* social links
|
* social links
|
||||||
* sexuality/gender flags
|
* templating and embeds on /u/... pages
|
||||||
|
|
71
_server.py
71
_server.py
|
@ -135,39 +135,39 @@ def api_account_login():
|
||||||
|
|
||||||
for i in username:
|
for i in username:
|
||||||
if i not in "abcdefghijklmnopqrstuvwxyz0123456789_-":
|
if i not in "abcdefghijklmnopqrstuvwxyz0123456789_-":
|
||||||
return return_dynamic_content_type(json.dumps({
|
return {
|
||||||
"valid": False,
|
"valid": False,
|
||||||
"reason": "User doesn't exist."
|
"reason": "User doesn't exist."
|
||||||
}), "application/json")
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
open(f"{SAVING_DIRECTORY}{username}.json", "r")
|
open(f"{SAVING_DIRECTORY}{username}.json", "r")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return return_dynamic_content_type(json.dumps({
|
return {
|
||||||
"valid": False,
|
"valid": False,
|
||||||
"reason": "User doesn't exist."
|
"reason": "User doesn't exist."
|
||||||
}), "application/json")
|
}
|
||||||
|
|
||||||
token = generate_token(username, passhash)
|
token = generate_token(username, passhash)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
enforced_username = open(f"{SAVING_DIRECTORY}tokens/{token}.txt", "r").read()
|
enforced_username = open(f"{SAVING_DIRECTORY}tokens/{token}.txt", "r").read()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return return_dynamic_content_type(json.dumps({
|
return {
|
||||||
"valid": False,
|
"valid": False,
|
||||||
"reason": "Invalid password"
|
"reason": "Invalid password"
|
||||||
}), "application/json")
|
}
|
||||||
|
|
||||||
if enforced_username != username:
|
if enforced_username != username:
|
||||||
return return_dynamic_content_type(json.dumps({
|
return {
|
||||||
"valid": False,
|
"valid": False,
|
||||||
"reason": "Invalid password"
|
"reason": "Invalid password"
|
||||||
}), "application/json")
|
}
|
||||||
|
|
||||||
return return_dynamic_content_type(json.dumps({
|
return {
|
||||||
"valid": True,
|
"valid": True,
|
||||||
"token": token
|
"token": token
|
||||||
}), "application/json")
|
}
|
||||||
|
|
||||||
def api_account_signup():
|
def api_account_signup():
|
||||||
try:
|
try:
|
||||||
|
@ -191,17 +191,17 @@ def api_account_signup():
|
||||||
|
|
||||||
for i in username:
|
for i in username:
|
||||||
if i not in "abcdefghijklmnopqrstuvwxyz0123456789_-":
|
if i not in "abcdefghijklmnopqrstuvwxyz0123456789_-":
|
||||||
return return_dynamic_content_type(json.dumps({
|
return {
|
||||||
"valid": False,
|
"valid": False,
|
||||||
"reason": "Username can only contain a-z, 0-9, underscores, and hyphens."
|
"reason": "Username can only contain a-z, 0-9, underscores, and hyphens."
|
||||||
}), "application/json")
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
open(f"{SAVING_DIRECTORY}{username}.json", "r")
|
open(f"{SAVING_DIRECTORY}{username}.json", "r")
|
||||||
return return_dynamic_content_type(json.dumps({
|
return {
|
||||||
"valid": False,
|
"valid": False,
|
||||||
"reason": "Username taken."
|
"reason": "Username taken."
|
||||||
}), "application/json")
|
}
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -280,6 +280,40 @@ def api_account_self():
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
|
def api_account_change():
|
||||||
|
username = open(f'{SAVING_DIRECTORY}tokens/{request.cookies["token"]}.txt', 'r').read()
|
||||||
|
x = json.loads(request.data)
|
||||||
|
|
||||||
|
if generate_token(username, x["current"]) != request.cookies["token"]:
|
||||||
|
flask.abort(401)
|
||||||
|
|
||||||
|
new = generate_token(username, x["new"])
|
||||||
|
os.rename(f'{SAVING_DIRECTORY}tokens/{request.cookies["token"]}.txt', f'{SAVING_DIRECTORY}tokens/{new}.txt')
|
||||||
|
|
||||||
|
return {
|
||||||
|
"token": new
|
||||||
|
}
|
||||||
|
|
||||||
|
def api_account_delete():
|
||||||
|
username = open(f'{SAVING_DIRECTORY}tokens/{request.cookies["token"]}.txt', 'r').read()
|
||||||
|
x = json.loads(request.data)
|
||||||
|
|
||||||
|
token = generate_token(username, x["passhash"])
|
||||||
|
if generate_token(username, x["passhash"]) != request.cookies["token"]:
|
||||||
|
flask.abort(401)
|
||||||
|
|
||||||
|
os.remove(f"{SAVING_DIRECTORY}tokens/{token}.txt")
|
||||||
|
os.remove(f"{SAVING_DIRECTORY}{username}.json")
|
||||||
|
|
||||||
|
f = json.loads(open(f"{SAVING_DIRECTORY}public/list.json", "r").read())
|
||||||
|
if username in f:
|
||||||
|
f.remove(username)
|
||||||
|
g = open(f"{SAVING_DIRECTORY}public/list.json", "w")
|
||||||
|
g.write(json.dumps(f))
|
||||||
|
g.close()
|
||||||
|
|
||||||
|
return "200 OK"
|
||||||
|
|
||||||
def api_save():
|
def api_save():
|
||||||
username = open(f'{SAVING_DIRECTORY}tokens/{request.cookies["token"]}.txt', 'r').read()
|
username = open(f'{SAVING_DIRECTORY}tokens/{request.cookies["token"]}.txt', 'r').read()
|
||||||
x = json.loads(request.data)
|
x = json.loads(request.data)
|
||||||
|
@ -357,9 +391,9 @@ def api_save():
|
||||||
return "200 OK"
|
return "200 OK"
|
||||||
|
|
||||||
def api_browse():
|
def api_browse():
|
||||||
return return_dynamic_content_type(
|
return list_public(
|
||||||
json.dumps(list_public(request.args.get("sort"), int(request.args.get("page")) if "page" in request.args else 0)),
|
request.args.get("sort"), # type: ignore
|
||||||
"application/json"
|
int(request.args.get("page")) if "page" in request.args else 0 # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
def home():
|
def home():
|
||||||
|
@ -399,6 +433,7 @@ app.route("/login")(create_file_serve("login.html"))
|
||||||
app.route("/signup")(create_file_serve("signup.html"))
|
app.route("/signup")(create_file_serve("signup.html"))
|
||||||
app.route("/logout")(create_file_serve("logout.html"))
|
app.route("/logout")(create_file_serve("logout.html"))
|
||||||
app.route("/browse")(create_file_serve("browse.html"))
|
app.route("/browse")(create_file_serve("browse.html"))
|
||||||
|
app.route("/settings")(create_file_serve("settings.html"))
|
||||||
|
|
||||||
app.route("/editor")(create_file_serve("editor.html"))
|
app.route("/editor")(create_file_serve("editor.html"))
|
||||||
app.route("/u/<path:property>")(create_file_serve("user.html"))
|
app.route("/u/<path:property>")(create_file_serve("user.html"))
|
||||||
|
@ -411,6 +446,8 @@ app.route("/api/account/login", methods=["POST"])(api_account_login)
|
||||||
app.route("/api/account/signup", methods=["POST"])(api_account_signup)
|
app.route("/api/account/signup", methods=["POST"])(api_account_signup)
|
||||||
app.route("/api/account/info/<path:user>", methods=["GET"])(api_account_info_)
|
app.route("/api/account/info/<path:user>", methods=["GET"])(api_account_info_)
|
||||||
app.route("/api/account/self", methods=["GET"])(api_account_self)
|
app.route("/api/account/self", methods=["GET"])(api_account_self)
|
||||||
|
app.route("/api/account/change", methods=["POST"])(api_account_change)
|
||||||
|
app.route("/api/account/delete", methods=["DELETE"])(api_account_delete)
|
||||||
app.route("/api/save", methods=["PATCH"])(api_save)
|
app.route("/api/save", methods=["PATCH"])(api_save)
|
||||||
app.route("/api/browse", methods=["GET"])(api_browse)
|
app.route("/api/browse", methods=["GET"])(api_browse)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
.home-container {
|
.home-container {
|
||||||
width: 20em;
|
width: 20em;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
margin: 0 auto;
|
margin: 10px auto;
|
||||||
border: 2px var(--secondary) solid;
|
border: 2px var(--secondary) solid;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
|
@ -14,6 +14,5 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<script async src="/js/editor.js"></script>
|
<script async src="/js/editor.js"></script>
|
||||||
<a href="/logout">Log Out</a>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -42,6 +42,14 @@
|
||||||
Shortened url:<br>
|
Shortened url:<br>
|
||||||
https://infopg.web.app/u/{{USERNAME}}
|
https://infopg.web.app/u/{{USERNAME}}
|
||||||
</div>
|
</div>
|
||||||
|
<div tabindex="5" onclick="window.location.href = '/logout'" class="home-container">
|
||||||
|
<p>Log out</p>
|
||||||
|
You can always log back in
|
||||||
|
</div>
|
||||||
|
<div tabindex="6" onclick="window.location.href = '/settings'" class="home-container">
|
||||||
|
<p>User Settings</p>
|
||||||
|
Change password, delete acc, etc.
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
[...document.querySelectorAll("[onclick][tabindex]")].forEach(function(val, index) {
|
[...document.querySelectorAll("[onclick][tabindex]")].forEach(function(val, index) {
|
||||||
|
|
68
public/js/settings.js
Normal file
68
public/js/settings.js
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
|
||||||
|
let c = 0;
|
||||||
|
|
||||||
|
function log(str) {
|
||||||
|
c++;
|
||||||
|
document.getElementById("log").innerText = str;
|
||||||
|
setTimeout(
|
||||||
|
function() {
|
||||||
|
--c;
|
||||||
|
if (!c) {
|
||||||
|
document.getElementById("log").innerText = " ";
|
||||||
|
}
|
||||||
|
}, 3000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dom("submit").addEventListener("click", function() {
|
||||||
|
let current = sha256(dom("current").value);
|
||||||
|
let newPass = sha256(dom("new").value);
|
||||||
|
let verify = sha256(dom("verify").value);
|
||||||
|
|
||||||
|
if (newPass == verify && newPass && dom("new").value) {
|
||||||
|
fetch("/api/account/change", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
current: current,
|
||||||
|
new: newPass
|
||||||
|
})
|
||||||
|
}).then(response => {
|
||||||
|
if (response.status == 200) {
|
||||||
|
log("Success! Password has been changed! You will need to log in on any other devices again.");
|
||||||
|
response.json().then(json => {
|
||||||
|
localStorage.setItem("token", json.token);
|
||||||
|
setCookie("token", json.token);
|
||||||
|
});
|
||||||
|
dom("current").value = "";
|
||||||
|
} else if (response.status == 401) {
|
||||||
|
log("Your current password is incorrect!");
|
||||||
|
} else {
|
||||||
|
log("Something went wrong! Try again later.");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
log("Your passwords don't match!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dom("delete").addEventListener("click", function() {
|
||||||
|
if (confirm("Are you 100% SURE you want to PERMANENTLY DELETE your account?")) {
|
||||||
|
let x = prompt("By typing in your password below to confirm your identity, you acknowledge that this action is 100% IRREVERSIBLE.");
|
||||||
|
if (x) {
|
||||||
|
fetch("/api/account/delete", {
|
||||||
|
method: "DELETE",
|
||||||
|
body: JSON.stringify({
|
||||||
|
passhash: sha256(x)
|
||||||
|
})
|
||||||
|
}).then(response => {
|
||||||
|
if (response.status == 200) {
|
||||||
|
window.location.href = "/logout";
|
||||||
|
} else if (response.status == 401) {
|
||||||
|
log("Wrong password!");
|
||||||
|
} else {
|
||||||
|
log("Something went wrong! Try again later.")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -29,11 +29,6 @@ dom("submit").addEventListener("click", function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status == 429) {
|
|
||||||
dom("post").removeAttribute("disabled");
|
|
||||||
dom("post-text").removeAttribute("disabled");
|
|
||||||
showlog("You are being ratelimited! Try again in a few moments...");
|
|
||||||
} else {
|
|
||||||
response.json().then((json) => {
|
response.json().then((json) => {
|
||||||
if (json.valid) {
|
if (json.valid) {
|
||||||
setCookie("token", json.token);
|
setCookie("token", json.token);
|
||||||
|
@ -44,7 +39,6 @@ dom("submit").addEventListener("click", function() {
|
||||||
showlog(`Unable to create account! Reason: ${json.reason}`);
|
showlog(`Unable to create account! Reason: ${json.reason}`);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
dom("submit").removeAttribute("disabled");
|
dom("submit").removeAttribute("disabled");
|
||||||
|
|
37
public/settings.html
Normal file
37
public/settings.html
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Home - InfoPage</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="/css/base.css">
|
||||||
|
<script src="/js/base.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
if (localStorage.getItem("token")) {
|
||||||
|
setCookie("token", localStorage.getItem("token"));
|
||||||
|
} else {
|
||||||
|
window.location.href = "/logout";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Settings</h1>
|
||||||
|
<h2>Password change</h2>
|
||||||
|
<input id="current" type="password" placeholder="Current password..."><br>
|
||||||
|
<input id="new" type="password" placeholder="New password..."><br>
|
||||||
|
<input id="verify" type="password" placeholder="Verify password..."><br>
|
||||||
|
<button id="submit">Submit</button>
|
||||||
|
|
||||||
|
<p id="log"></p>
|
||||||
|
|
||||||
|
<h2>Delete account</h2>
|
||||||
|
<button id="delete">Delete account</button><br><br>
|
||||||
|
|
||||||
|
<a href="/home">Return home</a>
|
||||||
|
|
||||||
|
<script src="/js/settings.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue