OSSecureFileWrapper/index.ts

241 lines
6.7 KiB
TypeScript
Raw Normal View History

2018-02-01 15:27:01 +00:00
import * as rsa from "node-rsa";
2018-02-25 11:20:30 +00:00
import "isomorphic-fetch";
import * as btb from "blob-to-buffer"
2018-02-01 15:27:01 +00:00
export interface File {
_id: string;
type: "binary" | "text";
name: string;
time: string;
preview: string;
version: string;
2018-06-17 12:56:49 +00:00
meta: string;
2018-02-01 15:27:01 +00:00
}
export interface History {
file: File;
history: File[];
}
export default class SecureFile {
private Server: string;
private Username: string;
private PrivateKey: string;
private jwt_enabled: boolean;
private JWT: string;
constructor(server: string, username: string, private_key: string, jwt = false) {
2018-02-01 15:27:01 +00:00
this.Server = server;
2018-02-25 11:24:52 +00:00
if (this.Server.endsWith("/")) {
this.Server += "api";
} else {
this.Server += "/api";
}
2018-02-01 15:27:01 +00:00
this.Username = username;
this.PrivateKey = private_key;
this.jwt_enabled = jwt;
2018-02-01 15:27:01 +00:00
}
private async getCode() {
2018-03-04 00:39:30 +00:00
var myHeaders = new Headers();
myHeaders.append('pragma', 'no-cache');
myHeaders.append('cache-control', 'no-cache');
var myInit = {
method: 'GET',
headers: myHeaders,
};
2018-03-10 17:33:47 +00:00
try {
var code_res = await fetch(this.Server + "/code?username=" + this.Username, myInit);
} catch (err) {
//TODO probably better fail check
throw new NoConnection();
}
2018-06-10 19:02:27 +00:00
if (code_res.status == 403) throw new Error("Unauthorized");
2018-03-02 11:23:25 +00:00
statusParser(code_res);
2018-02-01 15:27:01 +00:00
let code = (await code_res.json()).code;
let r = new rsa(this.PrivateKey, "pkcs1-pem");
return { code: code, signature: r.sign(code).toString("base64") };
}
private async getJWT() {
let res = await this.makeRequest("/jwt", "GET", {}, undefined, false);
statusParser(res);
let jwt = await res.text();
this.JWT = jwt;
}
public async makeRequest(endpoint: string, method: "POST" | "GET" | "PUT" | "DELETE", query: any, body?: Buffer, jwt = false) {
if (this.jwt_enabled && jwt) {
if (!this.JWT) await this.getJWT();
query.jwt = this.JWT;
} else {
let code = await this.getCode();
query.code = code.code;
query.signature = code.signature;
}
2018-02-01 15:27:01 +00:00
let query_str = "?";
let first = true;
for (let key in query) {
if (!first) query_str += "&";
query_str += encodeURIComponent(key) + "=" + encodeURIComponent(query[key]);
first = false;
}
2018-03-04 00:39:30 +00:00
var myHeaders = new Headers();
myHeaders.append('pragma', 'no-cache');
myHeaders.append('cache-control', 'no-cache');
2018-03-10 17:33:47 +00:00
try {
let res = await fetch(this.Server + endpoint + query_str, { method: method, body: body, headers: myHeaders });
if (res.status === 418) { // JWT invalid
this.JWT = undefined;
return this.makeRequest(endpoint, method, query, body, jwt);
}
2018-03-10 17:33:47 +00:00
return res;
} catch (err) {
2018-06-11 20:10:14 +00:00
if (err instanceof TypeError || err.errno === "ECONNREFUSED")
throw new NoConnection();
console.log(err);
throw err;
2018-03-10 17:33:47 +00:00
}
2018-02-01 15:27:01 +00:00
}
2018-02-25 10:39:01 +00:00
async test(): Promise<{ user: string, test: true }> {
let res = await this.makeRequest("/test", "GET", {}, undefined, this.jwt_enabled);
2018-02-25 10:39:01 +00:00
statusParser(res);
return await res.json();
}
2018-02-05 09:32:48 +00:00
async list(folder?: string): Promise<File[]> {
if (!folder) folder = "root";
let res = await this.makeRequest("/files", "GET", { folder: folder }, undefined, this.jwt_enabled)
2018-02-01 15:27:01 +00:00
statusParser(res);
return await res.json();
}
2018-06-17 12:56:49 +00:00
async create(name: string, data: Buffer, type: "text" | "binary", folder?: string, preview?: Buffer, meta?: string): Promise<File> {
2018-06-14 14:42:30 +00:00
let params: any = { type: type, name: name };
2018-02-18 13:03:23 +00:00
if (preview) {
params.preview = preview.toString("base64");
2018-02-18 13:03:23 +00:00
}
if (folder) {
params.folder = folder;
}
2018-06-17 12:56:49 +00:00
if (meta) {
params.meta = meta;
}
let res = await this.makeRequest("/files", "POST", params, data, this.jwt_enabled)
2018-02-01 15:27:01 +00:00
statusParser(res);
return res.json();
}
async get(id: string, version?: string): Promise<Buffer> {
let res: Response;
if (typeof version === "string") {
res = await this.makeRequest(`/files/${id}/history/${version}`, "GET", {}, undefined, this.jwt_enabled);
2018-02-01 15:27:01 +00:00
} else {
res = await this.makeRequest("/files/" + id, "GET", {}, undefined, this.jwt_enabled);
2018-02-01 15:27:01 +00:00
}
statusParser(res);
2018-02-25 11:20:30 +00:00
if ((<any>res).buffer) {
return (<any>res).buffer();
} else {
return new Promise<Buffer>(async (resolve, reject) => {
btb(await res.blob(), (err, buffer) => {
if (err) reject(err);
else resolve(buffer);
})
})
}
2018-02-01 15:27:01 +00:00
}
2018-06-17 12:56:49 +00:00
async update(id: string, data: Buffer, preview?: Buffer, meta?: string): Promise<File> {
2018-02-01 15:27:01 +00:00
let put: any = {};
if (preview) put.preview = preview.toString("base64");
2018-06-17 12:56:49 +00:00
if (meta) put.meta = meta;
let res = await this.makeRequest("/files/" + id, "PUT", put, data, this.jwt_enabled);
2018-02-01 15:27:01 +00:00
statusParser(res);
return res.json();
}
async delete(id: string): Promise<boolean> {
let res = await this.makeRequest("/files/" + id, "DELETE", {}, undefined, this.jwt_enabled);
2018-02-01 15:27:01 +00:00
statusParser(res);
return res.json();
}
async history(id: string): Promise<History> {
let res = await this.makeRequest(`/files/${id}/history`, "GET", {}, undefined, this.jwt_enabled);
2018-02-01 15:27:01 +00:00
statusParser(res);
return res.json();
}
}
2018-03-10 17:33:47 +00:00
export class NoConnection extends Error {
type: string;
constructor() {
super("No connection");
this.type = "noconnection"
}
}
2018-02-01 15:27:01 +00:00
export class Unauthorized extends Error {
2018-03-10 15:40:59 +00:00
type: string;
2018-02-01 15:27:01 +00:00
constructor() {
super("Not authorized");
2018-03-10 15:40:59 +00:00
this.type = "unauthorized"
2018-02-01 15:27:01 +00:00
}
}
export class NoPermission extends Error {
type: string;
constructor() {
super("No permission");
this.type = "nopermission"
}
}
export class InvalidJWT extends Error {
type: string;
constructor() {
super("Invalid JWT");
this.type = "invalidjwt"
}
}
2018-02-01 15:27:01 +00:00
export class NotFound extends Error {
2018-03-10 15:40:59 +00:00
type: string;
2018-02-01 15:27:01 +00:00
constructor() {
super("Not found");
2018-03-10 15:40:59 +00:00
this.type = "notfound"
2018-02-01 15:27:01 +00:00
}
}
export class BadRequest extends Error {
2018-03-10 15:40:59 +00:00
type: string;
2018-02-01 15:27:01 +00:00
constructor() {
super("Bad request");
2018-03-10 15:40:59 +00:00
this.type = "badrequest"
2018-02-01 15:27:01 +00:00
}
}
function statusParser(res: Response) {
if (res.status !== 200) {
switch (res.status) {
case 400:
throw new BadRequest();
case 404:
throw new NotFound();
case 403:
throw new NoPermission();
case 401:
2018-02-01 15:27:01 +00:00
throw new Unauthorized();
case 418:
throw new InvalidJWT();
2018-02-01 15:27:01 +00:00
default:
throw new Error(res.statusText);
}
}
}