Automatic account deletion

This commit is contained in:
yflory 2018-03-21 18:27:20 +01:00
parent 632e6c2e6c
commit a718603b36
19 changed files with 153 additions and 50 deletions

View File

@ -46,7 +46,8 @@
"localforage": "^1.5.2",
"html2canvas": "^0.4.1",
"croppie": "^2.5.0",
"sortablejs": "#^1.6.0"
"sortablejs": "#^1.6.0",
"saferphore": "^0.0.1"
},
"resolutions": {
"bootstrap": "^v4.0.0"

View File

@ -31,7 +31,7 @@ define(function () {
out.websocketError = 'Αδυναμία σύνδεσης στον διακομιστή...';
out.typeError = "Αυτό το pad δεν είναι συμβατό με την επιλεγμένη εφαρμογή";
out.onLogout = 'Έχετε αποσυνδεθεί, <a href="/" target="_blank">κάντε "κλικ" εδώ</a> για να συνδεθείτε<br>ή πατήστε <em>Escape</em> για να προσπελάσετε το έγγραφο σε λειτουργία ανάγνωσης μόνο.';
out.onLogout = 'Έχετε αποσυνδεθεί, {0}κάντε "κλικ" εδώ{1} για να συνδεθείτε<br>ή πατήστε <em>Escape</em> για να προσπελάσετε το έγγραφο σε λειτουργία ανάγνωσης μόνο.';
out.wrongApp = "Αδυναμία προβολής του περιεχομένου αυτής της συνεδρίας στον περιηγητή σας. Παρακαλώ δοκιμάστε επαναφόρτωση της σελίδας.";
out.loading = "Φόρτωση...";

View File

@ -152,7 +152,7 @@ define(function () {
out.websocketError = "Error al conectarse al servidor WebSocket";
out.typeError = "Este documento no es compatible con la aplicación seleccionada";
out.onLogout = "Tu sesión está cerrada, <a href=\"/\" target=\"_blank\">haz clic aquí</a> para iniciar sesión<br>o pulsa <em>Escape</em> para acceder al documento en modo sólo lectura.";
out.onLogout = "Tu sesión está cerrada, {0}haz clic aquí{1} para iniciar sesión<br>o pulsa <em>Escape</em> para acceder al documento en modo sólo lectura.";
out.loading = "Cargando...";
out.error = "Error";
out.language = "Idioma";

View File

@ -27,7 +27,7 @@ define(function () {
out.websocketError = 'Impossible de se connecter au serveur WebSocket...';
out.typeError = "Ce pad n'est pas compatible avec l'application sélectionnée";
out.onLogout = 'Vous êtes déconnecté de votre compte utilisateur, <a href="/" target="_blank">cliquez ici</a> pour vous authentifier<br>ou appuyez sur <em>Échap</em> pour accéder au pad en mode lecture seule.';
out.onLogout = 'Vous êtes déconnecté de votre compte utilisateur, {0}cliquez ici{1} pour vous authentifier<br>ou appuyez sur <em>Échap</em> pour accéder au pad en mode lecture seule.';
out.wrongApp = "Impossible d'afficher le contenu de ce document temps-réel dans votre navigateur. Vous pouvez essayer de recharger la page.";
out.padNotPinned = 'Ce pad va expirer après 3 mois d\'inactivité, {0}connectez-vous{1} ou {2}enregistrez-vous{3} pour le préserver.';
out.anonymousStoreDisabled = "L'administrateur de cette instance de CryptPad a désactivé le drive pour les utilisateurs non enregistrés. Vous devez vous connecter pour pouvoir utiliser CryptDrive.";
@ -546,6 +546,8 @@ define(function () {
out.settings_deleteHint = "La suppression de votre compte utilisateur est permanente. Votre CryptDrive et votre liste de pads seront supprimés du serveur. Le reste de vos pads sera supprimé après 90 jours d'inactivité si personne ne les a stockés dans leur CryptDrive.";
out.settings_deleteButton = "Supprimer votre compte";
out.settings_deleteModal = "Veuillez envoyer les informations suivantes à votre administrateur CryptPad afin que vos données soient supprimées du serveur.";
out.settings_deleteConfirm = "Êtes-vous sûr de vouloir supprimer votre compte utilisateur ? Cette action est irréversible.";
out.settings_deleted = "Votre compte utilisateur a été supprimé. Appuyez sur OK pour être rédirigé(e) vers la page d'accueil.";
out.settings_anonymous = "Vous n'êtes pas connecté. Ces préférences seront utilisées pour ce navigateur.";
out.settings_publicSigningKey = "Clé publique de signature";

View File

@ -28,7 +28,7 @@ define(function () {
out.websocketError = 'Unable to connect to the websocket server...';
out.typeError = "This pad is not compatible with the selected application";
out.onLogout = 'You are logged out, <a href="/" target="_blank">click here</a> to log in<br>or press <em>Escape</em> to access your pad in read-only mode.';
out.onLogout = 'You are logged out, {0}click here{1} to log in<br>or press <em>Escape</em> to access your pad in read-only mode.';
out.wrongApp = "Unable to display the content of that realtime session in your browser. Please try to reload that page.";
out.padNotPinned = 'This pad will expire after 3 months of inactivity, {0}login{1} or {2}register{3} to preserve it.';
out.anonymousStoreDisabled = "The webmaster of this CryptPad instance has disabled the store for anonymous users. You have to log in to be able to use CryptDrive.";
@ -550,6 +550,8 @@ define(function () {
out.settings_deleteHint = "Account deletion is permanent. Your CryptDrive and your list of pads will be deleted from the server. The rest of your pads will be deleted in 90 days if nobody else has stored them in their CryptDrive.";
out.settings_deleteButton = "Delete your account";
out.settings_deleteModal = "Share the following information with your CryptPad administrator in order to have your data removed from their server.";
out.settings_deleteConfirm = "Clicking OK will delete your account permanently. Are you sure?";
out.settings_deleted = "Your user account is now deleted. Press OK to go to the home page.";
out.settings_anonymous = "You are not logged in. Settings here are specific to this browser.";
out.settings_publicSigningKey = "Public Signing Key";

View File

@ -38,7 +38,7 @@ define(function () {
out.websocketError = 'Incapaz de se conectar com o servidor websocket...';
out.typeError = "Este bloco não é compatível com a aplicação selecionada";
out.onLogout = 'você foi desconectado, <a href="/" target="_blank">clique aqui</a> para se conectar, <br>ou pressione <em>ESC</em> para acessar seu bloco em modo somente leitura.';
out.onLogout = 'você foi desconectado, {0}clique aqui{1} para se conectar, <br>ou pressione <em>ESC</em> para acessar seu bloco em modo somente leitura.';
out.wrongApp = "Incapaz de mostrar o conteúdo em tempo real no seu navegador. Por favor tente recarregar a página.";
out.loading = "Carregando...";

View File

@ -13,7 +13,7 @@ define(function () {
out.common_connectionLost = out.updated_0_common_connectionLost;
out.websocketError = "Conexiune inexistentă către serverul websocket...";
out.typeError = "Această filă nu este compatibilă cu aplicația aleasă";
out.onLogout = "Nu mai ești autentificat, <a href=\"/\" target=\"_blank\">apasă aici</a> să te autentifici<br>sau apasă <em>Escape</em>să accesezi fila în modul citire.";
out.onLogout = "Nu mai ești autentificat, {0}apasă aici{1} să te autentifici<br>sau apasă <em>Escape</em>să accesezi fila în modul citire.";
out.wrongApp = "Momentan nu putem arăta conținutul sesiunii în timp real în fereastra ta. Te rugăm reîncarcă pagina.";
out.loading = "Încarcă...";
out.error = "Eroare";

View File

@ -31,7 +31,7 @@ define(function () {
out.websocketError = '無法連結上 websocket 伺服器...';
out.typeError = "這個編輯檔與所選的應用程式並不相容";
out.onLogout = '你已登出, <a href="/" target="_blank">點擊這裏</a> 來登入<br>或按<em>Escape</em> 來以唯讀模型使用你的編輯檔案';
out.onLogout = '你已登出, {0}點擊這裏{1} 來登入<br>或按<em>Escape</em> 來以唯讀模型使用你的編輯檔案';
out.wrongApp = "無法在瀏覽器顯示即時期間的內容,請試著再重新載入本頁。";
out.loading = "載入中...";

2
rpc.js
View File

@ -1481,7 +1481,7 @@ RPC.create = function (
case 'REMOVE_PINS':
return void removePins(Env, safeKey, function (e, response) {
if (e) { return void Respond(e); }
Respond(void 0, response);
Respond(void 0, "OK");
});
// restricted to privileged users...
case 'UPLOAD':

View File

@ -1148,7 +1148,7 @@ define([
// so we can just use those and only check for errors
var $container = $('<span>', {'class':'cp-limit-container'});
var todo = function (err, data) {
if (err) { return void console.error(err); }
if (err || !data) { return void console.error(err || 'No data'); }
var usage = data.usage;
var limit = data.limit;

View File

@ -622,6 +622,12 @@ define([
window.location.href = '/login/';
};
common.startAccountDeletion = function (cb) {
// Logout other tabs
LocalStore.logout(null, true);
cb();
};
var onMessage = function (cmd, data, cb) {
cb = cb || function () {};
switch (cmd) {
@ -701,6 +707,10 @@ define([
case 'DRIVE_REMOVE': {
common.drive.onRemove.fire(data); break;
}
// Account deletion
case 'DELETE_ACCOUNT': {
common.startAccountDeletion(cb); break;
}
}
};
@ -779,11 +789,11 @@ define([
if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); }
if (cfg.userHash && sessionStorage) {
/*if (cfg.userHash && sessionStorage) {
// copy User_hash into sessionStorage because cross-domain iframes
// on safari replaces localStorage with sessionStorage or something
sessionStorage.setItem(Constants.userHashKey, cfg.userHash);
}
}*/
if (cfg.userHash) {
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));

View File

@ -16,9 +16,11 @@ define([
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
'/bower_components/chainpad/chainpad.dist.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/nthen/index.js',
'/bower_components/saferphore/index.js',
], function (Sortify, UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
CpNfWorker, NetConfig, AppConfig,
Crypto, ChainPad, Listmap) {
Crypto, ChainPad, Listmap, nThen, Saferphore) {
var Store = {};
var postMessage = function () {};
@ -421,28 +423,78 @@ define([
});
};
var getOwnedPads = function () {
var list = [];
store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) {
var data = store.userObject.getFileData(id);
var edPublic = store.proxy.edPublic;
// Push channels owned by someone else or channel that should have expired
// because of the expiration time
if (data.owners && data.owners.length === 1 && data.owners.indexOf(edPublic) !== -1) {
list.push(Hash.hrefToHexChannelId(data.href));
}
});
return list;
};
var removeOwnedPads = function (waitFor) {
// Delete owned pads
var ownedPads = getOwnedPads();
var sem = Saferphore.create(10);
ownedPads.forEach(function (c) {
var w = waitFor();
sem.take(function (give) {
Store.removeOwnedChannel(c, give(function (obj) {
if (obj && obj.error) { console.error(obj.error); }
w();
}));
});
});
};
Store.deleteAccount = function (data, cb) {
var toSign = {
intent: 'Please delete my account.'
};
var edPublic = store.proxy.edPublic;
var secret = Hash.getSecrets('drive', storeHash);
toSign.drive = secret.channel;
toSign.edPublic = store.proxy.edPublic;
var signKey = Crypto.Nacl.util.decodeBase64(secret.keys.signKey);
console.log(edPublic);
console.log(secret.channel);
Store.anonRpcMsg({
msg: 'GET_METADATA',
data: secret.channel
}, function (data) {
console.log(data[0]);
var metadata = data[0];
// Owned drive
if (metadata && metadata.owners && metadata.owners.length === 1 &&
metadata.owners.indexOf(edPublic) !== -1) {
nThen(function (waitFor) {
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
store.proxy[Constants.tokenKey] = token;
postMessage("DELETE_ACCOUNT", token, waitFor());
}).nThen(function (waitFor) {
removeOwnedPads(waitFor);
}).nThen(function (waitFor) {
// Delete Pin Store
store.rpc.removePins(waitFor(function (err) {
if (err) { console.error(err); }
}));
}).nThen(function (waitFor) {
// Delete Drive
Store.removeOwnedChannel(secret.channel, waitFor());
}).nThen(function () {
store.network.disconnect();
cb({
state: true
});
});
return;
}
// Not owned drive
var toSign = {
intent: 'Please delete my account.'
};
toSign.drive = secret.channel;
toSign.edPublic = edPublic;
var signKey = Crypto.Nacl.util.decodeBase64(secret.keys.signKey);
var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey);
var proofTxt = Crypto.Nacl.util.encodeBase64(proof);
cb({
@ -529,8 +581,12 @@ define([
// Reset the drive part of the userObject (from settings)
Store.resetDrive = function (data, cb) {
store.proxy.drive = store.fo.getStructure();
onSync(cb);
nThen(function (waitFor) {
removeOwnedPads(waitFor);
}).nThen(function (waitFor) {
store.proxy.drive = store.fo.getStructure();
onSync(cb);
});
};
/**

View File

@ -92,7 +92,7 @@ define([
});
};
var logoutHandlers = [];
LocalStore.logout = function (cb) {
LocalStore.logout = function (cb, isDeletion) {
[
Constants.userNameKey,
Constants.userHashKey,
@ -112,9 +112,11 @@ define([
}
eraseTempSessionValues();
logoutHandlers.forEach(function (h) {
if (typeof (h) === "function") { h(); }
});
if (!isDeletion) {
logoutHandlers.forEach(function (h) {
if (typeof (h) === "function") { h(); }
});
}
if (typeof(AppConfig.customizeLogout) === 'function') {
return void AppConfig.customizeLogout(cb);

View File

@ -165,6 +165,17 @@ define([
});
};
exp.removePins = function (cb) {
rpc.send('REMOVE_PINS', undefined, function (e, response) {
if (e) { return void cb(e); }
if (response && response.length && response[0] === "OK") {
cb();
} else {
cb('INVALID_RESPONSE');
}
});
};
exp.uploadComplete = function (cb) {
rpc.send('UPLOAD_COMPLETE', null, function (e, res) {
if (e) { return void cb(e); }

View File

@ -17,7 +17,7 @@ define([
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,

View File

@ -41,7 +41,7 @@ define([
var patchTransformer = config.patchTransformer;
var validateContent = config.validateContent;
var avgSyncMilliseconds = config.avgSyncMilliseconds;
var logLevel = typeof(config.logLevel) !== 'undefined'? config.logLevel : 2;
var logLevel = typeof(config.logLevel) !== 'undefined'? config.logLevel : 1;
var readOnly = config.readOnly || false;
var sframeChan = config.sframeChan;
var metadataMgr = config.metadataMgr;

View File

@ -622,6 +622,7 @@ define([
window.location.hash = hash;
};
console.log(secret.channel);
var cpNfCfg = {
sframeChan: sframeChan,
channel: secret.channel,

View File

@ -397,19 +397,6 @@ define([
UI.addTooltips();
ctx.sframeChan.on('EV_LOGOUT', function () {
$(window).on('keyup', function (e) {
if (e.keyCode === 27) {
UI.removeLoadingScreen();
}
});
UI.addLoadingScreen({hideTips: true});
UI.errorLoadingScreen(Messages.onLogout, true);
logoutHandlers.forEach(function (h) {
if (typeof (h) === "function") { h(); }
});
});
ctx.sframeChan.on('Q_INCOMING_FRIEND_REQUEST', function (confirmMsg, cb) {
UI.confirm(confirmMsg, cb, null, true);
});
@ -425,6 +412,23 @@ define([
var feedback = ctx.metadataMgr.getPrivateData().feedbackAllowed;
Feedback.init(feedback);
} catch (e) { Feedback.init(false); }
ctx.sframeChan.on('EV_LOGOUT', function () {
$(window).on('keyup', function (e) {
if (e.keyCode === 27) {
UI.removeLoadingScreen();
}
});
UI.addLoadingScreen({hideTips: true});
var origin = ctx.metadataMgr.getPrivateData().origin;
var href = origin + "/login/";
var onLogoutMsg = Messages._getKey('onLogout', ['<a href="' + href + '" target="_blank">', '</a>']);
UI.errorLoadingScreen(onLogoutMsg, true);
logoutHandlers.forEach(function (h) {
if (typeof (h) === "function") { h(); }
});
});
ctx.sframeChan.ready();
cb(funcs);
});

View File

@ -326,7 +326,7 @@ define([
$('<span>', {'class': 'cp-sidebarlayout-description'})
.append(Messages.settings_deleteHint).appendTo($div);
//var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var $button = $('<button>', {'id': 'cp-settings-delete', 'class': 'btn btn-danger'})
@ -334,13 +334,26 @@ define([
$button.click(function () {
$spinner.show();
sframeChan.query("Q_SETTINGS_DELETE_ACCOUNT", null, function (err, data) {
var msg = h('div.cp-app-settings-delete-alert', [
h('p', Messages.settings_deleteModal),
h('pre', JSON.stringify(data, 0, 2))
]);
UI.alert(msg);
$spinner.hide();
UI.confirm(Messages.settings_deleteConfirm, function (yes) {
sframeChan.query("Q_SETTINGS_DELETE_ACCOUNT", null, function (err, data) {
// Owned drive
if (data.state === true) {
sframeChan.query('Q_SETTINGS_LOGOUT', null, function () {});
UI.alert(Messages.settings_deleted, function () {
common.gotoURL('/');
});
$ok.show();
$spinner.hide();
return;
}
// Not owned drive
var msg = h('div.cp-app-settings-delete-alert', [
h('p', Messages.settings_deleteModal),
h('pre', JSON.stringify(data, 0, 2))
]);
UI.alert(msg);
$spinner.hide();
});
});
// TODO
/*
@ -356,6 +369,7 @@ define([
});
$spinner.hide().appendTo($div);
$ok.hide().appendTo($div);
return $div;
};