merge communities-allow-list and lint compliance
This commit is contained in:
commit
f951951077
@ -72,6 +72,10 @@
|
|||||||
z-index: 100000; // alertify container
|
z-index: 100000; // alertify container
|
||||||
font: @colortheme_app-font;
|
font: @colortheme_app-font;
|
||||||
|
|
||||||
|
.cp-checkmark {
|
||||||
|
color: @cryptpad_text_col;
|
||||||
|
}
|
||||||
|
|
||||||
.cp-inline-alert-text {
|
.cp-inline-alert-text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
@ -182,6 +186,7 @@
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
span {
|
span {
|
||||||
|
.tools_unselectable();
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
@ -190,12 +195,16 @@
|
|||||||
border-left: 1px solid lighten(@alertify-base, 10%);
|
border-left: 1px solid lighten(@alertify-base, 10%);
|
||||||
border-right: 1px solid lighten(@alertify-base, 10%);
|
border-right: 1px solid lighten(@alertify-base, 10%);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:not(.disabled):hover {
|
||||||
background-color: @alertify-light-bg;
|
background-color: @alertify-light-bg;
|
||||||
}
|
}
|
||||||
|
&.disabled {
|
||||||
|
color: #949494;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
span.alertify-tabs-active {
|
span.alertify-tabs-active {
|
||||||
background-color: @alertify-fore;
|
background-color: @alertify-fore !important;
|
||||||
border-left: 1px solid @alertify-fore;
|
border-left: 1px solid @alertify-fore;
|
||||||
border-right: 1px solid @alertify-fore;
|
border-right: 1px solid @alertify-fore;
|
||||||
color: @alertify-base;
|
color: @alertify-base;
|
||||||
@ -386,18 +395,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
div.wide {
|
div.wide {
|
||||||
div.alertify-tabs {
|
|
||||||
p.msg:not(:last-child) {
|
|
||||||
border-bottom: 1px solid @alertify-fore;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.cp-share-columns {
|
.cp-share-columns {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row;
|
flex-flow: row;
|
||||||
|
|
||||||
& > .cp-share-column {
|
& > .cp-share-column {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
padding: 0 10px;
|
//padding: 0 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
&.contains-nav {
|
&.contains-nav {
|
||||||
nav {
|
nav {
|
||||||
@ -414,7 +418,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-right: 1px solid @alertify-fore;
|
margin-right: @alertify_padding-base;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
margin-left: @alertify_padding-base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > .cp-share-column-mid {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
button {
|
||||||
|
width: 50px;
|
||||||
|
margin: 0;
|
||||||
|
min-width: 0;
|
||||||
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,6 +68,23 @@
|
|||||||
color: @cryptpad_text_col;
|
color: @cryptpad_text_col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Access modal
|
||||||
|
.cp-overlay-container {
|
||||||
|
position: relative;
|
||||||
|
.cp-overlay {
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(255,255,255,0.5);
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cp-access-margin-right {
|
||||||
|
margin-right: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
// teams invite modal
|
// teams invite modal
|
||||||
.cp-teams-invite-block {
|
.cp-teams-invite-block {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -109,6 +109,27 @@
|
|||||||
color: @colortheme_alertify-primary-text;
|
color: @colortheme_alertify-primary-text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.fa-times {
|
||||||
|
padding-left: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 25px;
|
||||||
|
color: @cryptpad_text_col;
|
||||||
|
&:hover {
|
||||||
|
color: lighten(@cryptpad_text_col, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.list {
|
||||||
|
.cp-usergrid-user {
|
||||||
|
width: auto;
|
||||||
|
max-width: calc(100% - 6px);
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: inline-flex;
|
||||||
|
flex: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
lib/api.js
16
lib/api.js
@ -14,21 +14,7 @@ module.exports.create = function (config) {
|
|||||||
.on('channelClose', historyKeeper.channelClose)
|
.on('channelClose', historyKeeper.channelClose)
|
||||||
.on('channelMessage', historyKeeper.channelMessage)
|
.on('channelMessage', historyKeeper.channelMessage)
|
||||||
.on('channelOpen', historyKeeper.channelOpen)
|
.on('channelOpen', historyKeeper.channelOpen)
|
||||||
.on('sessionClose', function (userId, reason) {
|
.on('sessionClose', historyKeeper.sessionClose)
|
||||||
if (['BAD_MESSAGE', 'SOCKET_ERROR', 'SEND_MESSAGE_FAIL_2'].indexOf(reason) !== -1) {
|
|
||||||
if (reason && reason.code === 'ECONNRESET') { return; }
|
|
||||||
return void log.error('SESSION_CLOSE_WITH_ERROR', {
|
|
||||||
userId: userId,
|
|
||||||
reason: reason,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (['SOCKET_CLOSED', 'SOCKET_ERROR'].indexOf(reason)) { return; }
|
|
||||||
log.verbose('SESSION_CLOSE_ROUTINE', {
|
|
||||||
userId: userId,
|
|
||||||
reason: reason,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on('error', function (error, label, info) {
|
.on('error', function (error, label, info) {
|
||||||
if (!error) { return; }
|
if (!error) { return; }
|
||||||
/* labels:
|
/* labels:
|
||||||
|
|||||||
@ -261,6 +261,8 @@ Channel.writePrivateMessage = function (Env, args, cb, Server) {
|
|||||||
msg // the actual message content. Generally a string
|
msg // the actual message content. Generally a string
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// XXX RESTRICT respect allow lists
|
||||||
|
|
||||||
// historyKeeper already knows how to handle metadata and message validation, so we just pass it off here
|
// historyKeeper already knows how to handle metadata and message validation, so we just pass it off here
|
||||||
// if the message isn't valid it won't be stored.
|
// if the message isn't valid it won't be stored.
|
||||||
Env.historyKeeper.channelMessage(Server, channelStruct, fullMessage);
|
Env.historyKeeper.channelMessage(Server, channelStruct, fullMessage);
|
||||||
|
|||||||
@ -2,21 +2,24 @@
|
|||||||
const Data = module.exports;
|
const Data = module.exports;
|
||||||
|
|
||||||
const Meta = require("../metadata");
|
const Meta = require("../metadata");
|
||||||
const BatchRead = require("../batch-read");
|
|
||||||
const WriteQueue = require("../write-queue");
|
const WriteQueue = require("../write-queue");
|
||||||
const Core = require("./core");
|
const Core = require("./core");
|
||||||
const Util = require("../common-util");
|
const Util = require("../common-util");
|
||||||
|
const HK = require("../hk-util");
|
||||||
|
|
||||||
const batchMetadata = BatchRead("GET_METADATA");
|
Data.getMetadataRaw = function (Env, channel /* channelName */, _cb) {
|
||||||
Data.getMetadata = function (Env, channel, cb/* , Server */) {
|
const cb = Util.once(Util.mkAsync(_cb));
|
||||||
if (!Core.isValidId(channel)) { return void cb('INVALID_CHAN'); }
|
if (!Core.isValidId(channel)) { return void cb('INVALID_CHAN'); }
|
||||||
if (channel.length !== 32) { return cb("INVALID_CHAN_LENGTH"); }
|
if (channel.length !== HK.STANDARD_CHANNEL_LENGTH) { return cb("INVALID_CHAN_LENGTH"); }
|
||||||
|
|
||||||
// FIXME get metadata from the server cache if it is available
|
var cached = Env.metadata_cache[channel];
|
||||||
batchMetadata(channel, cb, function (done) {
|
if (HK.isMetadataMessage(cached)) {
|
||||||
|
return void cb(void 0, cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
Env.batchMetadata(channel, cb, function (done) {
|
||||||
var ref = {};
|
var ref = {};
|
||||||
var lineHandler = Meta.createLineHandler(ref, Env.Log.error);
|
var lineHandler = Meta.createLineHandler(ref, Env.Log.error);
|
||||||
|
|
||||||
return void Env.msgStore.readChannelMetadata(channel, lineHandler, function (err) {
|
return void Env.msgStore.readChannelMetadata(channel, lineHandler, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
// stream errors?
|
// stream errors?
|
||||||
@ -27,6 +30,28 @@ Data.getMetadata = function (Env, channel, cb/* , Server */) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Data.getMetadata = function (Env, channel, cb, Server, netfluxId) {
|
||||||
|
Data.getMetadataRaw(Env, channel, function (err, metadata) {
|
||||||
|
if (err) { return void cb(err); }
|
||||||
|
|
||||||
|
if (!(metadata && metadata.restricted)) {
|
||||||
|
// if it's not restricted then just call back
|
||||||
|
return void cb(void 0, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
const session = HK.getNetfluxSession(Env, netfluxId);
|
||||||
|
const allowed = HK.listAllowedUsers(metadata);
|
||||||
|
|
||||||
|
if (!HK.isUserSessionAllowed(allowed, session)) {
|
||||||
|
return void cb(void 0, {
|
||||||
|
restricted: metadata.restricted,
|
||||||
|
allowed: allowed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
cb(void 0, metadata);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/* setMetadata
|
/* setMetadata
|
||||||
- write a new line to the metadata log if a valid command is provided
|
- write a new line to the metadata log if a valid command is provided
|
||||||
- data is an object: {
|
- data is an object: {
|
||||||
@ -46,7 +71,7 @@ Data.setMetadata = function (Env, safeKey, data, cb, Server) {
|
|||||||
if (Meta.commands.indexOf(command) === -1) { return void cb('UNSUPPORTED_COMMAND'); }
|
if (Meta.commands.indexOf(command) === -1) { return void cb('UNSUPPORTED_COMMAND'); }
|
||||||
|
|
||||||
queueMetadata(channel, function (next) {
|
queueMetadata(channel, function (next) {
|
||||||
Data.getMetadata(Env, channel, function (err, metadata) {
|
Data.getMetadataRaw(Env, channel, function (err, metadata) {
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
return void next();
|
return void next();
|
||||||
@ -108,21 +133,70 @@ Data.setMetadata = function (Env, safeKey, data, cb, Server) {
|
|||||||
return void next();
|
return void next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send the message back to the person who changed it
|
||||||
|
// since we know they're allowed to see it
|
||||||
cb(void 0, metadata);
|
cb(void 0, metadata);
|
||||||
next();
|
next();
|
||||||
|
|
||||||
const metadata_cache = Env.metadata_cache;
|
const metadata_cache = Env.metadata_cache;
|
||||||
const channel_cache = Env.channel_cache;
|
const channel_cache = Env.channel_cache;
|
||||||
|
|
||||||
|
// update the cached metadata
|
||||||
metadata_cache[channel] = metadata;
|
metadata_cache[channel] = metadata;
|
||||||
|
|
||||||
|
// as well as the metadata that's attached to the index...
|
||||||
|
// XXX determine if we actually need this...
|
||||||
var index = Util.find(channel_cache, [channel, 'index']);
|
var index = Util.find(channel_cache, [channel, 'index']);
|
||||||
if (index && typeof(index) === 'object') { index.metadata = metadata; }
|
if (index && typeof(index) === 'object') { index.metadata = metadata; }
|
||||||
|
|
||||||
Server.channelBroadcast(channel, JSON.stringify(metadata), Env.historyKeeper.id);
|
// it's easy to check if the channel is restricted
|
||||||
|
const isRestricted = metadata.restricted;
|
||||||
|
// and these values will be used in any case
|
||||||
|
const s_metadata = JSON.stringify(metadata);
|
||||||
|
const hk_id = Env.historyKeeper.id;
|
||||||
|
|
||||||
|
if (!isRestricted) {
|
||||||
|
// pre-allow-list behaviour
|
||||||
|
// if it's not restricted, broadcast the new metadata to everyone
|
||||||
|
return void Server.channelBroadcast(channel, s_metadata, hk_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise derive the list of users (unsafeKeys) that are allowed to stay
|
||||||
|
const allowed = HK.listAllowedUsers(metadata);
|
||||||
|
// anyone who is not allowed will get the same error message
|
||||||
|
const s_error = JSON.stringify({
|
||||||
|
error: 'ERESTRICTED',
|
||||||
|
channel: channel,
|
||||||
|
});
|
||||||
|
|
||||||
|
// iterate over the channel's userlist
|
||||||
|
const toRemove = [];
|
||||||
|
Server.getChannelUserList(channel).forEach(function (userId) {
|
||||||
|
const session = HK.getNetfluxSession(Env, userId);
|
||||||
|
|
||||||
|
// if the user is allowed to remain, send them the metadata
|
||||||
|
if (HK.isUserSessionAllowed(allowed, session)) {
|
||||||
|
return void Server.send(userId, [
|
||||||
|
0,
|
||||||
|
hk_id,
|
||||||
|
'MSG',
|
||||||
|
userId,
|
||||||
|
s_metadata
|
||||||
|
], function () {});
|
||||||
|
}
|
||||||
|
// otherwise they are not in the list.
|
||||||
|
// send them an error and kick them out!
|
||||||
|
Server.send(userId, [
|
||||||
|
0,
|
||||||
|
hk_id,
|
||||||
|
'MSG',
|
||||||
|
userId,
|
||||||
|
s_error
|
||||||
|
], function () {});
|
||||||
|
});
|
||||||
|
|
||||||
|
Server.removeFromChannel(channel, toRemove);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -147,7 +147,6 @@ module.exports.create = function (config, cb) {
|
|||||||
error: err,
|
error: err,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!metadata || (metadata && !metadata.restricted)) {
|
if (!metadata || (metadata && !metadata.restricted)) {
|
||||||
// the channel doesn't have metadata, or it does and it's not restricted
|
// the channel doesn't have metadata, or it does and it's not restricted
|
||||||
// either way, let them join.
|
// either way, let them join.
|
||||||
@ -168,14 +167,12 @@ module.exports.create = function (config, cb) {
|
|||||||
// otherwise they're not allowed.
|
// otherwise they're not allowed.
|
||||||
// respond with a special error that includes the list of keys
|
// respond with a special error that includes the list of keys
|
||||||
// which would be allowed...
|
// which would be allowed...
|
||||||
// FIXME bonus points if you hash the keys to limit data exposure
|
// FIXME RESTRICT bonus points if you hash the keys to limit data exposure
|
||||||
cb("ERESTRICTED", allowed);
|
cb("ERESTRICTED", allowed);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
sessionClose: function (userId, reason) {
|
sessionClose: function (userId, reason) {
|
||||||
HK.closeNetfluxSession(Env, userId);
|
HK.closeNetfluxSession(Env, userId);
|
||||||
|
|
||||||
// TODO RESTRICT drop user session data
|
|
||||||
if (['BAD_MESSAGE', 'SOCKET_ERROR', 'SEND_MESSAGE_FAIL_2'].indexOf(reason) !== -1) {
|
if (['BAD_MESSAGE', 'SOCKET_ERROR', 'SEND_MESSAGE_FAIL_2'].indexOf(reason) !== -1) {
|
||||||
if (reason && reason.code === 'ECONNRESET') { return; }
|
if (reason && reason.code === 'ECONNRESET') { return; }
|
||||||
return void Log.error('SESSION_CLOSE_WITH_ERROR', {
|
return void Log.error('SESSION_CLOSE_WITH_ERROR', {
|
||||||
@ -184,7 +181,7 @@ module.exports.create = function (config, cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reason && reason === 'SOCKET_CLOSED') { return; }
|
if (['SOCKET_CLOSED', 'SOCKET_ERROR'].indexOf(reason)) { return; }
|
||||||
Log.verbose('SESSION_CLOSE_ROUTINE', {
|
Log.verbose('SESSION_CLOSE_ROUTINE', {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
reason: reason,
|
reason: reason,
|
||||||
|
|||||||
169
lib/hk-util.js
169
lib/hk-util.js
@ -4,7 +4,7 @@ var HK = module.exports;
|
|||||||
|
|
||||||
const nThen = require('nthen');
|
const nThen = require('nthen');
|
||||||
const Util = require("./common-util");
|
const Util = require("./common-util");
|
||||||
const Meta = require("./metadata");
|
const MetaRPC = require("./commands/metadata");
|
||||||
const Nacl = require('tweetnacl/nacl-fast');
|
const Nacl = require('tweetnacl/nacl-fast');
|
||||||
|
|
||||||
const now = function () { return (new Date()).getTime(); };
|
const now = function () { return (new Date()).getTime(); };
|
||||||
@ -71,10 +71,37 @@ const sliceCpIndex = function (cpIndex, line) {
|
|||||||
return start.concat(end);
|
return start.concat(end);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isMetadataMessage = function (parsed) {
|
const isMetadataMessage = HK.isMetadataMessage = function (parsed) {
|
||||||
return Boolean(parsed && parsed.channel);
|
return Boolean(parsed && parsed.channel);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HK.listAllowedUsers = function (metadata) {
|
||||||
|
return (metadata.owners || []).concat((metadata.allowed || []));
|
||||||
|
};
|
||||||
|
|
||||||
|
HK.getNetfluxSession = function (Env, netfluxId) {
|
||||||
|
return Env.netfluxUsers[netfluxId];
|
||||||
|
};
|
||||||
|
|
||||||
|
HK.isUserSessionAllowed = function (allowed, session) {
|
||||||
|
if (!session) { return false; }
|
||||||
|
for (var unsafeKey in session) {
|
||||||
|
if (allowed.indexOf(unsafeKey) !== -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
HK.authenticateNetfluxSession = function (Env, netfluxId, unsafeKey) {
|
||||||
|
var user = Env.netfluxUsers[netfluxId] = Env.netfluxUsers[netfluxId] || {};
|
||||||
|
user[unsafeKey] = +new Date();
|
||||||
|
};
|
||||||
|
|
||||||
|
HK.closeNetfluxSession = function (Env, netfluxId) {
|
||||||
|
delete Env.netfluxUsers[netfluxId];
|
||||||
|
};
|
||||||
|
|
||||||
// validateKeyStrings supplied by clients must decode to 32-byte Uint8Arrays
|
// validateKeyStrings supplied by clients must decode to 32-byte Uint8Arrays
|
||||||
const isValidValidateKeyString = function (key) {
|
const isValidValidateKeyString = function (key) {
|
||||||
try {
|
try {
|
||||||
@ -151,6 +178,29 @@ const checkExpired = function (Env, Server, channel) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getMetadata = HK.getMetadata = function (Env, channelName, _cb) {
|
||||||
|
var cb = Util.once(Util.mkAsync(_cb));
|
||||||
|
|
||||||
|
var metadata = Env.metadata_cache[channelName];
|
||||||
|
if (metadata && typeof(metadata) === 'object') {
|
||||||
|
return void cb(undefined, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaRPC.getMetadataRaw(Env, channelName, function (err, metadata) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return void cb(err);
|
||||||
|
}
|
||||||
|
if (!(metadata && typeof(metadata.channel) === 'string' && metadata.channel.length === STANDARD_CHANNEL_LENGTH)) {
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache it
|
||||||
|
Env.metadata_cache[channelName] = metadata;
|
||||||
|
cb(undefined, metadata);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/* computeIndex
|
/* computeIndex
|
||||||
can call back with an error or a computed index which includes:
|
can call back with an error or a computed index which includes:
|
||||||
* cpIndex:
|
* cpIndex:
|
||||||
@ -180,13 +230,19 @@ const computeIndex = function (Env, channelName, cb) {
|
|||||||
let metadata;
|
let metadata;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
const ref = {};
|
|
||||||
|
|
||||||
const CB = Util.once(cb);
|
const CB = Util.once(cb);
|
||||||
|
|
||||||
const offsetByHash = {};
|
const offsetByHash = {};
|
||||||
let size = 0;
|
let size = 0;
|
||||||
nThen(function (w) {
|
nThen(function (w) {
|
||||||
|
getMetadata(Env, channelName, w(function (err, _metadata) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
throw new Error(err); // XXX
|
||||||
|
}
|
||||||
|
metadata = _metadata;
|
||||||
|
}));
|
||||||
|
}).nThen(function (w) {
|
||||||
// iterate over all messages in the channel log
|
// iterate over all messages in the channel log
|
||||||
// old channels can contain metadata as the first message of the log
|
// old channels can contain metadata as the first message of the log
|
||||||
// remember metadata the first time you encounter it
|
// remember metadata the first time you encounter it
|
||||||
@ -195,14 +251,15 @@ const computeIndex = function (Env, channelName, cb) {
|
|||||||
let msg;
|
let msg;
|
||||||
// keep an eye out for the metadata line if you haven't already seen it
|
// keep an eye out for the metadata line if you haven't already seen it
|
||||||
// but only check for metadata on the first line
|
// but only check for metadata on the first line
|
||||||
if (!i && !metadata && msgObj.buff.indexOf('{') === 0) {
|
if (!i && msgObj.buff.indexOf('{') === 0) { // XXX RESTRICT metadata...
|
||||||
i++; // always increment the message counter
|
i++; // always increment the message counter
|
||||||
msg = tryParse(Env, msgObj.buff.toString('utf8'));
|
msg = tryParse(Env, msgObj.buff.toString('utf8'));
|
||||||
if (typeof msg === "undefined") { return readMore(); }
|
if (typeof msg === "undefined") { return readMore(); }
|
||||||
|
|
||||||
// validate that the current line really is metadata before storing it as such
|
// validate that the current line really is metadata before storing it as such
|
||||||
if (isMetadataMessage(msg)) {
|
if (isMetadataMessage(msg)) { // XXX RESTRICT
|
||||||
metadata = msg;
|
//metadata = msg; // XXX RESTRICT
|
||||||
|
// skip this, as you already have metadata...
|
||||||
return readMore();
|
return readMore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,26 +302,8 @@ const computeIndex = function (Env, channelName, cb) {
|
|||||||
size = msgObj.offset + msgObj.buff.length + 1;
|
size = msgObj.offset + msgObj.buff.length + 1;
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}).nThen(function (w) {
|
|
||||||
// create a function which will iterate over amendments to the metadata
|
|
||||||
const handler = Meta.createLineHandler(ref, Log.error);
|
|
||||||
|
|
||||||
// initialize the accumulator in case there was a foundational metadata line in the log content
|
|
||||||
if (metadata) { handler(void 0, metadata); }
|
|
||||||
|
|
||||||
// iterate over the dedicated metadata log (if it exists)
|
|
||||||
// proceed even in the event of a stream error on the metadata log
|
|
||||||
store.readDedicatedMetadata(channelName, handler, w(function (err) {
|
|
||||||
if (err) {
|
|
||||||
return void Log.error("DEDICATED_METADATA_ERROR", err);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}).nThen(function () {
|
}).nThen(function () {
|
||||||
// when all is done, cache the metadata in memory
|
// return the computed index
|
||||||
if (ref.index) { // but don't bother if no metadata was found...
|
|
||||||
metadata = Env.metadata_cache[channelName] = ref.meta;
|
|
||||||
}
|
|
||||||
// and return the computed index
|
|
||||||
CB(null, {
|
CB(null, {
|
||||||
// Only keep the checkpoints included in the last 100 messages
|
// Only keep the checkpoints included in the last 100 messages
|
||||||
cpIndex: sliceCpIndex(cpIndex, i),
|
cpIndex: sliceCpIndex(cpIndex, i),
|
||||||
@ -293,9 +332,7 @@ const getIndex = (Env, channelName, cb) => {
|
|||||||
// if there is a channel in memory and it has an index cached, return it
|
// if there is a channel in memory and it has an index cached, return it
|
||||||
if (chan && chan.index) {
|
if (chan && chan.index) {
|
||||||
// enforce async behaviour
|
// enforce async behaviour
|
||||||
return void setTimeout(function () {
|
return void Util.mkAsync(cb)(undefined, chan.index);
|
||||||
cb(undefined, chan.index);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Env.batchIndexReads(channelName, cb, function (done) {
|
Env.batchIndexReads(channelName, cb, function (done) {
|
||||||
@ -569,7 +606,7 @@ const handleRPC = function (Env, Server, seq, userId, parsed) {
|
|||||||
Server.send(userId, [seq, 'ACK']);
|
Server.send(userId, [seq, 'ACK']);
|
||||||
try {
|
try {
|
||||||
// slice off the sequence number and pass in the rest of the message
|
// slice off the sequence number and pass in the rest of the message
|
||||||
Env.rpc(Server, rpc_call, function (err, output) {
|
Env.rpc(Server, userId, rpc_call, function (err, output) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify([parsed[0], 'ERROR', err])]);
|
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify([parsed[0], 'ERROR', err])]);
|
||||||
return;
|
return;
|
||||||
@ -646,6 +683,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
|
|||||||
// And then check if the channel is expired. If it is, send the error and abort
|
// And then check if the channel is expired. If it is, send the error and abort
|
||||||
// FIXME this is hard to read because 'checkExpired' has side effects
|
// FIXME this is hard to read because 'checkExpired' has side effects
|
||||||
if (checkExpired(Env, Server, channelName)) { return void waitFor.abort(); }
|
if (checkExpired(Env, Server, channelName)) { return void waitFor.abort(); }
|
||||||
|
|
||||||
// always send metadata with GET_HISTORY requests
|
// always send metadata with GET_HISTORY requests
|
||||||
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(index.metadata)], w);
|
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(index.metadata)], w);
|
||||||
}));
|
}));
|
||||||
@ -662,7 +700,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
|
|||||||
}, (err) => {
|
}, (err) => {
|
||||||
if (err && err.code !== 'ENOENT') {
|
if (err && err.code !== 'ENOENT') {
|
||||||
if (err.message !== 'EINVAL') { Log.error("HK_GET_HISTORY", err); }
|
if (err.message !== 'EINVAL') { Log.error("HK_GET_HISTORY", err); }
|
||||||
const parsedMsg = {error:err.message, channel: channelName, txid: txid};
|
const parsedMsg = {error:err.message, channel: channelName, txid: txid}; // XXX history retrieval error format
|
||||||
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg)]);
|
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg)]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -789,9 +827,9 @@ const handleGetFullHistory = function (Env, Server, seq, userId, parsed) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const directMessageCommands = {
|
const directMessageCommands = {
|
||||||
GET_HISTORY: handleGetHistory,
|
GET_HISTORY: handleGetHistory, // XXX RESTRICT
|
||||||
GET_HISTORY_RANGE: handleGetHistoryRange,
|
GET_HISTORY_RANGE: handleGetHistoryRange, // XXX RESTRICT
|
||||||
GET_FULL_HISTORY: handleGetFullHistory,
|
GET_FULL_HISTORY: handleGetFullHistory, // XXX RESTRICT
|
||||||
};
|
};
|
||||||
|
|
||||||
/* onDirectMessage
|
/* onDirectMessage
|
||||||
@ -812,16 +850,63 @@ HK.onDirectMessage = function (Env, Server, seq, userId, json) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the requested history is for an expired channel, abort
|
var first = parsed[0];
|
||||||
// Note the if we don't have the keys for that channel in metadata_cache, we'll
|
|
||||||
// have to abort later (once we know the expiration time)
|
|
||||||
if (checkExpired(Env, Server, parsed[1])) { return; }
|
|
||||||
|
|
||||||
// look up the appropriate command in the map of commands or fall back to RPC
|
if (typeof(directMessageCommands[first]) !== 'function') {
|
||||||
var command = directMessageCommands[parsed[0]] || handleRPC;
|
// it's either an unsupported command or an RPC call
|
||||||
|
// either way, RPC has it covered
|
||||||
|
return void handleRPC(Env, Server, seq, userId, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
// run the command with the standard function signature
|
// otherwise it's some kind of history retrieval command...
|
||||||
command(Env, Server, seq, userId, parsed);
|
// go grab its metadata, because unfortunately people can ask for history
|
||||||
|
// whether or not they have joined the channel, so we can't rely on JOIN restriction
|
||||||
|
// to stop people from loading history they shouldn't see.
|
||||||
|
var channelName = parsed[1];
|
||||||
|
nThen(function (w) {
|
||||||
|
HK.getMetadata(Env, channelName, w(function (err, metadata) {
|
||||||
|
if (err) {
|
||||||
|
// stream errors?
|
||||||
|
// we should log these, but if we can't load metadata
|
||||||
|
// then it's probably not restricted or expired
|
||||||
|
// it's not like anything else will recover from this anyway
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// likewise, we can't do anything more here if there's no metadata
|
||||||
|
// jump to the next block
|
||||||
|
if (!metadata) { return; }
|
||||||
|
|
||||||
|
// If the requested history is for an expired channel, abort
|
||||||
|
// checkExpired has side effects and will disconnect users for you...
|
||||||
|
if (checkExpired(Env, Server, parsed[1])) {
|
||||||
|
// if the channel is expired just abort.
|
||||||
|
w.abort();
|
||||||
|
// XXX what do we tell the person who asked?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jump to handling the command if there's no restriction...
|
||||||
|
if (!metadata.restricted) { return; }
|
||||||
|
|
||||||
|
// check if the user is in the allow list...
|
||||||
|
const allowed = HK.listAllowedUsers(metadata);
|
||||||
|
const session = HK.getNetfluxSession(Env, userId);
|
||||||
|
|
||||||
|
if (HK.isUserSessionAllowed(allowed, session)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX NOT ALLOWED
|
||||||
|
// respond to txid with error as in handleGetHistory
|
||||||
|
// send the allow list anyway, it might not get used currently
|
||||||
|
// but will in the future
|
||||||
|
}));
|
||||||
|
}).nThen(function () {
|
||||||
|
// run the appropriate command from the map
|
||||||
|
directMessageCommands[first](Env, Server, seq, userId, parsed);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/* onChannelMessage
|
/* onChannelMessage
|
||||||
|
|||||||
192
lib/metadata.js
192
lib/metadata.js
@ -2,23 +2,169 @@ var Meta = module.exports;
|
|||||||
|
|
||||||
var deduplicate = require("./common-util").deduplicateString;
|
var deduplicate = require("./common-util").deduplicateString;
|
||||||
|
|
||||||
/* Metadata fields:
|
/* Metadata fields and the commands that can modify them
|
||||||
|
|
||||||
|
we assume that these commands can only be performed
|
||||||
|
by owners or in some cases pending owners. Thus
|
||||||
|
the owners field is guaranteed to exist.
|
||||||
|
|
||||||
* channel <STRING>
|
* channel <STRING>
|
||||||
* validateKey <STRING>
|
* validateKey <STRING>
|
||||||
* owners <ARRAY>
|
* owners <ARRAY>
|
||||||
* ADD_OWNERS
|
* ADD_OWNERS
|
||||||
* RM_OWNERS
|
* RM_OWNERS
|
||||||
|
* RESET_OWNERS
|
||||||
|
* pending_owners <ARRAY>
|
||||||
|
* ADD_PENDING_OWNERS
|
||||||
|
* RM_PENDING_OWNERS
|
||||||
* expire <NUMBER>
|
* expire <NUMBER>
|
||||||
|
* UPDATE_EXPIRATION (NOT_IMPLEMENTED)
|
||||||
|
* restricted <BOOLEAN>
|
||||||
|
* RESTRICT_ACCESS
|
||||||
|
* allowed <ARRAY>
|
||||||
|
* ADD_ALLOWED
|
||||||
|
* RM_ALLOWED
|
||||||
|
* RESET_ALLOWED
|
||||||
|
* ADD_OWNERS
|
||||||
|
* RESET_OWNERS
|
||||||
|
* mailbox <STRING|MAP>
|
||||||
|
* ADD_MAILBOX
|
||||||
|
* RM_MAILBOX
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var commands = {};
|
var commands = {};
|
||||||
|
|
||||||
var isValidOwner = function (owner) {
|
var isValidPublicKey = function (owner) {
|
||||||
return typeof(owner) === 'string' && owner.length === 44;
|
return typeof(owner) === 'string' && owner.length === 44;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// isValidPublicKey is a better indication of what the above function does
|
||||||
|
// I'm preserving this function name in case we ever want to expand its
|
||||||
|
// criteria at a later time...
|
||||||
|
var isValidOwner = isValidPublicKey;
|
||||||
|
|
||||||
|
// ["RESTRICT_ACCESS", [true], 1561623438989]
|
||||||
|
// ["RESTRICT_ACCESS", [false], 1561623438989]
|
||||||
|
commands.RESTRICT_ACCESS = function (meta, args) {
|
||||||
|
if (!Array.isArray(args) || typeof(args[0]) !== 'boolean') {
|
||||||
|
throw new Error('INVALID_STATE');
|
||||||
|
}
|
||||||
|
|
||||||
|
var bool = args[0];
|
||||||
|
|
||||||
|
// reject the proposed command if there is no change in state
|
||||||
|
if (meta.restricted === bool) { return false; }
|
||||||
|
|
||||||
|
// apply the new state
|
||||||
|
meta.restricted = args[0];
|
||||||
|
|
||||||
|
// if you're disabling access restrictions then you can assume
|
||||||
|
// then there is nothing more to do. Leave the existing list as-is
|
||||||
|
if (!bool) { return true; }
|
||||||
|
|
||||||
|
// you're all set if an allow list already exists
|
||||||
|
if (Array.isArray(meta.allowed)) { return true; }
|
||||||
|
|
||||||
|
// otherwise define it
|
||||||
|
meta.allowed = [];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ["ADD_ALLOWED", ["7eEqelGso3EBr5jHlei6av4r9w2B9XZiGGwA1EgZ-5I=", ...], 1561623438989]
|
||||||
|
commands.ADD_ALLOWED = function (meta, args) {
|
||||||
|
if (!Array.isArray(args)) {
|
||||||
|
throw new Error("INVALID_ARGS");
|
||||||
|
}
|
||||||
|
|
||||||
|
var allowed = meta.allowed || [];
|
||||||
|
|
||||||
|
var changed = false;
|
||||||
|
args.forEach(function (arg) {
|
||||||
|
// don't add invalid public keys
|
||||||
|
if (!isValidPublicKey(arg)) { return; }
|
||||||
|
// don't add owners to the allow list
|
||||||
|
if (meta.owners.indexOf(arg) >= 0) { return; }
|
||||||
|
// don't duplicate entries in the allow list
|
||||||
|
if (allowed.indexOf(arg) >= 0) { return; }
|
||||||
|
allowed.push(arg);
|
||||||
|
changed = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
meta.allowed = meta.allowed || allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ["RM_ALLOWED", ["7eEqelGso3EBr5jHlei6av4r9w2B9XZiGGwA1EgZ-5I=", ...], 1561623438989]
|
||||||
|
commands.RM_ALLOWED = function (meta, args) {
|
||||||
|
if (!Array.isArray(args)) {
|
||||||
|
throw new Error("INVALID_ARGS");
|
||||||
|
}
|
||||||
|
|
||||||
|
// there may not be anything to remove
|
||||||
|
if (!meta.allowed) { return false; }
|
||||||
|
|
||||||
|
var changed = false;
|
||||||
|
args.forEach(function (arg) {
|
||||||
|
var index = meta.allowed.indexOf(arg);
|
||||||
|
if (index < 0) { return; }
|
||||||
|
meta.allowed.splice(index, 1);
|
||||||
|
changed = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
};
|
||||||
|
|
||||||
|
var arrayHasChanged = function (A, B) {
|
||||||
|
var changed;
|
||||||
|
A.some(function (a) {
|
||||||
|
if (B.indexOf(a) < 0) { return (changed = true); }
|
||||||
|
});
|
||||||
|
if (changed) { return true; }
|
||||||
|
B.some(function (b) {
|
||||||
|
if (A.indexOf(b) < 0) { return (changed = true); }
|
||||||
|
});
|
||||||
|
return changed;
|
||||||
|
};
|
||||||
|
|
||||||
|
var filterInPlace = function (A, f) {
|
||||||
|
for (var i = A.length - 1; i >= 0; i--) {
|
||||||
|
if (f(A[i], i, A)) { A.splice(i, 1); }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ["RESET_ALLOWED", ["7eEqelGso3EBr5jHlei6av4r9w2B9XZiGGwA1EgZ-5I=", ...], 1561623438989]
|
||||||
|
commands.RESET_ALLOWED = function (meta, args) {
|
||||||
|
if (!Array.isArray(args)) { throw new Error("INVALID_ARGS"); }
|
||||||
|
|
||||||
|
var updated = args.filter(function (arg) {
|
||||||
|
// don't allow invalid public keys
|
||||||
|
if (!isValidPublicKey(arg)) { return false; }
|
||||||
|
// don't ever add owners to the allow list
|
||||||
|
if (meta.owners.indexOf(arg)) { return false; }
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// this is strictly an optimization...
|
||||||
|
// a change in length is a clear indicator of a functional change
|
||||||
|
if (meta.allowed && meta.allowed.length !== updated.length) {
|
||||||
|
meta.allowed = updated;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise we must check that the arrays contain distinct elements
|
||||||
|
// if there is no functional change, then return false
|
||||||
|
if (!arrayHasChanged(meta.allowed, updated)) { return false; }
|
||||||
|
|
||||||
|
// otherwise overwrite the in-memory data and indicate that there was a change
|
||||||
|
meta.allowed = updated;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
// ["ADD_OWNERS", ["7eEqelGso3EBr5jHlei6av4r9w2B9XZiGGwA1EgZ-5I="], 1561623438989]
|
// ["ADD_OWNERS", ["7eEqelGso3EBr5jHlei6av4r9w2B9XZiGGwA1EgZ-5I="], 1561623438989]
|
||||||
commands.ADD_OWNERS = function (meta, args) {
|
commands.ADD_OWNERS = function (meta, args) {
|
||||||
// bail out if args isn't an array
|
// bail out if args isn't an array
|
||||||
@ -40,6 +186,13 @@ commands.ADD_OWNERS = function (meta, args) {
|
|||||||
changed = true;
|
changed = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (changed && Array.isArray(meta.allowed)) {
|
||||||
|
// make sure owners are not included in the allow list
|
||||||
|
filterInPlace(meta.allowed, function (member) {
|
||||||
|
return meta.owners.indexOf(member) !== -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -71,6 +224,10 @@ commands.RM_OWNERS = function (meta, args) {
|
|||||||
changed = true;
|
changed = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (meta.owners.length === 0 && meta.restricted) {
|
||||||
|
meta.restricted = false;
|
||||||
|
}
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -141,6 +298,18 @@ commands.RESET_OWNERS = function (meta, args) {
|
|||||||
|
|
||||||
// overwrite the existing owners with the new one
|
// overwrite the existing owners with the new one
|
||||||
meta.owners = deduplicate(args.filter(isValidOwner));
|
meta.owners = deduplicate(args.filter(isValidOwner));
|
||||||
|
|
||||||
|
if (Array.isArray(meta.allowed)) {
|
||||||
|
// make sure owners are not included in the allow list
|
||||||
|
filterInPlace(meta.allowed, function (member) {
|
||||||
|
return meta.owners.indexOf(member) !== -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta.owners.length === 0 && meta.restricted) {
|
||||||
|
meta.restricted = false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,6 +347,25 @@ commands.ADD_MAILBOX = function (meta, args) {
|
|||||||
return changed;
|
return changed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
commands.RM_MAILBOX = function (meta, args) {
|
||||||
|
if (!Array.isArray(args)) { throw new Error("INVALID_ARGS"); }
|
||||||
|
if (!meta.mailbox || typeof(meta.mailbox) === 'undefined') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (typeof(meta.mailbox) === 'string' && args.length === 0) {
|
||||||
|
delete meta.mailbox;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var changed = false;
|
||||||
|
args.forEach(function (arg) {
|
||||||
|
if (meta.mailbox[arg] === 'undefined') { return; }
|
||||||
|
delete meta.mailbox[arg];
|
||||||
|
changed = true;
|
||||||
|
});
|
||||||
|
return changed;
|
||||||
|
};
|
||||||
|
|
||||||
commands.UPDATE_EXPIRATION = function () {
|
commands.UPDATE_EXPIRATION = function () {
|
||||||
throw new Error("E_NOT_IMPLEMENTED");
|
throw new Error("E_NOT_IMPLEMENTED");
|
||||||
};
|
};
|
||||||
|
|||||||
17
lib/rpc.js
17
lib/rpc.js
@ -9,6 +9,7 @@ const Block = require("./commands/block");
|
|||||||
const Metadata = require("./commands/metadata");
|
const Metadata = require("./commands/metadata");
|
||||||
const Channel = require("./commands/channel");
|
const Channel = require("./commands/channel");
|
||||||
const Upload = require("./commands/upload");
|
const Upload = require("./commands/upload");
|
||||||
|
const HK = require("./hk-util");
|
||||||
|
|
||||||
var RPC = module.exports;
|
var RPC = module.exports;
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ var isUnauthenticateMessage = function (msg) {
|
|||||||
return msg && msg.length === 2 && typeof(UNAUTHENTICATED_CALLS[msg[0]]) === 'function';
|
return msg && msg.length === 2 && typeof(UNAUTHENTICATED_CALLS[msg[0]]) === 'function';
|
||||||
};
|
};
|
||||||
|
|
||||||
var handleUnauthenticatedMessage = function (Env, msg, respond, Server) {
|
var handleUnauthenticatedMessage = function (Env, msg, respond, Server, netfluxId) {
|
||||||
Env.Log.silly('LOG_RPC', msg[0]);
|
Env.Log.silly('LOG_RPC', msg[0]);
|
||||||
|
|
||||||
var method = UNAUTHENTICATED_CALLS[msg[0]];
|
var method = UNAUTHENTICATED_CALLS[msg[0]];
|
||||||
@ -36,7 +37,7 @@ var handleUnauthenticatedMessage = function (Env, msg, respond, Server) {
|
|||||||
return void respond(err);
|
return void respond(err);
|
||||||
}
|
}
|
||||||
respond(err, [null, value, null]);
|
respond(err, [null, value, null]);
|
||||||
}, Server);
|
}, Server, netfluxId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const AUTHENTICATED_USER_TARGETED = {
|
const AUTHENTICATED_USER_TARGETED = {
|
||||||
@ -117,7 +118,7 @@ var handleAuthenticatedMessage = function (Env, unsafeKey, msg, respond, Server)
|
|||||||
return void Respond('UNSUPPORTED_RPC_CALL', msg);
|
return void Respond('UNSUPPORTED_RPC_CALL', msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
var rpc = function (Env, Server, data, respond) {
|
var rpc = function (Env, Server, userId, data, respond) {
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
Env.Log.debug('INVALID_ARG_FORMET', data);
|
Env.Log.debug('INVALID_ARG_FORMET', data);
|
||||||
return void respond('INVALID_ARG_FORMAT');
|
return void respond('INVALID_ARG_FORMAT');
|
||||||
@ -136,15 +137,16 @@ var rpc = function (Env, Server, data, respond) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isUnauthenticateMessage(msg)) {
|
if (isUnauthenticateMessage(msg)) {
|
||||||
return handleUnauthenticatedMessage(Env, msg, respond, Server);
|
return handleUnauthenticatedMessage(Env, msg, respond, Server, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
var signature = msg.shift();
|
var signature = msg.shift();
|
||||||
var publicKey = msg.shift();
|
var publicKey = msg.shift();
|
||||||
|
|
||||||
// make sure a user object is initialized in the cookie jar
|
// make sure a user object is initialized in the cookie jar
|
||||||
|
var session;
|
||||||
if (publicKey) {
|
if (publicKey) {
|
||||||
Core.getSession(Env.Sessions, publicKey);
|
session = Core.getSession(Env.Sessions, publicKey);
|
||||||
} else {
|
} else {
|
||||||
Env.Log.debug("NO_PUBLIC_KEY_PROVIDED", publicKey);
|
Env.Log.debug("NO_PUBLIC_KEY_PROVIDED", publicKey);
|
||||||
}
|
}
|
||||||
@ -174,6 +176,7 @@ var rpc = function (Env, Server, data, respond) {
|
|||||||
// check the signature on the message
|
// check the signature on the message
|
||||||
// refuse the command if it doesn't validate
|
// refuse the command if it doesn't validate
|
||||||
if (Core.checkSignature(Env, serialized, signature, publicKey) === true) {
|
if (Core.checkSignature(Env, serialized, signature, publicKey) === true) {
|
||||||
|
HK.authenticateNetfluxSession(Env, userId, publicKey);
|
||||||
return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
|
return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
|
||||||
}
|
}
|
||||||
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
||||||
@ -202,9 +205,9 @@ RPC.create = function (Env, cb) {
|
|||||||
Core.expireSessions(Sessions);
|
Core.expireSessions(Sessions);
|
||||||
}, Core.SESSION_EXPIRATION_TIME);
|
}, Core.SESSION_EXPIRATION_TIME);
|
||||||
|
|
||||||
cb(void 0, function (Server, data, respond) {
|
cb(void 0, function (Server, userId, data, respond) {
|
||||||
try {
|
try {
|
||||||
return rpc(Env, Server, data, respond);
|
return rpc(Env, Server, userId, data, respond);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Error from RPC with data " + JSON.stringify(data));
|
console.log("Error from RPC with data " + JSON.stringify(data));
|
||||||
console.log(e.stack);
|
console.log(e.stack);
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -113,9 +113,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chainpad-server": {
|
"chainpad-server": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/chainpad-server/-/chainpad-server-4.0.2.tgz",
|
|
||||||
"integrity": "sha512-9NrFsATd70uAdksxsCZBIJ/SiREmJ6QLYTNaeFLH/nJpeZ2b7wblVGABCj3JYWvngdEZ7Umc+afbWH8sUmtgeQ==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"nthen": "0.1.8",
|
"nthen": "0.1.8",
|
||||||
"pull-stream": "^3.6.9",
|
"pull-stream": "^3.6.9",
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chainpad-crypto": "^0.2.2",
|
"chainpad-crypto": "^0.2.2",
|
||||||
"chainpad-server": "^4.0.0",
|
"chainpad-server": "^4.0.3",
|
||||||
"express": "~4.16.0",
|
"express": "~4.16.0",
|
||||||
"fs-extra": "^7.0.0",
|
"fs-extra": "^7.0.0",
|
||||||
"get-folder-size": "^2.0.1",
|
"get-folder-size": "^2.0.1",
|
||||||
|
|||||||
@ -357,9 +357,142 @@ nThen(function (w) {
|
|||||||
bob.name = 'bob';
|
bob.name = 'bob';
|
||||||
//console.log("Initialized Bob");
|
//console.log("Initialized Bob");
|
||||||
}));
|
}));
|
||||||
|
}).nThen(function (w) {
|
||||||
|
// restrict access to oscar's mailbox channel
|
||||||
|
oscar.rpc.send('SET_METADATA', {
|
||||||
|
command: 'RESTRICT_ACCESS',
|
||||||
|
channel: oscar.mailboxChannel,
|
||||||
|
value: [ true ]
|
||||||
|
}, w(function (err, response) {
|
||||||
|
if (err) {
|
||||||
|
return void console.log(err);
|
||||||
|
}
|
||||||
|
var metadata = response[0];
|
||||||
|
if (!(metadata && metadata.restricted)) {
|
||||||
|
throw new Error("EXPECTED MAILBOX TO BE RESTRICTED");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function (w) {
|
||||||
|
// XXX RESTRICT GET_METADATA should fail because alice is not on the allow list
|
||||||
|
// expect INSUFFICIENT_PERMISSIONS
|
||||||
|
alice.anonRpc.send('GET_METADATA', oscar.mailboxChannel, w(function (err) {
|
||||||
|
if (!err) {
|
||||||
|
// XXX RESTRICT alice should not be permitted to read oscar's mailbox's metadata
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function (w) {
|
||||||
|
// add alice to oscar's mailbox's allow list for some reason
|
||||||
|
oscar.rpc.send('SET_METADATA', {
|
||||||
|
command: 'ADD_ALLOWED',
|
||||||
|
channel: oscar.mailboxChannel,
|
||||||
|
value: [
|
||||||
|
alice.edKeys.edPublic
|
||||||
|
]
|
||||||
|
}, w(function (err /*, metadata */) {
|
||||||
|
if (err) {
|
||||||
|
return void console.error(err);
|
||||||
|
}
|
||||||
|
//console.log('XXX', metadata);
|
||||||
|
}));
|
||||||
|
}).nThen(function (w) {
|
||||||
|
oscar.anonRpc.send('GET_METADATA', oscar.mailboxChannel, w(function (err, response) {
|
||||||
|
if (err) {
|
||||||
|
throw new Error("OSCAR SHOULD BE ABLE TO READ HIS OWN METADATA");
|
||||||
|
}
|
||||||
|
var metadata = response && response[0];
|
||||||
|
|
||||||
|
if (!metadata) {
|
||||||
|
throw new Error("EXPECTED METADATA");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.allowed[0] !== alice.edKeys.edPublic) {
|
||||||
|
throw new Error("EXPECTED ALICE TO BE ON ALLOW LIST");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function () {
|
||||||
|
// XXX RESTRICT alice should now be able to read oscar's mailbox metadata
|
||||||
|
/*
|
||||||
|
alice.anonRpc.send('GET_METADATA', oscar.mailboxChannel, function (err, response) {
|
||||||
|
if (err) {
|
||||||
|
PROBLEM
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}).nThen(function (w) {
|
||||||
|
//throw new Error("boop");
|
||||||
|
// add alice as an owner of oscar's mailbox for some reason
|
||||||
|
oscar.rpc.send('SET_METADATA', {
|
||||||
|
command: 'ADD_OWNERS',
|
||||||
|
channel: oscar.mailboxChannel,
|
||||||
|
value: [
|
||||||
|
alice.edKeys.edPublic
|
||||||
|
]
|
||||||
|
}, Util.mkTimeout(w(function (err) {
|
||||||
|
if (err === 'TIMEOUT') {
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
throw new Error("ADD_OWNERS_FAILURE");
|
||||||
|
}
|
||||||
|
}), 2000));
|
||||||
|
}).nThen(function (w) {
|
||||||
|
// alice should now be able to read oscar's mailbox metadata
|
||||||
|
alice.anonRpc.send('GET_METADATA', oscar.mailboxChannel, w(function (err, response) {
|
||||||
|
if (err) {
|
||||||
|
throw new Error("EXPECTED ALICE TO BE ALLOWED TO READ OSCAR'S METADATA");
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadata = response && response[0];
|
||||||
|
if (!metadata) { throw new Error("EXPECTED METADATA"); }
|
||||||
|
if (metadata.allowed.length !== 0) {
|
||||||
|
throw new Error("EXPECTED AN EMPTY ALLOW LIST");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function (w) {
|
||||||
|
// disable the access restrictionallow list
|
||||||
|
oscar.rpc.send('SET_METADATA', {
|
||||||
|
command: 'RESTRICT_ACCESS',
|
||||||
|
channel: oscar.mailboxChannel,
|
||||||
|
value: [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
}, w(function (err) {
|
||||||
|
if (err) {
|
||||||
|
throw new Error("COULD_NOT_DISABLE_RESTRICTED_ACCESS");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
// add alice to oscar's mailbox's allow list for some reason
|
||||||
|
oscar.rpc.send('SET_METADATA', {
|
||||||
|
command: 'ADD_ALLOWED',
|
||||||
|
channel: oscar.mailboxChannel,
|
||||||
|
value: [
|
||||||
|
bob.edKeys.edPublic
|
||||||
|
]
|
||||||
|
}, w(function (err) {
|
||||||
|
if (err) {
|
||||||
|
return void console.error(err);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function (w) {
|
||||||
|
oscar.anonRpc.send('GET_METADATA', oscar.mailboxChannel, w(function (err, response) {
|
||||||
|
if (err) {
|
||||||
|
throw new Error("OSCAR SHOULD BE ABLE TO READ HIS OWN METADATA");
|
||||||
|
}
|
||||||
|
var metadata = response && response[0];
|
||||||
|
|
||||||
|
if (!metadata) {
|
||||||
|
throw new Error("EXPECTED METADATA");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.allowed[0] !== bob.edKeys.edPublic) {
|
||||||
|
throw new Error("EXPECTED ALICE TO BE ON ALLOW LIST");
|
||||||
|
}
|
||||||
|
if (metadata.restricted) {
|
||||||
|
throw new Error("RESTRICTED_ACCESS_NOT_DISABLED");
|
||||||
|
}
|
||||||
|
}));
|
||||||
}).nThen(function () {
|
}).nThen(function () {
|
||||||
//setTimeout(w(), 500);
|
//setTimeout(w(), 500);
|
||||||
|
|
||||||
}).nThen(function (w) {
|
}).nThen(function (w) {
|
||||||
// Alice loads the roster...
|
// Alice loads the roster...
|
||||||
var rosterKeys = Crypto.Team.deriveMemberKeys(sharedConfig.rosterSeed, alice.curveKeys);
|
var rosterKeys = Crypto.Team.deriveMemberKeys(sharedConfig.rosterSeed, alice.curveKeys);
|
||||||
|
|||||||
@ -218,14 +218,15 @@ define([
|
|||||||
var titles = [];
|
var titles = [];
|
||||||
var active = 0;
|
var active = 0;
|
||||||
tabs.forEach(function (tab, i) {
|
tabs.forEach(function (tab, i) {
|
||||||
if (!tab.content || !tab.title) { return; }
|
if (!(tab.content || tab.disabled) || !tab.title) { return; }
|
||||||
var content = h('div.alertify-tabs-content', tab.content);
|
var content = h('div.alertify-tabs-content', tab.content);
|
||||||
var title = h('span.alertify-tabs-title', tab.title);
|
var title = h('span.alertify-tabs-title'+ (tab.disabled ? '.disabled' : ''), tab.title);
|
||||||
if (tab.icon) {
|
if (tab.icon) {
|
||||||
var icon = h('i', {class: tab.icon});
|
var icon = h('i', {class: tab.icon});
|
||||||
$(title).prepend(' ').prepend(icon);
|
$(title).prepend(' ').prepend(icon);
|
||||||
}
|
}
|
||||||
$(title).click(function () {
|
$(title).click(function () {
|
||||||
|
if (tab.disabled) { return; }
|
||||||
var old = tabs[active];
|
var old = tabs[active];
|
||||||
if (old.onHide) { old.onHide(); }
|
if (old.onHide) { old.onHide(); }
|
||||||
titles.forEach(function (t) { $(t).removeClass('alertify-tabs-active'); });
|
titles.forEach(function (t) { $(t).removeClass('alertify-tabs-active'); });
|
||||||
@ -239,7 +240,7 @@ define([
|
|||||||
});
|
});
|
||||||
titles.push(title);
|
titles.push(title);
|
||||||
contents.push(content);
|
contents.push(content);
|
||||||
if (tab.active) { active = i; }
|
if (tab.active && !tab.disabled) { active = i; }
|
||||||
});
|
});
|
||||||
if (contents.length) {
|
if (contents.length) {
|
||||||
$(contents[active]).addClass('alertify-tabs-content-active');
|
$(contents[active]).addClass('alertify-tabs-content-active');
|
||||||
@ -1192,15 +1193,20 @@ define([
|
|||||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide();
|
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide();
|
||||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide();
|
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide();
|
||||||
|
|
||||||
|
var state = false;
|
||||||
|
|
||||||
var spin = function () {
|
var spin = function () {
|
||||||
|
state = true;
|
||||||
$ok.hide();
|
$ok.hide();
|
||||||
$spinner.show();
|
$spinner.show();
|
||||||
};
|
};
|
||||||
var hide = function () {
|
var hide = function () {
|
||||||
|
state = false;
|
||||||
$ok.hide();
|
$ok.hide();
|
||||||
$spinner.hide();
|
$spinner.hide();
|
||||||
};
|
};
|
||||||
var done = function () {
|
var done = function () {
|
||||||
|
state = false;
|
||||||
$ok.show();
|
$ok.show();
|
||||||
$spinner.hide();
|
$spinner.hide();
|
||||||
};
|
};
|
||||||
@ -1211,6 +1217,7 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
getState: function () { return state; },
|
||||||
ok: $ok[0],
|
ok: $ok[0],
|
||||||
spinner: $spinner[0],
|
spinner: $spinner[0],
|
||||||
spin: spin,
|
spin: spin,
|
||||||
|
|||||||
@ -14,7 +14,7 @@ define([
|
|||||||
'/customize/application_config.js',
|
'/customize/application_config.js',
|
||||||
'/customize/pages.js',
|
'/customize/pages.js',
|
||||||
'/bower_components/nthen/index.js',
|
'/bower_components/nthen/index.js',
|
||||||
'/common/invitation.js',
|
'/common/inner/invitation.js',
|
||||||
|
|
||||||
'css!/customize/fonts/cptools/style.css',
|
'css!/customize/fonts/cptools/style.css',
|
||||||
'/bower_components/croppie/croppie.min.js',
|
'/bower_components/croppie/croppie.min.js',
|
||||||
@ -99,7 +99,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/*
|
||||||
var getPropertiesData = function (common, cb) {
|
var getPropertiesData = function (common, cb) {
|
||||||
var data = {};
|
var data = {};
|
||||||
NThen(function (waitFor) {
|
NThen(function (waitFor) {
|
||||||
@ -127,6 +127,32 @@ define([
|
|||||||
cb(void 0, data);
|
cb(void 0, data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
var getPropertiesData = function (common, opts, cb) {
|
||||||
|
opts = opts || {};
|
||||||
|
var data = {};
|
||||||
|
NThen(function (waitFor) {
|
||||||
|
var base = common.getMetadataMgr().getPrivateData().origin;
|
||||||
|
common.getPadAttribute('', waitFor(function (err, val) {
|
||||||
|
if (err || !val) {
|
||||||
|
waitFor.abort();
|
||||||
|
return void cb(err || 'EEMPTY');
|
||||||
|
}
|
||||||
|
if (!val.fileType) {
|
||||||
|
delete val.owners;
|
||||||
|
delete val.expire;
|
||||||
|
}
|
||||||
|
Util.extend(data, val);
|
||||||
|
if (data.href) { data.href = base + data.href; }
|
||||||
|
if (data.roHref) { data.roHref = base + data.roHref; }
|
||||||
|
}), opts.href);
|
||||||
|
}).nThen(function () {
|
||||||
|
cb(void 0, data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
var createOwnerModal = function (common, data) {
|
var createOwnerModal = function (common, data) {
|
||||||
var friends = common.getFriends(true);
|
var friends = common.getFriends(true);
|
||||||
var sframeChan = common.getSframeChannel();
|
var sframeChan = common.getSframeChannel();
|
||||||
@ -425,8 +451,8 @@ define([
|
|||||||
var link = h('div.cp-share-columns', [
|
var link = h('div.cp-share-columns', [
|
||||||
div1,
|
div1,
|
||||||
div2
|
div2
|
||||||
/*drawRemove()[0],
|
// drawRemove()[0],
|
||||||
drawAdd()[0]*/
|
//drawAdd()[0]
|
||||||
]);
|
]);
|
||||||
var linkButtons = [{
|
var linkButtons = [{
|
||||||
className: 'cancel',
|
className: 'cancel',
|
||||||
@ -436,6 +462,8 @@ define([
|
|||||||
}];
|
}];
|
||||||
return UI.dialog.customModal(link, {buttons: linkButtons});
|
return UI.dialog.customModal(link, {buttons: linkButtons});
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
/*
|
||||||
var getRightsProperties = function (common, data, cb) {
|
var getRightsProperties = function (common, data, cb) {
|
||||||
var $div = $('<div>');
|
var $div = $('<div>');
|
||||||
if (!data) { return void cb(void 0, $div); }
|
if (!data) { return void cb(void 0, $div); }
|
||||||
@ -707,7 +735,10 @@ define([
|
|||||||
|
|
||||||
cb(void 0, $div);
|
cb(void 0, $div);
|
||||||
};
|
};
|
||||||
var getPadProperties = function (common, data, cb) {
|
*/
|
||||||
|
|
||||||
|
var getPadProperties = function (common, data, opts, cb) {
|
||||||
|
opts = opts || {};
|
||||||
var $d = $('<div>');
|
var $d = $('<div>');
|
||||||
if (!data) { return void cb(void 0, $d); }
|
if (!data) { return void cb(void 0, $d); }
|
||||||
|
|
||||||
@ -721,7 +752,7 @@ define([
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.roHref) {
|
if (data.roHref && !opts.noReadOnly) {
|
||||||
$('<label>', {'for': 'cp-app-prop-rolink'}).text(Messages.viewShare).appendTo($d);
|
$('<label>', {'for': 'cp-app-prop-rolink'}).text(Messages.viewShare).appendTo($d);
|
||||||
$d.append(UI.dialog.selectable(data.roHref, {
|
$d.append(UI.dialog.selectable(data.roHref, {
|
||||||
id: 'cp-app-prop-rolink',
|
id: 'cp-app-prop-rolink',
|
||||||
@ -859,35 +890,42 @@ define([
|
|||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
UIElements.getProperties = function (common, data, cb) {
|
|
||||||
var c1;
|
UIElements.getProperties = function (common, opts, cb) {
|
||||||
var c2;
|
var data;
|
||||||
|
var content;
|
||||||
var button = [{
|
var button = [{
|
||||||
className: 'primary',
|
className: 'cancel',
|
||||||
name: Messages.okButton,
|
name: Messages.filePicker_close,
|
||||||
onClick: function () {},
|
onClick: function () {},
|
||||||
keys: [13]
|
keys: [13,27]
|
||||||
}];
|
}];
|
||||||
NThen(function (waitFor) {
|
NThen(function (waitFor) {
|
||||||
getPadProperties(common, data, waitFor(function (e, c) {
|
getPropertiesData(common, opts, waitFor(function (e, _data) {
|
||||||
c1 = UI.dialog.customModal(c[0], {
|
if (e) {
|
||||||
buttons: button
|
waitFor.abort();
|
||||||
});
|
return void cb(e);
|
||||||
|
}
|
||||||
|
data = _data;
|
||||||
}));
|
}));
|
||||||
getRightsProperties(common, data, waitFor(function (e, c) {
|
}).nThen(function (waitFor) {
|
||||||
c2 = UI.dialog.customModal(c[0], {
|
getPadProperties(common, data, opts, waitFor(function (e, c) {
|
||||||
|
if (e) {
|
||||||
|
waitFor.abort();
|
||||||
|
return void cb(e);
|
||||||
|
}
|
||||||
|
content = UI.dialog.customModal(c[0], {
|
||||||
buttons: button
|
buttons: button
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}).nThen(function () {
|
}).nThen(function () {
|
||||||
var tabs = UI.dialog.tabs([{
|
var tabs = UI.dialog.tabs([{
|
||||||
title: Messages.fc_prop,
|
title: Messages.fc_prop,
|
||||||
content: c1
|
icon: "fa fa-info-circle",
|
||||||
}, {
|
content: content
|
||||||
title: Messages.creation_propertiesTitle,
|
|
||||||
content: c2
|
|
||||||
}]);
|
}]);
|
||||||
cb (void 0, $(tabs));
|
var modal = UI.openCustomModal(tabs);
|
||||||
|
cb (void 0, modal);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -901,7 +939,15 @@ define([
|
|||||||
var name = data.displayName || data.name || Messages.anonymous;
|
var name = data.displayName || data.name || Messages.anonymous;
|
||||||
var avatar = h('span.cp-usergrid-avatar.cp-avatar');
|
var avatar = h('span.cp-usergrid-avatar.cp-avatar');
|
||||||
UIElements.displayAvatar(common, $(avatar), data.avatar, name);
|
UIElements.displayAvatar(common, $(avatar), data.avatar, name);
|
||||||
return h('div.cp-usergrid-user'+(data.selected?'.cp-selected':'')+(config.large?'.large':''), {
|
var removeBtn, el;
|
||||||
|
if (config.remove) {
|
||||||
|
removeBtn = h('span.fa.fa-times');
|
||||||
|
$(removeBtn).click(function () {
|
||||||
|
config.remove(el);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
el = h('div.cp-usergrid-user'+(data.selected?'.cp-selected':'')+(config.large?'.large':''), {
|
||||||
'data-ed': data.edPublic,
|
'data-ed': data.edPublic,
|
||||||
'data-teamid': data.teamId,
|
'data-teamid': data.teamId,
|
||||||
'data-curve': data.curvePublic || '',
|
'data-curve': data.curvePublic || '',
|
||||||
@ -911,17 +957,20 @@ define([
|
|||||||
style: 'order:'+i+';'
|
style: 'order:'+i+';'
|
||||||
},[
|
},[
|
||||||
avatar,
|
avatar,
|
||||||
h('span.cp-usergrid-user-name', name)
|
h('span.cp-usergrid-user-name', name),
|
||||||
|
data.notRemovable ? undefined : removeBtn
|
||||||
]);
|
]);
|
||||||
|
return el;
|
||||||
}).filter(function (x) { return x; });
|
}).filter(function (x) { return x; });
|
||||||
|
|
||||||
var noOthers = icons.length === 0 ? '.cp-usergrid-empty' : '';
|
var noOthers = icons.length === 0 ? '.cp-usergrid-empty' : '';
|
||||||
|
var classes = noOthers + (config.large?'.large':'') + (config.list?'.list':'');
|
||||||
|
|
||||||
var inputFilter = h('input', {
|
var inputFilter = h('input', {
|
||||||
placeholder: Messages.share_filterFriend
|
placeholder: Messages.share_filterFriend
|
||||||
});
|
});
|
||||||
|
|
||||||
var div = h('div.cp-usergrid-container' + noOthers + (config.large?'.large':''), [
|
var div = h('div.cp-usergrid-container' + classes, [
|
||||||
label ? h('label', label) : undefined,
|
label ? h('label', label) : undefined,
|
||||||
h('div.cp-usergrid-filter', (config.noFilter || config.noSelect) ? undefined : [
|
h('div.cp-usergrid-filter', (config.noFilter || config.noSelect) ? undefined : [
|
||||||
inputFilter
|
inputFilter
|
||||||
@ -2374,6 +2423,26 @@ define([
|
|||||||
});
|
});
|
||||||
updateIcon(data.element.is(':visible'));
|
updateIcon(data.element.is(':visible'));
|
||||||
break;
|
break;
|
||||||
|
case 'access':
|
||||||
|
button = $('<button>', {
|
||||||
|
'class': 'fa fa-unlock-alt cp-toolbar-icon-access',
|
||||||
|
title: "ACCESS", // XXX
|
||||||
|
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'})
|
||||||
|
.text("ACCESS")) // XXX
|
||||||
|
.click(common.prepareFeedback(type))
|
||||||
|
.click(function () {
|
||||||
|
common.isPadStored(function (err, data) {
|
||||||
|
if (!data) {
|
||||||
|
return void UI.alert(Messages.autostore_notAvailable);
|
||||||
|
}
|
||||||
|
require(['/common/inner/access.js'], function (Access) {
|
||||||
|
Access.getAccessModal(common, {}, function (e) {
|
||||||
|
if (e) { console.error(e); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
break;
|
||||||
case 'properties':
|
case 'properties':
|
||||||
button = $('<button>', {
|
button = $('<button>', {
|
||||||
'class': 'fa fa-info-circle cp-toolbar-icon-properties',
|
'class': 'fa fa-info-circle cp-toolbar-icon-properties',
|
||||||
@ -2386,12 +2455,8 @@ define([
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
return void UI.alert(Messages.autostore_notAvailable);
|
return void UI.alert(Messages.autostore_notAvailable);
|
||||||
}
|
}
|
||||||
getPropertiesData(common, function (e, data) {
|
UIElements.getProperties(common, {}, function (e) {
|
||||||
if (e) { return void console.error(e); }
|
if (e) { return void console.error(e); }
|
||||||
UIElements.getProperties(common, data, function (e, $prop) {
|
|
||||||
if (e) { return void console.error(e); }
|
|
||||||
UI.openCustomModal($prop[0]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -4144,7 +4209,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
UIElements.onServerError = function (common, err, toolbar, cb) {
|
UIElements.onServerError = function (common, err, toolbar, cb) {
|
||||||
if (["EDELETED", "EEXPIRED"].indexOf(err.type) === -1) { return; }
|
if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; }
|
||||||
var priv = common.getMetadataMgr().getPrivateData();
|
var priv = common.getMetadataMgr().getPrivateData();
|
||||||
var msg = err.type;
|
var msg = err.type;
|
||||||
if (err.type === 'EEXPIRED') {
|
if (err.type === 'EEXPIRED') {
|
||||||
@ -4158,11 +4223,13 @@ define([
|
|||||||
if (err.loaded) {
|
if (err.loaded) {
|
||||||
msg += Messages.errorCopy;
|
msg += Messages.errorCopy;
|
||||||
}
|
}
|
||||||
|
} else if (err.type === 'ERESTRICTED') {
|
||||||
|
msg = Messages.restrictedError || "RESTRICTED"; // XXX
|
||||||
}
|
}
|
||||||
var sframeChan = common.getSframeChannel();
|
var sframeChan = common.getSframeChannel();
|
||||||
sframeChan.event('EV_SHARE_OPEN', {hidden: true});
|
sframeChan.event('EV_SHARE_OPEN', {hidden: true});
|
||||||
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
|
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
|
||||||
UI.errorLoadingScreen(msg, true, true);
|
UI.errorLoadingScreen(msg, Boolean(err.loaded), Boolean(err.loaded));
|
||||||
(cb || function () {})();
|
(cb || function () {})();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -68,6 +68,19 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Util.mkTimeout = function (_f, ms) {
|
||||||
|
ms = ms || 0;
|
||||||
|
var f = Util.once(_f);
|
||||||
|
|
||||||
|
var timeout = setTimeout(function () {
|
||||||
|
f('TIMEOUT');
|
||||||
|
}, ms);
|
||||||
|
|
||||||
|
return Util.both(f, function () {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Util.response = function () {
|
Util.response = function () {
|
||||||
var pending = {};
|
var pending = {};
|
||||||
var timeouts = {};
|
var timeouts = {};
|
||||||
|
|||||||
@ -8,6 +8,9 @@ define([
|
|||||||
'/common/common-interface.js',
|
'/common/common-interface.js',
|
||||||
'/common/common-constants.js',
|
'/common/common-constants.js',
|
||||||
'/common/common-feedback.js',
|
'/common/common-feedback.js',
|
||||||
|
|
||||||
|
'/common/inner/access.js',
|
||||||
|
|
||||||
'/bower_components/nthen/index.js',
|
'/bower_components/nthen/index.js',
|
||||||
'/common/hyperscript.js',
|
'/common/hyperscript.js',
|
||||||
'/common/proxy-manager.js',
|
'/common/proxy-manager.js',
|
||||||
@ -23,6 +26,7 @@ define([
|
|||||||
UI,
|
UI,
|
||||||
Constants,
|
Constants,
|
||||||
Feedback,
|
Feedback,
|
||||||
|
Access,
|
||||||
nThen,
|
nThen,
|
||||||
h,
|
h,
|
||||||
ProxyManager,
|
ProxyManager,
|
||||||
@ -80,6 +84,7 @@ define([
|
|||||||
var faTrash = 'fa-trash';
|
var faTrash = 'fa-trash';
|
||||||
var faCopy = 'fa-clone';
|
var faCopy = 'fa-clone';
|
||||||
var faDelete = 'fa-eraser';
|
var faDelete = 'fa-eraser';
|
||||||
|
var faAccess = 'fa-unlock-alt';
|
||||||
var faProperties = 'fa-info-circle';
|
var faProperties = 'fa-info-circle';
|
||||||
var faTags = 'fa-hashtag';
|
var faTags = 'fa-hashtag';
|
||||||
var faUploadFiles = 'cptools-file-upload';
|
var faUploadFiles = 'cptools-file-upload';
|
||||||
@ -117,9 +122,9 @@ define([
|
|||||||
var $addIcon = $('<span>', {"class": "fa fa-plus"});
|
var $addIcon = $('<span>', {"class": "fa fa-plus"});
|
||||||
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
|
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
|
||||||
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
|
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
|
||||||
var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"});
|
var $ownedIcon = $('<span>', {"class": "fa fa-id-badge"});
|
||||||
var $sharedIcon = $('<span>', {"class": "fa " + faShared});
|
var $sharedIcon = $('<span>', {"class": "fa " + faShared});
|
||||||
var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
|
//var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
|
||||||
var $tagsIcon = $('<span>', {"class": "fa " + faTags});
|
var $tagsIcon = $('<span>', {"class": "fa " + faTags});
|
||||||
var $passwordIcon = $('<span>', {"class": "fa fa-lock"});
|
var $passwordIcon = $('<span>', {"class": "fa fa-lock"});
|
||||||
var $expirableIcon = $('<span>', {"class": "fa fa-clock-o"});
|
var $expirableIcon = $('<span>', {"class": "fa fa-clock-o"});
|
||||||
@ -453,6 +458,10 @@ define([
|
|||||||
'data-icon': faDelete,
|
'data-icon': faDelete,
|
||||||
}, Messages.fc_remove_sharedfolder)),
|
}, Messages.fc_remove_sharedfolder)),
|
||||||
$separator.clone()[0],
|
$separator.clone()[0],
|
||||||
|
h('li', h('a.cp-app-drive-context-access.dropdown-item', {
|
||||||
|
'tabindex': '-1',
|
||||||
|
'data-icon': faAccess,
|
||||||
|
}, "ACCESS")), // XXX
|
||||||
h('li', h('a.cp-app-drive-context-properties.dropdown-item', {
|
h('li', h('a.cp-app-drive-context-properties.dropdown-item', {
|
||||||
'tabindex': '-1',
|
'tabindex': '-1',
|
||||||
'data-icon': faProperties,
|
'data-icon': faProperties,
|
||||||
@ -1212,7 +1221,7 @@ define([
|
|||||||
hide.push('savelocal');
|
hide.push('savelocal');
|
||||||
hide.push('openro');
|
hide.push('openro');
|
||||||
hide.push('openincode');
|
hide.push('openincode');
|
||||||
hide.push('properties');
|
hide.push('properties', 'access');
|
||||||
hide.push('hashtag');
|
hide.push('hashtag');
|
||||||
hide.push('makeacopy');
|
hide.push('makeacopy');
|
||||||
}
|
}
|
||||||
@ -1243,7 +1252,7 @@ define([
|
|||||||
});
|
});
|
||||||
if (paths.length > 1) {
|
if (paths.length > 1) {
|
||||||
hide.push('restore');
|
hide.push('restore');
|
||||||
hide.push('properties');
|
hide.push('properties', 'access');
|
||||||
hide.push('rename');
|
hide.push('rename');
|
||||||
hide.push('openparent');
|
hide.push('openparent');
|
||||||
hide.push('hashtag');
|
hide.push('hashtag');
|
||||||
@ -1273,7 +1282,7 @@ define([
|
|||||||
'deleteowned', 'removesf', 'properties', 'hashtag'];
|
'deleteowned', 'removesf', 'properties', 'hashtag'];
|
||||||
break;
|
break;
|
||||||
case 'default':
|
case 'default':
|
||||||
show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'hashtag', 'makeacopy'];
|
show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'access', 'hashtag', 'makeacopy'];
|
||||||
break;
|
break;
|
||||||
case 'trashtree': {
|
case 'trashtree': {
|
||||||
show = ['empty'];
|
show = ['empty'];
|
||||||
@ -1811,10 +1820,10 @@ define([
|
|||||||
var $owned = $ownedIcon.clone().appendTo($state);
|
var $owned = $ownedIcon.clone().appendTo($state);
|
||||||
$owned.attr('title', Messages.fm_padIsOwned);
|
$owned.attr('title', Messages.fm_padIsOwned);
|
||||||
$span.addClass('cp-app-drive-element-owned');
|
$span.addClass('cp-app-drive-element-owned');
|
||||||
} else if (data.owners && data.owners.length) {
|
} /* else if (data.owners && data.owners.length) {
|
||||||
var $owner = $ownerIcon.clone().appendTo($state);
|
var $owner = $ownerIcon.clone().appendTo($state);
|
||||||
$owner.attr('title', Messages.fm_padIsOwnedOther);
|
$owner.attr('title', Messages.fm_padIsOwnedOther);
|
||||||
}
|
} */
|
||||||
};
|
};
|
||||||
var thumbsUrls = {};
|
var thumbsUrls = {};
|
||||||
var addFileData = function (element, $element) {
|
var addFileData = function (element, $element) {
|
||||||
@ -3086,9 +3095,8 @@ define([
|
|||||||
}).appendTo($openDir);
|
}).appendTo($openDir);
|
||||||
}
|
}
|
||||||
$('<a>').text(Messages.fc_prop).click(function () {
|
$('<a>').text(Messages.fc_prop).click(function () {
|
||||||
APP.getProperties(r.id, function (e, $prop) {
|
APP.getProperties(r.id, function (e) {
|
||||||
if (e) { return void logError(e); }
|
if (e) { return void logError(e); }
|
||||||
UI.alert($prop[0], undefined, true);
|
|
||||||
});
|
});
|
||||||
}).appendTo($openDir);
|
}).appendTo($openDir);
|
||||||
}
|
}
|
||||||
@ -3836,12 +3844,11 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var getProperties = APP.getProperties = function (el, cb) {
|
APP.getProperties = function (el, cb) {
|
||||||
if (!manager.isFile(el) && !manager.isSharedFolder(el)) {
|
if (!manager.isFile(el) && !manager.isSharedFolder(el)) {
|
||||||
return void cb('NOT_FILE');
|
return void cb('NOT_FILE');
|
||||||
}
|
}
|
||||||
//var ro = manager.isReadOnlyFile(el);
|
//var ro = manager.isReadOnlyFile(el);
|
||||||
var base = APP.origin;
|
|
||||||
var data;
|
var data;
|
||||||
if (manager.isSharedFolder(el)) {
|
if (manager.isSharedFolder(el)) {
|
||||||
data = JSON.parse(JSON.stringify(manager.getSharedFolderData(el)));
|
data = JSON.parse(JSON.stringify(manager.getSharedFolderData(el)));
|
||||||
@ -3850,42 +3857,42 @@ define([
|
|||||||
}
|
}
|
||||||
if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
|
if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
|
||||||
|
|
||||||
if (data.href) {
|
var opts = {};
|
||||||
data.href = base + data.href;
|
opts.href = Hash.getRelativeHref(data.href || data.roHref);
|
||||||
}
|
|
||||||
if (data.roHref) {
|
|
||||||
data.roHref = base + data.roHref;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPath[0] === TEMPLATE) {
|
|
||||||
data.isTemplate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (manager.isSharedFolder(el)) {
|
if (manager.isSharedFolder(el)) {
|
||||||
var ro = folders[el] && folders[el].version >= 2;
|
var ro = folders[el] && folders[el].version >= 2;
|
||||||
if (!ro) { delete data.roHref; }
|
if (!ro) { opts.noReadOnly = true; }
|
||||||
//data.noPassword = true;
|
}
|
||||||
//data.noEditPassword = true;
|
UIElements.getProperties(common, opts, cb);
|
||||||
data.noExpiration = true;
|
};
|
||||||
// this is here to allow users to check the channel id of a shared folder
|
APP.getAccess = function (el, cb) {
|
||||||
// we should remove it at some point
|
if (!manager.isFile(el) && !manager.isSharedFolder(el)) {
|
||||||
data.sharedFolder = true;
|
return void cb('NOT_FILE');
|
||||||
|
}
|
||||||
|
var data;
|
||||||
|
if (manager.isSharedFolder(el)) {
|
||||||
|
data = JSON.parse(JSON.stringify(manager.getSharedFolderData(el)));
|
||||||
|
} else {
|
||||||
|
data = JSON.parse(JSON.stringify(manager.getFileData(el)));
|
||||||
|
}
|
||||||
|
if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
|
||||||
|
|
||||||
|
var opts = {};
|
||||||
|
opts.href = Hash.getRelativeHref(data.href || data.roHref);
|
||||||
|
opts.channel = data.channel;
|
||||||
|
|
||||||
|
// Transfer ownership: templates are stored as templates for other users/teams
|
||||||
|
if (currentPath[0] === TEMPLATE) {
|
||||||
|
opts.isTemplate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((manager.isFile(el) && data.roHref) || manager.isSharedFolder(el)) { // Only for pads!
|
// Shared folders: no expiration date
|
||||||
sframeChan.query('Q_GET_PAD_METADATA', {
|
if (manager.isSharedFolder(el)) {
|
||||||
channel: data.channel
|
opts.noExpiration = true;
|
||||||
}, function (err, val) {
|
|
||||||
if (!err && !(val && val.error)) {
|
|
||||||
data.owners = val.owners;
|
|
||||||
data.expire = val.expire;
|
|
||||||
data.pending_owners = val.pending_owners;
|
|
||||||
}
|
|
||||||
UIElements.getProperties(common, data, cb);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
UIElements.getProperties(common, data, cb);
|
|
||||||
|
Access.getAccessModal(common, opts, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!APP.loggedIn) {
|
if (!APP.loggedIn) {
|
||||||
@ -4259,9 +4266,19 @@ define([
|
|||||||
// ANON_SHARED_FOLDER
|
// ANON_SHARED_FOLDER
|
||||||
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
|
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
|
||||||
}
|
}
|
||||||
getProperties(el, function (e, $prop) {
|
APP.getProperties(el, function (e) {
|
||||||
|
if (e) { return void logError(e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if ($this.hasClass("cp-app-drive-context-access")) {
|
||||||
|
if (paths.length !== 1) { return; }
|
||||||
|
el = manager.find(paths[0].path);
|
||||||
|
if (paths[0].path[0] === SHARED_FOLDER && APP.newSharedFolder) {
|
||||||
|
// ANON_SHARED_FOLDER
|
||||||
|
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
|
||||||
|
}
|
||||||
|
APP.getAccess(el, function (e) {
|
||||||
if (e) { return void logError(e); }
|
if (e) { return void logError(e); }
|
||||||
UI.openCustomModal($prop[0]);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if ($this.hasClass("cp-app-drive-context-hashtag")) {
|
else if ($this.hasClass("cp-app-drive-context-hashtag")) {
|
||||||
|
|||||||
1144
www/common/inner/access.js
Normal file
1144
www/common/inner/access.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -1479,6 +1479,8 @@ define([
|
|||||||
|
|
||||||
var $properties = common.createButton('properties', true);
|
var $properties = common.createButton('properties', true);
|
||||||
toolbar.$drawer.append($properties);
|
toolbar.$drawer.append($properties);
|
||||||
|
var $access = common.createButton('access', true);
|
||||||
|
toolbar.$drawer.append($access);
|
||||||
};
|
};
|
||||||
|
|
||||||
config.onReady = function (info) {
|
config.onReady = function (info) {
|
||||||
|
|||||||
@ -1628,10 +1628,11 @@ define([
|
|||||||
// data.send === true ==> send the request
|
// data.send === true ==> send the request
|
||||||
Store.requestPadAccess = function (clientId, data, cb) {
|
Store.requestPadAccess = function (clientId, data, cb) {
|
||||||
var owner = data.owner;
|
var owner = data.owner;
|
||||||
var owners = data.owners;
|
|
||||||
|
|
||||||
// If the owner was not is the pad metadata, check if it is a friend.
|
// If the owner was not is the pad metadata, check if it is a friend.
|
||||||
// We'll contact the first owner for whom we know the mailbox
|
// We'll contact the first owner for whom we know the mailbox
|
||||||
|
/* // XXX check mailbox in our contacts is not compatible with the new "mute pad" feature
|
||||||
|
var owners = data.owners;
|
||||||
if (!owner && Array.isArray(owners)) {
|
if (!owner && Array.isArray(owners)) {
|
||||||
var friends = store.proxy.friends || {};
|
var friends = store.proxy.friends || {};
|
||||||
// If we have friends, check if an owner is one of them (with a mailbox)
|
// If we have friends, check if an owner is one of them (with a mailbox)
|
||||||
@ -1648,6 +1649,7 @@ define([
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// If send is true, send the request to the owner.
|
// If send is true, send the request to the owner.
|
||||||
if (owner) {
|
if (owner) {
|
||||||
|
|||||||
@ -900,10 +900,11 @@ define([
|
|||||||
cb = cb || function () {};
|
cb = cb || function () {};
|
||||||
var sfId = Env.user.userObject.getSFIdFromHref(data.href);
|
var sfId = Env.user.userObject.getSFIdFromHref(data.href);
|
||||||
if (sfId) {
|
if (sfId) {
|
||||||
var sfData = Env.user.proxy[UserObject.SHARED_FOLDERS][sfId];
|
var sfData = getSharedFolderData(Env, sfId);
|
||||||
|
var sfValue = data.attr ? sfData[data.attr] : JSON.parse(JSON.stringify(sfData));
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
cb(null, {
|
cb(null, {
|
||||||
value: sfData[data.attr],
|
value: sfValue,
|
||||||
atime: 1
|
atime: 1
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -702,6 +702,8 @@ define([
|
|||||||
|
|
||||||
var $properties = common.createButton('properties', true);
|
var $properties = common.createButton('properties', true);
|
||||||
toolbar.$drawer.append($properties);
|
toolbar.$drawer.append($properties);
|
||||||
|
var $access = common.createButton('access', true);
|
||||||
|
toolbar.$drawer.append($access);
|
||||||
|
|
||||||
createFilePicker();
|
createFilePicker();
|
||||||
|
|
||||||
|
|||||||
@ -1250,22 +1250,24 @@ define([
|
|||||||
});
|
});
|
||||||
// REQUEST_ACCESS is used both to check IF we can contact an owner (send === false)
|
// REQUEST_ACCESS is used both to check IF we can contact an owner (send === false)
|
||||||
// AND also to send the request if we want (send === true)
|
// AND also to send the request if we want (send === true)
|
||||||
sframeChan.on('Q_REQUEST_ACCESS', function (send, cb) {
|
sframeChan.on('Q_REQUEST_ACCESS', function (data, cb) {
|
||||||
if (readOnly && hashes.editHash) {
|
if (readOnly && hashes.editHash) {
|
||||||
return void cb({error: 'ALREADYKNOWN'});
|
return void cb({error: 'ALREADYKNOWN'});
|
||||||
}
|
}
|
||||||
|
var send = data.send;
|
||||||
|
var metadata = data.metadata;
|
||||||
var owner, owners;
|
var owner, owners;
|
||||||
var crypto = Crypto.createEncryptor(secret.keys);
|
var _secret = secret;
|
||||||
|
if (metadata && metadata.roHref) {
|
||||||
|
var _parsed = Utils.Hash.parsePadUrl(metadata.roHref);
|
||||||
|
_secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password);
|
||||||
|
}
|
||||||
|
var crypto = Crypto.createEncryptor(_secret.keys);
|
||||||
nThen(function (waitFor) {
|
nThen(function (waitFor) {
|
||||||
// Try to get the owner's mailbox from the pad metadata first.
|
// Try to get the owner's mailbox from the pad metadata first.
|
||||||
// If it's is an older owned pad, check if the owner is a friend
|
// If it's is an older owned pad, check if the owner is a friend
|
||||||
// or an acquaintance (from async-store directly in requestAccess)
|
// or an acquaintance (from async-store directly in requestAccess)
|
||||||
Cryptpad.getPadMetadata({
|
var todo = function (obj) {
|
||||||
channel: secret.channel
|
|
||||||
}, waitFor(function (obj) {
|
|
||||||
obj = obj || {};
|
|
||||||
if (obj.error) { return; }
|
|
||||||
|
|
||||||
owners = obj.owners;
|
owners = obj.owners;
|
||||||
|
|
||||||
var mailbox;
|
var mailbox;
|
||||||
@ -1284,22 +1286,107 @@ define([
|
|||||||
owner = data;
|
owner = data;
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); }
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we already have metadata, use it, otherwise, try to get it
|
||||||
|
if (metadata) { return void todo(metadata); }
|
||||||
|
|
||||||
|
Cryptpad.getPadMetadata({
|
||||||
|
channel: _secret.channel
|
||||||
|
}, waitFor(function (obj) {
|
||||||
|
obj = obj || {};
|
||||||
|
if (obj.error) { return; }
|
||||||
|
todo(obj);
|
||||||
}));
|
}));
|
||||||
}).nThen(function () {
|
}).nThen(function () {
|
||||||
// If we are just checking (send === false) and there is a mailbox field, cb state true
|
// If we are just checking (send === false) and there is a mailbox field, cb state true
|
||||||
// If there is no mailbox, we'll have to check if an owner is a friend in the worker
|
// If there is no mailbox, we'll have to check if an owner is a friend in the worker
|
||||||
|
/* // XXX
|
||||||
if (owner && !send) {
|
if (owner && !send) {
|
||||||
return void cb({state: true});
|
return void cb({state: true});
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
if (!send) { return void cb({state: Boolean(owner)}); }
|
||||||
|
|
||||||
Cryptpad.padRpc.requestAccess({
|
Cryptpad.padRpc.requestAccess({
|
||||||
send: send,
|
send: send,
|
||||||
channel: secret.channel,
|
channel: _secret.channel,
|
||||||
owner: owner,
|
owner: owner,
|
||||||
owners: owners
|
owners: owners
|
||||||
}, cb);
|
}, cb);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add or remove our mailbox from the list if we're an owner
|
||||||
|
sframeChan.on('Q_UPDATE_MAILBOX', function (data, cb) {
|
||||||
|
var metadata = data.metadata;
|
||||||
|
var add = data.add;
|
||||||
|
var _secret = secret;
|
||||||
|
if (metadata && (metadata.href || metadata.roHref)) {
|
||||||
|
var _parsed = Utils.Hash.parsePadUrl(metadata.href || metadata.roHref);
|
||||||
|
_secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password);
|
||||||
|
}
|
||||||
|
var crypto = Crypto.createEncryptor(_secret.keys);
|
||||||
|
nThen(function (waitFor) {
|
||||||
|
// If we already have metadata, use it, otherwise, try to get it
|
||||||
|
if (metadata) { return; }
|
||||||
|
|
||||||
|
Cryptpad.getPadMetadata({
|
||||||
|
channel: secret.channel
|
||||||
|
}, waitFor(function (obj) {
|
||||||
|
obj = obj || {};
|
||||||
|
if (obj.error) {
|
||||||
|
waitFor.abort();
|
||||||
|
return void cb(obj);
|
||||||
|
}
|
||||||
|
metadata = obj;
|
||||||
|
}));
|
||||||
|
}).nThen(function () {
|
||||||
|
// Get and maybe migrate the existing mailbox object
|
||||||
|
var owners = metadata.owners;
|
||||||
|
if (!Array.isArray(owners) || owners.indexOf(edPublic) === -1) {
|
||||||
|
return void cb({ error: 'INSUFFICIENT_PERMISSIONS' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a mailbox
|
||||||
|
if (!add) {
|
||||||
|
// Old format: this is the mailbox of the first owner
|
||||||
|
if (typeof (metadata.mailbox) === "string" && metadata.mailbox) {
|
||||||
|
// Not our mailbox? abort
|
||||||
|
if (owners[0] !== edPublic) {
|
||||||
|
return void cb({ error: 'INSUFFICIENT_PERMISSIONS' });
|
||||||
|
}
|
||||||
|
// Remove it
|
||||||
|
return void Cryptpad.setPadMetadata({
|
||||||
|
channel: _secret.channel,
|
||||||
|
command: 'RM_MAILBOX',
|
||||||
|
value: []
|
||||||
|
}, cb);
|
||||||
|
} else if (metadata.mailbox) { // New format
|
||||||
|
return void Cryptpad.setPadMetadata({
|
||||||
|
channel: _secret.channel,
|
||||||
|
command: 'RM_MAILBOX',
|
||||||
|
value: [edPublic]
|
||||||
|
}, cb);
|
||||||
|
}
|
||||||
|
return void cb({
|
||||||
|
error: 'NO_MAILBOX'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Add a mailbox
|
||||||
|
var toAdd = {};
|
||||||
|
toAdd[edPublic] = crypto.encrypt(JSON.stringify({
|
||||||
|
notifications: notifications,
|
||||||
|
curvePublic: curvePublic
|
||||||
|
}));
|
||||||
|
Cryptpad.setPadMetadata({
|
||||||
|
channel: _secret.channel,
|
||||||
|
command: 'ADD_MAILBOX',
|
||||||
|
value: toAdd
|
||||||
|
}, cb);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
sframeChan.on('EV_BURN_PAD', function (channel) {
|
sframeChan.on('EV_BURN_PAD', function (channel) {
|
||||||
if (!burnAfterReading) { return; }
|
if (!burnAfterReading) { return; }
|
||||||
Cryptpad.burnPad({
|
Cryptpad.burnPad({
|
||||||
|
|||||||
@ -574,6 +574,7 @@ MessengerUI, Messages) {
|
|||||||
return $shareBlock;
|
return $shareBlock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
var createRequest = function (toolbar, config) {
|
var createRequest = function (toolbar, config) {
|
||||||
if (!config.metadataMgr) {
|
if (!config.metadataMgr) {
|
||||||
throw new Error("You must provide a `metadataMgr` to display the request access button");
|
throw new Error("You must provide a `metadataMgr` to display the request access button");
|
||||||
@ -590,13 +591,13 @@ MessengerUI, Messages) {
|
|||||||
// If we have access to the owner's mailbox, display the button and enable it
|
// If we have access to the owner's mailbox, display the button and enable it
|
||||||
// false => check if we can contact the owner
|
// false => check if we can contact the owner
|
||||||
// true ==> send the request
|
// true ==> send the request
|
||||||
Common.getSframeChannel().query('Q_REQUEST_ACCESS', false, function (err, obj) {
|
Common.getSframeChannel().query('Q_REQUEST_ACCESS', {send:false}, function (err, obj) {
|
||||||
if (obj && obj.state) {
|
if (obj && obj.state) {
|
||||||
var locked = false;
|
var locked = false;
|
||||||
$requestBlock.show().click(function () {
|
$requestBlock.show().click(function () {
|
||||||
if (locked) { return; }
|
if (locked) { return; }
|
||||||
locked = true;
|
locked = true;
|
||||||
Common.getSframeChannel().query('Q_REQUEST_ACCESS', true, function (err, obj) {
|
Common.getSframeChannel().query('Q_REQUEST_ACCESS', {send:true}, function (err, obj) {
|
||||||
if (obj && obj.state) {
|
if (obj && obj.state) {
|
||||||
UI.log(Messages.requestEdit_sent);
|
UI.log(Messages.requestEdit_sent);
|
||||||
$requestBlock.hide();
|
$requestBlock.hide();
|
||||||
@ -614,6 +615,7 @@ MessengerUI, Messages) {
|
|||||||
|
|
||||||
return $requestBlock;
|
return $requestBlock;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
var createTitle = function (toolbar, config) {
|
var createTitle = function (toolbar, config) {
|
||||||
var $titleContainer = $('<span>', {
|
var $titleContainer = $('<span>', {
|
||||||
@ -1226,7 +1228,7 @@ MessengerUI, Messages) {
|
|||||||
tb['fileshare'] = createFileShare;
|
tb['fileshare'] = createFileShare;
|
||||||
tb['title'] = createTitle;
|
tb['title'] = createTitle;
|
||||||
tb['pageTitle'] = createPageTitle;
|
tb['pageTitle'] = createPageTitle;
|
||||||
tb['request'] = createRequest;
|
//tb['request'] = createRequest;
|
||||||
tb['lag'] = $.noop;
|
tb['lag'] = $.noop;
|
||||||
tb['spinner'] = createSpinner;
|
tb['spinner'] = createSpinner;
|
||||||
tb['state'] = $.noop;
|
tb['state'] = $.noop;
|
||||||
|
|||||||
@ -132,6 +132,7 @@ define([
|
|||||||
title: Title.getTitleConfig(),
|
title: Title.getTitleConfig(),
|
||||||
});
|
});
|
||||||
toolbar.$rightside.append(common.createButton('forget', true));
|
toolbar.$rightside.append(common.createButton('forget', true));
|
||||||
|
toolbar.$rightside.append(common.createButton('access', true));
|
||||||
toolbar.$rightside.append(common.createButton('properties', true));
|
toolbar.$rightside.append(common.createButton('properties', true));
|
||||||
if (common.isLoggedIn()) {
|
if (common.isLoggedIn()) {
|
||||||
toolbar.$rightside.append(common.createButton('hashtag', true));
|
toolbar.$rightside.append(common.createButton('hashtag', true));
|
||||||
|
|||||||
@ -1171,6 +1171,8 @@ define([
|
|||||||
var $forgetPad = common.createButton('forget', true, {}, forgetCb);
|
var $forgetPad = common.createButton('forget', true, {}, forgetCb);
|
||||||
$rightside.append($forgetPad);
|
$rightside.append($forgetPad);
|
||||||
|
|
||||||
|
var $access = common.createButton('access', true);
|
||||||
|
$drawer.append($access);
|
||||||
var $properties = common.createButton('properties', true);
|
var $properties = common.createButton('properties', true);
|
||||||
$drawer.append($properties);
|
$drawer.append($properties);
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ define([
|
|||||||
'/common/hyperscript.js',
|
'/common/hyperscript.js',
|
||||||
'/customize/application_config.js',
|
'/customize/application_config.js',
|
||||||
'/common/messenger-ui.js',
|
'/common/messenger-ui.js',
|
||||||
'/common/invitation.js',
|
'/common/inner/invitation.js',
|
||||||
'/customize/messages.js',
|
'/customize/messages.js',
|
||||||
|
|
||||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user