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 {
|
try {
|
||||||
await FileSender.setPassword(password, file);
|
await FileSender.setPassword(existingPassword, password, file);
|
||||||
metrics.addedPassword({ size: file.size });
|
metrics.addedPassword({ size: file.size });
|
||||||
file.password = password;
|
file.password = password;
|
||||||
state.storage.writeFiles();
|
state.storage.writeFiles();
|
||||||
|
@ -19,11 +19,9 @@ async function sendPassword(file, authKey, rawAuth) {
|
|||||||
xhr.onreadystatechange = () => {
|
xhr.onreadystatechange = () => {
|
||||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
return resolve(xhr.response);
|
|
||||||
}
|
|
||||||
if (xhr.status === 401) {
|
|
||||||
const nonce = xhr.getResponseHeader('WWW-Authenticate').split(' ')[1];
|
const nonce = xhr.getResponseHeader('WWW-Authenticate').split(' ')[1];
|
||||||
file.nonce = nonce;
|
file.nonce = nonce;
|
||||||
|
return resolve(xhr.response);
|
||||||
}
|
}
|
||||||
reject(new Error(xhr.status));
|
reject(new Error(xhr.status));
|
||||||
}
|
}
|
||||||
@ -262,7 +260,7 @@ export default class FileSender extends Nanobus {
|
|||||||
return this.uploadFile(encrypted, metadata, new Uint8Array(rawAuth));
|
return this.uploadFile(encrypted, metadata, new Uint8Array(rawAuth));
|
||||||
}
|
}
|
||||||
|
|
||||||
static async setPassword(password, file) {
|
static async setPassword(existingPassword, password, file) {
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const secretKey = await window.crypto.subtle.importKey(
|
const secretKey = await window.crypto.subtle.importKey(
|
||||||
'raw',
|
'raw',
|
||||||
@ -293,6 +291,28 @@ export default class FileSender extends Nanobus {
|
|||||||
false,
|
false,
|
||||||
['deriveKey']
|
['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(
|
const newAuthKey = await window.crypto.subtle.deriveKey(
|
||||||
{
|
{
|
||||||
name: 'PBKDF2',
|
name: 'PBKDF2',
|
||||||
@ -309,11 +329,12 @@ export default class FileSender extends Nanobus {
|
|||||||
['sign']
|
['sign']
|
||||||
);
|
);
|
||||||
const rawAuth = await window.crypto.subtle.exportKey('raw', newAuthKey);
|
const rawAuth = await window.crypto.subtle.exportKey('raw', newAuthKey);
|
||||||
|
const aKey = existingPassword ? oldAuthKey : authKey;
|
||||||
try {
|
try {
|
||||||
await sendPassword(file, authKey, rawAuth);
|
await sendPassword(file, aKey, rawAuth);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message === '401' && file.nonce !== e.nonce) {
|
if (e.message === '401' && file.nonce !== e.nonce) {
|
||||||
await sendPassword(file, authKey, rawAuth);
|
await sendPassword(file, aKey, rawAuth);
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,25 @@ function passwordComplete(state, password) {
|
|||||||
const el = html([
|
const el = html([
|
||||||
`<div class="selectPassword">${state.translate('passwordResult', {
|
`<div class="selectPassword">${state.translate('passwordResult', {
|
||||||
password: '<pre></pre>'
|
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');
|
const passwordOriginal = document.createElement('div');
|
||||||
passwordOriginal.className = 'passwordOriginal';
|
passwordOriginal.className = 'passwordOriginal';
|
||||||
passwordOriginal.innerText = password;
|
passwordOriginal.innerText = password;
|
||||||
@ -19,11 +36,35 @@ function passwordComplete(state, password) {
|
|||||||
const passwordStar = document.createElement('div');
|
const passwordStar = document.createElement('div');
|
||||||
passwordStar.className = 'passwordStar';
|
passwordStar.className = 'passwordStar';
|
||||||
passwordStar.innerText = password.replace(/./g, '●');
|
passwordStar.innerText = password.replace(/./g, '●');
|
||||||
el.lastElementChild.appendChild(passwordOriginal);
|
|
||||||
el.lastElementChild.appendChild(passwordStar);
|
el.firstElementChild.appendChild(passwordOriginal);
|
||||||
|
el.firstElementChild.appendChild(passwordStar);
|
||||||
|
|
||||||
return el;
|
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) {
|
function expireInfo(file, translate, emit) {
|
||||||
const hours = Math.floor(EXPIRE_SECONDS / 60 / 60);
|
const hours = Math.floor(EXPIRE_SECONDS / 60 / 60);
|
||||||
const el = html([
|
const el = html([
|
||||||
@ -99,6 +140,21 @@ module.exports = function(state, emit) {
|
|||||||
</div>
|
</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() {
|
function showPopup() {
|
||||||
const popupText = document.querySelector('.popuptext');
|
const popupText = document.querySelector('.popuptext');
|
||||||
popupText.classList.add('show');
|
popupText.classList.add('show');
|
||||||
|
@ -52,11 +52,12 @@ module.exports = function(state, emit) {
|
|||||||
|
|
||||||
function setPassword(event) {
|
function setPassword(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
const existingPassword = null;
|
||||||
const password = document.getElementById('unlock-input').value;
|
const password = document.getElementById('unlock-input').value;
|
||||||
if (password.length > 0) {
|
if (password.length > 0) {
|
||||||
document.getElementById('copy').classList.remove('wait-password');
|
document.getElementById('copy').classList.remove('wait-password');
|
||||||
document.getElementById('copy-btn').disabled = false;
|
document.getElementById('copy-btn').disabled = false;
|
||||||
emit('password', { password, file });
|
emit('password', { existingPassword, password, file });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,6 +648,25 @@ tbody {
|
|||||||
background: #efeff1;
|
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 {
|
.send-new {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
@ -857,7 +876,8 @@ tbody {
|
|||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unlock-btn {
|
#unlock-btn,
|
||||||
|
#unlock-reset-btn {
|
||||||
flex: 0 1 165px;
|
flex: 0 1 165px;
|
||||||
background: #0297f8;
|
background: #0297f8;
|
||||||
border-radius: 0 6px 6px 0;
|
border-radius: 0 6px 6px 0;
|
||||||
@ -874,7 +894,8 @@ tbody {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unlock-btn:hover {
|
#unlock-btn:hover,
|
||||||
|
#unlock-reset-btn:hover {
|
||||||
background-color: #0287e8;
|
background-color: #0287e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1163,7 +1184,8 @@ tbody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#copy-btn,
|
#copy-btn,
|
||||||
#unlock-btn {
|
#unlock-btn,
|
||||||
|
#unlock-reset-btn {
|
||||||
border-radius: 0 0 6px 6px;
|
border-radius: 0 0 6px 6px;
|
||||||
flex: 0 1 65px;
|
flex: 0 1 65px;
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,7 @@ footerLinkTerms = Terms
|
|||||||
footerLinkCookies = Cookies
|
footerLinkCookies = Cookies
|
||||||
requirePasswordCheckbox = Require a password to download this file
|
requirePasswordCheckbox = Require a password to download this file
|
||||||
addPasswordButton = Add password
|
addPasswordButton = Add password
|
||||||
|
changePasswordButton = Change
|
||||||
passwordTryAgain = Incorrect password. Try again.
|
passwordTryAgain = Incorrect password. Try again.
|
||||||
// This label is followed by the password needed to download a file
|
// This label is followed by the password needed to download a file
|
||||||
passwordResult = Password: { $password }
|
passwordResult = Password: { $password }
|
||||||
|
Loading…
Reference in New Issue
Block a user