From: ken Date: Mon, 4 Sep 2017 04:15:13 +0000 (+0000) Subject: bug fixes X-Git-Url: https://git.kengrimes.com/?p=henge%2Fkiak.git;a=commitdiff_plain;h=4e6773ca79a062ba35c0659e32951e11a3db9cf6 bug fixes --- diff --git a/bootstrapp.html b/bootstrapp.html index 277b632..9fa3cd1 100644 --- a/bootstrapp.html +++ b/bootstrapp.html @@ -4,13 +4,27 @@ Strapp.io - + - - + + + + + + + +
- + diff --git a/src/strapp.js b/src/strapp.js index c37ba16..a179716 100644 --- a/src/strapp.js +++ b/src/strapp.js @@ -16,6 +16,7 @@ const strapp = (() => { .then((response) => resolve(response)) .catch((err) => reject(err)) }), + fs: fs, on: (eventName, fn) => { if (!events[eventName]) events[eventName] = [] diff --git a/src/strappFileSystem.js b/src/strappFileSystem.js index 8101311..bc2b0b2 100644 --- a/src/strappFileSystem.js +++ b/src/strappFileSystem.js @@ -8,139 +8,33 @@ */ import strapp from './strapp.js' import localforage from 'localforage' -localforage.configure({ - name: 'strapp', - version: 0.1 +localforage.config({ + name: 'strapp' }) /** extend localforage with an assign operation */ -localforage.assignItem = (path, data) => new Promise((resolve, reject) => { - localforage.getItem(path, data).then((fdata) => { - localforage.setItem(path, Object.assign(fdata, data)) - .then(resolve).catch(reject) - }).catch((err) => { - localforage.setItem(path, data) - .then(resolve).catch(reject) - }) -}) -localforage.getOrSetItem = (path, defaults) => new Promise((resolve, reject) => localforage.getItem(path).then(resolve).catch((err) => localforage.setItem(path, defaults).then(resolve).catch(reject))) +localforage.assignItem = (path, data) => localforage.getItem(path).then((fdata) => localforage.setItem(path, fdata ? Object.assign(fdata, data) : data)) +localforage.getOrSetItem = (path, defaults) => localforage.getItem(path).then((data) => data === null ? localforage.setItem(path, defaults) : Promise.resolve(data)) +const _bootTime = new Date() -const strappRuntime = { - run: new StrappDirectory({ - perm: 0xFFF4, - files: { - keyboard: new StrappDevice(), - mouse: new StrappDevice(), - touch: new StrappDevice(), - client: new StrappDirectory({ - perm: 0xFF0, - files: { - create: new StrappFile({ - GET(){}, - POST(){} //TODO: create client action - }) - } - }), - relay: new StrappDirectory({ - perm: 0x000, - files: { - create: new StrappFile({ - GET(){}, - POST(){} //TODO: create relay - }) - } - }), - spc: new StrappDirectory({ - perm: 0xF00, - files: { - create: new StrappFile({ - GET(){}, - POST(){} //TODO: spc creation (probably only internal) - }) - } - }) - } - }) -} - -const StrappFileSystem = (() => { - /* Internal State */ - let rootUser - const rootDir = new StrappDirectory(StrappFile.literal({ - type: 'strapp/directory', - perm: 0xFF4, - path: '.' - })) - - /* Internal Functions */ - const _genKeyPair = () => { - //TODO - return { - pubKey: '', - privKey: '' - } - } - const _request = (location, method, data) => - rootDir.request(location.split('/'), method, rootUser.pubKey, data) - - /* API Definition */ - const StrappFileSystem = { - request: (location, method, data) => - new Promise((resolve, reject) => StrappFileSystem.loadWaiters.push([ - location, method, data, resolve, reject - ])), - get: (location) => StrappFileSystem.request(location, 'GET'), - set: (location, data) => StrappFileSystem.request(location, 'PUT', data), - resolveRootUser: (path) => - localforage.getItem(path) - .then((data) => Promise.resolve, - (err) => localforage.setItem(path, _genKeyPair())) - .then((data) => Promise.resolve(rootUser = data)) - } - StrappFileSystem.loadWaiters = [] - StrappFileSystem.bootTime = new Date() - - /* Init sequence */ - StrappFileSystem.resolveRootUser('acct/local') - .then((data) => Promise.all( - [ - ['.', { - acct: StrappDirectory.literal() - }], - ['acct', { - local: StrappFile.literal() - }] - ].map((entry) => localforage.getOrSetItem(entry[0],entry[1])) - )) - .then((loadedFiles) => { - rootDir.loadFiles(loadedFiles[0]) - StrappFileSystem.request = _request - StrappFileSystem.loadWaiters - .map((w) => _request(w[0], w[1], w[2], w[3]).then(w[4], w[5])) - delete StrappFileSystem.loadWaiters - }) - - return StrappFileSystem -})() - const StrappFile = (() => { class StrappFile extends Object { constructor(...props) { super() - return Object.assign(this, new.target.defaults, ...props) + return Object.assign(this, new.target.literal(), ...props) } static literal(...props) { - return Object.assign(Object.create(null, StrappFile.defaults), ...props) + return Object.assign(Object.create(null), this.defaults, ...props) } - static parse(fileLiteral) { + static parse(fileLiteral, ...props) { switch(fileLiteral.type) { case 'application/JSON': - return new StrappJSON(fileLiteral) + return new StrappJSON(fileLiteral, ...props) case 'strapp/directory': - return new StrappDirectory(fileLiteral) + return new StrappDirectory(fileLiteral, ...props) case 'strapp/file': default: - return new StrappFile(fileLiteral) + return new StrappFile(fileLiteral, ...props) } } resolveRequest(method, pubKey, data, locStack) { @@ -151,8 +45,8 @@ const StrappFile = (() => { let reqPerms = 0 switch(method) { case 'OPTIONS': - return new Promise((resolve, reject) => { - this.resolveClientPerms.then((perms) => { + return this.resolveClientPerms(pubKey).then( + (perms) => { let allow = ['OPTIONS'] if (perms & 0x9) allow.push('CONNECT') @@ -160,9 +54,10 @@ const StrappFile = (() => { allow.push('DELETE').push('PUT').push('POST') if (perms & 0x4) allow.push('GET').push('HEAD') - resolve(allow.join(', ')) - }).catch(reject) - }) + return Promise.resolve(allow.join(', ')) + }, + (err) => Promise.reject(500) + ) default: return Promise.reject(405) case 'PUT': @@ -178,35 +73,26 @@ const StrappFile = (() => { reqPerms = 0x9 break } - return new Promise((resolve, reject) => { - this.resolveClientPerms.then((perms) => { - if ((reqPerms & perms) === reqPerms) - this[method](pubKey, data).then(resolve).catch(reject) - else - reject(401) - }).catch(reject) - }) + return this.resolveClientPerms(pubKey) + .then((perms) => { console.log(`got ${perms} for ${reqPerms} in ${this.path}, owned by ${this.owner} with ${this.perms.toString(16)}`) + return Promise.resolve(perms)}) + .then( + (perms) => ((reqPerms & perms) === reqPerms) ? + this[method](pubKey, data) : Promise.reject(401), + (err) => Promise.reject(500)) } resolveClientPerms(pubKey) { - return new Promise((resolve, reject) => { - if (!pubKey || pubKey === '') - resolve(this.perms >>> 12 & 0xF) - else if (pubKey === this.owner) - resolve((this.perms >>> 8) & 0xF) - else - localforage.getItem(`acct/${pubKey}`).then((account) => { - let grpLen = account.groups ? account.groups.length : 0 - let found = false - for (let i = 0; i < grpLen; i++) { - if (account.groups[i] === this.group) { - resolve((this.perms >>> 4) & 0xF) - found = true - break - } - } - if (!found) - resolve(this.perms & 0xF) - }).catch(reject) + if (!pubKey || pubKey === '') + return Promise.resolve((this.perms >>> 12) & 0xF) + return localforage.getItem(`acct/${this.owner}`).then((fData) => { + if (fData && 'pubKey' in fData && fData.pubKey === pubKey) + return Promise.resolve((this.perms >>> 8) & 0xF) + return localforage.getItem(`acct/${pubKey}`).then((account) => { + if (account && account.groups && + account.groups.some((group) => group === this.group)) + return Promise.resolve((this.perms >>> 4) & 0xF) + return Promise.resolve(this.perms & 0xF) + }) }) } HEAD(pubKey) { @@ -219,27 +105,14 @@ const StrappFile = (() => { return localforage.setItem(this.path, data) } POST(pubKey, data) { - return new Promise((resolve, reject) => { - localforage.getItem(this.path) - .then((fData) => - this.setItem(this.path, fData + data) - .then(resolve) - .catch(reject) - ) - .catch(reject) - }) + return localforage.getItem(this.path).then( + (fData) => localforage.setItem(this.path, fData + data)) } DELETE(pubKey) { return localforage.removeItem(this.path) } - OPTIONS(pubKey) { - return new Promise((resolve, reject) => { - this.resolveClientPerms(pubKey).then((perms) => { - }).catch(reject) - }) - } - CONNECT(pubKey) { - return this.GET(pubKey) + CONNECT(pubKey) { //TODO + return Promise.reject(501) } TRACE(opt) { } @@ -248,13 +121,12 @@ const StrappFile = (() => { } StrappFile.defaults = { type: 'strapp/file', - perm: 0xF00, + perms: 0xF00, owner: 'local', group: '', - changed: StrappFileSystem.bootTime, - created: StrappFileSystem.bootTime, - accessed: StrappFileSystem.bootTime, - files: undefined + changed: _bootTime, + created: _bootTime, + accessed: _bootTime } return StrappFile })() @@ -283,7 +155,10 @@ const StrappDirectory = (() => { const _traverse_loaded = function(method, pubKey, data, locStack) { if (locStack[0] in this.files) return this.files[locStack[0]].resolveRequest(method, pubKey, data, locStack.slice(1)) - return Promise.reject(404) + console.log(`didnt find ${locStack[0]} in ${this.path}`) + localforage.getItem(this.path).then(console.log) + console.log(this.files) + return Promise.resolve(0) } const _traverse_loading = function(method, pubKey, data, locStack) { if (this.loadWaiters) @@ -295,41 +170,38 @@ const StrappDirectory = (() => { class StrappDirectory extends StrappFile { static literal(...props) { - return Object.assign(Object.create(null, StrappDirectory.defaults), ...props) - } - loadFiles(fileData) { - Object.keys(fileData) - .map((key) => (this.files[key] = StrappFile.parse(fileData[key]))) - } - resolveOwnFiles() { - return new Promise((resolve, reject) => { - localforage.getItem(this.path).then((fileData) => { - this.loadFiles(fileData) - resolve() - }).catch(reject) - }) + return StrappFile.literal(this.defaults, ...props) } traverse(method, pubKey, data, locStack) { + if (this.files) { + this.traverse = _traverse_loaded + return this.traverse(method, pubKey, data, locStack) + } + this.files = {} this.traverse = _traverse_loading this.loadWaiters = [] - this.resolveOwnFiles().then(() => { + return localforage.getItem(this.path).then((fileData) => { + if (fileData) + this.loadFiles(fileData) this.traverse = _traverse_loaded this.loadWaiters - .map((w) => _traverse_loaded(w[0], w[1], w[2], w[3]).then(w[4], w[5])) + .map((w) => this.traverse(w[0], w[1], w[2], w[3]).then(w[4], w[5])) delete this.loadWaiters + return this.traverse(method, pubKey, data, locStack) }) - return _traverse_loading(method, pubKey, data, locStack) } - request(method, pubKey, data, locStack) { - return new Promise((resolve, reject) => { - super.resolveRequest(method, pubKey, data, locStack) - .then(resolve) - .catch((err) => { - if (err === 404) - return this.traverse(method, pubKey, data, locStack) - return Promise.reject(err) - }) - }) + loadFile (fileName, fileLiteral) { + this.files[fileName] = StrappFile.parse(fileLiteral, { path: this.path + '/' + fileName }) + } + loadFiles (fileData) { + Object.keys(fileData).map((key) => this.loadFile(key, fileData[key])) + } + resolveRequest(method, pubKey, data, locStack) { + return super.resolveRequest(method, pubKey, data, locStack) + .catch((err) => + err === 404 ? + this.traverse(method, pubKey, data, locStack) : + Promise.reject(err)) } CONNECT(opts) { //send routing message to the directory (handle the next part here) @@ -356,5 +228,159 @@ const StrappJSON = (() => { return StrappJSON })() + + +const StrappFileSystem = (() => { + /* Internal State */ + let rootKey, rootPubKey + const rootDir = new StrappDirectory(StrappFile.literal({ + type: 'strapp/directory', + perms: 0xFF4, + path: '.', + files: {}, + loadFile(fileName, fileLiteral) { + this.files[fileName] = StrappFile.parse(fileLiteral, { path: fileName }) + } + })) + const _strappRuntime = { + '.': rootDir, + '/': rootDir, + run: new StrappDirectory({ + perms: 0xFFF4, + files: { + keyboard: new StrappDevice(), + mouse: new StrappDevice(), + touch: new StrappDevice(), + client: new StrappDirectory({ + perms: 0xFF0, + files: { + create: new StrappFile({ + GET(){}, + POST(){} //TODO: create client action + }) + } + }), + relay: new StrappDirectory({ + perms: 0x000, + files: { + create: new StrappFile({ + GET(){}, + POST(){} //TODO: create relay + }) + } + }), + spc: new StrappDirectory({ + perms: 0xF00, + files: { + create: new StrappFile({ + GET(){}, + POST(){} //TODO: spc creation (probably only internal) + }) + } + }) + } + }) + } + + /* Internal Functions */ + const _request = (location, method, data) => + rootDir.resolveRequest(method, rootPubKey, data, location.split('/')) + + /* API Definition */ + const StrappFileSystem = { + request: (location, method, data) => + new Promise((resolve, reject) => StrappFileSystem.loadWaiters.push([ + location, method, data, resolve, reject + ])), + get: (location) => StrappFileSystem.request(location, 'GET'), + set: (location, data) => StrappFileSystem.request(location, 'PUT', data) + } + StrappFileSystem.loadWaiters = [] + + + /* Init sequence */ + const _defaultFS = { + '.': { + acct: StrappDirectory.literal() + }, + 'acct': { + local: StrappFile.literal() + } + } + const _algo = { + name: 'RSA-OAEP', + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { name: 'SHA-1' } + } + + const _loadRootKeys = () => + Promise.all([ + localforage.getItem('/keys/id_rsa'), + localforage.getItem('/keys/id_rsa.pub') + ]) + .then((files) => + files[0] === null || files[1] === null ? + window.crypto.subtle.generateKey(_algo, true, ['encrypt', 'decrypt']) + .then((keyPair) => Promise.all([ + window.crypto.subtle.exportKey('jwk', keyPair.privateKey), + window.crypto.subtle.exportKey('jwk', keyPair.publicKey) + ])) + .then((jwks) => Promise.all([ + localforage.setItem('/keys/id_rsa', jwks[0]), + localforage.setItem('/keys/id_rsa.pub', jwks[1]) + ])) : + Promise.resolve(files)) + .then((jwks) => { + let hashAlg = jwks[0].alg.replace(_algo.name, '') + if (hashAlg.length === 0) + hashAlg = 'SHA-1' + else + hashAlg = 'SHA' + hashAlg + if (jwks[0].alg.indexOf(_algo.name) !== 0 || hashAlg != _algo.hash.name) { + console.log('Secure hash algorithm updated, old keys deleted') + return Promise.all([localforage.removeItem('/keys/id_rsa'), + localforage.removeItem('/keys/id_rsa.pub') + ]).then(_loadRootKeys) + } + rootPubKey = jwks[1].n + return localforage.assignItem('acct/local', { pubKey: jwks[1].n }).then( + () => window.crypto.subtle.importKey('jwk', jwks[0], _algo, true, ['decrypt'])) + .then((key) => Promise.resolve(rootKey = key)) + }) + + const _init = () => localforage.getItem('.') + .then((data) => + data === null ? + Promise.all( + Object.keys(_defaultFS) + .map((key) => localforage.setItem(key, _defaultFS[key]))) + .then(() => localforage.getItem('.')) : + Promise.resolve(data)) + .then((rootFiles) => { + rootDir.loadFiles(rootFiles) + Object.assign(rootDir.files, _strappRuntime) + _loadRootKeys().then((data) => { + StrappFileSystem.request = _request + StrappFileSystem.loadWaiters + .map((w) => _request(w[0], w[1], w[2]).then(w[3], w[4])) + delete StrappFileSystem.loadWaiters + }) + }) + + _init() + + //localforage.clear().then(_init) + + return StrappFileSystem +})() + + export default StrappFileSystem -export { StrappFile, StrappPeerConnection, StrappDirectory } +export { + StrappFile, + StrappPeerConnection, + StrappDirectory, + StrappDevice, + StrappJSON +} diff --git a/strappServer.js b/strappServer.js index 2f8d383..5cced85 100644 --- a/strappServer.js +++ b/strappServer.js @@ -15,15 +15,17 @@ const dlog = (msg) => console.log(msg) class Server { constructor(...props) { Object.assign(this, new.target.defaults, ...props) + this.pendingResponses = new Array(this.responseCacheSize) this.init() + return this } init() { if (this.cacheDir) this.loadCache(this.cacheDir, '') - require('fs').readFile('./strapp.js', (err, data) => { + require('fs').readFile('./www/strapp.min.js', (err, data) => { if (err) - throw new Error (err) - this.cache['strapp.js'] = ['application/javascript',data] + throw new Error(err) + this.cache['strapp.min.js'] = ['application/javascript',data] }) this.startHttpd() } @@ -64,7 +66,7 @@ class Server { const htArgv = request.url.slice(1).split('?') dlog(`request for ${request.url} received`) if (request.method === 'GET' && htArgv[0] in this.cache) { - dlog(`found in cache`) + dlog(`found ${htArgv[0]} in cache`) response.writeHead(200, { 'Content-Type': this.cache[htArgv[0]][0] }) response.write(this.cache[htArgv[0]][1]) response.end() @@ -146,9 +148,17 @@ class Server { this.app.send(`${args[0]} ${request.method} ${pubKey} ${msgID} ${data}`) } else { - response.writeHead(200, { 'Content-Type': 'text/html' }) - response.write(Server.bootstrapp) - response.end() + require('fs').readFile('./bootstrapp.html', (err, data) => { + if (err) { + response.writeHead(500) + response.end() + } + else { + response.writeHead(200, { 'Content-Type': 'text/html' }) + response.write(data) + response.end() + } + }) } } } @@ -158,7 +168,7 @@ Server.defaults = { host: undefined, cache: {}, cacheDir: './www', - pendingResponses: new Array(20), + responseCacheSize: 40, msgID: 0, answers: {}, remoteAdminKey: undefined, @@ -167,17 +177,6 @@ Server.defaults = { httpd: undefined, wsd: undefined } -Server.bootstrapp = ` - - - bootstrapp - - - - - - -` //TODO: module.exports = Server