diff --git a/.gitignore b/.gitignore index 3429ee7..18c3786 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ node_modules/ yarn.lock -*.js private.pem diff --git a/entry.js b/entry.js new file mode 100644 index 0000000..e69de29 diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..d158cdb --- /dev/null +++ b/index.d.ts @@ -0,0 +1,36 @@ +/// +export interface File { + _id: string; + type: "binary" | "text"; + name: string; + time: string; + preview: string; + version: string; +} +export interface History { + file: File; + history: File[]; +} +export default class SecureFile { + private Server; + private Username; + private PrivateKey; + constructor(server: string, username: string, private_key: string); + private getCode(); + private makeRequest(endpoint, method, query, body); + list(): Promise; + create(name: string, data: Buffer, type: "text" | "binary", preview?: Buffer): Promise; + get(id: string, version?: string): Promise; + update(id: string, data: Buffer, preview?: Buffer): Promise; + delete(id: string): Promise; + history(id: string): Promise; +} +export declare class Unauthorized extends Error { + constructor(); +} +export declare class NotFound extends Error { + constructor(); +} +export declare class BadRequest extends Error { + constructor(); +} diff --git a/index.js b/index.js new file mode 100644 index 0000000..1dc012a --- /dev/null +++ b/index.js @@ -0,0 +1,104 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const rsa = require("node-rsa"); +const node_fetch_1 = require("node-fetch"); +class SecureFile { + constructor(server, username, private_key) { + this.Server = server; + this.Username = username; + this.PrivateKey = private_key; + } + async getCode() { + let code_res = await node_fetch_1.default(this.Server + "/code?username=" + this.Username); + //ToDo check status Codes + let code = (await code_res.json()).code; + let r = new rsa(this.PrivateKey, "pkcs1-pem"); + return { code: code, signature: r.sign(code).toString("base64") }; + } + async makeRequest(endpoint, method, query, body) { + let code = await this.getCode(); + query.code = code.code; + query.signature = code.signature; + let query_str = "?"; + let first = true; + for (let key in query) { + if (!first) + query_str += "&"; + query_str += encodeURIComponent(key) + "=" + encodeURIComponent(query[key]); + first = false; + } + return await node_fetch_1.default(this.Server + endpoint + query_str, { method: method, body: body }); + } + async list() { + let res = await this.makeRequest("/files", "GET", {}, undefined); + statusParser(res); + return await res.json(); + } + async create(name, data, type, preview) { + let res = await this.makeRequest("/files", "POST", { type: type, name: name, preview: preview }, data); + statusParser(res); + return res.json(); + } + async get(id, version) { + let res; + if (typeof version === "string") { + res = await this.makeRequest(`/files/${id}/history/${version}`, "GET", {}, undefined); + } + else { + res = await this.makeRequest("/files/" + id, "GET", {}, undefined); + } + statusParser(res); + return res.buffer(); + } + async update(id, data, preview) { + let put = {}; + if (preview) + put.preview = preview; + let res = await this.makeRequest("/files/" + id, "PUT", put, data); + statusParser(res); + return res.json(); + } + async delete(id) { + let res = await this.makeRequest("/files/" + id, "DELETE", {}, undefined); + statusParser(res); + return res.json(); + } + async history(id) { + let res = await this.makeRequest(`/files/${id}/history`, "GET", {}, undefined); + statusParser(res); + return res.json(); + } +} +exports.default = SecureFile; +class Unauthorized extends Error { + constructor() { + super("Not authorized"); + } +} +exports.Unauthorized = Unauthorized; +class NotFound extends Error { + constructor() { + super("Not found"); + } +} +exports.NotFound = NotFound; +class BadRequest extends Error { + constructor() { + super("Bad request"); + } +} +exports.BadRequest = BadRequest; +function statusParser(res) { + if (res.status !== 200) { + switch (res.status) { + case 400: + throw new BadRequest(); + case 404: + throw new NotFound(); + case 403: + throw new Unauthorized(); + default: + throw new Error(res.statusText); + } + } +} diff --git a/package.json b/package.json index 285c18d..3b3d165 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,10 @@ { - "name": "SecureFileWrapper", - "version": "1.0.0", + "name": "secure-file-wrapper", + "version": "1.0.1", "main": "index.js", "author": "Fabian Stamm ", "license": "MIT", + "types": "index.d.ts", "scripts": { "build": "tsc", "test": "nodeunit test.js" diff --git a/test.d.ts b/test.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/test.js b/test.js new file mode 100644 index 0000000..0dae075 --- /dev/null +++ b/test.js @@ -0,0 +1,81 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const index_1 = require("./index"); +const fs_1 = require("fs"); +const buffer_1 = require("buffer"); +let sf; +const testname = "ouiavgbsop687463743"; +const testdata = new buffer_1.Buffer("Ich bin ein Test"); +const newTestData = new buffer_1.Buffer("neue test daten"); +const testprev = new buffer_1.Buffer("Ich bin..."); +let testid; +let testver; +let testver2; +module.exports = { + setUp: function (finished) { + let pk = fs_1.readFileSync("./private.pem"); + sf = new index_1.default("http://localhost:3005", "test", pk.toString("utf8")); + finished(); + }, + create: async function (test) { + test.expect(2); + let res = await sf.create(testname, testdata, "text", testprev); + test.ok(res); + test.ok(res._id); + testid = res._id; + testver = res.version; + test.done(); + }, + get: async function (test) { + test.expect(2); + let res = await sf.get(testid); + test.ok(res); + test.equal(res.toString(), testdata.toString()); + test.done(); + }, + list: async function (test) { + test.expect(4); + let res = await sf.list(); + console.log(res); + test.ok(res); + test.ok(Array.isArray(res), "Is from type Array"); + test.ok(res.length > 0, "Do elements exist?"); + res.forEach(e => { + if (e._id === testid) { + test.ok(true, "Element is in List"); + } + }); + test.done(); + }, + update: async function (test) { + test.expect(5); + let res = await sf.update(testid, newTestData); + test.ok(res); + test.ok(res._id); + test.notEqual(res.version, testver, "Is new version_id generated?"); + testver2 = res.version; + let res2 = await sf.get(testid); + test.ok(res2); + test.equal(res2.toString(), newTestData.toString(), "Is fetched data the updated?"); + test.done(); + }, + history: async function (test) { + test.expect(7); + let his = await sf.history(testid); + test.ok(his); + test.ok(his.file); + test.ok(his.history); + test.equal(his.history.length, 1); + test.equal(his.history[0].version, testver); + test.equal(his.file.version, testver2, "Is correct version"); + let arch = await sf.get(testid, testver); + test.equal(arch.toString(), testdata.toString(), "Is old version data correct"); + test.done(); + }, + delete: async function (test) { + test.expect(1); + let res = await sf.delete(testid); + test.ok(res); + test.done(); + } +}; diff --git a/tsconfig.json b/tsconfig.json index 6b23ea0..dbeee88 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,13 @@ { "compilerOptions": { /* Basic Options */ - "watch": true, "target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */