add account deletion and password changing

This commit is contained in:
trinkey 2024-03-19 17:32:20 -04:00
parent 902e561a09
commit 22c337f8aa
8 changed files with 179 additions and 36 deletions

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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>

View file

@ -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
View 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.")
}
});
}
}
});

View file

@ -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
View 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>