Ability to change the password of a pad

pull/1/head
yflory 7 years ago
parent d1e8baca68
commit 54d63df1cf

@ -204,7 +204,16 @@
} }
span.cp-password-container { span.cp-password-container {
display: flex;
align-items: center;
margin-bottom: 15px; margin-bottom: 15px;
justify-content: space-between;
& > * {
margin-bottom: 0 !important;
}
button {
margin: 0 !important;
}
} }
input[type="checkbox"], input[type="radio"] { input[type="checkbox"], input[type="radio"] {
@ -220,10 +229,6 @@
} }
} }
nav {
text-align: right;
button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button) { button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button) {
background-color: @colortheme_alertify-cancel; background-color: @colortheme_alertify-cancel;
@ -298,6 +303,9 @@
button.btn { button.btn {
margin: 6px 4px; margin: 6px 4px;
} }
nav {
text-align: right;
} }
} }
} }

@ -5,7 +5,6 @@
input { input {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
margin-bottom: 0 !important; // Override margin from alertify
} }
label, .fa { label, .fa {
margin-left: 10px; margin-left: 10px;

@ -1117,6 +1117,16 @@ define(function () {
out.password_submit = "Valider"; out.password_submit = "Valider";
out.password_show = "Afficher"; out.password_show = "Afficher";
// Change password in pad properties
out.properties_addPassword = "Ajouter un mot de passe";
out.properties_changePassword = "Modifier le mot de passe";
out.properties_confirmNew = "Êtes-vous sûr ? Ajouter un mot de passe changera l'URL de ce pad. Les utilisateurs ne connaissant pas le nouveau mot de passe perdront l'accès au pad.";
out.properties_confirmChange = "Êtes-vous sûr ? Les utilisateurs ne connaissant pas le nouveau mot de passe perdront l'accès au pad.";
out.properties_passwordError = "Une erreur est survenue lors de la modification du mot de passe. Veuillez réessayer.";
out.properties_passwordWarning = "Le mot de passe a été modifié avec succès mais nous n'avons pas réussi à mettre à jour votre CryptDrive avec les nouvelles informations. Vous devrez peut-être supprimer manuellement l'ancienne version de ce pad.<br>Appuyez sur OK pour recharger le pad et mettre à jour vos droits d'accès.";
out.properties_passwordSuccess = "Le mot de passe a été modifié avec succès.<br>Appuyez sur OK pour mettre à jour vos droits d'accès.";
out.properties_changePasswordButton = "Valider";
// New share modal // New share modal
out.share_linkCategory = "Partage"; out.share_linkCategory = "Partage";
out.share_linkAccess = "Droits d'accès"; out.share_linkAccess = "Droits d'accès";

@ -1155,13 +1155,23 @@ define(function () {
out.creation_newPadModalDescriptionAdvanced = "You can check the box (or press <b>Space</b> to change its value) if you want to display the pad creation screen (for owned pads, expiring pads, etc.)."; out.creation_newPadModalDescriptionAdvanced = "You can check the box (or press <b>Space</b> to change its value) if you want to display the pad creation screen (for owned pads, expiring pads, etc.).";
out.creation_newPadModalAdvanced = "Display the pad creation screen"; out.creation_newPadModalAdvanced = "Display the pad creation screen";
// Password prompt on the loadind screen // Password prompt on the loading screen
out.password_info = "The pad you're trying to open is protected with a password. Enter the correct password to access its content."; out.password_info = "The pad you're trying to open is protected with a password. Enter the correct password to access its content.";
out.password_error = "Pad not found!<br>This error can be caused by two factors: either the password in invalid, or the pad has been deleted from the server."; out.password_error = "Pad not found!<br>This error can be caused by two factors: either the password in invalid, or the pad has been deleted from the server.";
out.password_placeholder = "Type the password here..."; out.password_placeholder = "Type the password here...";
out.password_submit = "Submit"; out.password_submit = "Submit";
out.password_show = "Show"; out.password_show = "Show";
// Change password in pad properties
out.properties_addPassword = "Add a password";
out.properties_changePassword = "Change the password";
out.properties_confirmNew = "Are you sure? Adding a password will change this pad's URL. Users without the password will lose access to this pad";
out.properties_confirmChange = "Are you sure? Users without the new password will lose access to this pad";
out.properties_passwordError = "An error occured while trying to change the password. Please try again.";
out.properties_passwordWarning = "The password was successfully changed but we were unable to update your CryptDrive with the new data. You may have to remove the old version of the pad manually.<br>Press OK to reload and update your acces rights.";
out.properties_passwordSuccess = "The password was successfully changed.<br>Press OK to reload and update your access rights.";
out.properties_changePasswordButton = "Submit";
// New share modal // New share modal
out.share_linkCategory = "Share link"; out.share_linkCategory = "Share link";
out.share_linkAccess = "Access rights"; out.share_linkAccess = "Access rights";

@ -136,12 +136,6 @@ define([
$d.append(UI.dialog.selectable(owners, { $d.append(UI.dialog.selectable(owners, {
id: 'cp-app-prop-owners', id: 'cp-app-prop-owners',
})); }));
/* TODO
if (owned) {
var $deleteOwned = $('button').text(Messages.fc_delete_owned).click(function () {
});
$d.append($deleteOwned);
}*/
var expire = Messages.creation_expireFalse; var expire = Messages.creation_expireFalse;
if (data.expire && typeof (data.expire) === "number") { if (data.expire && typeof (data.expire) === "number") {
@ -153,11 +147,12 @@ define([
id: 'cp-app-prop-expire', id: 'cp-app-prop-expire',
})); }));
if (typeof data.password !== "undefined") { var hasPassword = typeof data.password !== "undefined";
if (hasPassword) {
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue) $('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
.appendTo($d); .appendTo($d);
var password = UI.passwordInput({ var password = UI.passwordInput({
id: 'cp-app-prop-expire', id: 'cp-app-prop-password',
readonly: 'readonly' readonly: 'readonly'
}); });
var $pwInput = $(password).find('.cp-password-input'); var $pwInput = $(password).find('.cp-password-input');
@ -167,6 +162,51 @@ define([
$d.append(password); $d.append(password);
} }
var parsed = Hash.parsePadUrl(data.href);
if (owned && parsed.hashData.type === 'pad') {
var sframeChan = common.getSframeChannel();
var changePwTitle = Messages.properties_changePassword;
var changePwConfirm = Messages.properties_confirmChange;
if (!hasPassword) {
changePwTitle = Messages.properties_addPassword;
changePwConfirm = Messages.properties_confirmNew;
}
$('<label>', {'for': 'cp-app-prop-change-password'})
.text(changePwTitle).appendTo($d);
var newPassword = UI.passwordInput({
id: 'cp-app-prop-change-password',
style: 'flex: 1;'
});
var passwordOk = h('button', Messages.properties_changePasswordButton);
var changePass = h('span.cp-password-container', [
newPassword,
passwordOk
]);
$(passwordOk).click(function () {
UI.confirm(changePwConfirm, function (yes) {
if (!yes) { return; }
sframeChan.query("Q_PAD_PASSWORD_CHANGE", {
href: data.href,
password: $(newPassword).val()
}, function (err, data) {
if (err || data.error) {
return void UI.alert(Messages.properties_passwordError);
}
UI.findOKButton().click();
if (data.warning) {
return void UI.alert(Messages.properties_passwordWarning, function () {
common.gotoURL(hasPassword ? undefined : data.href);
}, {force: true});
}
return void UI.alert(Messages.properties_passwordSuccess, function () {
common.gotoURL(hasPassword ? undefined : data.href);
}, {force: true});
});
});
});
$d.append(changePass);
}
cb(void 0, $d); cb(void 0, $d);
}; };
var getPadProperties = function (common, data, cb) { var getPadProperties = function (common, data, cb) {

@ -5,6 +5,7 @@ define([
'/common/common-hash.js', '/common/common-hash.js',
'/common/common-realtime.js', '/common/common-realtime.js',
'/common/outer/network-config.js', '/common/outer/network-config.js',
'/bower_components/chainpad/chainpad.dist.js',
], function (Crypto, CPNetflux, Util, Hash, Realtime, NetConfig) { ], function (Crypto, CPNetflux, Util, Hash, Realtime, NetConfig) {
var finish = function (S, err, doc) { var finish = function (S, err, doc) {
if (S.done) { return; } if (S.done) { return; }

@ -580,6 +580,9 @@ define([
pad.joinPad = function (data) { pad.joinPad = function (data) {
postMessage("JOIN_PAD", data); postMessage("JOIN_PAD", data);
}; };
pad.leavePad = function (data, cb) {
postMessage("LEAVE_PAD", data, cb);
};
pad.sendPadMsg = function (data, cb) { pad.sendPadMsg = function (data, cb) {
postMessage("SEND_PAD_MSG", data, cb); postMessage("SEND_PAD_MSG", data, cb);
}; };
@ -591,6 +594,91 @@ define([
pad.onConnectEvent = Util.mkEvent(); pad.onConnectEvent = Util.mkEvent();
pad.onErrorEvent = Util.mkEvent(); pad.onErrorEvent = Util.mkEvent();
common.changePadPassword = function (Crypt, href, newPassword, edPublic, cb) {
if (!href) { return void cb({ error: 'EINVAL_HREF' }); }
var parsed = Hash.parsePadUrl(href);
if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); }
var warning = false;
var newHash;
var oldChannel;
if (parsed.hashData.password) {
newHash = parsed.hash;
} else {
newHash = Hash.createRandomHash(parsed.type, newPassword);
}
var newHref = '/' + parsed.type + '/#' + newHash;
var optsGet = {};
var optsPut = {
password: newPassword
};
Nthen(function (waitFor) {
if (parsed.hashData && parsed.hashData.password) {
common.getPadAttribute('password', waitFor(function (err, password) {
optsGet.password = password;
}), href);
}
common.getPadAttribute('owners', waitFor(function (err, owners) {
if (!Array.isArray(owners) || owners.indexOf(edPublic) === -1) {
// We're not an owner, we shouldn't be able to change the password!
waitFor.abort();
return void cb({ error: 'EPERM' });
}
optsPut.owners = owners;
}), href);
common.getPadAttribute('expire', waitFor(function (err, expire) {
optsPut.expire = (expire - (+new Date())) / 1000; // Lifetime in seconds
}), href);
}).nThen(function (waitFor) {
Crypt.get(parsed.hash, waitFor(function (err, val) {
if (err) {
waitFor.abort();
return void cb({ error: err });
}
Crypt.put(newHash, val, waitFor(function (err) {
if (err) {
waitFor.abort();
return void cb({ error: err });
}
}), optsPut);
}), optsGet);
}).nThen(function (waitFor) {
var secret = Hash.getSecrets(parsed.type, parsed.hash, optsGet.password);
oldChannel = secret.channel;
pad.leavePad({
channel: oldChannel
}, waitFor());
pad.onDisconnectEvent.fire(true);
}).nThen(function (waitFor) {
common.removeOwnedChannel(oldChannel, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();
return void cb(obj);
}
}));
}).nThen(function (waitFor) {
common.setPadAttribute('password', newPassword, waitFor(function (err) {
if (err) { warning = true; }
}), href);
var secret = Hash.getSecrets(parsed.type, newHash, newPassword);
common.setPadAttribute('channel', secret.channel, waitFor(function (err) {
if (err) { warning = true; }
}), href);
if (parsed.hashData.password) { return; } // same hash
common.setPadAttribute('href', newHref, waitFor(function (err) {
if (err) { warning = true; }
}), href);
}).nThen(function () {
cb({
warning: warning,
hash: newHash,
href: newHref
});
});
};
// Loading events // Loading events
common.loading = {}; common.loading = {};
common.loading.onDriveEvent = Util.mkEvent(); common.loading.onDriveEvent = Util.mkEvent();
@ -862,7 +950,6 @@ define([
w.addEventListener('statechange', onStateChange); w.addEventListener('statechange', onStateChange);
return; return;
} }
// XXX
// New version detected (from another tab): kill? // New version detected (from another tab): kill?
console.error('New version detected: ABORT?'); console.error('New version detected: ABORT?');
}; };

@ -1059,6 +1059,13 @@ define([
}; };
CpNfWorker.start(conf); CpNfWorker.start(conf);
}; };
Store.leavePad = function (clientId, data, cb) {
var channel = channels[data.channel];
if (!channel || !channel.wc) { return void cb ({error: 'EINVAL'}); }
channel.wc.leave();
delete channels[data.channel];
cb();
};
Store.sendPadMsg = function (clientId, data, cb) { Store.sendPadMsg = function (clientId, data, cb) {
var msg = data.msg; var msg = data.msg;
var channel = channels[data.channel]; var channel = channels[data.channel];

@ -67,6 +67,7 @@ define([
// Pad // Pad
SEND_PAD_MSG: Store.sendPadMsg, SEND_PAD_MSG: Store.sendPadMsg,
JOIN_PAD: Store.joinPad, JOIN_PAD: Store.joinPad,
LEAVE_PAD: Store.leavePad,
GET_FULL_HISTORY: Store.getFullHistory, GET_FULL_HISTORY: Store.getFullHistory,
IS_NEW_CHANNEL: Store.isNewChannel, IS_NEW_CHANNEL: Store.isNewChannel,
// Drive // Drive

@ -41,6 +41,7 @@ define([
if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); } if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); }
var data = exp.getFileData(id); var data = exp.getFileData(id);
data[attr] = clone(value); data[attr] = clone(value);
console.log(data);
cb(null); cb(null);
}; };
exp.getPadAttribute = function (href, attr, cb) { exp.getPadAttribute = function (href, attr, cb) {

@ -140,6 +140,11 @@ define([
toolbar.initializing(); toolbar.initializing();
return; return;
} }
if (text) {
// text is a boolean here. It means we won't try to reconnect
toolbar.failed();
return;
}
toolbar.reconnecting(); toolbar.reconnecting();
}); });
break; break;
@ -339,7 +344,7 @@ define([
}; };
var onConnectionChange = function (info) { var onConnectionChange = function (info) {
if (state === STATE.DELETED) { return; } if (state === STATE.DELETED) { return; }
stateChange(info.state ? STATE.INITIALIZING : STATE.DISCONNECTED); stateChange(info.state ? STATE.INITIALIZING : STATE.DISCONNECTED, info.permanent);
/*if (info.state) { /*if (info.state) {
UI.findOKButton().click(); UI.findOKButton().click();
} else { } else {

@ -79,10 +79,12 @@ define([
evInfiniteSpinner.fire(); evInfiniteSpinner.fire();
}, 2000); }, 2000);
sframeChan.on('EV_RT_DISCONNECT', function () { sframeChan.on('EV_RT_DISCONNECT', function (isPermanent) {
isReady = false; isReady = false;
chainpad.abort(); chainpad.abort();
onConnectionChange({ state: false }); // Permanent flag is here to choose if we wnat to display
// "reconnecting" or "disconnected" in the toolbar state
onConnectionChange({ state: false, permanent: isPermanent });
}); });
sframeChan.on('EV_RT_ERROR', function (err) { sframeChan.on('EV_RT_ERROR', function (err) {
isReady = false; isReady = false;

@ -112,8 +112,8 @@ define([], function () {
} }
}; };
padRpc.onDisconnectEvent.reg(function () { padRpc.onDisconnectEvent.reg(function (permanent) {
sframeChan.event('EV_RT_DISCONNECT'); sframeChan.event('EV_RT_DISCONNECT', permanent);
}); });
padRpc.onConnectEvent.reg(function (data) { padRpc.onConnectEvent.reg(function (data) {

@ -143,12 +143,11 @@ define([
// or get it from the pad attributes // or get it from the pad attributes
var needPassword = parsed.hashData && parsed.hashData.password; var needPassword = parsed.hashData && parsed.hashData.password;
if (needPassword) { if (needPassword) {
// Check if we have a password, and check if it is correct (file exists).
// It we don't have a correct password, display the password prompt.
// Maybe the file has been deleted from the server or the password has been changed.
Cryptpad.getPadAttribute('password', waitFor(function (err, val) { Cryptpad.getPadAttribute('password', waitFor(function (err, val) {
if (val) { var askPassword = function (wrongPasswordStored) {
// We already know the password, use it!
password = val;
todo();
} else {
// Ask for the password and check if the pad exists // Ask for the password and check if the pad exists
// If the pad doesn't exist, it means the password isn't correct // If the pad doesn't exist, it means the password isn't correct
// or the pad has been deleted // or the pad has been deleted
@ -162,7 +161,14 @@ define([
cb(false); cb(false);
} else { } else {
todo(); todo();
if (wrongPasswordStored) {
// Store the correct password
Cryptpad.setPadAttribute('password', password, function () {
correctPassword(); correctPassword();
}, parsed.getUrl());
} else {
correctPassword();
}
cb(true); cb(true);
} }
}; };
@ -178,6 +184,19 @@ define([
Cryptpad.isNewChannel(window.location.href, password, next); Cryptpad.isNewChannel(window.location.href, password, next);
}); });
sframeChan.event("EV_PAD_PASSWORD"); sframeChan.event("EV_PAD_PASSWORD");
};
if (val) {
password = val;
Cryptpad.getFileSize(window.location.href, password, function (e, size) {
if (size !== 0) {
return void todo();
}
// Wrong password or deleted file?
askPassword(true);
});
} else {
askPassword();
} }
}), parsed.getUrl()); }), parsed.getUrl());
return; return;
@ -619,6 +638,11 @@ define([
}); });
}); });
sframeChan.on('Q_PAD_PASSWORD_CHANGE', function (data, cb) {
var href = data.href || window.location.href;
Cryptpad.changePadPassword(Cryptget, href, data.password, edPublic, cb);
});
if (cfg.addRpc) { if (cfg.addRpc) {
cfg.addRpc(sframeChan, Cryptpad, Utils); cfg.addRpc(sframeChan, Cryptpad, Utils);
} }

@ -196,7 +196,6 @@ define({
// Anonymous users can empty their drive and remove FS_hash from localStorage // Anonymous users can empty their drive and remove FS_hash from localStorage
'EV_BURN_ANON_DRIVE': true, 'EV_BURN_ANON_DRIVE': true,
// Inner drive needs to send command and receive updates from the async store // Inner drive needs to send command and receive updates from the async store
'Q_DRIVE_USEROBJECT': true, 'Q_DRIVE_USEROBJECT': true,
'Q_DRIVE_GETOBJECT': true, 'Q_DRIVE_GETOBJECT': true,
@ -214,6 +213,8 @@ define({
// Notifications about connection and disconnection from the network // Notifications about connection and disconnection from the network
'EV_NETWORK_DISCONNECT': true, 'EV_NETWORK_DISCONNECT': true,
'EV_NETWORK_RECONNECT': true, 'EV_NETWORK_RECONNECT': true,
// Reload on new version
'EV_NEW_VERSION': true,
// Pad creation screen: create a pad with the selected attributes (owned, expire) // Pad creation screen: create a pad with the selected attributes (owned, expire)
'Q_CREATE_PAD': true, 'Q_CREATE_PAD': true,
@ -224,19 +225,18 @@ define({
// The exact protocol is defined in common/test.js // The exact protocol is defined in common/test.js
'EV_TESTDATA': true, 'EV_TESTDATA': true,
// Critical error outside the iframe during loading screen
'EV_LOADING_ERROR': true,
// Ask for the pad password when a pad is protected // Ask for the pad password when a pad is protected
'EV_PAD_PASSWORD': true, 'EV_PAD_PASSWORD': true,
'Q_PAD_PASSWORD_VALUE': true, 'Q_PAD_PASSWORD_VALUE': true,
// Change pad password
'Q_PAD_PASSWORD_CHANGE': true,
// Loading events to display in the loading screen // Loading events to display in the loading screen
'EV_LOADING_INFO': true, 'EV_LOADING_INFO': true,
// Critical error outside the iframe during loading screen
'EV_LOADING_ERROR': true,
// Get all existing tags // Get all existing tags
'Q_GET_ALL_TAGS': true, 'Q_GET_ALL_TAGS': true,
// Reload on new version
'EV_NEW_VERSION': true,
}); });

Loading…
Cancel
Save