Added password changing

This commit is contained in:
Tracker-Friendly 2024-06-27 17:57:05 +01:00
parent b1f854b70b
commit 0c35db92ba
3 changed files with 178 additions and 74 deletions

View File

@ -7,6 +7,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="/static/css/style.css" /> <link rel="stylesheet" type="text/css" href="/static/css/style.css" />
<script type="text/javascript" src="/static/js/hash-wasm.js"></script>
<script type="text/javascript" src="/static/js/crypto-js.js"></script> <script type="text/javascript" src="/static/js/crypto-js.js"></script>
<script type="text/javascript" src="/static/js/marked.js"></script> <script type="text/javascript" src="/static/js/marked.js"></script>
<script type="text/javascript" src="/static/js/purify.js"></script> <script type="text/javascript" src="/static/js/purify.js"></script>
@ -14,18 +15,16 @@
</head> </head>
<noscript> <noscript>
<head>
<meta http-equiv="refresh" content="0; url=/login"/> <meta http-equiv="refresh" content="0; url=/login"/>
</head>
</noscript> </noscript>
<body> <body>
<div class="topBar" id="topBar"> <div class="topBar" id="topBar">
<div class="modernToolbar"> <div class="modernToolbar">
<button class="usernameBox hidden" onclick="handleGesture()" id="backButton"><div class="vcenter"><img src="/static/svg/arrow-back.svg"></div></button> <button class="usernameBox hidden" onclick="handleGesture()" id="backButton"><div class="vcenter"><img alt="Back arrow" src="/static/svg/arrow-back.svg"></div></button>
<button class="count" id="wordCountBox">0 words</button> <button class="count" id="wordCountBox">0 words</button>
<button onclick="toggleMarkdown()" class="usernameBox"><div class="vcenter"><img src="/static/svg/markdown.svg"></div></button> <button onclick="toggleMarkdown()" class="usernameBox"><div class="vcenter"><img alt="Enable markdown" src="/static/svg/markdown.svg"></div></button>
<button id="usernameBox" class="usernameBox"><div class="vcenter"><img src="/static/svg/acct-settings.svg"></div></button> <button id="usernameBox" class="usernameBox"><div class="vcenter"><img alt="Account settings" src="/static/svg/acct-settings.svg"></div></button>
</div> </div>
</div> </div>
@ -33,10 +32,8 @@
<button id="newNote" class="newNote" style="margin-left: 5px;"> <button id="newNote" class="newNote" style="margin-left: 5px;">
<div class="vcenter"><img id="newNoteImage" draggable="false" alt="" src="/static/svg/add.svg"></div> <div class="vcenter"><img id="newNoteImage" draggable="false" alt="" src="/static/svg/add.svg"></div>
</button> </button>
<!--<button class="newNote" style="margin-left: 37.5px; background-color: var(--bar) !important;">-->
</button>
<button id="removeBox" class="newNote remove"> <button id="removeBox" class="newNote remove">
<div class="vcenter"><img id="newNoteImage" draggable="false" alt="" src="/static/svg/delete.svg"></div> <div class="vcenter"><img id="deleteNoteImage" draggable="false" alt="" src="/static/svg/delete.svg"></div>
</button> </button>
<div id="notesDiv" class="notesDiv"> <div id="notesDiv" class="notesDiv">
<button class="loadingStuff" id="loadingStuff">Meow :3</button> <button class="loadingStuff" id="loadingStuff">Meow :3</button>
@ -61,8 +58,9 @@
<progress id="storageProgressThing" value="0" max="100"></progress><br> <progress id="storageProgressThing" value="0" max="100"></progress><br>
<p id="storageThing">Cannot connect to the server</p> <p id="storageThing">Cannot connect to the server</p>
<div class="section"></div> <div class="section"></div>
<p>Account managment</p> <p>Account management</p>
<button id="deleteMyAccountButton"><img src="/static/svg/delete_forever.svg" alt="">Delete account</button> <button id="deleteMyAccountButton"><img src="/static/svg/delete_forever.svg" alt="">Delete account</button>
<button id="changePasswordButton"><img src="/static/svg/password.svg" alt="">Change password</button>
<button id="exportNotesButton"><img src="/static/svg/download.svg" alt="">Export notes</button> <button id="exportNotesButton"><img src="/static/svg/download.svg" alt="">Export notes</button>
<button id="importNotesButton"><img src="/static/svg/upload.svg" alt="">Import notes</button> <button id="importNotesButton"><img src="/static/svg/upload.svg" alt="">Import notes</button>
<button id="sessionManagerButton"><img src="/static/svg/list.svg" alt="">View sessions</button> <button id="sessionManagerButton"><img src="/static/svg/list.svg" alt="">View sessions</button>

