diff --git a/www/common/application_config_internal.js b/www/common/application_config_internal.js index 72897680c..d4c1954e4 100644 --- a/www/common/application_config_internal.js +++ b/www/common/application_config_internal.js @@ -20,7 +20,7 @@ define(function() { * users and these users will be redirected to the login page if they still try to access * the app */ - config.registeredOnlyTypes = ['teams', 'file', 'contacts', 'oodoc', 'ooslide', 'sheet', 'notifications']; + config.registeredOnlyTypes = ['file', 'contacts', 'oodoc', 'ooslide', 'sheet', 'notifications']; /* CryptPad is available is multiple languages, but only English and French are maintained * by the developers. The other languages may be outdated, and any missing string for a langauge diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 2fa3fb308..c67181574 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1651,7 +1651,7 @@ define([ }), h('br'), linkMessage = h('textarea', { - placeholder: 'note...' + placeholder: 'note...' // XXX }) ]), linkSpin = h('div', { @@ -1677,7 +1677,6 @@ define([ var hash = Hash.createRandomHash('invite', pw); var hashData = Hash.parseTypeHash('invite', hash); href = origin + '/teams/#' + hash; - console.log(hashData); if (!name || !name.trim()) { $(linkError).text('empty name...').show(); // XXX return true; @@ -1707,7 +1706,7 @@ define([ password: pw, message: msg, bytes64: bytes64, - href: href, + hash: hash, teamId: config.teamId, }, waitFor(function (obj) { if (obj && obj.error) { diff --git a/www/common/outer/team.js b/www/common/outer/team.js index 3bb86cd4b..884c92598 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -1267,7 +1267,7 @@ define([ var name = data.name; var password = data.password; var msg = data.message; - var href = data.href; + var hash = data.hash; var bytes64 = data.bytes64; */ return void cb(); @@ -1277,6 +1277,20 @@ define([ }); */ }; + var getLinkData = function (ctx, data, cId, cb) { + /* + var password = data.password; + var hash = data.hash; + var bytes64 = data.bytes64; + */ + return void cb(); + /* + cb({ + error: 'NOT_IMPLEMENTED' + }); + */ + }; + Team.init = function (cfg, waitFor, emit) { var team = {}; @@ -1434,6 +1448,9 @@ define([ if (cmd === 'CREATE_INVITE_LINK') { return void createInviteLink(ctx, data, clientId, cb); } + if (cmd === 'GET_LINK_DATA') { + return void getLinkData(ctx, data, clientId, cb); + } }; return team; diff --git a/www/teams/inner.js b/www/teams/inner.js index 4ba4cf2f4..626f5ddcd 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -17,6 +17,7 @@ define([ '/common/messenger-ui.js', '/customize/messages.js', + '/bower_components/scrypt-async/scrypt-async.min.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/teams/app-team.less', @@ -42,6 +43,7 @@ define([ var APP = {}; var driveAPP = {}; //var SHARED_FOLDER_NAME = Messages.fm_sharedFolderName; + var Scrypt = window.scrypt; var copyObjectValue = function (objRef, objToCopy) { for (var k in objRef) { delete objRef[k]; } @@ -137,6 +139,9 @@ define([ 'general': [ 'cp-team-info', ], + 'link': [ + 'cp-team-link', + ], }; var teamCategories = { 'back': { @@ -179,8 +184,10 @@ define([ var $categories = $('
', {'class': 'cp-sidebarlayout-categories'}) .appendTo(APP.$leftside); + var hash = common.getMetadataMgr().getPrivateData().teamInviteHash; + var categories = team ? teamCategories : mainCategories; - var active = team ? 'drive' : 'list'; + var active = team ? 'drive' : (hash ? 'link' : 'list'); if (team && APP.team) { var $category = $('
', {'class': 'cp-sidebarlayout-category cp-team-cat-header'}).appendTo($categories); @@ -1012,6 +1019,90 @@ define([ ]); }, true); + makeBlock('link', function (common, cb) { + // XXX get team data first or login first? + if (!driveAPP.loggedIn) { + var anonLogin, anonRegister; + var anonContent = h('div', [ + h('p', "You've been invited to a team. Only registered users can join a team. Login or register..."), // XXX + h('div', [ + anonLogin = h('button.btn.btn-primary', Messages.login_login), + anonRegister = h('button.btn.btn-secondary', Messages.login_register), + ]) + ]); + $(anonLogin).click(function () { + common.setLoginRedirect(function () { + common.gotoURL('/login/'); + }); + }); + $(anonRegister).click(function () { + common.setLoginRedirect(function () { + common.gotoURL('/register/'); + }); + }); + return void cb(anonContent); + } + var hash = common.getMetadataMgr().getPrivateData().teamInviteHash; + var hashData = Hash.parseTypeHash('invite', hash); + var password = hashData.password; + + var div; + + var process = function (pw) { + var $div = $(div); + $div.empty(); + var bytes64; + nThen(function (waitFor) { + $div.append(h('div', [ + h('i.fa.fa-spin.fa-spinner'), + h('span', 'Scrypt...') // XXX + ])); + setTimeout(waitFor(), 150); + }).nThen(function (waitFor) { + Scrypt(hashData.key, + (pw || '') + (AppConfig.loginSalt || ''), // salt + 8, // memoryCost (n) + 1024, // block size parameter (r) + 192, // dkLen + 200, // interruptStep + waitFor(function (_bytes) { + bytes64 = _bytes; + }), + 'base64'); // format, could be 'base64' + }).nThen(function (waitFor) { + APP.module.execCommand('GET_LINK_DATA', { + bytes64: bytes64, + hash: hash, + pw: pw, + }, waitFor(function () { + $div.empty(); + // TODO + // Accept/decline/decide later UI + })); + }); + }; + + var content = []; + if (password) { + // XXX XXX + content.push(h('p', "You've been invited to join a CryptPad Team, but the person who created the invitation protected it with a secret passphrase that they expect you to know.")); + content.push(h('p', "Entering the correct phrase will decrypt the team's info and allow you to accept or decline the invitation.")); + var pwInput = UI.passwordInput(); + content.push(pwInput); + var submitPw = h('button.btn.btn-secondary', Messages.password_submit); + $(submitPw).click(function () { + var val = $(pwInput).find('input').val(); + if (!val) { return; } + process(val); + }); + content.push(submitPw); + } + div = h('div', content); + cb(div); + + if (!password) { process(); } + }); + var redrawTeam = function (common) { if (!APP.team) { return; } var teamId = APP.team; @@ -1053,7 +1144,7 @@ define([ readOnly = driveAPP.readOnly = metadataMgr.getPrivateData().readOnly; driveAPP.loggedIn = common.isLoggedIn(); - if (!driveAPP.loggedIn) { throw new Error('NOT_LOGGED_IN'); } + //if (!driveAPP.loggedIn) { throw new Error('NOT_LOGGED_IN'); } common.setTabTitle(Messages.type.teams); @@ -1103,6 +1194,22 @@ define([ onEvent: onEvent }); + var hash = privateData.teamInviteHash; + if (!hash && !driveAPP.loggedIn) { + UI.alert(Messages.mustLogin, function () { + common.setLoginRedirect(function () { + common.gotoURL('/login/'); + }); + }, {forefront: true}); + return; + } + if (!hash) { + delete mainCategories.link; + } else if (!driveAPP.loggedIn) { + delete mainCategories.list; + delete mainCategories.create; + } + $('body').css('display', ''); loadMain(common); diff --git a/www/teams/main.js b/www/teams/main.js index f12308207..559e90c7c 100644 --- a/www/teams/main.js +++ b/www/teams/main.js @@ -37,6 +37,7 @@ define([ window.addEventListener('message', onMsg); }).nThen(function (/*waitFor*/) { var teamId; + var hash = window.location.hash.slice(1); var addRpc = function (sframeChan, Cryptpad) { sframeChan.on('Q_SET_TEAM', function (data, cb) { teamId = data; @@ -86,11 +87,23 @@ define([ } }); }; + var getSecrets = function (Cryptpad, Utils, cb) { + var Hash = Utils.Hash; + var hash = Hash.createRandomHash('profile'); + var secret = Hash.getSecrets('team', hash); + cb(null, secret); + }; + var addData = function (meta) { + if (!hash) { return; } + meta.teamInviteHash = hash; + }; SFCommonO.start({ + getSecrets: getSecrets, noHash: true, noRealtime: true, //driveEvents: true, addRpc: addRpc, + addData: addData, isDrive: true, // Used for history... }); });