Started work on URL-Token based password reset system. Email not yet implemented.

This commit is contained in:
rainbow napkin 2024-12-28 04:30:08 -05:00
parent 8ee92541de
commit ed698f40c7
22 changed files with 580 additions and 16 deletions

View file

@ -96,10 +96,10 @@ span.emote-list-trash-icon{
top: -0.5em;
right: -0.5em;
z-index: 1;
align-items: center;
justify-content: center;
}
i.emote-list-trash-icon{
flex: 1;
text-align: center;
margin: auto;
}

35
www/css/passwordReset.css Normal file
View file

@ -0,0 +1,35 @@
/*Canopy - The next generation of stoner streaming software
Copyright (C) 2024 Rainbownapkin and the TTN Community
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
h3, p{
text-align: center;
}
form{
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5em;
margin: 0 17%;
}
.reset-pass-prompt{
width: 100%
}
#reset-pass-button{
width: 6em;
height: 3em;
}

View file

@ -59,6 +59,7 @@ p.profile-toke-count{
min-height: 1.5em;
max-height: 5.8em;
display: none;
border-bottom-right-radius: 0;
}
/*Little hacky but this keeps initial max-height from fucking up resizing*/

View file

@ -90,6 +90,24 @@ class canopyAdminUtils{
}
}
async genPasswordResetLink(user){
var response = await fetch(`/api/admin/genPasswordReset`,{
method: "POST",
headers: {
"Content-Type": "application/json"
},
//Unfortunately JSON doesn't natively handle ES6 maps, and god forbid someone update the standard in a way that's backwards compatible...
body: JSON.stringify({user})
});
if(response.status == 200){
return await response.json();
}else{
utils.ux.displayResponseError(await response.json());
}
}
async setPermission(permMap){
var response = await fetch(`/api/admin/permissions`,{
method: "POST",

82
www/js/passwordReset.js Normal file
View file

@ -0,0 +1,82 @@
/*Canopy - The next generation of stoner streaming software
Copyright (C) 2024 Rainbownapkin and the TTN Community
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
class registerPrompt{
constructor(){
//Grab user prompt
this.user = document.querySelector("#reset-pass-username-prompt");
//Detect if we're initiating or completing a password request
this.initiating = this.user != null
//If we're working with an existing request
if(!this.initiating){
//Grab pass prompts
this.pass = document.querySelector("#reset-pass-prompt");
this.passConfirm = document.querySelector("#reset-pass-confirm-prompt");
//Strip reset token from query string
this.token = window.location.search.replace('?token=','');
}
//Grab register button
this.button = document.querySelector("#reset-pass-button");
//Grab altcha widget
this.altcha = document.querySelector("altcha-widget");
//Setup null property to hold verification payload from altcha widget
this.verification = null
//Run input setup after DOM content has completely loaded to ensure altcha event listeners work
document.addEventListener('DOMContentLoaded', this.setupInput.bind(this));
}
setupInput(){
//Add verification event listener to altcha widget
this.altcha.addEventListener("verified", this.verify.bind(this));
//Add register event listener to register button
this.button.addEventListener("click", this.register.bind(this));
}
verify(event){
//pull verification payload from event
this.verification = event.detail.payload;
}
register(){
//If altcha verification isn't complete
if(this.verification == null){
//don't bother
return;
}
//If we're initiating a password change request
if(this.initiating){
//If we're completing a password change
}else{
//if the confirmation password doesn't match
if(this.pass.value != this.passConfirm.value){
//Scream and shout
new canopyUXUtils.popup(`<p>Confirmation password does not match!</p>`);
return;
}
//Send the registration informaiton off to the server
utils.ajax.resetPassword(this.token , this.pass.value , this.passConfirm.value , this.verification);
}
}
}
const registerForm = new registerPrompt();

View file

@ -66,4 +66,4 @@ class registerPrompt{
}
}
const registerForm = new registerPrompt();
const registerForm = new resetPrompt();

View file

@ -215,8 +215,8 @@ class canopyUXUtils{
//Bit hacky but the only way to remove an event listener while keeping the function bound to this
//Isn't javascript precious?
this.keyClose = ((event)=>{
//If we hit enter
if(event.key == "Enter"){
//If we hit enter or escape
if(event.key == "Enter" || event.key == "Escape"){
//Close the pop-up
this.closePopup();
//Remove this event listener
@ -434,7 +434,6 @@ class canopyAjaxUtils{
}
}
//We need to fix this one to use displayResponseError function
async updateProfile(update){
const response = await fetch(`/api/account/update`,{
method: "POST",
@ -479,6 +478,22 @@ class canopyAjaxUtils{
}
}
async resetPassword(token, pass, confirmPass, verification){
const response = await fetch(`/api/account/passwordReset`,{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({token, pass, confirmPass, verification})
});
if(response.status == 200){
return await response.json();
}else{
utils.ux.displayResponseError(await response.json());
}
}
async newChannel(name, description, thumbnail, verification){
var response = await fetch(`/api/channel/register`,{
method: "POST",
@ -547,7 +562,6 @@ class canopyAjaxUtils{
headers: {
"Content-Type": "application/json"
},
//Unfortunately JSON doesn't natively handle ES6 maps, and god forbid someone update the standard in a way that's backwards compatible...
body: JSON.stringify({chanName, user, rank})
});