View File

@ -34,6 +34,7 @@ let closeErrorButton = document.getElementById("closeErrorButton")
let cancelErrorButton = document.getElementById("cancelErrorButton") let cancelErrorButton = document.getElementById("cancelErrorButton")
let errorInput = document.getElementById("errorInput") let errorInput = document.getElementById("errorInput")
let exitThing = document.getElementById("exitThing") let exitThing = document.getElementById("exitThing")
let exitImportThing = document.getElementById("exitImportThing")
let exitSessionsThing = document.getElementById("exitSessionsThing") let exitSessionsThing = document.getElementById("exitSessionsThing")
let sessionManagerButton = document.getElementById("sessionManagerButton") let sessionManagerButton = document.getElementById("sessionManagerButton")
let importNotesButton = document.getElementById("importNotesButton") let importNotesButton = document.getElementById("importNotesButton")
@ -41,6 +42,7 @@ let sessionManagerDiv = document.getElementById("sessionManagerDiv")
let importNotesDiv = document.getElementById("importDiv") let importNotesDiv = document.getElementById("importDiv")
let sessionDiv = document.getElementById("sessionDiv") let sessionDiv = document.getElementById("sessionDiv")
let deleteMyAccountButton = document.getElementById("deleteMyAccountButton") let deleteMyAccountButton = document.getElementById("deleteMyAccountButton")
let changePasswordButton = document.getElementById("changePasswordButton")
let storageThing = document.getElementById("storageThing") let storageThing = document.getElementById("storageThing")
let storageProgressThing = document.getElementById("storageProgressThing") let storageProgressThing = document.getElementById("storageProgressThing")
let usernameThing = document.getElementById("usernameThing") let usernameThing = document.getElementById("usernameThing")
@ -112,16 +114,6 @@ if (/Android|iPhone|iPod/i.test(navigator.userAgent)) {
touchstartY = event.changedTouches[0].screenY; touchstartY = event.changedTouches[0].screenY;
}, false); }, false);
noteBox.addEventListener("touchend", function (event) {
touchendX = event.changedTouches[0].screenX;
touchendY = event.changedTouches[0].screenY;
if (touchendX > touchstartX + 75) {
handleGesture();
} else if (touchendX < touchstartX - 75) {
enableMarkdown();
}
}, false);
markdown.addEventListener("touchstart", function (event) { markdown.addEventListener("touchstart", function (event) {
touchstartX = event.changedTouches[0].screenX; touchstartX = event.changedTouches[0].screenX;
touchstartY = event.changedTouches[0].screenY; touchstartY = event.changedTouches[0].screenY;
@ -214,14 +206,14 @@ async function checknetwork() {
}) })
.then((response) => response) .then((response) => response)
.then((response) => { .then((response) => {
if (response.status == 400) { if (response.status === 400) {
displayError("Something went wrong! Signing you out...") displayError("Something went wrong! Signing you out...")
closeErrorButton.classList.add("hidden") closeErrorButton.classList.add("hidden")
//usernameBox.innerText = "" //usernameBox.innerText = ""
setTimeout(function () { setTimeout(function () {
window.location.replace("/logout") window.location.replace("/logout")
}, 2500); }, 2500);
} else if (response.status == 200) { } else if (response.status === 200) {
updateUserInfo() updateUserInfo()
} else { } else {
noteBox.readOnly = true noteBox.readOnly = true
@ -320,6 +312,129 @@ deleteMyAccountButton.addEventListener("click", () => {
}) })
} }
}); });
async function waitForConfirm() {
let resolvePromise;
const promise = new Promise(resolve => resolvePromise = resolve);
closeErrorButton.addEventListener("click", () => {
resolvePromise();
});
await promise;
}
async function hashpass(pass) {
let key = pass
for (let i = 0; i < 128; i++) {
key = await hashwasm.sha3(key)
}
return key
}
changePasswordButton.addEventListener("click", () => {
optionsDiv.classList.add("hidden")
async function doStuff() {
async function fatalError(notes, passwordBackup) {
displayError("Something went wrong! Your password change has failed. Attempting to revert changes...")
password = passwordBackup
localStorage.setItem("DONOTSHARE-password", password)
let changePasswordBackResponse = await fetch(remote + "/api/changepassword", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey,
newPassword: await hashpass(oldPass)
}),
headers: {
"Content-Type": "application/json; charset=UTF-8",
"X-Burgernotes-Version": "200"
}
})
if (changePasswordBackResponse.status === 200) {
let responseStatus = await importNotes(notes)
if (responseStatus === 500) {
closeErrorButton.classList.remove("hidden")
displayError("Failed to revert changes. Please delete this user account and sign-up again, then re-import the notes. Click Ok to download the notes to import later.")
await waitForConfirm()
downloadObjectAsJson(notes, "data")
} else {
closeErrorButton.classList.remove("hidden")
displayError("Password change failed! Changes have been reverted.")
updateNotes()
}
} else {
displayError("Failed to revert changes. Please delete this user account and sign-up again, then re-import the notes. Click Ok to download the notes to import later.")
downloadObjectAsJson(notes, "data")
}
}
displayError("Confirm your current password to change it")
errorInput.type = "password"
errorInput.classList.remove("hidden")
await waitForConfirm()
const oldPass = errorInput.value
errorInput.classList.add("hidden")
if (await hashwasm.sha512(oldPass) !== password) {
displayError("Incorrect password!")
} else {
errorInput.value = ""
displayError("Enter your new password")
errorInput.classList.remove("hidden")
await waitForConfirm()
errorInput.classList.add("hidden")
const newPass = errorInput.value
errorInput.type = "text"
errorInput.value = ""
if (newPass.length < 8) {
displayError("Password must be at least 8 characters!")
} else {
displayError("Changing your password. This process may take up to 5 minutes. Do NOT close the tab!")
closeErrorButton.classList.add("hidden")
const response = await fetch(remote + "/api/changepassword", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey,
newPassword: await hashpass(newPass)
}),
headers: {
"Content-Type": "application/json; charset=UTF-8",
"X-Burgernotes-Version": "200"
}
})
if (response.status === 200) {
let notes = await exportNotes()
let passwordBackup = password
password = await hashwasm.sha512(newPass)
localStorage.setItem("DONOTSHARE-password", password)
let purgeNotes = await fetch(remote + "/api/purgenotes", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
if (purgeNotes.status !== 200) {
fatalError(notes, passwordBackup)
} else {
let responseStatus = await importNotes(notes)
errorDiv.classList.add("hidden")
if (responseStatus !== 200) {
fatalError(notes, passwordBackup)
} else {
closeErrorButton.classList.remove("hidden")
displayError("Password changed!")
updateNotes()
}
}
} else {
closeErrorButton.classList.remove("hidden")
const data = await response.json()
console.log(data)
displayError(data["error"])
}
}
}
}
doStuff()
})
importNotesButton.addEventListener("click", () => { importNotesButton.addEventListener("click", () => {
optionsDiv.classList.add("hidden") optionsDiv.classList.add("hidden")
importNotesDiv.classList.remove("hidden") importNotesDiv.classList.remove("hidden")
@ -523,7 +638,7 @@ function updateNotes() {
async function doStuff() { async function doStuff() {
noteBox.readOnly = true noteBox.readOnly = true
selectedNote = 0 selectedNote = 0
if (selectLatestNote == false) { if (selectLatestNote === false) {
noteBox.placeholder = "" noteBox.placeholder = ""
} }
noteBox.value = "" noteBox.value = ""
@ -538,13 +653,12 @@ function updateNotes() {
let highestID = 0 let highestID = 0
// First decrypt note data, then render // First decrypt note data, then render
let noteData;
for (let i in responseData) { for (let i in responseData) {
noteData = responseData[i] noteData = responseData[i]
let bytes = CryptoJS.AES.decrypt(noteData["title"], password); let bytes = CryptoJS.AES.decrypt(noteData["title"], password);
let decryptedTitle = bytes.toString(CryptoJS.enc.Utf8); noteData["title"] = bytes.toString(CryptoJS.enc.Utf8)
noteData["title"] = decryptedTitle
if (noteData["id"] > highestID) { if (noteData["id"] > highestID) {
highestID = noteData["id"] highestID = noteData["id"]
@ -564,7 +678,7 @@ function updateNotes() {
console.log(noteData["title"]) console.log(noteData["title"])
if (noteData["title"] == "") { if (noteData["title"] === "") {
console.log(noteData["title"]) console.log(noteData["title"])
console.log("case") console.log("case")
noteData["title"] = "New note" noteData["title"] = "New note"
@ -598,7 +712,7 @@ function updateNotes() {
} }
document.querySelectorAll(".loadingStuff").forEach((el) => el.remove()); document.querySelectorAll(".loadingStuff").forEach((el) => el.remove());
if (selectLatestNote == true) { if (selectLatestNote === true) {
selectNote(highestID) selectNote(highestID)
selectLatestNote = false selectLatestNote = false
} }
@ -621,7 +735,7 @@ newNote.addEventListener("click", () => {
noteButton.classList.add("noteButton") noteButton.classList.add("noteButton")
notesDiv.append(noteButton) notesDiv.append(noteButton)
noteButton.innerText = "New note" noteButton.innerText = "New note"
noteButton.style.order = -1 noteButton.style.order = "-1"
noteButton.classList.add("selected") noteButton.classList.add("selected")
noteBox.placeholder = "Type something!" noteBox.placeholder = "Type something!"
@ -650,7 +764,7 @@ newNote.addEventListener("click", () => {
}); });
}); });
function downloadObjectAsJson(exportObj, exportName) { function downloadObjectAsJson(exportObj, exportName) {
let dataStr = "data:text/json;charset=utf-8," + DOMPurify.sanitize(JSON.stringify(exportObj)); let dataStr = "data:text/json;charset=utf-8," + JSON.stringify(exportObj);
let downloadAnchorNode = document.createElement("a"); let downloadAnchorNode = document.createElement("a");
downloadAnchorNode.setAttribute("href", dataStr); downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", exportName + ".json"); downloadAnchorNode.setAttribute("download", exportName + ".json");
@ -659,8 +773,8 @@ function downloadObjectAsJson(exportObj, exportName) {
downloadAnchorNode.remove(); downloadAnchorNode.remove();
} }
function exportNotes() { async function exportNotes() {
fetch(remote + "/api/exportnotes", { let exportNotesFetch = await fetch(remote + "/api/exportnotes", {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
secretKey: secretkey secretKey: secretkey
@ -669,9 +783,7 @@ function exportNotes() {
"Content-Type": "application/json; charset=UTF-8" "Content-Type": "application/json; charset=UTF-8"
} }
}) })
.then((response) => { let responseData = await exportNotesFetch.json()
async function doStuff() {
let responseData = await response.json()
for (let i in responseData) { for (let i in responseData) {
exportNotes.innerText = "Decrypting " + i + "/" + noteCount exportNotes.innerText = "Decrypting " + i + "/" + noteCount
@ -681,26 +793,18 @@ function exportNotes() {
let bytesd = CryptoJS.AES.decrypt(responseData[i]["content"], password); let bytesd = CryptoJS.AES.decrypt(responseData[i]["content"], password);
responseData[i]["content"] = bytesd.toString(CryptoJS.enc.Utf8) responseData[i]["content"] = bytesd.toString(CryptoJS.enc.Utf8)
} }
let jsonString = JSON.parse(JSON.stringify(responseData)) return responseData
downloadObjectAsJson(jsonString, "data")
optionsDiv.classList.add("hidden")
displayError("Exported notes!")
}
doStuff()
})
} }
function importNotes(plaintextNotes) { async function importNotes(plaintextNotes) {
for (let i in plaintextNotes) { for (let i in plaintextNotes) {
let originalTitle = plaintextNotes[i]["title"]; let originalTitle = plaintextNotes[i]["title"];
let encryptedTitle = CryptoJS.AES.encrypt(originalTitle, password).toString(); plaintextNotes[i]["title"] = CryptoJS.AES.encrypt(originalTitle, password).toString();
plaintextNotes[i]["title"] = encryptedTitle;
let originalContent = plaintextNotes[i]["content"]; let originalContent = plaintextNotes[i]["content"];
let encryptedContent = CryptoJS.AES.encrypt(originalContent, password).toString(); plaintextNotes[i]["content"] = CryptoJS.AES.encrypt(originalContent, password).toString();
plaintextNotes[i]["content"] = encryptedContent;
} }
fetch(remote + "/api/importnotes", { let importNotesFetch = await fetch(remote + "/api/importnotes", {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
"secretKey": localStorage.getItem("DONOTSHARE-secretkey"), "secretKey": localStorage.getItem("DONOTSHARE-secretkey"),
@ -710,21 +814,7 @@ function importNotes(plaintextNotes) {
"Content-Type": "application/json; charset=UTF-8" "Content-Type": "application/json; charset=UTF-8"
} }
}) })
.then((response) => { return importNotesFetch.status
async function doStuff() {
if (response.status === 500) {
optionsDiv.classList.add("hidden")
importNotesDiv.classList.add("hidden")
displayError("Something went wrong! Perhaps your note file was invalid?")
} else {
optionsDiv.classList.add("hidden")
importNotesDiv.classList.add("hidden")
displayError("Notes uploaded!")
updateNotes()
}
}
doStuff()
})
} }
function firstNewVersion() { function firstNewVersion() {
@ -757,16 +847,31 @@ function disableMarkdown() {
} }
exportNotesButton.addEventListener("click", () => { exportNotesButton.addEventListener("click", () => {
exportNotes() let responseData = exportNotes()
downloadObjectAsJson(responseData, "data")
optionsDiv.classList.add("hidden")
displayError("Exported notes!")
}); });
importFile.addEventListener('change', function(e) { importFile.addEventListener('change', function() {
let fileread = new FileReader() let fileread = new FileReader()
fileread.addEventListener( fileread.addEventListener(
"load", "load",
() => { () => {
let decrypted = JSON.parse(fileread.result) let decrypted = JSON.parse(fileread.result.toString())
importNotes(decrypted) importNotes(decrypted)
.then((responseStatus) => {
if (responseStatus === 500) {
optionsDiv.classList.add("hidden")
importNotesDiv.classList.add("hidden")
displayError("Something went wrong! Perhaps your note file was invalid?")
} else {
optionsDiv.classList.add("hidden")
importNotesDiv.classList.add("hidden")
displayError("Notes uploaded!")
updateNotes()
}
})
}, },
false, false,
); );

1
static/svg/password.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#000000"><path d="M80-200v-67.33h800V-200H80Zm40.67-250-44-24.67 38-65.33H40v-50h74.67l-38-64.67 44-24.66 36.66 64 36.67-64 44 24.66L200-590h74.67v50H200l38 65.33L194-450l-36.67-64.67L120.67-450Zm322.66 0-44-26 38-65.33h-74.66v-50h74.66l-38-64.67 44-24.67 36.67 64 36.67-64 44 24.67-38 64.67h74.66v50h-74.66l38 65.33-44 26L480-514.67 443.33-450ZM766-450l-44-26 38-65.33h-74.67v-50H760L722-656l44-24.67 36.67 64 36.66-64 44 24.67-38 64.67H920v50h-74.67l38 65.33-44 26-36.66-64.67L766-450Z"/></svg>

After

Width:  |  Height:  |  Size: 594 B