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,
passwordOk
]);
var pLocked = false;
$(passwordOk).click(function () {
var newPass = $(newPassword).find('input').val();
if (data.password === newPass ||
(!data.password && !newPass)) {
return void UI.alert(Messages.properties_passwordSame);
}
if (pLocked) { return; }
pLocked = true;
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", {
teamId: typeof(owned) !== "boolean" ? owned : undefined,
href: data.href || data.roHref,
@ -579,6 +583,8 @@ define([
}, function (err, data) {
if (err || data.error) {
console.error(err || data.error);
pLocked = false;
$(passwordOk).text(Messages.properties_changePasswordButton);
return void UI.alert(Messages.properties_passwordError);
}
UI.findOKButton().click();
@ -2065,10 +2071,7 @@ define([
var cryptKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
var src = origin + Hash.getBlobPathFromHex(hexFileName);
common.getFileSize(hexFileName, function (e, data) {
if (e || !data) {
displayDefault();
return void console.error(e || "404 avatar");
}
if (e || !data) { return void displayDefault(); }
if (typeof data !== "number") { return void displayDefault(); }
if (Util.bytesToMegabytes(data) > 0.5) { return void displayDefault(); }
var $img = $('<media-tag>').appendTo($container);

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

@ -1878,6 +1878,12 @@ define([
$span.addClass('cp-app-drive-element-sharedf');
_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);
$shared.attr('title', Messages.fm_canBeShared);
}
@ -4515,7 +4521,7 @@ define([
onClose: cb
});
};
if (typeof (deprecated) === "object") {
if (typeof (deprecated) === "object" && APP.editable) {
Object.keys(deprecated).forEach(function (fId) {
var data = deprecated[fId];
var sfId = manager.user.userObject.getSFIdFromHref(data.href);

@ -1526,8 +1526,7 @@ define([
Store.leavePad = function (clientId, data, cb) {
var channel = channels[data.channel];
if (!channel || !channel.cpNf) { return void cb ({error: 'EINVAL'}); }
channel.cpNf.stop();
delete channels[data.channel];
Store.dropChannel(data.channel);
cb();
};
Store.sendPadMsg = function (clientId, data, cb) {
@ -1542,6 +1541,20 @@ define([
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
// of the pad AND to send the request if we want
// data.send === false ==> check if we can contact them
@ -1831,7 +1844,7 @@ define([
// Clients management
var driveEventClients = [];
var dropChannel = function (chanId) {
var dropChannel = Store.dropChannel = function (chanId) {
try {
store.messenger.leavePad(chanId);
} catch (e) { console.error(e); }
@ -1900,7 +1913,7 @@ define([
store.manager.user.userObject.getHref(data) : data.href;
var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets(parsed.type, parsed.hash, o);
SF.updatePassword({
SF.updatePassword(Store, {
oldChannel: secret.channel,
password: n,
href: href
@ -2044,6 +2057,15 @@ define([
/////////////////////// 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 proxy = store.proxy;
var unpin = function (data, cb) {

@ -103,29 +103,31 @@ define([
cb(sf.rt, sf.metadata);
});
});
sf.teams.push(store);
sf.teams.push({
cb: cb,
store: store,
id: id
});
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
sf.queue.push({
sf.teams.push({
cb: cb,
store: store,
id: id
});
sf.teams.push(store);
if (handler) { handler(id, sf.rt); }
return sf.rt;
return;
}
sf = allSharedFolders[secret.channel] = {
queue: [{
teams: [{
cb: cb,
store: store,
id: id
}],
teams: [store],
readOnly: Boolean(secondaryKey)
};
@ -151,10 +153,10 @@ define([
// New Shared folder: no migration required
rt.proxy.version = 2;
}
if (!sf.queue) {
if (!sf.teams) {
return;
}
sf.queue.forEach(function (obj) {
sf.teams.forEach(function (obj) {
var leave = function () { SF.leave(secret.channel, teamId); };
var uo = obj.store.manager.addProxy(obj.id, rt, leave, secondaryKey);
SF.checkMigration(secondaryKey, rt.proxy, uo, function () {
@ -163,15 +165,17 @@ define([
});
sf.metadata = info.metadata;
sf.ready = true;
delete sf.queue;
});
rt.proxy.on('error', function (info) {
if (info && info.error) {
if (info.error === "EDELETED" ) {
try {
// Deprecate the shared folder from each team
sf.teams.forEach(function (store) {
store.manager.deprecateProxy(id, secret.channel);
// XXX We can't deprecate a read-only proxy: the read-only seed will change...
// 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) {}
delete allSharedFolders[secret.channel];
@ -200,8 +204,8 @@ define([
var clients = sf.teams;
if (!Array.isArray(clients)) { return; }
var idx;
clients.some(function (store, i) {
if (store.id === teamId) {
clients.some(function (obj, i) {
if (obj.store.id === teamId) {
idx = i;
return true;
}
@ -217,6 +221,7 @@ define([
}
};
// Update the password locally
SF.updatePassword = function (Store, data, network, cb) {
var oldChannel = data.oldChannel;
var href = data.href;
@ -229,13 +234,18 @@ define([
sf.rt.stop();
}
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) {
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]) || {};
if (!sfId || !shared[sfId]) { return; }
var sf = JSON.parse(JSON.stringify(shared[sfId]));
sf.password = password;
sf.channel = secret.channel;
sf.href = '/drive/#'+Hash.getEditHashFromKeys(secret); // XXX encrypt
sf.roHref = '/drive/#'+Hash.getViewHashFromKeys(secret);
SF.load({
network: network,
store: s,

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

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

@ -174,7 +174,16 @@ define([
var parsed = Utils.Hash.parsePadUrl(window.location.href);
var todo = function () {
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
@ -197,68 +206,96 @@ define([
// 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
Cryptpad.getPadAttribute('password', waitFor(function (err, val) {
var askPassword = function (wrongPasswordStored) {
// Ask for the password and check if the pad exists
// If the pad doesn't exist, it means the password isn't correct
// or the pad has been deleted
var correctPassword = waitFor();
sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) {
password = data;
var next = function (e, isNew) {
if (Boolean(isNew)) {
// Ask again in the inner iframe
// We should receive a new Q_PAD_PASSWORD_VALUE
cb(false);
var askPassword = function (wrongPasswordStored) {
// Ask for the password and check if the pad exists
// If the pad doesn't exist, it means the password isn't correct
// or the pad has been deleted
var correctPassword = waitFor();
sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) {
password = data;
var next = function (e, isNew) {
if (Boolean(isNew)) {
// Ask again in the inner iframe
// We should receive a new Q_PAD_PASSWORD_VALUE
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 {
todo();
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);
correctPassword();
}
};
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;
cb(true);
}
// Not a file, so we can use `isNewChannel`
Cryptpad.isNewChannel(window.location.href, password, next);
});
sframeChan.event("EV_PAD_PASSWORD");
};
};
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);
});
sframeChan.event("EV_PAD_PASSWORD");
};
if (!val && sessionStorage.newPadPassword) {
val = sessionStorage.newPadPassword;
var done = waitFor();
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;
}
password = val;
Cryptpad.getFileSize(window.location.href, password, waitFor(function (e, size) {
if (size !== 0) {
return void todo();
}
if (parsed.hashData.mode === 'view' && (val || !parsed.hashData.password)) {
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, w(function (e, size) {
if (size !== 0) { return void todo(); }
// 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
// password will never work
sframeChan.event("EV_PAD_PASSWORD_ERROR");
waitFor.abort();
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?
askPassword(true);
}));
}), parsed.getUrl());
}).nThen(done);
}
}).nThen(function (waitFor) {
if (cfg.afterSecrets) {

@ -1221,5 +1221,9 @@
"team_title": "Équipe : {0}",
"team_quota": "Limite de stockage de votre équipe",
"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_quota": "Your team's 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 noPassword = function (str) {
if (!str) { return; }
var value = str.replace(/\/p\/?/, '/');
return Hash.getRelativeHref(value);
var parsed = Hash.parsePadUrl(str);
return parsed.getUrl().replace(/\/p\/?/, '/');
};
var href = noPassword(_href);
getFiles([FILES_DATA]).some(function (id) {
@ -471,8 +471,8 @@ define([
var result;
var noPassword = function (str) {
if (!str) { return; }
var value = str.replace(/\/p\/?/, '/');
return Hash.getRelativeHref(value);
var parsed = Hash.parsePadUrl(str);
return parsed.getUrl().replace(/\/p\/?/, '/');
};
var href = noPassword(_href);
getFiles([SHARED_FOLDERS]).some(function (id) {

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

Loading…
Cancel
Save