bug fixes master origin/HEAD origin/master
authorken <ken@kengrimes.com>
Mon, 4 Sep 2017 04:15:13 +0000 (04:15 +0000)
committerken <ken@kengrimes.com>
Mon, 4 Sep 2017 04:15:13 +0000 (04:15 +0000)
bootstrapp.html
src/strapp.js
src/strappFileSystem.js
strappServer.js

index 277b632..9fa3cd1 100644 (file)
@@ -4,13 +4,27 @@
     <meta charset="utf-8" />
     <title>Strapp.io</title>
     <!--STRAPP_FAVICO-->
-    <link rel=icon href="./www/favicon-96x96.png">
+    <link rel=icon href="favicon-96x96.png">
     <!--STRAPP_MANIFEST-->
-    <link rel=manifest href="./www/manifest.json">
-    <script type="text/plain" src="https://rawgit.com/PeculiarVentures/webcrypto-liner/master/dist/webcrypto-liner.shim.js"></script>
+    <link rel=manifest href="manifest.json">
+    <!-- if IE11, <script type="text/javascript" src="promiz.js"></script>
+    <!--<script type="text/javascript" src="webcrypto-liner.shim.js"></script>-->
+    <script type="text/javascript" src="https://rawgit.com/PeculiarVentures/webcrypto-liner/master/dist/webcrypto-liner.shim.js"></script>
     <script type="text/javascript" src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
+    <!--STRAPP_SRC-->
+    <script type="text/javascript" src="strapp.min.js"></script>
+  </head>
+  <body>
+    <div id='testdiv'></div>
     <script type="text/javascript">
-      <!--STRAPP_SRC-->
+      const fileNames = [ 'acct/local', 'acct', '.' ]
+      Promise.all(fileNames.map(strapp.GET)).then((files) => {
+      let html = ''
+      fileNames.forEach((fileName, idx) => {
+        html += '<p>' + fileName + ': ' + JSON.stringify(files[idx]) + '</p>'
+      })
+      document.getElementById('testdiv').innerHTML = html
+      })
     </script>
-  </head>
+  </body>
 </html>
index c37ba16..a179716 100644 (file)
@@ -16,6 +16,7 @@ const strapp = (() => {
         .then((response) => resolve(response))
         .catch((err) => reject(err))
     }),
+    fs: fs,
     on: (eventName, fn) => {
       if (!events[eventName])
         events[eventName] = []
index 8101311..bc2b0b2 100644 (file)
 */
 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
+}
index 2f8d383..5cced85 100644 (file)
@@ -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 = `
-<!DOCTYPE html>
-<html>
-  <title>bootstrapp</title>
-  <head>
-    <script src='/strapp.js'></script>
-  </head>
-  <body>
-  </body>
-</html>
-`
 
 //TODO: module.exports = Server