2019-10-10 10:28:44 +00:00
|
|
|
import * as WebSocket from "ws";
|
|
|
|
import { Server, IncomingMessage } from "http";
|
2019-09-18 19:54:28 +00:00
|
|
|
import { DatabaseManager } from "./database/database";
|
|
|
|
import Logging from "@hibas123/logging";
|
2019-09-19 14:24:35 +00:00
|
|
|
import Query from "./database/query";
|
|
|
|
import Session from "./database/session";
|
2019-10-01 16:24:26 +00:00
|
|
|
import shortid = require("shortid");
|
2019-09-18 19:54:28 +00:00
|
|
|
|
2019-10-10 10:28:44 +00:00
|
|
|
import * as JWT from "jsonwebtoken";
|
|
|
|
|
|
|
|
async function verifyJWT(token: string, publicKey: string) {
|
|
|
|
return new Promise<any | undefined>((yes) => {
|
|
|
|
JWT.verify(token, publicKey, (err, decoded) => {
|
|
|
|
if (err)
|
|
|
|
yes(undefined);
|
|
|
|
else
|
|
|
|
yes(decoded);
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
import { URLSearchParams } from "url";
|
|
|
|
|
2019-09-18 19:54:28 +00:00
|
|
|
type QueryTypes = "get" | "set" | "push" | "subscribe" | "unsubscribe";
|
|
|
|
|
|
|
|
export class ConnectionManager {
|
2019-10-10 10:28:44 +00:00
|
|
|
static server: WebSocket.Server;
|
2019-09-19 14:24:35 +00:00
|
|
|
|
2019-09-18 19:54:28 +00:00
|
|
|
static bind(server: Server) {
|
2019-10-10 10:28:44 +00:00
|
|
|
this.server = new WebSocket.Server({ server });
|
2019-09-18 19:54:28 +00:00
|
|
|
this.server.on("connection", this.onConnection.bind(this));
|
|
|
|
}
|
|
|
|
|
2019-10-10 10:28:44 +00:00
|
|
|
private static async onConnection(socket: WebSocket, req: IncomingMessage) {
|
|
|
|
Logging.log("New Connection:");
|
|
|
|
const sendError = (msg: string) => socket.send(JSON.stringify({ ns: "error_msg", args: [msg] }));
|
|
|
|
|
|
|
|
|
2019-09-19 14:24:35 +00:00
|
|
|
const session = new Session();
|
|
|
|
|
2019-10-10 10:28:44 +00:00
|
|
|
let query = new URLSearchParams(req.url.split("?").pop());
|
|
|
|
|
|
|
|
const database = query.get("database");
|
|
|
|
const db = DatabaseManager.getDatabase(database);
|
|
|
|
if (!db) {
|
|
|
|
sendError("Invalid Database!");
|
|
|
|
socket.close();
|
|
|
|
return;
|
2019-09-18 19:54:28 +00:00
|
|
|
}
|
|
|
|
|
2019-10-10 10:28:44 +00:00
|
|
|
const accesskey = query.get("accesskey");
|
|
|
|
if (db.accesskey) {
|
|
|
|
if (!accesskey || accesskey !== db.accesskey) {
|
|
|
|
sendError("Unauthorized!");
|
|
|
|
socket.close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2019-09-18 19:54:28 +00:00
|
|
|
|
2019-10-10 10:28:44 +00:00
|
|
|
const authkey = query.get("authkey");
|
|
|
|
if (authkey && db.publickey) {
|
|
|
|
let res = await verifyJWT(authkey, db.publickey);
|
|
|
|
if (!res || !res.uid) {
|
|
|
|
sendError("Invalid JWT");
|
|
|
|
socket.close();
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
session.uid = res.uid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const rootkey = query.get("rootkey");
|
|
|
|
if (rootkey && db.rootkey) {
|
|
|
|
if (rootkey === db.rootkey) {
|
|
|
|
session.root = true;
|
|
|
|
Logging.warning(`Somebody logged into ${database} via rootkey`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const stored = new Map<string, Query>();
|
|
|
|
const answer = (id: string, data: any, err: boolean = false) => {
|
|
|
|
socket.send(JSON.stringify({ ns: "message", args: [id, err, data] }));
|
|
|
|
}
|
|
|
|
|
|
|
|
const handler = new Map<string, ((...args: any[]) => void)>();
|
|
|
|
handler.set("query", async (id: string, type: QueryTypes, path: string[], data: any) => {
|
|
|
|
Logging.debug(`Request with id '${id}' from type '${type}' and path '${path}' with data`, data)
|
2019-09-18 19:54:28 +00:00
|
|
|
try {
|
2019-09-19 14:24:35 +00:00
|
|
|
const perms = db.rules.hasPermission(path, session);
|
|
|
|
const noperm = new Error("No permisison!");
|
2019-09-18 19:54:28 +00:00
|
|
|
if (!db)
|
|
|
|
answer(id, "Database not found!", true);
|
|
|
|
else {
|
2019-09-19 14:24:35 +00:00
|
|
|
const query = stored.get(id) || db.getQuery(path);
|
2019-09-18 19:54:28 +00:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case "get":
|
2019-09-19 14:24:35 +00:00
|
|
|
if (!perms.read)
|
|
|
|
throw noperm;
|
2019-09-18 19:54:28 +00:00
|
|
|
answer(id, await query.get());
|
2019-09-19 14:24:35 +00:00
|
|
|
return;
|
2019-09-18 19:54:28 +00:00
|
|
|
case "set":
|
2019-09-19 14:24:35 +00:00
|
|
|
if (!perms.write)
|
|
|
|
throw noperm;
|
2019-09-18 19:54:28 +00:00
|
|
|
answer(id, await query.set(data));
|
2019-09-19 14:24:35 +00:00
|
|
|
return;
|
2019-09-18 19:54:28 +00:00
|
|
|
case "push":
|
2019-09-19 14:24:35 +00:00
|
|
|
if (!perms.write)
|
|
|
|
throw noperm;
|
2019-09-18 19:54:28 +00:00
|
|
|
answer(id, await query.push(data));
|
2019-09-19 14:24:35 +00:00
|
|
|
return;
|
2019-09-18 19:54:28 +00:00
|
|
|
case "subscribe":
|
2019-09-19 14:24:35 +00:00
|
|
|
if (!perms.read)
|
|
|
|
throw noperm;
|
2019-10-01 16:24:26 +00:00
|
|
|
|
|
|
|
let subscriptionID = shortid.generate();
|
|
|
|
|
2019-09-19 14:24:35 +00:00
|
|
|
query.subscribe(data, (data) => {
|
2019-10-10 10:28:44 +00:00
|
|
|
socket.send(JSON.stringify({ ns: "event", args: [subscriptionID, data] }));
|
2019-09-19 14:24:35 +00:00
|
|
|
});
|
|
|
|
stored.set(id, query);
|
2019-10-01 16:24:26 +00:00
|
|
|
answer(id, subscriptionID);
|
2019-09-19 14:24:35 +00:00
|
|
|
return;
|
2019-09-18 19:54:28 +00:00
|
|
|
case "unsubscribe":
|
2019-09-19 14:24:35 +00:00
|
|
|
query.unsubscribe();
|
|
|
|
stored.delete(id);
|
2019-10-01 16:24:26 +00:00
|
|
|
answer(id, true);
|
2019-09-19 14:24:35 +00:00
|
|
|
return;
|
2019-09-18 19:54:28 +00:00
|
|
|
}
|
|
|
|
|
2019-09-19 14:24:35 +00:00
|
|
|
answer(id, "Invalid request!", true);
|
2019-09-18 19:54:28 +00:00
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
Logging.error(err);
|
|
|
|
answer(id, err.message, true);
|
|
|
|
}
|
2019-10-10 10:28:44 +00:00
|
|
|
})
|
2019-09-18 19:54:28 +00:00
|
|
|
|
2019-10-10 10:28:44 +00:00
|
|
|
socket.on("message", (rawData: string) => {
|
|
|
|
let data: { ns: string, args: any[] } = JSON.parse(rawData);
|
|
|
|
let h = handler.get(data.ns);
|
|
|
|
if (h) {
|
|
|
|
h(...data.args);
|
|
|
|
}
|
2019-09-18 19:54:28 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
socket.on("disconnect", () => {
|
2019-10-10 10:28:44 +00:00
|
|
|
stored.forEach(query => query.unsubscribe());
|
2019-09-19 14:24:35 +00:00
|
|
|
stored.clear();
|
|
|
|
socket.removeAllListeners();
|
2019-09-18 19:54:28 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|