Merge branch 'sfPassword' into ro

pull/1/head
yflory 5 years ago
commit 9477ae0895

@ -0,0 +1,14 @@
/*
* You can override the translation text using this file.
* The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
in a 'customize' directory (/customize/translations/messages.{LANG}.js).
* If you want to check all the existing translation keys, you can open the internal language file
but you should not change it directly (/common/translations/messages.{LANG}.js)
*/
define(['/common/translations/messages.ja.js'], function (Messages) {
// Replace the existing keys in your copied file here:
// Messages.button_newpad = "New Rich Text Document";
return Messages;
});

@ -564,14 +564,18 @@ define([
newPassword, newPassword,
passwordOk passwordOk
]); ]);
var pLocked = false;
$(passwordOk).click(function () { $(passwordOk).click(function () {
var newPass = $(newPassword).find('input').val(); var newPass = $(newPassword).find('input').val();
if (data.password === newPass || if (data.password === newPass ||
(!data.password && !newPass)) { (!data.password && !newPass)) {
return void UI.alert(Messages.properties_passwordSame); return void UI.alert(Messages.properties_passwordSame);
} }
if (pLocked) { return; }
pLocked = true;
UI.confirm(changePwConfirm, function (yes) { UI.confirm(changePwConfirm, function (yes) {
if (!yes) { return; } if (!yes) { pLocked = false; return; }
$(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'}));
sframeChan.query("Q_PAD_PASSWORD_CHANGE", { sframeChan.query("Q_PAD_PASSWORD_CHANGE", {
teamId: typeof(owned) !== "boolean" ? owned : undefined, teamId: typeof(owned) !== "boolean" ? owned : undefined,
href: data.href || data.roHref, href: data.href || data.roHref,
@ -579,6 +583,8 @@ define([
}, function (err, data) { }, function (err, data) {
if (err || data.error) { if (err || data.error) {
console.error(err || data.error); console.error(err || data.error);
pLocked = false;
$(passwordOk).text(Messages.properties_changePasswordButton);
return void UI.alert(Messages.properties_passwordError); return void UI.alert(Messages.properties_passwordError);
} }
UI.findOKButton().click(); UI.findOKButton().click();
@ -2065,10 +2071,7 @@ define([
var cryptKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey); var cryptKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
var src = origin + Hash.getBlobPathFromHex(hexFileName); var src = origin + Hash.getBlobPathFromHex(hexFileName);
common.getFileSize(hexFileName, function (e, data) { common.getFileSize(hexFileName, function (e, data) {
if (e || !data) { if (e || !data) { return void displayDefault(); }
displayDefault();
return void console.error(e || "404 avatar");
}
if (typeof data !== "number") { return void displayDefault(); } if (typeof data !== "number") { return void displayDefault(); }
if (Util.bytesToMegabytes(data) > 0.5) { return void displayDefault(); } if (Util.bytesToMegabytes(data) > 0.5) { return void displayDefault(); }
var $img = $('<media-tag>').appendTo($container); var $img = $('<media-tag>').appendTo($container);

@ -959,9 +959,7 @@ define([
href: href, href: href,
oldChannel: oldChannel, oldChannel: oldChannel,
password: newPassword password: newPassword
}, waitFor(function (obj) { }, waitFor());
console.error(obj);
}));
return; return;
} }
pad.leavePad({ pad.leavePad({
@ -998,8 +996,10 @@ define([
} }
})); }));
if (!isSharedFolder) { if (!isSharedFolder) {
common.unpinPads([oldChannel], waitFor(), teamId); postMessage("CHANGE_PAD_PASSWORD_PIN", {
common.pinPads([newSecret.channel], waitFor(), teamId); oldChannel: oldChannel,
channel: newSecret.channel
}, waitFor());
} }
}).nThen(function () { }).nThen(function () {
cb({ cb({

@ -1878,6 +1878,12 @@ define([
$span.addClass('cp-app-drive-element-sharedf'); $span.addClass('cp-app-drive-element-sharedf');
_addOwnership($span, $state, data); _addOwnership($span, $state, data);
var hrefData = Hash.parsePadUrl(data.href || data.roHref);
if (hrefData.hashData && hrefData.hashData.password) {
var $password = $passwordIcon.clone().appendTo($state);
$password.attr('title', Messages.fm_passwordProtected || '');
}
var $shared = $sharedIcon.clone().appendTo($state); var $shared = $sharedIcon.clone().appendTo($state);
$shared.attr('title', Messages.fm_canBeShared); $shared.attr('title', Messages.fm_canBeShared);
} }
@ -4515,7 +4521,7 @@ define([
onClose: cb onClose: cb
}); });
}; };
if (typeof (deprecated) === "object") { if (typeof (deprecated) === "object" && APP.editable) {
Object.keys(deprecated).forEach(function (fId) { Object.keys(deprecated).forEach(function (fId) {
var data = deprecated[fId]; var data = deprecated[fId];
var sfId = manager.user.userObject.getSFIdFromHref(data.href); var sfId = manager.user.userObject.getSFIdFromHref(data.href);

@ -1526,8 +1526,7 @@ define([
Store.leavePad = function (clientId, data, cb) { Store.leavePad = function (clientId, data, cb) {
var channel = channels[data.channel]; var channel = channels[data.channel];
if (!channel || !channel.cpNf) { return void cb ({error: 'EINVAL'}); } if (!channel || !channel.cpNf) { return void cb ({error: 'EINVAL'}); }
channel.cpNf.stop(); Store.dropChannel(data.channel);
delete channels[data.channel];
cb(); cb();
}; };
Store.sendPadMsg = function (clientId, data, cb) { Store.sendPadMsg = function (clientId, data, cb) {
@ -1542,6 +1541,20 @@ define([
channel.sendMessage(msg, clientId, cb); channel.sendMessage(msg, clientId, cb);
}; };
// Unpin and pin the new channel in all team when changing a pad password
Store.changePadPasswordPin = function (clientId, data, cb) {
var oldChannel = data.oldChannel;
var channel = data.channel;
nThen(function (waitFor) {
getAllStores().forEach(function (s) {
var allData = s.manager.findChannel(channel);
if (!allData.length) { return; }
s.rpc.unpin([oldChannel], waitFor());
s.rpc.pin([channel], waitFor());
});
}).nThen(cb);
};
// requestPadAccess is used to check if we have a way to contact the owner // requestPadAccess is used to check if we have a way to contact the owner
// of the pad AND to send the request if we want // of the pad AND to send the request if we want
// data.send === false ==> check if we can contact them // data.send === false ==> check if we can contact them
@ -1831,7 +1844,7 @@ define([
// Clients management // Clients management
var driveEventClients = []; var driveEventClients = [];
var dropChannel = function (chanId) { var dropChannel = Store.dropChannel = function (chanId) {
try { try {
store.messenger.leavePad(chanId); store.messenger.leavePad(chanId);
} catch (e) { console.error(e); } } catch (e) { console.error(e); }
@ -1900,7 +1913,7 @@ define([
store.manager.user.userObject.getHref(data) : data.href; store.manager.user.userObject.getHref(data) : data.href;
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets(parsed.type, parsed.hash, o); var secret = Hash.getSecrets(parsed.type, parsed.hash, o);
SF.updatePassword({ SF.updatePassword(Store, {
oldChannel: secret.channel, oldChannel: secret.channel,
password: n, password: n,
href: href href: href
@ -2044,6 +2057,15 @@ define([
/////////////////////// Init ///////////////////////////////////// /////////////////////// Init /////////////////////////////////////
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
Store.refreshDriveUI = function () {
getAllStores().forEach(function (_s) {
var send = _s.id ? _s.sendEvent : sendDriveEvent;
send('DRIVE_CHANGE', {
path: ['drive', UserObject.FILES_DATA]
});
});
};
var onReady = function (clientId, returned, cb) { var onReady = function (clientId, returned, cb) {
var proxy = store.proxy; var proxy = store.proxy;
var unpin = function (data, cb) { var unpin = function (data, cb) {

@ -103,29 +103,31 @@ define([
cb(sf.rt, sf.metadata); cb(sf.rt, sf.metadata);
}); });
}); });
sf.teams.push(store); sf.teams.push({
cb: cb,
store: store,
id: id
});
if (handler) { handler(id, sf.rt); } if (handler) { handler(id, sf.rt); }
return sf.rt; return;
} }
if (sf && sf.queue && sf.rt) { if (sf && !sf.ready && sf.rt) {
// The shared folder is loading, add our callbacks to the queue // The shared folder is loading, add our callbacks to the queue
sf.queue.push({ sf.teams.push({
cb: cb, cb: cb,
store: store, store: store,
id: id id: id
}); });
sf.teams.push(store);
if (handler) { handler(id, sf.rt); } if (handler) { handler(id, sf.rt); }
return sf.rt; return;
} }
sf = allSharedFolders[secret.channel] = { sf = allSharedFolders[secret.channel] = {
queue: [{ teams: [{
cb: cb, cb: cb,
store: store, store: store,
id: id id: id
}], }],
teams: [store],
readOnly: Boolean(secondaryKey) readOnly: Boolean(secondaryKey)
}; };
@ -151,10 +153,10 @@ define([
// New Shared folder: no migration required // New Shared folder: no migration required
rt.proxy.version = 2; rt.proxy.version = 2;
} }
if (!sf.queue) { if (!sf.teams) {
return; return;
} }
sf.queue.forEach(function (obj) { sf.teams.forEach(function (obj) {
var leave = function () { SF.leave(secret.channel, teamId); }; var leave = function () { SF.leave(secret.channel, teamId); };
var uo = obj.store.manager.addProxy(obj.id, rt, leave, secondaryKey); var uo = obj.store.manager.addProxy(obj.id, rt, leave, secondaryKey);
SF.checkMigration(secondaryKey, rt.proxy, uo, function () { SF.checkMigration(secondaryKey, rt.proxy, uo, function () {
@ -163,15 +165,17 @@ define([
}); });
sf.metadata = info.metadata; sf.metadata = info.metadata;
sf.ready = true; sf.ready = true;
delete sf.queue;
}); });
rt.proxy.on('error', function (info) { rt.proxy.on('error', function (info) {
if (info && info.error) { if (info && info.error) {
if (info.error === "EDELETED" ) { if (info.error === "EDELETED" ) {
try { try {
// Deprecate the shared folder from each team // Deprecate the shared folder from each team
sf.teams.forEach(function (store) { // XXX We can't deprecate a read-only proxy: the read-only seed will change...
store.manager.deprecateProxy(id, secret.channel); // We can only remove it
sf.teams.forEach(function (obj) {
console.log(obj.store.id, obj.store, obj.id);
obj.store.manager.deprecateProxy(obj.id, secret.channel);
}); });
} catch (e) {} } catch (e) {}
delete allSharedFolders[secret.channel]; delete allSharedFolders[secret.channel];
@ -200,8 +204,8 @@ define([
var clients = sf.teams; var clients = sf.teams;
if (!Array.isArray(clients)) { return; } if (!Array.isArray(clients)) { return; }
var idx; var idx;
clients.some(function (store, i) { clients.some(function (obj, i) {
if (store.id === teamId) { if (obj.store.id === teamId) {
idx = i; idx = i;
return true; return true;
} }
@ -217,6 +221,7 @@ define([
} }
}; };
// Update the password locally
SF.updatePassword = function (Store, data, network, cb) { SF.updatePassword = function (Store, data, network, cb) {
var oldChannel = data.oldChannel; var oldChannel = data.oldChannel;
var href = data.href; var href = data.href;
@ -229,13 +234,18 @@ define([
sf.rt.stop(); sf.rt.stop();
} }
var nt = nThen; var nt = nThen;
sf.teams.forEach(function (s) { sf.teams.forEach(function (obj) {
// XXX if we're a viewer in this team, we can't update the keys
nt = nt(function (waitFor) { nt = nt(function (waitFor) {
var sfId = s.manager.user.userObject.getSFIdFromHref(href); var s = obj.store;
var sfId = obj.id;
var shared = Util.find(s.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {}; var shared = Util.find(s.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {};
if (!sfId || !shared[sfId]) { return; } if (!sfId || !shared[sfId]) { return; }
var sf = JSON.parse(JSON.stringify(shared[sfId])); var sf = JSON.parse(JSON.stringify(shared[sfId]));
sf.password = password; sf.password = password;
sf.channel = secret.channel;
sf.href = '/drive/#'+Hash.getEditHashFromKeys(secret); // XXX encrypt
sf.roHref = '/drive/#'+Hash.getViewHashFromKeys(secret);
SF.load({ SF.load({
network: network, network: network,
store: s, store: s,

@ -79,6 +79,7 @@ define([
GIVE_PAD_ACCESS: Store.givePadAccess, GIVE_PAD_ACCESS: Store.givePadAccess,
GET_PAD_METADATA: Store.getPadMetadata, GET_PAD_METADATA: Store.getPadMetadata,
SET_PAD_METADATA: Store.setPadMetadata, SET_PAD_METADATA: Store.setPadMetadata,
CHANGE_PAD_PASSWORD_PIN: Store.changePadPasswordPin,
// Drive // Drive
DRIVE_USEROBJECT: Store.userObjectCommand, DRIVE_USEROBJECT: Store.userObjectCommand,
// Settings, // Settings,

@ -54,6 +54,9 @@ define([
var deprecateProxy = function (Env, id, channel) { var deprecateProxy = function (Env, id, channel) {
Env.unpinPads([channel], function () {}); Env.unpinPads([channel], function () {});
Env.user.userObject.deprecateSharedFolder(id); Env.user.userObject.deprecateSharedFolder(id);
if (Env.Store && Env.Store.refreshDriveUI) {
Env.Store.refreshDriveUI();
}
}; };
/* /*
@ -547,7 +550,12 @@ define([
if (isNew) { if (isNew) {
return void cb({ error: 'ENOTFOUND' }); return void cb({ error: 'ENOTFOUND' });
} }
var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets(parsed.type, parsed.hash, newPassword);
data.password = newPassword; data.password = newPassword;
data.channel = secret.channel;
data.href = '/drive/#'+Hash.getEditHashFromKeys(secret); // XXX encrypt
data.roHref = '/drive/#'+Hash.getViewHashFromKeys(secret);
_addSharedFolder(Env, { _addSharedFolder(Env, {
path: ['root'], path: ['root'],
folderData: data, folderData: data,

@ -174,7 +174,16 @@ define([
var parsed = Utils.Hash.parsePadUrl(window.location.href); var parsed = Utils.Hash.parsePadUrl(window.location.href);
var todo = function () { var todo = function () {
secret = Utils.secret = Utils.Hash.getSecrets(parsed.type, void 0, password); secret = Utils.secret = Utils.Hash.getSecrets(parsed.type, void 0, password);
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; })); Cryptpad.getShareHashes(secret, waitFor(function (err, h) {
hashes = h;
if (password && !parsed.hashData.password) {
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.hash = h.fileHash || h.editHash || h.viewHash || window.location.hash;
window.onhashchange = ohc;
ohc({reset: true});
}
}));
}; };
if (!parsed.hashData) { // No hash, no need to check for a password if (!parsed.hashData) { // No hash, no need to check for a password
@ -197,68 +206,96 @@ define([
// 2c: 'view' pad and '/p/' and a wrong password stored --> the seed is incorrect // 2c: 'view' pad and '/p/' and a wrong password stored --> the seed is incorrect
// 2d: 'view' pad and '/p/' and password never stored (security feature) --> password-prompt // 2d: 'view' pad and '/p/' and password never stored (security feature) --> password-prompt
Cryptpad.getPadAttribute('password', waitFor(function (err, val) { var askPassword = function (wrongPasswordStored) {
var askPassword = function (wrongPasswordStored) { // 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 var correctPassword = waitFor();
var correctPassword = waitFor(); sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) {
sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) { password = data;
password = data; var next = function (e, isNew) {
var next = function (e, isNew) { if (Boolean(isNew)) {
if (Boolean(isNew)) { // Ask again in the inner iframe
// Ask again in the inner iframe // We should receive a new Q_PAD_PASSWORD_VALUE
// We should receive a new Q_PAD_PASSWORD_VALUE cb(false);
cb(false); } else {
todo();
if (wrongPasswordStored) {
// Store the correct password
nThen(function (w) {
// XXX noPasswordStored: return; ?
Cryptpad.setPadAttribute('password', password, w(), parsed.getUrl());
Cryptpad.setPadAttribute('channel', secret.channel, w(), parsed.getUrl());
if (parsed.hashData.mode === 'edit') {
var href = window.location.pathname + '#' + Utils.Hash.getEditHashFromKeys(secret);
Cryptpad.setPadAttribute('href', href, w(), parsed.getUrl());
var roHref = window.location.pathname + '#' + Utils.Hash.getViewHashFromKeys(secret);
Cryptpad.setPadAttribute('roHref', roHref, w(), parsed.getUrl());
}
}).nThen(correctPassword);
} else { } else {
todo(); correctPassword();
if (wrongPasswordStored) {
// Store the correct password
nThen(function (w) {
Cryptpad.setPadAttribute('password', password, w(), parsed.getUrl());
Cryptpad.setPadAttribute('channel', secret.channel, w(), parsed.getUrl());
}).nThen(correctPassword);
} else {
correctPassword();
}
cb(true);
} }
}; cb(true);
if (parsed.type === "file") {
// `isNewChannel` doesn't work for files (not a channel)
// `getFileSize` is not adapted to channels because of metadata
Cryptpad.getFileSize(window.location.href, password, function (e, size) {
next(e, size === 0);
});
return;
} }
// Not a file, so we can use `isNewChannel` };
Cryptpad.isNewChannel(window.location.href, password, next); if (parsed.type === "file") {
}); // `isNewChannel` doesn't work for files (not a channel)
sframeChan.event("EV_PAD_PASSWORD"); // `getFileSize` is not adapted to channels because of metadata
}; Cryptpad.getFileSize(window.location.href, password, function (e, size) {
next(e, size === 0);
});
return;
}
// Not a file, so we can use `isNewChannel`
Cryptpad.isNewChannel(window.location.href, password, next);
});
sframeChan.event("EV_PAD_PASSWORD");
};
if (!val && sessionStorage.newPadPassword) { var done = waitFor();
val = sessionStorage.newPadPassword; var stored = false;
nThen(function (w) {
Cryptpad.getPadAttribute('title', w(function (err, data) {
stored = (!err && typeof (data) === "string");
}));
Cryptpad.getPadAttribute('password', w(function (err, val) {
password = val;
}), parsed.getUrl());
}).nThen(function (w) {
if (!password && !stored && sessionStorage.newPadPassword) {
password = sessionStorage.newPadPassword;
delete sessionStorage.newPadPassword; delete sessionStorage.newPadPassword;
} }
password = val; if (parsed.type === "file") {
Cryptpad.getFileSize(window.location.href, password, waitFor(function (e, size) { // `isNewChannel` doesn't work for files (not a channel)
if (size !== 0) { // `getFileSize` is not adapted to channels because of metadata
return void todo(); Cryptpad.getFileSize(window.location.href, password, w(function (e, size) {
} if (size !== 0) { return void todo(); }
if (parsed.hashData.mode === 'view' && (val || !parsed.hashData.password)) { // Wrong password or deleted file?
askPassword(true);
}));
return;
}
// Not a file, so we can use `isNewChannel`
Cryptpad.isNewChannel(window.location.href, password, w(function(e, isNew) {
if (!isNew) { return void todo(); }
if (parsed.hashData.mode === 'view' && (password || !parsed.hashData.password)) {
// Error, wrong password stored, the view seed has changed with the password // Error, wrong password stored, the view seed has changed with the password
// password will never work // password will never work
sframeChan.event("EV_PAD_PASSWORD_ERROR"); sframeChan.event("EV_PAD_PASSWORD_ERROR");
waitFor.abort(); waitFor.abort();
return; return;
} }
if (!stored && !parsed.hashData.password) {
// We've received a link without /p/ and it doesn't work without a password: abort
return void todo();
}
// Wrong password or deleted file? // Wrong password or deleted file?
askPassword(true); askPassword(true);
})); }));
}), parsed.getUrl()); }).nThen(done);
} }
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
if (cfg.afterSecrets) { if (cfg.afterSecrets) {

@ -1221,5 +1221,9 @@
"team_title": "Équipe : {0}", "team_title": "Équipe : {0}",
"team_quota": "Limite de stockage de votre équipe", "team_quota": "Limite de stockage de votre équipe",
"drive_quota": "Votre limite de stockage", "drive_quota": "Votre limite de stockage",
"settings_codeBrackets": "Fermer automatiquement les parenthèses" "settings_codeBrackets": "Fermer automatiquement les parenthèses",
"team_viewers": "Lecteurs",
"drive_sfPassword": "Votre dossier partagé {0} n'est plus disponible. Il a soit été supprimé par son propriétaire ou il est protégé par un nouveau mot de passe. Vous pouvez supprimer ce dossier de votre CryptDrive ou retrouver l'accès en tapant le nouveau mot de passe.",
"drive_sfPasswordError": "Mot de passe incorrect",
"password_error_seed": "Pad introuvable !<br>Cette erreur peut provenir de deux facteurs. Soit un mot de passe a été ajouté ou modifié, soit le pad a été supprimé par son propriétaire."
} }

@ -1221,5 +1221,9 @@
"team_title": "Team: {0}", "team_title": "Team: {0}",
"team_quota": "Your team's storage limit", "team_quota": "Your team's storage limit",
"drive_quota": "Your storage limit", "drive_quota": "Your storage limit",
"settings_codeBrackets": "Auto-close brackets" "settings_codeBrackets": "Auto-close brackets",
"team_viewers": "Viewers",
"drive_sfPassword": "Your shared folder {0} is no longer available. It has either been deleted by its owner or it is now protected with a new password. You can remove this folder from your CryptDrive, or recover access using the new password.",
"drive_sfPasswordError": "Wrong password",
"password_error_seed": "Pad not found!<br>This error can be caused by two factors: either a password was added/changed, or the pad has been deleted from the server."
} }

@ -453,8 +453,8 @@ define([
var result; var result;
var noPassword = function (str) { var noPassword = function (str) {
if (!str) { return; } if (!str) { return; }
var value = str.replace(/\/p\/?/, '/'); var parsed = Hash.parsePadUrl(str);
return Hash.getRelativeHref(value); return parsed.getUrl().replace(/\/p\/?/, '/');
}; };
var href = noPassword(_href); var href = noPassword(_href);
getFiles([FILES_DATA]).some(function (id) { getFiles([FILES_DATA]).some(function (id) {
@ -471,8 +471,8 @@ define([
var result; var result;
var noPassword = function (str) { var noPassword = function (str) {
if (!str) { return; } if (!str) { return; }
var value = str.replace(/\/p\/?/, '/'); var parsed = Hash.parsePadUrl(str);
return Hash.getRelativeHref(value); return parsed.getUrl().replace(/\/p\/?/, '/');
}; };
var href = noPassword(_href); var href = noPassword(_href);
getFiles([SHARED_FOLDERS]).some(function (id) { getFiles([SHARED_FOLDERS]).some(function (id) {

@ -398,7 +398,7 @@ define([
var isOwner = Object.keys(privateData.teams || {}).filter(function (id) { var isOwner = Object.keys(privateData.teams || {}).filter(function (id) {
return privateData.teams[id].owner; return privateData.teams[id].owner;
}).length >= Constants.MAX_TEAMS_OWNED; // && !privateData.devMode; }).length >= Constants.MAX_TEAMS_OWNED && !privateData.devMode;
var getWarningBox = function () { var getWarningBox = function () {
return h('div.alert.alert-warning', { return h('div.alert.alert-warning', {

Loading…
Cancel
Save