Run prettier.

This commit is contained in:
2024-02-24 11:09:34 -05:00
parent 8e7e0ed490
commit d5267be38c
90 changed files with 5789 additions and 2265 deletions

View File

@ -28,23 +28,23 @@ function App() {
* TODOC
* @param {*} callback
*/
App.prototype.readOutput = function(callback) {
App.prototype.readOutput = function (callback) {
this._on_output = callback;
}
};
/**
* TODOC
* @param {*} api
* @returns
*/
App.prototype.makeFunction = function(api) {
App.prototype.makeFunction = function (api) {
let self = this;
let result = function() {
let result = function () {
let id = g_next_id++;
while (!id || g_calls[id]) {
id = g_next_id++;
}
let promise = new Promise(function(resolve, reject) {
let promise = new Promise(function (resolve, reject) {
g_calls[id] = {resolve: resolve, reject: reject};
});
let message = {
@ -58,16 +58,16 @@ App.prototype.makeFunction = function(api) {
};
Object.defineProperty(result, 'name', {value: api[0], writable: false});
return result;
}
};
/**
* TODOC
* @param {*} message
*/
App.prototype.send = function(message) {
App.prototype.send = function (message) {
if (this._send_queue) {
if (this._on_output) {
this._send_queue.forEach(x => this._on_output(x));
this._send_queue.forEach((x) => this._on_output(x));
this._send_queue = null;
} else if (message) {
this._send_queue.push(message);
@ -90,43 +90,48 @@ function socket(request, response, client) {
let credentials = auth.query(request.headers);
let refresh = auth.makeRefresh(credentials);
response.onClose = async function() {
response.onClose = async function () {
if (process && process.task) {
process.task.kill();
}
if (process) {
process.timeout = 0;
}
}
};
response.onMessage = async function(event) {
response.onMessage = async function (event) {
if (event.opCode == 0x1 || event.opCode == 0x2) {
let message;
try {
message = JSON.parse(event.data);
} catch (error) {
print("ERROR", error, event.data, event.data.length, event.opCode);
print('ERROR', error, event.data, event.data.length, event.opCode);
return;
}
if (message.action == "hello") {
if (message.action == 'hello') {
let packageOwner;
let packageName;
let blobId;
let match;
let parentApp;
if (match = /^\/([&%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(message.path)) {
if (
(match = /^\/([&%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(message.path))
) {
blobId = match[1];
} else if (match = /^\/\~([^\/]+)\/([^\/]+)\/$/.exec(message.path)) {
} else if ((match = /^\/\~([^\/]+)\/([^\/]+)\/$/.exec(message.path))) {
packageOwner = match[1];
packageName = match[2];
blobId = await new Database(packageOwner).get('path:' + packageName);
if (!blobId) {
response.send(JSON.stringify({
message: 'tfrpc',
method: "error",
params: [message.path + ' not found'],
id: -1,
}), 0x1);
response.send(
JSON.stringify({
message: 'tfrpc',
method: 'error',
params: [message.path + ' not found'],
id: -1,
}),
0x1
);
return;
}
if (packageOwner != 'core') {
@ -137,12 +142,15 @@ function socket(request, response, client) {
};
}
}
response.send(JSON.stringify({
action: "session",
credentials: credentials,
parentApp: parentApp,
id: blobId,
}), 0x1);
response.send(
JSON.stringify({
action: 'session',
credentials: credentials,
parentApp: parentApp,
id: blobId,
}),
0x1
);
options.api = message.api || [];
options.credentials = credentials;
@ -152,19 +160,26 @@ function socket(request, response, client) {
let sessionId = makeSessionId();
if (blobId) {
if (message.edit_only) {
response.send(JSON.stringify({action: 'ready', edit_only: true}), 0x1);
response.send(
JSON.stringify({action: 'ready', edit_only: true}),
0x1
);
} else {
process = await core.getSessionProcessBlob(blobId, sessionId, options);
process = await core.getSessionProcessBlob(
blobId,
sessionId,
options
);
}
}
if (process) {
process.app.readOutput(function(message) {
process.app.readOutput(function (message) {
response.send(JSON.stringify(message), 0x1);
});
process.app.send();
}
let ping = function() {
let ping = function () {
let now = Date.now();
let again = true;
if (now - process.lastActive < process.timeout) {
@ -177,14 +192,14 @@ function socket(request, response, client) {
again = false;
} else {
// Idle. Ping them.
response.send("", 0x9);
response.send('', 0x9);
process.lastPing = now;
}
if (again && process.timeout) {
setTimeout(ping, process.timeout);
}
}
};
if (process && process.timeout > 0) {
setTimeout(ping, process.timeout);
@ -224,11 +239,16 @@ function socket(request, response, client) {
if (process) {
process.lastActive = Date.now();
}
}
};
response.upgrade(100, refresh ? {
'Set-Cookie': `session=${refresh.token}; path=/; Max-Age=${refresh.interval}; Secure; SameSite=Strict`,
} : {});
response.upgrade(
100,
refresh
? {
'Set-Cookie': `session=${refresh.token}; path=/; Max-Age=${refresh.interval}; Secure; SameSite=Strict`,
}
: {}
);
}
export { socket, App };
export {socket, App};

View File

@ -1,15 +1,17 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<title>Tilde Friends Sign-in</title>
<link type="text/css" rel="stylesheet" href="/static/style.css">
<link type="image/png" rel="shortcut icon" href="/static/favicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link type="text/css" rel="stylesheet" href="/static/style.css" />
<link type="image/png" rel="shortcut icon" href="/static/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<h1 style="text-align: center">Tilde Friends Sign-in</h1>
<tf-auth id="auth"></tf-auth>
<script>window.litDisableBundleWarning = true;</script>
<script>
window.litDisableBundleWarning = true;
</script>
<script type="module">
import {LitElement, html} from '/lit/lit-all.min.js';
let g_data = $AUTH_DATA;

View File

@ -1,7 +1,7 @@
import * as core from './core.js';
import * as form from './form.js';
let gDatabase = new Database("auth");
let gDatabase = new Database('auth');
const kRefreshInterval = 1 * 7 * 24 * 60 * 60 * 1000;
@ -54,8 +54,20 @@ function makeJwt(payload) {
id = ssb.createIdentity(':auth');
}
const final_payload = b64url(base64Encode(JSON.stringify(Object.assign({}, payload, {exp: (new Date().valueOf()) + kRefreshInterval}))));
const jwt = [b64url(base64Encode(JSON.stringify({alg: 'HS256', typ: 'JWT'}))), final_payload, b64url(ssb.hmacsha256sign(final_payload, ':auth', id))].join('.');
const final_payload = b64url(
base64Encode(
JSON.stringify(
Object.assign({}, payload, {
exp: new Date().valueOf() + kRefreshInterval,
})
)
)
);
const jwt = [
b64url(base64Encode(JSON.stringify({alg: 'HS256', typ: 'JWT'}))),
final_payload,
b64url(ssb.hmacsha256sign(final_payload, ':auth', id)),
].join('.');
return jwt;
}
@ -77,7 +89,7 @@ function readSession(session) {
if (id?.length && ssb.hmacsha256verify(id[0], payload, signature)) {
const result = JSON.parse(utf8Decode(base64Decode(unb64url(payload))));
const now = new Date().valueOf()
const now = new Date().valueOf();
if (now < result.exp) {
print(`JWT valid for another ${(result.exp - now) / 1000} seconds.`);
@ -141,8 +153,8 @@ function makeAdministrator(name) {
if (!core.globalSettings.permissions[name]) {
core.globalSettings.permissions[name] = [];
}
if (core.globalSettings.permissions[name].indexOf("administration") == -1) {
core.globalSettings.permissions[name].push("administration");
if (core.globalSettings.permissions[name].indexOf('administration') == -1) {
core.globalSettings.permissions[name].push('administration');
}
core.setGlobalSettings(core.globalSettings);
@ -159,7 +171,7 @@ function getCookies(headers) {
if (headers.cookie) {
let parts = headers.cookie.split(/,|;/);
for (let i in parts) {
let equals = parts[i].indexOf("=");
let equals = parts[i].indexOf('=');
let name = parts[i].substring(0, equals).trim();
let value = parts[i].substring(equals + 1).trim();
cookies[name] = value;
@ -177,7 +189,17 @@ function getCookies(headers) {
function isNameValid(name) {
// TODO(tasiaiso): convert this into a regex
let c = name.charAt(0);
return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) && name.split().map(x => x >= ('a' && x <= 'z') || x >= ('A' && x <= 'Z') || x >= ('0' && x <= '9'));
return (
((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) &&
name
.split()
.map(
(x) =>
x >= ('a' && x <= 'z') ||
x >= ('A' && x <= 'Z') ||
x >= ('0' && x <= '9')
)
);
}
/**
@ -189,13 +211,19 @@ function isNameValid(name) {
function handler(request, response) {
// TODO(tasiaiso): split this function
let session = getCookies(request.headers).session;
if (request.uri == "/login") {
if (request.uri == '/login') {
let formData = form.decodeForm(request.query);
if (query(request.headers)?.permissions?.authenticated) {
if (formData.return) {
response.writeHead(303, {"Location": formData.return});
response.writeHead(303, {Location: formData.return});
} else {
response.writeHead(303, {"Location": (request.client.tls ? 'https://' : 'http://') + request.headers.host + '/', "Content-Length": "0"});
response.writeHead(303, {
Location:
(request.client.tls ? 'https://' : 'http://') +
request.headers.host +
'/',
'Content-Length': '0',
});
}
response.end();
return;
@ -204,22 +232,23 @@ function handler(request, response) {
let sessionIsNew = false;
let loginError;
if (request.method == "POST" || formData.submit) {
if (request.method == 'POST' || formData.submit) {
sessionIsNew = true;
formData = form.decodeForm(utf8Decode(request.body), formData);
if (formData.submit == "Login") {
let account = gDatabase.get("user:" + formData.name);
if (formData.submit == 'Login') {
let account = gDatabase.get('user:' + formData.name);
account = account ? JSON.parse(account) : account;
if (formData.register == '1') {
if (!account &&
if (
!account &&
isNameValid(formData.name) &&
formData.password == formData.confirm) {
formData.password == formData.confirm
) {
let users = new Set();
let users_original = gDatabase.get('users');
try {
users = new Set(JSON.parse(users_original));
} catch {
}
} catch {}
if (!users.has(formData.name)) {
users.add(formData.name);
}
@ -237,10 +266,12 @@ function handler(request, response) {
loginError = 'Error registering account.';
}
} else if (formData.change == '1') {
if (account &&
if (
account &&
isNameValid(formData.name) &&
formData.new_password == formData.confirm &&
verifyPassword(formData.password, account.password)) {
verifyPassword(formData.password, account.password)
) {
session = makeJwt({name: formData.name});
account = {password: hashPassword(formData.new_password)};
gDatabase.set('user:' + formData.name, JSON.stringify(account));
@ -248,9 +279,11 @@ function handler(request, response) {
loginError = 'Error changing password.';
}
} else {
if (account &&
if (
account &&
account.password &&
verifyPassword(formData.password, account.password)) {
verifyPassword(formData.password, account.password)
) {
session = makeJwt({name: formData.name});
if (noAdministrator()) {
makeAdministrator(formData.name);
@ -268,32 +301,52 @@ function handler(request, response) {
let cookie = `session=${session}; path=/; Max-Age=${kRefreshInterval}; ${request.client.tls ? 'Secure; ' : ''}SameSite=Strict; HttpOnly`;
let entry = readSession(session);
if (entry && formData.return) {
response.writeHead(303, {"Location": formData.return, "Set-Cookie": cookie});
response.writeHead(303, {
Location: formData.return,
'Set-Cookie': cookie,
});
response.end();
} else {
File.readFile("core/auth.html").then(function(data) {
let html = utf8Decode(data);
let auth_data = {
session_is_new: sessionIsNew,
name: entry?.name,
error: loginError,
code_of_conduct: core.globalSettings.code_of_conduct,
have_administrator: !noAdministrator(),
};
html = utf8Encode(html.replace('$AUTH_DATA', JSON.stringify(auth_data)));
response.writeHead(200, {"Content-Type": "text/html; charset=utf-8", "Set-Cookie": cookie, "Content-Length": html.length});
response.end(html);
}).catch(function(error) {
response.writeHead(404, {"Content-Type": "text/plain; charset=utf-8", "Connection": "close"});
response.end("404 File not found");
});
File.readFile('core/auth.html')
.then(function (data) {
let html = utf8Decode(data);
let auth_data = {
session_is_new: sessionIsNew,
name: entry?.name,
error: loginError,
code_of_conduct: core.globalSettings.code_of_conduct,
have_administrator: !noAdministrator(),
};
html = utf8Encode(
html.replace('$AUTH_DATA', JSON.stringify(auth_data))
);
response.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
'Set-Cookie': cookie,
'Content-Length': html.length,
});
response.end(html);
})
.catch(function (error) {
response.writeHead(404, {
'Content-Type': 'text/plain; charset=utf-8',
Connection: 'close',
});
response.end('404 File not found');
});
}
} else if (request.uri == "/login/logout") {
response.writeHead(303, {"Set-Cookie": `session=; path=/; ${request.client.tls ? 'Secure; ' : ''}SameSite=Strict; expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly`, "Location": "/login" + (request.query ? "?" + request.query : "")});
} else if (request.uri == '/login/logout') {
response.writeHead(303, {
'Set-Cookie': `session=; path=/; ${request.client.tls ? 'Secure; ' : ''}SameSite=Strict; expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly`,
Location: '/login' + (request.query ? '?' + request.query : ''),
});
response.end();
} else {
response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8", "Connection": "close"});
response.end("Hello, " + request.client.peerName + ".");
response.writeHead(200, {
'Content-Type': 'text/plain; charset=utf-8',
Connection: 'close',
});
response.end('Hello, ' + request.client.peerName + '.');
}
}
@ -307,7 +360,7 @@ function getPermissions(session) {
let entry = readSession(session);
if (entry) {
permissions = getPermissionsForUser(entry.name);
permissions.authenticated = entry.name !== "guest";
permissions.authenticated = entry.name !== 'guest';
}
return permissions || {};
}
@ -319,7 +372,11 @@ function getPermissions(session) {
*/
function getPermissionsForUser(userName) {
let permissions = {};
if (core.globalSettings && core.globalSettings.permissions && core.globalSettings.permissions[userName]) {
if (
core.globalSettings &&
core.globalSettings.permissions &&
core.globalSettings.permissions[userName]
) {
for (let i in core.globalSettings.permissions[userName]) {
permissions[core.globalSettings.permissions[userName][i]] = true;
}
@ -336,10 +393,12 @@ function query(headers) {
let session = getCookies(headers).session;
let entry;
let autologin = tildefriends.args.autologin;
if (entry = autologin ? {name: autologin} : readSession(session)) {
if ((entry = autologin ? {name: autologin} : readSession(session))) {
return {
session: entry,
permissions: autologin ? getPermissionsForUser(autologin) : getPermissions(session),
permissions: autologin
? getPermissionsForUser(autologin)
: getPermissions(session),
};
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,12 +4,12 @@
* @returns
*/
function decode(encoded) {
let result = "";
let result = '';
for (let i = 0; i < encoded.length; i++) {
let c = encoded[i];
if (c == "+") {
result += " ";
} else if (c == "%") {
if (c == '+') {
result += ' ';
} else if (c == '%') {
result += String.fromCharCode(parseInt(encoded.slice(i + 1, i + 3), 16));
i += 2;
} else {
@ -41,4 +41,4 @@ function decodeForm(encoded, initial) {
return result;
}
export { decodeForm };
export {decodeForm};

View File

@ -6,12 +6,12 @@
*/
function parseUrl(url) {
// XXX: Hack.
let match = url.match(new RegExp("(\\w+)://([^/:]+)(?::(\\d+))?(.*)"));
let match = url.match(new RegExp('(\\w+)://([^/:]+)(?::(\\d+))?(.*)'));
return {
protocol: match[1],
host: match[2],
path: match[4],
port: match[3] ? parseInt(match[3]) : match[1] == "http" ? 80 : 443,
port: match[3] ? parseInt(match[3]) : match[1] == 'http' ? 80 : 443,
};
}
@ -32,7 +32,7 @@ function parseResponse(data) {
} else if (!firstLine) {
firstLine = line;
} else {
let colon = line.indexOf(":");
let colon = line.indexOf(':');
headers[line.substring(colon)] = line.substring(colon + 1);
}
}
@ -48,57 +48,66 @@ function parseResponse(data) {
*/
export function fetch(url, options, allowed_hosts) {
let parsed = parseUrl(url);
return new Promise(function(resolve, reject) {
return new Promise(function (resolve, reject) {
if ((allowed_hosts ?? []).indexOf(parsed.host) == -1) {
throw new Error(`fetch() request to host ${parsed.host} is not allowed.`);
}
let socket = new Socket();
let buffer = new Uint8Array(0);
return socket.connect(parsed.host, parsed.port).then(function() {
socket.read(function(data) {
if (data && data.length) {
let newBuffer = new Uint8Array(buffer.length + data.length);
newBuffer.set(buffer, 0);
newBuffer.set(data, buffer.length);
buffer = newBuffer;
} else {
let result = parseHttpResponse(buffer);
if (!result) {
reject(new Exception('Parse failed.'));
}
if (typeof result == 'number') {
if (result == -2) {
reject('Incomplete request.');
} else {
reject('Bad request.');
}
} else if (typeof result == 'object') {
resolve({
body: buffer.slice(result.bytes_parsed),
status: result.status,
message: result.message,
headers: result.headers,
});
return socket
.connect(parsed.host, parsed.port)
.then(function () {
socket.read(function (data) {
if (data && data.length) {
let newBuffer = new Uint8Array(buffer.length + data.length);
newBuffer.set(buffer, 0);
newBuffer.set(data, buffer.length);
buffer = newBuffer;
} else {
reject(new Exception('Unexpected parse result.'));
let result = parseHttpResponse(buffer);
if (!result) {
reject(new Exception('Parse failed.'));
}
if (typeof result == 'number') {
if (result == -2) {
reject('Incomplete request.');
} else {
reject('Bad request.');
}
} else if (typeof result == 'object') {
resolve({
body: buffer.slice(result.bytes_parsed),
status: result.status,
message: result.message,
headers: result.headers,
});
} else {
reject(new Exception('Unexpected parse result.'));
}
resolve(parseResponse(utf8Decode(buffer)));
}
resolve(parseResponse(utf8Decode(buffer)));
}
});
});
if (parsed.port == 443) {
return socket.startTls();
}
}).then(function() {
let body = typeof options?.body == 'string' ? utf8Encode(options.body) : (options.body || new Uint8Array(0));
let headers = utf8Encode(`${options?.method ?? 'GET'} ${parsed.path} HTTP/1.0\r\nHost: ${parsed.host}\r\nConnection: close\r\nContent-Length: ${body.length}\r\n\r\n`);
let fullRequest = new Uint8Array(headers.length + body.length);
fullRequest.set(headers, 0);
fullRequest.set(body, headers.length);
socket.write(fullRequest);
}).catch(function(error) {
reject(error);
});
if (parsed.port == 443) {
return socket.startTls();
}
})
.then(function () {
let body =
typeof options?.body == 'string'
? utf8Encode(options.body)
: options.body || new Uint8Array(0);
let headers = utf8Encode(
`${options?.method ?? 'GET'} ${parsed.path} HTTP/1.0\r\nHost: ${parsed.host}\r\nConnection: close\r\nContent-Length: ${body.length}\r\n\r\n`
);
let fullRequest = new Uint8Array(headers.length + body.length);
fullRequest.set(headers, 0);
fullRequest.set(body, headers.length);
socket.write(fullRequest);
})
.catch(function (error) {
reject(error);
});
});
}

View File

@ -102,22 +102,54 @@ a:active {
}
/* Solarized Color Scheme Colors */
.base03 { color: #002b36; }
.base02 { color: #073642; }
.base01 { color: #586e75; }
.base00 { color: #657b83; }
.base0 { color: #839496; }
.base1 { color: #93a1a1; }
.base2 { color: #eee8d5; }
.base3 { color: #fdf6e3; }
.yellow { color: #b58900; }
.orange { color: #cb4b16; }
.red { color: #dc322f; }
.magenta { color: #d33682; }
.violet { color: #6c71c4; }
.blue { color: #268bd2; }
.cyan { color: #2aa198; }
.green { color: #859900; }
.base03 {
color: #002b36;
}
.base02 {
color: #073642;
}
.base01 {
color: #586e75;
}
.base00 {
color: #657b83;
}
.base0 {
color: #839496;
}
.base1 {
color: #93a1a1;
}
.base2 {
color: #eee8d5;
}
.base3 {
color: #fdf6e3;
}
.yellow {
color: #b58900;
}
.orange {
color: #cb4b16;
}
.red {
color: #dc322f;
}
.magenta {
color: #d33682;
}
.violet {
color: #6c71c4;
}
.blue {
color: #268bd2;
}
.cyan {
color: #2aa198;
}
.green {
color: #859900;
}
.permissions {
position: absolute;

View File

@ -8,7 +8,11 @@ let g_calls = {};
* @returns
*/
function get_is_browser() {
try { return window !== undefined && console !== undefined; } catch { return false; }
try {
return window !== undefined && console !== undefined;
} catch {
return false;
}
}
if (k_is_browser) {
@ -23,21 +27,31 @@ if (k_is_browser) {
* @returns
*/
function make_rpc(target, prop, receiver) {
return function() {
return function () {
let id = g_next_id++;
while (!id || g_calls[id] !== undefined) {
id = g_next_id++;
}
let promise = new Promise(function(resolve, reject) {
let promise = new Promise(function (resolve, reject) {
g_calls[id] = {resolve: resolve, reject: reject};
});
if (k_is_browser) {
window.parent.postMessage({message: 'tfrpc', method: prop, params: [...arguments], id: id}, '*');
window.parent.postMessage(
{message: 'tfrpc', method: prop, params: [...arguments], id: id},
'*'
);
return promise;
} else {
return app.postMessage({message: 'tfrpc', method: prop, params: [...arguments], id: id}).then(x => promise);
return app
.postMessage({
message: 'tfrpc',
method: prop,
params: [...arguments],
id: id,
})
.then((x) => promise);
}
}
};
}
/**
@ -63,16 +77,22 @@ function call_rpc(message) {
let method = g_api[message.method];
if (method) {
try {
Promise.resolve(method(...message.params)).then(function(result) {
send({message: 'tfrpc', id: id, result: result});
}).catch(function(error) {
send({message: 'tfrpc', id: id, error: error});
});
Promise.resolve(method(...message.params))
.then(function (result) {
send({message: 'tfrpc', id: id, result: result});
})
.catch(function (error) {
send({message: 'tfrpc', id: id, error: error});
});
} catch (error) {
send({message: 'tfrpc', id: id, error: error});
}
} else {
send({message: 'tfrpc', id: id, error: `Method '${message.method}' not found.`});
send({
message: 'tfrpc',
id: id,
error: `Method '${message.method}' not found.`,
});
}
} else if (message.error !== undefined) {
if (g_calls[id]) {
@ -93,11 +113,11 @@ function call_rpc(message) {
}
if (k_is_browser) {
window.addEventListener('message', function(event) {
window.addEventListener('message', function (event) {
call_rpc(event.data);
});
} else {
core.register('message', function(message) {
core.register('message', function (message) {
call_rpc(message?.message);
});
}