Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Allow authed user to change password #652

Merged
merged 2 commits into from
Sep 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 92 additions & 1 deletion cmd/server/assets/login/_loginscripts.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,95 @@
</div>
</div>
</div>
{{end}}
{{end}}

{{define "login/pwd-validate-js"}}
{{if .requirements.HasRequirements}}
let $lenReq = $('#length-req');
let $upperReq = $('#upper-req');
let $lowerReq = $('#lower-req');
let $numReq = $('#num-req');
let $specialReq = $('#special-req');
{{end}}

function checkPasswordValid(pwd) {
{{if .requirements.HasRequirements}}
let upper = 0;
let lower = 0;
let digit = 0;
let special = 0;
let specialPattern = new RegExp(/[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/);
for (let i = 0; i < pwd.length; i++) {
let c = pwd.charAt(i);
if (!isNaN(parseInt(c, 10))) {
digit++;
} else if (specialPattern.test(c)) {
special++;
} else if (c == c.toUpperCase()) {
upper++;
} else if (c == c.toLowerCase()) {
lower++;
}
}

let errClass = "oi oi-circle-x pr-1";
let checkClass = "oi oi-circle-check pr-1";

{{if gt .requirements.Length 0}}
if (pwd.length < {{.requirements.Length}}) {
$lenReq.find("#icon").attr("class", errClass)
$lenReq.addClass("text-danger");
return false;
} else {
$lenReq.find("#icon").attr("class", checkClass)
$lenReq.addClass("text-muted");
}
{{end}}

{{if gt .requirements.Uppercase 0}}
if (upper < {{.requirements.Uppercase}}) {
$upperReq.find("#icon").attr("class", errClass);
$upperReq.addClass("text-danger");
return false;
} else {
$upperReq.find("#icon").attr("class", checkClass);
$upperReq.addClass("text-muted");
}
{{end}}

{{if gt .requirements.Lowercase 0}}
if (lower < {{.requirements.Lowercase}}) {
$lowerReq.find("#icon").attr("class", errClass);
$lowerReq.addClass("text-danger");
return false;
} else {
$lowerReq.find("#icon").attr("class", checkClass);
$lowerReq.addClass("text-muted");
}
{{end}}

{{if gt .requirements.Number 0}}
if (digit < {{.requirements.Number}}) {
$numReq.find("#icon").attr("class", errClass);
$numReq.addClass("text-danger");
return false;
} else {
$numReq.find("#icon").attr("class", checkClass);
$numReq.addClass("text-muted");
}
{{end}}

{{if gt .requirements.Special 0}}
if (special < {{.requirements.Special}}) {
$specialReq.find("#icon").attr("class", errClass);
$specialReq.addClass("text-danger");
return false;
} else {
$specialReq.find("#icon").attr("class", checkClass);
$specialReq.addClass("text-muted");
}
{{end}}
{{end}}
return true;
}
{{end}}
2 changes: 1 addition & 1 deletion cmd/server/assets/login/account.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ <h6 class="card-title mt-3">System admin</h6>
<li class="list-group-item">
<div class="card-text">Password was last changed <span class="text-info">{{$user.PasswordAgeString}}</span>
ago</div>
<a href="/login/change-password" class="card-link">Reset password</a>
<a href="/login/change-password" class="card-link">Change password</a>
</li>
</ul>
</div>
Expand Down
154 changes: 154 additions & 0 deletions cmd/server/assets/login/change-password.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
{{define "login/change-password"}}
<!doctype html>
<html lang="en">

<head>
{{template "floatingform" .}}
{{template "head" .}}
{{template "firebase" .}}
</head>

<body class="tab-content">
{{template "navbar" .}}

<main role="main" class="container">
{{template "flash" .}}

<div class="d-flex vh-100">
<div class="d-flex w-100 justify-content-center align-self-center">
<div class="col-sm-6">
<div class="card shadow-sm">
<div class="card-header">Change password</div>
<div class="card-body">
<form id="passwordForm" class="floating-form" action="/login/change-password" method="POST">
{{.csrfField}}
<div class="form-label-group">
<input type="email" id="email" name="email" class="form-control" placeholder="Email address"
value="{{.currentUser.Email}}" required autofocus disabled/>
<label for="email">Email address</label>
</div>

<div class="form-label-group mb-2">
<input type="password" id="password" class="form-control" placeholder="Password"
autocomplete="new-password" required />
<label for="password">Password</label>
</div>
<div class="form-label-group">
<input type="password" id="retype" class="form-control" placeholder="Retype password"
autocomplete="new-password" required />
<label for="retype">Retype password</label>
</div>

{{if .requirements.HasRequirements}}
<p class="card-text ml-4">
<small class="form-text text-muted">
<span class="row">Password should be:</span>
{{if gt .requirements.Length 0}}
<span class="row ml-1" id="length-req">
<span id="icon"></span>At least {{.requirements.Length}} characters long
</span>
{{end}}
{{if gt .requirements.Uppercase 0}}
<span class="row ml-1" id="upper-req">
<span id="icon"></span>Contain {{.requirements.Uppercase}} uppercase letter
</span>
{{end}}
{{if gt .requirements.Lowercase 0}}
<span class="row ml-1" id="lower-req">
<span id="icon"></span>Contain {{.requirements.Lowercase}} lowercase letter
</span>
{{end}}
{{if gt .requirements.Number 0}}
<span class="row ml-1" id="num-req">
<span id="icon"></span>Contain {{.requirements.Number}} number
</span>
{{end}}
{{if gt .requirements.Special 0}}
<span class="row ml-1" id="special-req">
<span id="icon"></span>Contain {{.requirements.Special}} special character
</span>
{{end}}
</small>
</p>
{{end}}

<button type="submit" id="submit" class="btn btn-primary btn-block" disabled>Set password</button>
</form>
</div>
<div class="card-body">
<a class="card-link" href="/">&larr; Login</a>
</div>
</div>
</div>
</div>
</div>
</main>

{{template "scripts" .}}
<script type="text/javascript">
$(function() {
let $form = $('#passwordForm');
let $submit = $('#submit');
let $email = $('#email');
let $password = $('#password');
let $retype = $('#retype');

firebase.auth().onAuthStateChanged(function(user) {
if (!user) {
window.location.assign("/signout");
return;
}

$submit.prop('disabled', false);
});

$password.keyup(function() {
$submit.prop('disabled', !checkPasswordValid($password.val()));
});

$form.on('submit', function(event) {
try {
return changePassword();
} catch(e) {
flash.clear();
flash.error(error);
}
});

function changePassword() {
let email = $email.val();
let pwd = $password.val();
if (pwd != $retype.val()) {
flash.error("Password and retyped passwords must match.");
return false;
}

if (!checkPasswordValid(pwd)) {
return false;
}

// Disable the submit button so we only attempt once.
$submit.prop('disabled', true);

return firebase.auth().currentUser.updatePassword(pwd)
.then(function() {
return true;
}).catch(function(error) {
if (err.code == 'auth/requires-recent-login') {
window.location.assign('/login?redir=login/change-password');
}

flash.clear();
flash.error(error);
$submit.prop('disabled', false);
return false;
});
}

{{template "login/pwd-validate-js" .}}
});
</script>
</body>

</html>
{{end}}
Loading