mirror of
https://github.com/jech/galene.git
synced 2024-12-22 23:35:46 +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 {boolean} privileged
|
||||
* @param {string} kind
|
||||
* @param {string} error
|
||||
* @param {any} message
|
||||
*/
|
||||
function gotUserMessage(id, dest, username, time, privileged, kind, message) {
|
||||
function gotUserMessage(id, dest, username, time, privileged, kind, error, message) {
|
||||
switch(kind) {
|
||||
case 'kicked':
|
||||
case 'error':
|
||||
|
@ -2600,12 +2601,66 @@ function gotUserMessage(id, dest, username, time, privileged, kind, message) {
|
|||
}
|
||||
clearChat();
|
||||
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:
|
||||
console.warn(`Got unknown user message ${kind}`);
|
||||
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;
|
||||
|
||||
|
@ -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() {
|
||||
for(let id in serverConnection.up)
|
||||
serverConnection.up[id].restartIce();
|
||||
|
|
|
@ -205,7 +205,7 @@ function ServerConnection() {
|
|||
* 'id' is non-null, 'privileged' indicates whether the message was
|
||||
* 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;
|
||||
/**
|
||||
|
@ -453,7 +453,7 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
else if(sc.onusermessage)
|
||||
sc.onusermessage.call(
|
||||
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;
|
||||
case 'ping':
|
||||
|
|
Loading…
Reference in a new issue