mirror of
https://github.com/jech/galene.git
synced 2024-11-26 02:25:58 +01:00
Implement user interface for token management.
This commit is contained in:
parent
8aa95f5e22
commit
8c1510023f
2 changed files with 184 additions and 3 deletions
183
static/galene.js
183
static/galene.js
|
@ -2569,9 +2569,10 @@ function gotFileTransferEvent(state, data) {
|
||||||
* @param {Date} time
|
* @param {Date} time
|
||||||
* @param {boolean} privileged
|
* @param {boolean} privileged
|
||||||
* @param {string} kind
|
* @param {string} kind
|
||||||
|
* @param {string} error
|
||||||
* @param {any} message
|
* @param {any} message
|
||||||
*/
|
*/
|
||||||
function gotUserMessage(id, dest, username, time, privileged, kind, message) {
|
function gotUserMessage(id, dest, username, time, privileged, kind, error, message) {
|
||||||
switch(kind) {
|
switch(kind) {
|
||||||
case 'kicked':
|
case 'kicked':
|
||||||
case 'error':
|
case 'error':
|
||||||
|
@ -2600,12 +2601,66 @@ function gotUserMessage(id, dest, username, time, privileged, kind, message) {
|
||||||
}
|
}
|
||||||
clearChat();
|
clearChat();
|
||||||
break;
|
break;
|
||||||
|
case 'token':
|
||||||
|
if(!privileged) {
|
||||||
|
console.error(`Got unprivileged message of kind ${kind}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(error) {
|
||||||
|
displayError(`Token operation failed: ${message}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(typeof message != 'object') {
|
||||||
|
displayError('Unexpected type for token');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let f = formatToken(message);
|
||||||
|
localMessage(f[0] + ': ' + f[1]);
|
||||||
|
break;
|
||||||
|
case 'tokenlist':
|
||||||
|
if(!privileged) {
|
||||||
|
console.error(`Got unprivileged message of kind ${kind}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(error) {
|
||||||
|
displayError(`Token operation failed: ${message}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let s = '';
|
||||||
|
for(let i = 0; i < message.length; i++) {
|
||||||
|
let f = formatToken(message[i]);
|
||||||
|
s = s + f[0] + ': ' + f[1] + "\n";
|
||||||
|
}
|
||||||
|
localMessage(s);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn(`Got unknown user message ${kind}`);
|
console.warn(`Got unknown user message ${kind}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} token
|
||||||
|
*/
|
||||||
|
function formatToken(token) {
|
||||||
|
let url = new URL(window.location.href);
|
||||||
|
let params = new URLSearchParams();
|
||||||
|
params.append('token', token.token);
|
||||||
|
url.search = params.toString();
|
||||||
|
let foruser = ''
|
||||||
|
if(token.username)
|
||||||
|
foruser = ` for user ${token.username}`;
|
||||||
|
/** @type{Date} */
|
||||||
|
let expires = null;
|
||||||
|
if(token.expires)
|
||||||
|
expires = new Date(token.expires);
|
||||||
|
return [
|
||||||
|
(expires && (expires >= new Date())) ?
|
||||||
|
`Invitation${foruser} valid until ${expires.toLocaleString()}` :
|
||||||
|
`Expired invitation${foruser}`,
|
||||||
|
url.toString(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
const urlRegexp = /https?:\/\/[-a-zA-Z0-9@:%/._\\+~#&()=?]+[-a-zA-Z0-9@:%/_\\+~#&()=]/g;
|
const urlRegexp = /https?:\/\/[-a-zA-Z0-9@:%/._\\+~#&()=?]+[-a-zA-Z0-9@:%/_\\+~#&()=]/g;
|
||||||
|
|
||||||
|
@ -2924,6 +2979,132 @@ commands.subgroups = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Object<string,number>}
|
||||||
|
*/
|
||||||
|
const units = {
|
||||||
|
s: 1000,
|
||||||
|
min: 60 * 1000,
|
||||||
|
h: 60 * 60 * 1000,
|
||||||
|
d: 24 * 60 * 60 * 1000,
|
||||||
|
mon: 31 * 24 * 60 * 60 * 1000,
|
||||||
|
yr: 365 * 24 * 60 * 60 * 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @returns {string|number}
|
||||||
|
*/
|
||||||
|
function parseExpiration(s) {
|
||||||
|
if(!s)
|
||||||
|
return units.d;
|
||||||
|
let re = /^([0-9]+)(s|min|h|d|yr)$/
|
||||||
|
let e = re.exec(s)
|
||||||
|
if(e) {
|
||||||
|
let unit = units[e[2]];
|
||||||
|
if(!unit)
|
||||||
|
throw new Error(`Couldn't find unit ${e[2]}`);
|
||||||
|
return parseInt(e[1]) * unit;
|
||||||
|
}
|
||||||
|
let d = new Date(s);
|
||||||
|
if(d.toString() === 'Invalid Date')
|
||||||
|
throw new Error("Couldn't parse expiration date");
|
||||||
|
return d.toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function protocol2Predicate() {
|
||||||
|
if(serverConnection.version === "1")
|
||||||
|
return "This server is too old";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeTokenPredicate() {
|
||||||
|
return protocol2Predicate() ||
|
||||||
|
(serverConnection.permissions.indexOf('token') < 0 ?
|
||||||
|
"You don't have permission to create tokens" : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function editTokenPredicate() {
|
||||||
|
return protocol2Predicate() ||
|
||||||
|
(serverConnection.permissions.indexOf('token') < 0 ||
|
||||||
|
serverConnection.permissions.indexOf('op') < 0 ?
|
||||||
|
"You don't have permission to edit or list tokens" : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.invite = {
|
||||||
|
predicate: makeTokenPredicate,
|
||||||
|
description: "create an invitation link",
|
||||||
|
parameters: "[username] [expiration]",
|
||||||
|
f: (c, r) => {
|
||||||
|
let p = parseCommand(r);
|
||||||
|
let v = {
|
||||||
|
group: group,
|
||||||
|
};
|
||||||
|
if(p[0])
|
||||||
|
v.username = p[0];
|
||||||
|
if(p[1])
|
||||||
|
v.expires = parseExpiration(p[1]);
|
||||||
|
else
|
||||||
|
v.expires = units.d;
|
||||||
|
if(serverConnection.permissions.indexOf('present') >= 0)
|
||||||
|
v.permissions = ['present'];
|
||||||
|
else
|
||||||
|
v.permissions = [];
|
||||||
|
serverConnection.groupAction('maketoken', v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} t
|
||||||
|
*/
|
||||||
|
function parseToken(t) {
|
||||||
|
let m = /^https?:\/\/.*?token=([^?]+)/.exec(t);
|
||||||
|
if(m) {
|
||||||
|
return m[1];
|
||||||
|
} else if(!/^https?:\/\//.exec(t)) {
|
||||||
|
return t
|
||||||
|
} else {
|
||||||
|
throw new Error("Couldn't parse link");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.reinvite = {
|
||||||
|
predicate: editTokenPredicate,
|
||||||
|
description: "extend an invitation link",
|
||||||
|
parameters: "link [expiration]",
|
||||||
|
f: (c, r) => {
|
||||||
|
let p = parseCommand(r);
|
||||||
|
let v = {}
|
||||||
|
v.token = parseToken(p[0]);
|
||||||
|
if(p[1])
|
||||||
|
v.expires = parseExpiration(p[1]);
|
||||||
|
else
|
||||||
|
v.expires = units.d;
|
||||||
|
serverConnection.groupAction('edittoken', v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.revoke = {
|
||||||
|
predicate: editTokenPredicate,
|
||||||
|
description: "revoke an invitation link",
|
||||||
|
parameters: "link",
|
||||||
|
f: (c, r) => {
|
||||||
|
let token = parseToken(r);
|
||||||
|
serverConnection.groupAction('edittoken', {
|
||||||
|
token: token,
|
||||||
|
expires: -units.s,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.listtokens = {
|
||||||
|
predicate: editTokenPredicate,
|
||||||
|
description: "list invitation links",
|
||||||
|
f: (c, r) => {
|
||||||
|
serverConnection.groupAction('listtokens');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renegotiateStreams() {
|
function renegotiateStreams() {
|
||||||
for(let id in serverConnection.up)
|
for(let id in serverConnection.up)
|
||||||
serverConnection.up[id].restartIce();
|
serverConnection.up[id].restartIce();
|
||||||
|
|
|
@ -205,7 +205,7 @@ function ServerConnection() {
|
||||||
* 'id' is non-null, 'privileged' indicates whether the message was
|
* 'id' is non-null, 'privileged' indicates whether the message was
|
||||||
* sent by an operator.
|
* sent by an operator.
|
||||||
*
|
*
|
||||||
* @type {(this: ServerConnection, id: string, dest: string, username: string, time: Date, privileged: boolean, kind: string, message: unknown) => void}
|
* @type {(this: ServerConnection, id: string, dest: string, username: string, time: Date, privileged: boolean, kind: string, error: string, message: unknown) => void}
|
||||||
*/
|
*/
|
||||||
this.onusermessage = null;
|
this.onusermessage = null;
|
||||||
/**
|
/**
|
||||||
|
@ -453,7 +453,7 @@ ServerConnection.prototype.connect = async function(url) {
|
||||||
else if(sc.onusermessage)
|
else if(sc.onusermessage)
|
||||||
sc.onusermessage.call(
|
sc.onusermessage.call(
|
||||||
sc, m.source, m.dest, m.username, parseTime(m.time),
|
sc, m.source, m.dest, m.username, parseTime(m.time),
|
||||||
m.privileged, m.kind, m.value,
|
m.privileged, m.kind, m.error, m.value,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'ping':
|
case 'ping':
|
||||||
|
|
Loading…
Reference in a new issue