diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 94ea1cae5..4bf2367e2 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -544,7 +544,8 @@ define([ Object.keys(folders).forEach(function (id) { var f = folders[id]; var sfData = files.sharedFolders[id] || {}; - var parsed = Hash.parsePadUrl(sfData.href || sfData.roHref); + var href = manager.user.userObject.getHref(sfData); + var parsed = Hash.parsePadUrl(href); var secret = Hash.getSecrets('drive', parsed.hash, sfData.password); manager.addProxy(id, {proxy: f}, null, secret.keys.secondaryKey); }); diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index 270018eed..6fcdbc6cd 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -431,7 +431,7 @@ define([ var team; Object.keys(myTeams).some(function (k) { var _team = myTeams[k]; - if (_team.channel === content.teamChannel) { + if (_team.channel === content.teamData.channel) { teamId = k; team = _team; return true; diff --git a/www/common/outer/team.js b/www/common/outer/team.js index 86b06a186..e297dee65 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -990,6 +990,7 @@ define([ uo.setReadOnly(!secret.keys.secondaryKey, secret.keys.secondaryKey); } }); + ctx.updateMetadata(); ctx.emit('ROSTER_CHANGE_RIGHTS', teamId, team.clients); }; @@ -1006,10 +1007,14 @@ define([ teamData.hash = data.hash; teamData.keys.drive.edPrivate = data.keys.drive.edPrivate; teamData.keys.chat.edit = data.keys.chat.edit; + + var secret = Hash.getSecrets('team', data.hash, teamData.password); + team.secondaryKey = secret && secret.keys.secondaryKey; } else { delete teamData.hash; delete teamData.keys.drive.edPrivate; delete teamData.keys.chat.edit; + delete team.secondaryKey; } updateMyRights(ctx, teamId, data.hash); @@ -1055,14 +1060,14 @@ define([ // Viewer to editor if (user.role === "VIEWER" && data.data.role !== "VIEWER") { - return void changeEditRights(ctx, teamId, user, true, function (err) { + changeEditRights(ctx, teamId, user, true, function (err) { return void cb({error: err}); }); } // Editor to viewer if (user.role !== "VIEWER" && data.data.role === "VIEWER") { - return void changeEditRights(ctx, teamId, user, false, function (err) { + changeEditRights(ctx, teamId, user, false, function (err) { return void cb({error: err}); }); } diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 3ceeab773..ead731550 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -172,6 +172,22 @@ define([ return data; }; + var getSharedFolderData = function (Env, id) { + if (!Env.folders[id]) { return {}; } + var obj = Env.folders[id].proxy.metadata || {}; + for (var k in Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {}) { + var data = JSON.parse(JSON.stringify(Env.user.proxy[UserObject.SHARED_FOLDERS][id][k])); + if (data.href && data.href.indexOf('#') === -1) { + try { + data.href = Env.user.userObject.cryptor.decrypt(data.href); + } catch (e) {} + } + obj[k] = data; + } + return obj; + }; + + // Transform an absolute path into a path relative to the correct shared folder var _resolvePath = function (Env, path) { var res = { @@ -979,6 +995,7 @@ define([ setPadAttribute: callWithEnv(setPadAttribute), getTagsList: callWithEnv(getTagsList), getSecureFilesList: callWithEnv(getSecureFilesList), + getSharedFolderData: callWithEnv(getSharedFolderData), // Store getChannelsList: callWithEnv(getChannelsList), addPad: callWithEnv(addPad), @@ -1149,21 +1166,6 @@ define([ return Env.user.userObject.getOwnedPads(Env.edPublic); }; - var getSharedFolderData = function (Env, id) { - if (!Env.folders[id]) { return {}; } - var obj = Env.folders[id].proxy.metadata || {}; - for (var k in Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {}) { - var data = JSON.parse(JSON.stringify(Env.user.proxy[UserObject.SHARED_FOLDERS][id][k])); - if (data.href && data.href.indexOf('#') === -1) { - try { - data.href = Env.user.userObject.cryptor.decrypt(data.href); - } catch (e) {} - } - obj[k] = data; - } - return obj; - }; - var getFolderData = function (Env, path) { var resolved = _resolvePath(Env, path); if (!resolved || !resolved.userObject) { return {}; } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index e919f1506..16210b743 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -433,6 +433,10 @@ define([ Cryptpad.mailbox.execCommand(data, cb); }); + sframeChan.on('Q_STORE_IN_TEAM', function (data, cb) { + Cryptpad.storeInTeam(data, cb); + }); + }; addCommonRpc(sframeChan); @@ -465,10 +469,6 @@ define([ setDocumentTitle(); }); - sframeChan.on('Q_STORE_IN_TEAM', function (data, cb) { - Cryptpad.storeInTeam(data, cb); - }); - sframeChan.on('EV_SET_HASH', function (hash) { window.location.hash = hash; }); diff --git a/www/common/userObject.js b/www/common/userObject.js index 3925b1b4f..c57bc0bb0 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -29,29 +29,46 @@ define([ return name; }; + var createCryptor = module.createCryptor = function (key) { + var cryptor = {}; + if (!key) { + cryptor.encrypt = function (x) { return x; }; + cryptor.decrypt = function (x) { return x; }; + return cryptor; + } + try { + var c = Crypto.createEncryptor(key); + cryptor.encrypt = function (href) { + // Never encrypt blob href, they are always read-only + if (href.slice(0,7) === '/file/#') { return href; } + return c.encrypt(href); + }; + cryptor.decrypt = c.decrypt; + } catch (e) { + console.error(e); + } + return cryptor; + }; + module.getHref = function (pad, cryptor) { + if (pad.href && pad.href.indexOf('#') !== -1) { + // Href exists and is not encrypted: return href + return pad.href; + } + if (pad.href) { + // Href exists and is encrypted + var d = cryptor.decrypt(pad.href); + // If we can decrypt, return the decrypted value, otherwise continue and return roHref + if (d.indexOf('#') !== -1) { + return d; + } + } + return pad.roHref; + }; + module.init = function (files, config) { var exp = {}; - exp.cryptor = {}; - var createCryptor = function (key) { - if (!key) { - exp.cryptor.encrypt = function (x) { return x; }; - exp.cryptor.decrypt = function (x) { return x; }; - return; - } - try { - var c = Crypto.createEncryptor(key); - exp.cryptor.encrypt = function (href) { - // Never encrypt blob href, they are always read-only - if (href.slice(0,7) === '/file/#') { return href; } - return c.encrypt(href); - }; - exp.cryptor.decrypt = c.decrypt; - } catch (e) { - console.error(e); - } - }; - createCryptor(config.editKey); + exp.cryptor = createCryptor(config.editKey); exp.setReadOnly = function (state, key) { config.editKey = key; @@ -124,19 +141,7 @@ define([ }; var getHref = exp.getHref = function (pad) { - if (pad.href && pad.href.indexOf('#') !== -1) { - // Href exists and is not encrypted: return href - return pad.href; - } - if (pad.href) { - // Href exists and is encrypted - var d = exp.cryptor.decrypt(pad.href); - // If we can decrypt, return the decrypted value, otherwise continue and return roHref - if (d.indexOf('#') !== -1) { - return d; - } - } - return pad.roHref; + return module.getHref(pad, exp.cryptor); }; var type = function (dat) { diff --git a/www/drive/inner.js b/www/drive/inner.js index e0e695126..f5416a861 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -44,7 +44,8 @@ define([ nThen(function (waitFor) { Object.keys(drive.sharedFolders).forEach(function (fId) { var sfData = drive.sharedFolders[fId] || {}; - var parsed = Hash.parsePadUrl(sfData.href || sfData.roHref); + var href = (sfData.href && sfData.href.indexOf('#') !== -1) ? sfData.href : sfData.roHref; + var parsed = Hash.parsePadUrl(href); var secret = Hash.getSecrets('drive', parsed.hash, sfData.password); sframeChan.query('Q_DRIVE_GETOBJECT', { sharedFolder: fId diff --git a/www/teams/inner.js b/www/teams/inner.js index f4448de5f..859887c36 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -11,6 +11,7 @@ define([ '/bower_components/nthen/index.js', '/common/sframe-common.js', '/common/proxy-manager.js', + '/common/userObject.js', '/common/hyperscript.js', '/customize/application_config.js', '/common/messenger-ui.js', @@ -32,6 +33,7 @@ define([ nThen, SFCommon, ProxyManager, + UserObject, h, AppConfig, MessengerUI, @@ -53,7 +55,8 @@ define([ nThen(function (waitFor) { Object.keys(drive.sharedFolders).forEach(function (fId) { var sfData = drive.sharedFolders[fId] || {}; - var parsed = Hash.parsePadUrl(sfData.href || sfData.roHref); + var href = UserObject.getHref(sfData, APP.cryptor); + var parsed = Hash.parsePadUrl(href); var secret = Hash.getSecrets('drive', parsed.hash, sfData.password); sframeChan.query('Q_DRIVE_GETOBJECT', { sharedFolder: fId @@ -78,6 +81,30 @@ define([ var setEditable = DriveUI.setEditable; + var closeTeam = function (common, cb) { + var sframeChan = common.getSframeChannel(); + APP.module.execCommand('SUBSCRIBE', null, function () { + sframeChan.query('Q_SET_TEAM', null, function (err) { + if (err) { return void console.error(err); } + if (APP.drive && APP.drive.close) { APP.drive.close(); } + $('.cp-toolbar-title-value').text(Messages.type.teams); + sframeChan.event('EV_SET_TAB_TITLE', Messages.type.teams); + APP.team = null; + APP.teamEdPublic = null; + APP.drive = null; + APP.cryptor = null; + APP.buildUI(common); + if (APP.usageBar) { + APP.usageBar.stop(); + APP.usageBar = null; + } + if (cb) { + cb(common); + } + }); + }); + }; + var mainCategories = { 'list': [ 'cp-team-list', @@ -92,23 +119,7 @@ define([ var teamCategories = { 'back': { onClick: function (common) { - var sframeChan = common.getSframeChannel(); - APP.module.execCommand('SUBSCRIBE', null, function () { - sframeChan.query('Q_SET_TEAM', null, function (err) { - if (err) { return void console.error(err); } - if (APP.drive && APP.drive.close) { APP.drive.close(); } - $('.cp-toolbar-title-value').text(Messages.type.teams); - sframeChan.event('EV_SET_TAB_TITLE', Messages.type.teams); - APP.team = null; - APP.teamEdPublic = null; - APP.drive = null; - APP.buildUI(common); - if (APP.usageBar) { - APP.usageBar.stop(); - APP.usageBar = null; - } - }); - }); + closeTeam(common); } }, 'drive': [ @@ -311,8 +322,26 @@ define([ }); var MAX_TEAMS_SLOTS = Constants.MAX_TEAMS_SLOTS; - var refreshList = function (common, cb) { + var openTeam = function (common, id, team) { var sframeChan = common.getSframeChannel(); + APP.module.execCommand('SUBSCRIBE', id, function () { + var t = Messages._getKey('team_title', [Util.fixHTML(team.metadata.name)]); + sframeChan.query('Q_SET_TEAM', id, function (err) { + if (err) { return void console.error(err); } + // Change title + $('.cp-toolbar-title-value').text(t); + sframeChan.event('EV_SET_TAB_TITLE', t); + // Get secondary key + var secret = Hash.getSecrets('team', team.hash || team.roHash, team.password); + APP.cryptor = UserObject.createCryptor(secret.keys.secondaryKey); + // Load data + APP.team = id; + APP.teamEdPublic = Util.find(team, ['keys', 'drive', 'edPublic']); + buildUI(common, true, team.owner); + }); + }); + }; + var refreshList = function (common, cb) { var content = []; APP.module.execCommand('LIST_TEAMS', null, function (obj) { if (!obj) { return; } @@ -348,19 +377,7 @@ define([ ])); common.displayAvatar($(avatar), team.metadata.avatar, team.metadata.name); $(btn).click(function () { - APP.module.execCommand('SUBSCRIBE', id, function () { - var t = Messages._getKey('team_title', [Util.fixHTML(team.metadata.name)]); - sframeChan.query('Q_SET_TEAM', id, function (err) { - if (err) { return void console.error(err); } - // Change title - $('.cp-toolbar-title-value').text(t); - sframeChan.event('EV_SET_TAB_TITLE', t); - // Load data - APP.team = id; - APP.teamEdPublic = Util.find(team, ['keys', 'drive', 'edPublic']); - buildUI(common, true, team.owner); - }); - }); + openTeam(common, id, team); }); }); content.push(h('div.cp-team-list-container', list)); @@ -506,7 +523,7 @@ define([ var $actions = $(actions); var isMe = me && me.curvePublic === data.curvePublic; var myRole = me ? (ROLES.indexOf(me.role) || 1) : -1; - var theirRole = ROLES.indexOf(data.role) || 1; + var theirRole = ROLES.indexOf(data.role); var ADMIN = ROLES.indexOf('ADMIN'); // If they're an admin and I am an owner, I can promote them to owner if (!isMe && myRole > theirRole && theirRole === ADMIN && !data.pending) { @@ -895,6 +912,22 @@ define([ ]); }, true); + var redrawTeam = function (common) { + if (!APP.team) { return; } + var teamId = APP.team; + var name = $('.cp-toolbar-title-value').text(); + APP.module.execCommand('LIST_TEAMS', null, function (obj) { + if (!obj) { return; } + if (obj.error) { return void console.error(obj.error); } + var team = obj[teamId]; + if (!team) { return; } + closeTeam(common, function () { + openTeam(common, teamId, team); + }); + }); + }; + + var main = function () { var common; var readOnly; @@ -961,6 +994,10 @@ define([ } return; } + if (ev === 'ROSTER_CHANGE_RIGHTS') { + redrawTeam(common); + return; + } }; APP.module = common.makeUniversal('team', {