Passwords can now be reset
This commit is contained in:
parent
f3d77fdcf2
commit
fdcc31f049
|
@ -173,9 +173,9 @@ export default function(state, emitter) {
|
|||
}
|
||||
});
|
||||
|
||||
emitter.on('password', async ({ password, file }) => {
|
||||
emitter.on('password', async ({ existingPassword, password, file }) => {
|
||||
try {
|
||||
await FileSender.setPassword(password, file);
|
||||
await FileSender.setPassword(existingPassword, password, file);
|
||||
metrics.addedPassword({ size: file.size });
|
||||
file.password = password;
|
||||
state.storage.writeFiles();
|
||||
|
|
|
@ -19,11 +19,9 @@ async function sendPassword(file, authKey, rawAuth) {
|
|||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr.status === 200) {
|
||||
return resolve(xhr.response);
|
||||
}
|
||||
if (xhr.status === 401) {
|
||||
const nonce = xhr.getResponseHeader('WWW-Authenticate').split(' ')[1];
|
||||
file.nonce = nonce;
|
||||
return resolve(xhr.response);
|
||||
}
|
||||
reject(new Error(xhr.status));
|
||||
}
|
||||
|
@ -262,7 +260,7 @@ export default class FileSender extends Nanobus {
|
|||
return this.uploadFile(encrypted, metadata, new Uint8Array(rawAuth));
|
||||
}
|
||||
|
||||
static async setPassword(password, file) {
|
||||
static async setPassword(existingPassword, password, file) {
|
||||
const encoder = new TextEncoder();
|
||||
const secretKey = await window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
|
@ -293,6 +291,28 @@ export default class FileSender extends Nanobus {
|
|||
false,
|
||||
['deriveKey']
|
||||
);
|
||||
const oldPwdkey = await window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
encoder.encode(existingPassword),
|
||||
{ name: 'PBKDF2' },
|
||||
false,
|
||||
['deriveKey']
|
||||
);
|
||||
const oldAuthKey = await window.crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'PBKDF2',
|
||||
salt: encoder.encode(file.url),
|
||||
iterations: 100,
|
||||
hash: 'SHA-256'
|
||||
},
|
||||
oldPwdkey,
|
||||
{
|
||||
name: 'HMAC',
|
||||
hash: 'SHA-256'
|
||||
},
|
||||
true,
|
||||
['sign']
|
||||
);
|
||||
const newAuthKey = await window.crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'PBKDF2',
|
||||
|
@ -309,11 +329,12 @@ export default class FileSender extends Nanobus {
|
|||
['sign']
|
||||
);
|
||||
const rawAuth = await window.crypto.subtle.exportKey('raw', newAuthKey);
|
||||
const aKey = existingPassword ? oldAuthKey : authKey;
|
||||
try {
|
||||
await sendPassword(file, authKey, rawAuth);
|
||||
await sendPassword(file, aKey, rawAuth);
|
||||
} catch (e) {
|
||||
if (e.message === '401' && file.nonce !== e.nonce) {
|
||||
await sendPassword(file, authKey, rawAuth);
|
||||
await sendPassword(file, aKey, rawAuth);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,25 @@ function passwordComplete(state, password) {
|
|||
const el = html([
|
||||
`<div class="selectPassword">${state.translate('passwordResult', {
|
||||
password: '<pre></pre>'
|
||||
})}</div>`
|
||||
})}
|
||||
<button id="resetButton">${state.translate('changePasswordButton')}</button>
|
||||
<form id='reset-form' class="setPassword hidden" data-no-csrf>
|
||||
<input id="unlock-reset-input"
|
||||
class="unlock-input input-no-btn"
|
||||
maxlength="64"
|
||||
autocomplete="off"
|
||||
placeholder="${state.translate('unlockInputPlaceholder')}">
|
||||
<input type="submit"
|
||||
id="unlock-reset-btn"
|
||||
class="btn btn-hidden"
|
||||
value="Reset Password"/>
|
||||
</form>
|
||||
</div>`
|
||||
]);
|
||||
|
||||
el.querySelector('#resetButton').onclick = toggleResetInput;
|
||||
el.querySelector('#unlock-reset-input').oninput = inputChanged;
|
||||
|
||||
const passwordOriginal = document.createElement('div');
|
||||
passwordOriginal.className = 'passwordOriginal';
|
||||
passwordOriginal.innerText = password;
|
||||
|
@ -19,11 +36,35 @@ function passwordComplete(state, password) {
|
|||
const passwordStar = document.createElement('div');
|
||||
passwordStar.className = 'passwordStar';
|
||||
passwordStar.innerText = password.replace(/./g, '●');
|
||||
el.lastElementChild.appendChild(passwordOriginal);
|
||||
el.lastElementChild.appendChild(passwordStar);
|
||||
|
||||
el.firstElementChild.appendChild(passwordOriginal);
|
||||
el.firstElementChild.appendChild(passwordStar);
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function inputChanged() {
|
||||
const resetInput = document.getElementById('unlock-reset-input');
|
||||
const resetBtn = document.getElementById('unlock-reset-btn');
|
||||
if (resetInput.value.length > 0) {
|
||||
resetBtn.classList.remove('btn-hidden');
|
||||
resetInput.classList.remove('input-no-btn');
|
||||
} else {
|
||||
resetBtn.classList.add('btn-hidden');
|
||||
resetInput.classList.add('input-no-btn');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleResetInput(event) {
|
||||
const form = event.target.parentElement.querySelector('form');
|
||||
if (form.style.visibility === 'hidden' || form.style.visibility === '') {
|
||||
form.style.visibility = 'visible';
|
||||
} else {
|
||||
form.style.visibility = 'hidden';
|
||||
}
|
||||
inputChanged();
|
||||
}
|
||||
|
||||
function expireInfo(file, translate, emit) {
|
||||
const hours = Math.floor(EXPIRE_SECONDS / 60 / 60);
|
||||
const el = html([
|
||||
|
@ -99,6 +140,21 @@ module.exports = function(state, emit) {
|
|||
</div>
|
||||
`;
|
||||
|
||||
if (div.querySelector('#reset-form'))
|
||||
div.querySelector('#reset-form').onsubmit = resetPassword;
|
||||
|
||||
function resetPassword(event) {
|
||||
event.preventDefault();
|
||||
const existingPassword = document.querySelector('.passwordOriginal')
|
||||
.innerText;
|
||||
const password = document.querySelector('#unlock-reset-input').value;
|
||||
if (password.length > 0) {
|
||||
document.getElementById('copy').classList.remove('wait-password');
|
||||
document.getElementById('copy-btn').disabled = false;
|
||||
emit('password', { existingPassword, password, file });
|
||||
}
|
||||
}
|
||||
|
||||
function showPopup() {
|
||||
const popupText = document.querySelector('.popuptext');
|
||||
popupText.classList.add('show');
|
||||
|
|
|
@ -52,11 +52,12 @@ module.exports = function(state, emit) {
|
|||
|
||||
function setPassword(event) {
|
||||
event.preventDefault();
|
||||
const existingPassword = null;
|
||||
const password = document.getElementById('unlock-input').value;
|
||||
if (password.length > 0) {
|
||||
document.getElementById('copy').classList.remove('wait-password');
|
||||
document.getElementById('copy-btn').disabled = false;
|
||||
emit('password', { password, file });
|
||||
emit('password', { existingPassword, password, file });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -648,6 +648,25 @@ tbody {
|
|||
background: #efeff1;
|
||||
}
|
||||
|
||||
#resetButton {
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(12, 12, 13, 0.3);
|
||||
border-radius: 5px;
|
||||
font-size: 15px;
|
||||
margin-top: 5px;
|
||||
margin-left: 15px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 24px;
|
||||
cursor: pointer;
|
||||
color: #313131;
|
||||
}
|
||||
|
||||
#resetButton:hover {
|
||||
background: #efeff1;
|
||||
}
|
||||
|
||||
.send-new {
|
||||
font-size: 15px;
|
||||
margin: auto;
|
||||
|
@ -857,7 +876,8 @@ tbody {
|
|||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#unlock-btn {
|
||||
#unlock-btn,
|
||||
#unlock-reset-btn {
|
||||
flex: 0 1 165px;
|
||||
background: #0297f8;
|
||||
border-radius: 0 6px 6px 0;
|
||||
|
@ -874,7 +894,8 @@ tbody {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#unlock-btn:hover {
|
||||
#unlock-btn:hover,
|
||||
#unlock-reset-btn:hover {
|
||||
background-color: #0287e8;
|
||||
}
|
||||
|
||||
|
@ -1163,7 +1184,8 @@ tbody {
|
|||
}
|
||||
|
||||
#copy-btn,
|
||||
#unlock-btn {
|
||||
#unlock-btn,
|
||||
#unlock-reset-btn {
|
||||
border-radius: 0 0 6px 6px;
|
||||
flex: 0 1 65px;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ footerLinkTerms = Terms
|
|||
footerLinkCookies = Cookies
|
||||
requirePasswordCheckbox = Require a password to download this file
|
||||
addPasswordButton = Add password
|
||||
changePasswordButton = Change
|
||||
passwordTryAgain = Incorrect password. Try again.
|
||||
// This label is followed by the password needed to download a file
|
||||
passwordResult = Password: { $password }
|
||||
|
|
Loading…
Reference in New Issue