First working prototype
This commit is contained in:
@@ -1,74 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Home</title>
|
||||
<title>"Password Manager"</title>
|
||||
<link rel="stylesheet" href="../assets/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="grid-container">
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<div class="logo-container">
|
||||
<h2 class="logo">PayDirt</h2>
|
||||
<p class="subtitle">NFL Stats</p>
|
||||
</div>
|
||||
<a class="button" href="/search">Search Players</a>
|
||||
<br>
|
||||
<div class="center">
|
||||
<form id="infoForm">
|
||||
<h2>Personal Information</h2>
|
||||
<hr>
|
||||
<div class="inputGroup">
|
||||
<p>Password Name</p>
|
||||
<input type="text" id="name" placeholder="">
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<a class="user-FirstName button" href="/info">Welcome, %</a>
|
||||
<button id="logoutButton">Log Out</button>
|
||||
<button id="deleteButton" style="display: none;">Delete Account</button>
|
||||
<div class="inputGroup">
|
||||
<p>Password:</p>
|
||||
<input type="text" id="password" placeholder="">
|
||||
</div>
|
||||
<div class="inputGroup">
|
||||
<p>Set password?</p>
|
||||
<input type="checkbox" id="set" name="Set?">
|
||||
</div>
|
||||
<button type="submit">Submit</button>
|
||||
<a href="/login">Log Out</a>
|
||||
</form>
|
||||
|
||||
<div class="error">
|
||||
<p>Error Message</p>
|
||||
</div>
|
||||
<div class="box nw">
|
||||
<p><b>Watched Players</b></p>
|
||||
<table class="body" id="favorites">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="box ne">
|
||||
<p><b>Highest Contracts</b></p>
|
||||
<table class="body" id="highest">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="box sw">
|
||||
<div class="cursor-question">
|
||||
<p><b>Highest PayDirt Score</b></p>
|
||||
<span class="tooltip-text">A player's <b>PayDirt score</b> is calculated by weighting their offense score by their annual salary. It is used to evaluate the worth of their contract.</span>
|
||||
</div>
|
||||
<table class="body" id="paydirt">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="box se">
|
||||
<div class="cursor-question">
|
||||
<p><b>Highest Offense Score</b></p>
|
||||
<span class="tooltip-text">A player's <b>offense score</b> is calculated by combining several of their offense stats into a weighted score. It is indicative of their overall performance.</span>
|
||||
</div>
|
||||
<table class="body" id="offense">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="success">
|
||||
<p>Password successfully retrieved</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="module" src="variables.js"></script>
|
||||
<script type="module" src="home.js"></script>
|
||||
<script type="module" src="logout.js"></script>
|
||||
<script type="module" src="delete.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
343
public/home.js
343
public/home.js
@@ -1,212 +1,185 @@
|
||||
import { postData, verifyLogin } from "./client.js";
|
||||
import { formatSalary } from "./utils.js";
|
||||
|
||||
verifyLogin();
|
||||
const token = window.localStorage.getItem("token");
|
||||
|
||||
if (!token) {
|
||||
window.location.href = "/login";
|
||||
const infoForm = document.getElementById("infoForm");
|
||||
|
||||
const error = document.querySelector(".error");
|
||||
const errorMessage = document.querySelector(".error p");
|
||||
const success = document.querySelector(".success");
|
||||
const successMessage = document.querySelector(".success p");
|
||||
|
||||
openKeyDB();
|
||||
const encryptionKey = await getEncryptionKey()
|
||||
|
||||
//console.log(`Encryption Key:`);
|
||||
//console.log(encryptionKey);
|
||||
|
||||
async function handlePasswordSubmission(e) {
|
||||
console.log(await verifyLogin());
|
||||
e.preventDefault();
|
||||
const formData = {
|
||||
name: document.getElementById("name").value,
|
||||
password: document.getElementById("password").value,
|
||||
}
|
||||
const set = document.getElementById("set").checked;
|
||||
|
||||
console.log(set);
|
||||
|
||||
if (set) {
|
||||
//console.log("formdata SET so SETTING");
|
||||
setPassword(formData.name, formData.password);
|
||||
} else {
|
||||
//console.log("formdata GET so GETTING");
|
||||
getPassword(formData.name);
|
||||
}
|
||||
}
|
||||
|
||||
const highest = document.querySelector("#highest");
|
||||
infoForm.onsubmit = handlePasswordSubmission;
|
||||
|
||||
updateHighest();
|
||||
updateFavorites();
|
||||
updatePaydirt();
|
||||
updateOffense();
|
||||
async function setPassword(name, password) {
|
||||
const encoder = new TextEncoder();
|
||||
const encodedData = encoder.encode(password);
|
||||
|
||||
window.addEventListener("pageshow", e => {
|
||||
if (e.persisted) {
|
||||
updateFavorites();
|
||||
}
|
||||
})
|
||||
const iv = window.crypto.getRandomValues(new Uint8Array(12));
|
||||
|
||||
async function updateOffense() {
|
||||
if (!verifyLogin()) return;
|
||||
const cyphertextBuffer = await window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: iv
|
||||
},
|
||||
encryptionKey,
|
||||
encodedData
|
||||
);
|
||||
|
||||
|
||||
const offenseHeader = document.querySelector("#offense thead");
|
||||
const offenseBody = document.querySelector("#offense tbody");
|
||||
|
||||
|
||||
let resultObject = await postData("/getHighestOffense", { amount: 100 }, token);
|
||||
console.log(resultObject);
|
||||
|
||||
if (resultObject.matches.length === 0) {
|
||||
alert("Error loading scores");
|
||||
return;
|
||||
const data = {
|
||||
name: name,
|
||||
iv: bufferToBase64(iv),
|
||||
password: bufferToBase64(cyphertextBuffer)
|
||||
}
|
||||
|
||||
offenseHeader.innerHTML = "";
|
||||
const headerRow = document.createElement("tr");
|
||||
//Object.keys(resultObject.matches[0]).forEach(attribute => {
|
||||
// headerRow.innerHTML += `<td>${attribute}</td>`;
|
||||
//});
|
||||
headerRow.innerHTML = `
|
||||
<td>Rank</td>
|
||||
<td>Position</td>
|
||||
<td>Name</td>
|
||||
<td>Team</td>
|
||||
<td>Offense Score</td>
|
||||
`;
|
||||
offenseHeader.appendChild(headerRow);
|
||||
var resultObject = await postData("/setPassword", data, token);
|
||||
|
||||
offenseBody.innerHTML = "";
|
||||
let i = 0;
|
||||
resultObject.matches.forEach(player => {
|
||||
i++;
|
||||
const row = document.createElement("tr");
|
||||
|
||||
row.innerHTML = `
|
||||
<td>${i}</td>
|
||||
<td>${player.Position}</td>
|
||||
<td><a href="/player/${player.PlayerID}">${player.PlayerName}</a></td>
|
||||
<td>${player.Team}</td>
|
||||
<td>${Math.floor(player.OffenseScore)}</td>
|
||||
`;
|
||||
offenseBody.appendChild(row);
|
||||
});
|
||||
};
|
||||
|
||||
async function updatePaydirt() {
|
||||
if (!verifyLogin()) return;
|
||||
|
||||
|
||||
const paydirtHeader = document.querySelector("#paydirt thead");
|
||||
const paydirtBody = document.querySelector("#paydirt tbody");
|
||||
|
||||
|
||||
let resultObject = await postData("/getHighestPaydirt", { amount: 100 }, token);
|
||||
console.log(resultObject);
|
||||
|
||||
if (resultObject.matches.length === 0) {
|
||||
alert("Error loading scores");
|
||||
return;
|
||||
if (resultObject.message.includes("UNIQUE")) {
|
||||
resultObject = await postData("/updatePassword", data, token);
|
||||
}
|
||||
|
||||
paydirtHeader.innerHTML = "";
|
||||
const headerRow = document.createElement("tr");
|
||||
//Object.keys(resultObject.matches[0]).forEach(attribute => {
|
||||
// headerRow.innerHTML += `<td>${attribute}</td>`;
|
||||
//});
|
||||
headerRow.innerHTML = `
|
||||
<td>Rank</td>
|
||||
<td>Position</td>
|
||||
<td>Name</td>
|
||||
<td>Team</td>
|
||||
<td>Paydirt Score</td>
|
||||
`;
|
||||
paydirtHeader.appendChild(headerRow);
|
||||
if (resultObject.success) {
|
||||
error.style.display = "none";
|
||||
success.style.display = "flex";
|
||||
successMessage.innerHTML = `Password for "${name}" successfully set to "${password}"`
|
||||
} else {
|
||||
errorMessage.innerHTML = resultObject.message;
|
||||
success.style.display = "none";
|
||||
error.style.display = "flex";
|
||||
}
|
||||
}
|
||||
|
||||
paydirtBody.innerHTML = "";
|
||||
let i = 0;
|
||||
resultObject.matches.forEach(player => {
|
||||
i++;
|
||||
const row = document.createElement("tr");
|
||||
async function getPassword(name) {
|
||||
const data = {
|
||||
name: name
|
||||
}
|
||||
const resultObject = await postData("/getPassword", data, token);
|
||||
|
||||
row.innerHTML = `
|
||||
<td>${i}</td>
|
||||
<td>${player.Position}</td>
|
||||
<td><a href="/player/${player.PlayerID}">${player.PlayerName}</a></td>
|
||||
<td>${player.Team}</td>
|
||||
<td>${Math.floor(player.PaydirtScore)}</td>
|
||||
`;
|
||||
paydirtBody.appendChild(row);
|
||||
});
|
||||
};
|
||||
|
||||
async function updateHighest() {
|
||||
if (!verifyLogin()) return;
|
||||
|
||||
const tableHeader = document.querySelector("#highest thead");
|
||||
const tableBody = document.querySelector("#highest tbody");
|
||||
|
||||
let resultObject = await postData("/getHighest", { amount: 100 }, token);
|
||||
console.log(resultObject);
|
||||
|
||||
if (resultObject.matches.length === 0) {
|
||||
alert("Error loading highest");
|
||||
return;
|
||||
if (resultObject.success) {
|
||||
error.style.display = "none";
|
||||
success.style.display = "flex";
|
||||
} else {
|
||||
errorMessage.innerHTML = resultObject.message;
|
||||
success.style.display = "none";
|
||||
error.style.display = "flex";
|
||||
return;
|
||||
}
|
||||
|
||||
tableHeader.innerHTML = "";
|
||||
const headerRow = document.createElement("tr");
|
||||
//Object.keys(resultObject.matches[0]).forEach(attribute => {
|
||||
// headerRow.innerHTML += `<td>${attribute}</td>`;
|
||||
//});
|
||||
headerRow.innerHTML = `
|
||||
<td>Rank</td>
|
||||
<td>Position</td>
|
||||
<td>Name</td>
|
||||
<td>Team</td>
|
||||
<td>Contract</td>
|
||||
<td>Annual Salary</td>
|
||||
`;
|
||||
tableHeader.appendChild(headerRow);
|
||||
const ivBuffer = base64ToBuffer(resultObject.iv);
|
||||
const cyphertextBuffer = base64ToBuffer(resultObject.password);
|
||||
|
||||
tableBody.innerHTML = "";
|
||||
let i = 0;
|
||||
resultObject.matches.forEach(player => {
|
||||
i++;
|
||||
const row = document.createElement("tr");
|
||||
|
||||
//for (attribute in player) {
|
||||
// row.innerHTML += `<td>${player}</td>l`;
|
||||
//}
|
||||
row.innerHTML = `
|
||||
<td>${i}</td>
|
||||
<td>${player.Position}</td>
|
||||
<td><a href="/player/${player.PlayerID}">${player.PlayerName}</a></td>
|
||||
<td>${player.Team}</td>
|
||||
<td>${formatSalary(player.TrueAvgPerYear * player.Years)}</td>
|
||||
<td>${formatSalary(player.TrueAvgPerYear)}</td>
|
||||
`;
|
||||
tableBody.appendChild(row);
|
||||
});
|
||||
};
|
||||
|
||||
async function updateFavorites() {
|
||||
if (!verifyLogin()) return;
|
||||
|
||||
const tableHeader = document.querySelector("#favorites thead");
|
||||
const tableBody = document.querySelector("#favorites tbody");
|
||||
const decryptedBuffer = await window.crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: ivBuffer
|
||||
},
|
||||
encryptionKey,
|
||||
cyphertextBuffer
|
||||
);
|
||||
|
||||
let resultObject = await postData("/getWatchlist", {}, token);
|
||||
const decoder = new TextDecoder();
|
||||
const pw = decoder.decode(decryptedBuffer);
|
||||
document.getElementById("password").value = pw;
|
||||
//console.log("Password: " + pw);
|
||||
|
||||
tableHeader.innerHTML = "";
|
||||
const headerRow = document.createElement("tr");
|
||||
//Object.keys(resultObject.matches[0]).forEach(attribute => {
|
||||
// headerRow.innerHTML += `<td>${attribute}</td>`;
|
||||
//});
|
||||
headerRow.innerHTML = `
|
||||
<td>Rank</td>
|
||||
<td>Position</td>
|
||||
<td>Name</td>
|
||||
<td>Team</td>
|
||||
<td>Remove</td>
|
||||
`;
|
||||
tableHeader.appendChild(headerRow);
|
||||
//console.log(resultObject);
|
||||
|
||||
tableBody.innerHTML = "";
|
||||
console.log(resultObject.watchlist);
|
||||
let i = 0;
|
||||
resultObject.watchlist.forEach(player => {
|
||||
i++;
|
||||
const row = document.createElement("tr");
|
||||
successMessage.innerHTML = `Password for "${name}" is "${pw}"`
|
||||
}
|
||||
|
||||
row.innerHTML = `
|
||||
<td>${i}</td>
|
||||
<td>${player.Position}</td>
|
||||
<td><a href="/player/${player.PlayerID}">${player.PlayerName}</a></td>
|
||||
<td>${player.Team}</td>
|
||||
<td><a href="#" class="remove" data-id="${player.PlayerID}">Stop Watching</a></td>
|
||||
`;
|
||||
tableBody.appendChild(row);
|
||||
|
||||
|
||||
|
||||
function openKeyDB() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open("PasswordManagerVault", 1);
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
if (!db.objectStoreNames.contains("cryptoKeys")) {
|
||||
db.createObjectStore("cryptoKeys");
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll(".remove").forEach(element => {
|
||||
element.addEventListener("click", async e => {
|
||||
postData("/toggleWatched", { id: element.dataset.id }, token);
|
||||
element.closest("tr").remove();
|
||||
})
|
||||
async function saveEncryptionKey(cryptoKeyObject) {
|
||||
const db = await openKeyDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(["cryptoKeys"], "readwrite");
|
||||
const store = transaction.objectStore("cryptoKeys");
|
||||
|
||||
// We store the object under the label "masterEncryptionKey"
|
||||
const request = store.put(cryptoKeyObject, "masterEncryptionKey");
|
||||
|
||||
request.onsuccess = () => resolve(true);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
async function getEncryptionKey() {
|
||||
const db = await openKeyDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(["cryptoKeys"], "readonly");
|
||||
const store = transaction.objectStore("cryptoKeys");
|
||||
|
||||
const request = store.get("masterEncryptionKey");
|
||||
|
||||
request.onsuccess = () => {
|
||||
if (request.result) {
|
||||
resolve(request.result);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
};
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
function bufferToBase64(buffer) {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
let binary = '';
|
||||
for (let i = 0; i < bytes.byteLength; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return window.btoa(binary);
|
||||
}
|
||||
|
||||
function base64ToBuffer(base64) {
|
||||
const binaryString = window.atob(base64);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
118
public/login.js
118
public/login.js
@@ -6,11 +6,28 @@ const error = document.querySelector(".error");
|
||||
const errorMessage = document.querySelector(".error p");
|
||||
const success = document.querySelector(".success");
|
||||
|
||||
openKeyDB();
|
||||
|
||||
if (loginForm) loginForm.onsubmit = async e => {
|
||||
e.preventDefault();
|
||||
|
||||
let saltObject = await postData("/getSalt", {
|
||||
username: document.getElementById("logUser").value
|
||||
});
|
||||
|
||||
if (saltObject.message == "User not found") {
|
||||
errorMessage.innerHTML = saltObject.message;
|
||||
error.style.display = "flex";
|
||||
return;
|
||||
}
|
||||
|
||||
const { authenticationKeyString, encryptionKeyObject } = await generateKeys(document.getElementById("logPass").value, hexToUint8Array(saltObject.salt));
|
||||
|
||||
saveEncryptionKey(encryptionKeyObject);
|
||||
|
||||
let resultObject = await postData("/login", {
|
||||
username: document.getElementById("logUser").value,
|
||||
password: document.getElementById("logPass").value
|
||||
password: authenticationKeyString
|
||||
});
|
||||
if (resultObject.message.includes("success")) {
|
||||
error.style.display = "none";
|
||||
@@ -24,3 +41,102 @@ if (loginForm) loginForm.onsubmit = async e => {
|
||||
localStorage.setItem("token", resultObject.token);
|
||||
}
|
||||
};
|
||||
|
||||
async function generateKeys(password, salt) {
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
const base = await window.crypto.subtle.importKey(
|
||||
"raw",
|
||||
encoder.encode(password),
|
||||
"PBKDF2",
|
||||
false,
|
||||
["deriveBits", "deriveKey"]
|
||||
);
|
||||
|
||||
const authSalt = new Uint8Array([...salt, ...encoder.encode("auth")]);
|
||||
const authKeyBits = await window.crypto.subtle.deriveBits(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt: authSalt,
|
||||
iterations: 100000,
|
||||
hash: "SHA-256"
|
||||
},
|
||||
base,
|
||||
256
|
||||
);
|
||||
|
||||
const encryptionSalt = new Uint8Array([...salt, ...encoder.encode("enc")]);
|
||||
const encryptionKey = await window.crypto.subtle.deriveKey(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt: encryptionSalt,
|
||||
iterations: 100000,
|
||||
hash: "SHA-256"
|
||||
},
|
||||
base,
|
||||
{ name: "AES-GCM", length: 256 },
|
||||
false,
|
||||
["encrypt", "decrypt"]
|
||||
);
|
||||
|
||||
console.log(convertBufferToHex(authKeyBits));
|
||||
console.log(encryptionKey);
|
||||
|
||||
return {
|
||||
authenticationKeyString: convertBufferToHex(authKeyBits),
|
||||
encryptionKeyObject: encryptionKey
|
||||
};
|
||||
}
|
||||
|
||||
function openKeyDB() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open("PasswordManagerVault", 1);
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
if (!db.objectStoreNames.contains("cryptoKeys")) {
|
||||
db.createObjectStore("cryptoKeys");
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
async function saveEncryptionKey(cryptoKeyObject) {
|
||||
const db = await openKeyDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(["cryptoKeys"], "readwrite");
|
||||
const store = transaction.objectStore("cryptoKeys");
|
||||
|
||||
// We store the object under the label "masterEncryptionKey"
|
||||
const request = store.put(cryptoKeyObject, "masterEncryptionKey");
|
||||
|
||||
request.onsuccess = () => resolve(true);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
function convertBufferToHex(buffer) {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
|
||||
return Array.from(bytes)
|
||||
.map(byte => byte.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
function hexToUint8Array(hexString) {
|
||||
if (hexString.length % 2 !== 0) {
|
||||
throw new Error("Invalid hex string");
|
||||
}
|
||||
|
||||
const array = new Uint8Array(hexString.length / 2);
|
||||
|
||||
for (let i = 0; i < hexString.length; i += 2) {
|
||||
array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
74
public/paydirt.html
Normal file
74
public/paydirt.html
Normal file
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Home</title>
|
||||
<link rel="stylesheet" href="../assets/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="grid-container">
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<div class="logo-container">
|
||||
<h2 class="logo">PayDirt</h2>
|
||||
<p class="subtitle">NFL Stats</p>
|
||||
</div>
|
||||
<a class="button" href="/search">Search Players</a>
|
||||
<br>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<a class="user-FirstName button" href="/info">Welcome, %</a>
|
||||
<button id="logoutButton">Log Out</button>
|
||||
<button id="deleteButton" style="display: none;">Delete Account</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="box nw">
|
||||
<p><b>Watched Players</b></p>
|
||||
<table class="body" id="favorites">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="box ne">
|
||||
<p><b>Highest Contracts</b></p>
|
||||
<table class="body" id="highest">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="box sw">
|
||||
<div class="cursor-question">
|
||||
<p><b>Highest PayDirt Score</b></p>
|
||||
<span class="tooltip-text">A player's <b>PayDirt score</b> is calculated by weighting their offense score by their annual salary. It is used to evaluate the worth of their contract.</span>
|
||||
</div>
|
||||
<table class="body" id="paydirt">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="box se">
|
||||
<div class="cursor-question">
|
||||
<p><b>Highest Offense Score</b></p>
|
||||
<span class="tooltip-text">A player's <b>offense score</b> is calculated by combining several of their offense stats into a weighted score. It is indicative of their overall performance.</span>
|
||||
</div>
|
||||
<table class="body" id="offense">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module" src="variables.js"></script>
|
||||
<script type="module" src="home.js"></script>
|
||||
<script type="module" src="logout.js"></script>
|
||||
<script type="module" src="delete.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -8,16 +8,27 @@ const success = document.querySelector(".success");
|
||||
|
||||
if (registerForm) registerForm.onsubmit = async e => {
|
||||
e.preventDefault();
|
||||
|
||||
if (registerForm.regPass.value !== registerForm.regVerify.value) {
|
||||
error.style.display = "flex";
|
||||
errorMessage.innerHTML = "Passwords must match";
|
||||
return;
|
||||
}
|
||||
|
||||
const salt = window.crypto.getRandomValues(new Uint8Array(16));
|
||||
|
||||
|
||||
const { authenticationKeyString, encryptionKeyObject } = await generateKeys(registerForm.regPass.value, salt);
|
||||
|
||||
console.log(authenticationKeyString);
|
||||
console.log(encryptionKeyObject);
|
||||
|
||||
|
||||
let resultObject = await postData("/register", {
|
||||
username: registerForm.regUser.value,
|
||||
password: registerForm.regPass.value,
|
||||
role: registerForm.regRole.value
|
||||
password: authenticationKeyString,
|
||||
role: registerForm.regRole.value,
|
||||
salt: convertBufferToHex(salt)
|
||||
});
|
||||
if (resultObject.message.includes("User registered")) {
|
||||
error.style.display = "none";
|
||||
@@ -30,3 +41,57 @@ if (registerForm) registerForm.onsubmit = async e => {
|
||||
window.location.href = "/login";
|
||||
}
|
||||
};
|
||||
|
||||
async function generateKeys(password, salt) {
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
const base = await window.crypto.subtle.importKey(
|
||||
"raw",
|
||||
encoder.encode(password),
|
||||
"PBKDF2",
|
||||
false,
|
||||
["deriveBits", "deriveKey"]
|
||||
);
|
||||
|
||||
const authSalt = new Uint8Array([...salt, ...encoder.encode("auth")]);
|
||||
const authKeyBits = await window.crypto.subtle.deriveBits(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt: authSalt,
|
||||
iterations: 100000,
|
||||
hash: "SHA-256"
|
||||
},
|
||||
base,
|
||||
256
|
||||
);
|
||||
|
||||
const encryptionSalt = new Uint8Array([...salt, ...encoder.encode("enc")]);
|
||||
const encryptionKey = await window.crypto.subtle.deriveKey(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt: encryptionSalt,
|
||||
iterations: 100000,
|
||||
hash: "SHA-256"
|
||||
},
|
||||
base,
|
||||
{ name: "AES-GCM", length: 256 },
|
||||
false,
|
||||
["encrypt", "decrypt"]
|
||||
);
|
||||
|
||||
console.log(convertBufferToHex(authKeyBits));
|
||||
console.log(encryptionKey);
|
||||
|
||||
return {
|
||||
authenticationKeyString: convertBufferToHex(authKeyBits),
|
||||
encryptionKeyObject: encryptionKey
|
||||
};
|
||||
}
|
||||
|
||||
function convertBufferToHex(buffer) {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
return Array.from(bytes)
|
||||
.map(byte => byte.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user