From 44e0b2d571c71ebd410b78366c8bc9dc463a96c4 Mon Sep 17 00:00:00 2001 From: jordan Date: Tue, 1 Aug 2017 14:53:18 -0700 Subject: [PATCH] Refactored into strapp Modules. All in progress with File System current focus --- bootstrap.js | 117 ++++++-------------- strappCrypto.js | 57 ++++++++++ strappFileManager.js | 224 +++++++++++++++++++++++++++++++++++++++ strappFileSystem.js | 224 +++++++++++++++++++++++++++++++++++++++ strappPeerConnection.js | 159 +++++++++++++++++++++++++++ strappProtocolManager.js | 35 ++++++ 6 files changed, 729 insertions(+), 87 deletions(-) create mode 100644 strappCrypto.js create mode 100644 strappFileManager.js create mode 100644 strappFileSystem.js create mode 100644 strappPeerConnection.js create mode 100644 strappProtocolManager.js diff --git a/bootstrap.js b/bootstrap.js index a90539e..ba48382 100644 --- a/bootstrap.js +++ b/bootstrap.js @@ -1,68 +1,12 @@ /** - * @file Shared API + * @file Bootstrapping API * @author Jordan Lavatai and Ken Grimes * @version 0.0.1 - * @license APGL-3.0 + * @license AGPL-3.0 * @copyright Strapp.io */ -const dlog = (msg) => console.log(msg) - -/***********************/ -/** Helper functions **/ -/***********************/ - -/* SDP Interop helper functions to be used by beginRTCOffer and beginRTCAnswer */ - -/** @func Determines the multimedia signaling protocol for the given session description - * @desc Does some basic duck typing to determine the type of Session Description - * @arg {Object} Session Description Object - * @return {String} Returns 'planB' if Session Description is planB, 'Unified' if - * Session Description is Unified - */ -function determineSessionDescription(sessionDescription) { -} - -/** @func Converts a planB session description to Unified session description - * @arg {Object} Session Description Object - * @return {Object} PlanB Session Description - */ -function toUnified(sessionDescription) { -} - -/** @func Converts a Unified session description to planB session description - * @arg {Object} Session Description Object - * @return {Object} PlanB Session Description - */ -function toPlanB(sessionDescription) { -} - -/* WebSocket wielder helper functions */ - -/* Function for setting up webSocket wielders message handler */ -/* TBD */ -/* TBD */ - -/* SDP Negotiation helper functions */ - -/* addRemoteIceCandidate() */ -/* addIceCandidateCreationHandler(callback for sending?) */ -/* handleIceRequest() - WebSpocketless entity specific */ - - -/***********************/ -/** Exposed functions **/ -/***********************/ - -/** @func Attempts to create a websocket - * @desc If user agent supports web sockets, attempt to create a websocket - * with given domain - * @arg {String} domain - ip address or domain name of other end of websocket - * @return {Boolean} returns true if successful, false if unsucessful - */ -export function createWebSocket(domain) { - -} +import fs from "fileManager" /** @func Returns public key for the user * @desc Attempts to find a stored key. If unsucessful, ask user for key. If user @@ -71,37 +15,36 @@ export function createWebSocket(domain) { * @options specif * @return {Object} cryptoKey object */ -export function resolveKey() { - +function resolveKey() { + return new Promise( (resolve, reject) => { + /* See if publicKey has been generated before */ + const publicKey = fs.strappID + if (publicKey === '') { + resolve(generateKey()) + } + else { + resolve(publicKey) + } + }) } -/** @func Begins the SDP handshake process as the offerer - * @desc - * - * - */ -export function beginRTCOffer() { -} +/* Init FileSystem */ +fs.initFileSystem() -/** @func Begins the SDP handshake process as the answerer - * @desc - * - * - */ -export function beginRTCAnswer() { -} +/* Find or generate key */ +let key = resolveKey() + +/* Auth */ +/* Authorization-header: Strapp pubkey (no answer)*/ +/* Determine answer, add answer to "." */ + + +/* Setup hardcoded files e.g. "..", ".", "accounts", "ice", "sdp", "log", "run" */ +/* Add the files to the filesystem only if they dont already exist /* + +/* Connect to host */ + + -/** @func Generic way to message host from client - * @desc tbd - * @arg {Object} data object to be sent - */ -export function sendHostData(data) { -} -/** @func Generic way to message client from host - * @desc tbd - * @arg {Object} data object to be sent - */ -export function sendClientData(data){ -} diff --git a/strappCrypto.js b/strappCrypto.js new file mode 100644 index 0000000..cc6fd75 --- /dev/null +++ b/strappCrypto.js @@ -0,0 +1,57 @@ +/** + * @file Management for bootstrapp cryptography + * @desc Makes keys, encrypts and decrypts messages + * + * @author Jordan Lavatai and Ken Grimes + * @version 0.0.1 + * @license AGPL-3.0 + * @copyright Strapp.io + */ + +import {setItem, getItem} from "localForage" + +/** @func Generates a CryptoKey and returns a SHA-256 client key + * @desc Utilizes Web Crypto to create a CryptoKey using RSA specification. + * Stores both public and private representation of the key and returns + * the public key + * @return {String} clientKey + */ +function generateKey() { + crypto.subtle.generateKey( + { name:'RSA-OAEP', + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: {name: "SHA-256"} + }, + true, + ['encrypt', 'decrypt'] + ).then((cryptoKey) => { + /* TODO: Do we need to store the private key as well? */ + crypto.subtle.exportKey('jwk', cryptoKey) + .then((exportedKey) => { + setItem('publicKey', exportedKey.publicKey) + setItem('privateKey', exportedKey.privateKey) + console.log('public key is' + getItem('publicKey')) + console.log('private key is' + getItem('privateKey')) + return exportedKey.publicKey + }) + }) +} + +/** @func Encrypts data with a public key + * @desc + * @arg {String} Public Key + * @return {Object} The encrypted data + */ +function encryptData(publicKey, data) { + +} +/** @func Decrypts data with a private key + * @desc + * @arg {String} Private key to decrypt data + * @return {Object} The decrypted data + */ +function decryptData(privateKey, data) { +} + +function diff --git a/strappFileManager.js b/strappFileManager.js new file mode 100644 index 0000000..6f0b9d4 --- /dev/null +++ b/strappFileManager.js @@ -0,0 +1,224 @@ +/** + * @file File System Interface + * @desc Provides basic file commands for interacting with Strapp + * file system as well as storage (and backups) of file system + * @author Jordan Lavatai and Ken Grimes + * @version 0.0.1 + * @license AGPL-3.0 + * @copyright Strapp.io + */ + +import localforage from "localforage" +import StrappPeerConnection from "strappPeerConnection" + +/* File constructor */ +class File extends Object { + constructor(...props) { + super() + return Object.assign(this, new.target.defaults, ...props) + } + get() { + return this.data + } + post(postedData) { + this.data += postedData + this.lastModified = new Date() + } + put(putData) { + this.data = putData + this.lastModified = new Date() + } + delete() { + this.data = '' + this.lastModified = new Date() + } + connect() { + + } + options(publicKey) { + return this.availPermissions(publicKey) + } +} +/* TODO: Continue to flesh this out */ +File.defaults = { + name: '', + data: '', + mode: {}, + size: 0 + //lastModified: new Date()? + //lastAccessed: new Date()? + +} + + +/* Filesystem maintains the current session memory for the strapp instance. Files can be + created and killed from the filesystem without leveraging localForage. Files that are + in the filesystem can be stored to localForage while files that are in localForage can be loaded + to the filesystem. When a Filesystem is first intialized, it attempts to get its strappID it populates itself from localForage and + overwrites any files in its current memory. Files that have restore() as a property (which will be some method needed to + make the file functions e.g. strappPeerConnections will restore() themselves lazily, i.e. when they are needed.*/ + + /* TODO: Should it be possible to create/preserve/destroy a file to both localForage and fileSystem? (same time) */ + /* TODO: Should initFileSystem not overwrite files? */ + +/* These are the default files on all file systems */ +let defaultFiles = [ "..", ".", "accounts", "ice", "log", "run" ] + + +/* TODO: Protect data via closures? */ +const fs = { + /* TODO: What if files are added to file system before init is is called? */ + initFileSystem(){ + this.db = localforage.createInstance({ name: "database" }) + /* Iterate through all files on localforage, adding them to FileSystem + and calling their restore methods */ + this.db.iterate( (value, key, n) => { + /* just btw, return !undefined to exit early */ + this.loadFile(key, true) + + }).catch( (err) => { + console.log(`error: ${err} when iterating through localForage during initFileSystem`) + }) + /* Add the hardcoded default files if they dont already exist */ + /* Restore these files --> need the private/public key*/ + initialFiles.map( (val, idx, array) => { + if (this.fileExists(val)) { + let file = this.getFile(val) + let restoreProp = file['restore'] + if (restoreProp === undefined && typeof restoreFx === 'function') { + //restore file + } + /* Else don't do anything, file exists in FS and doesnt need to be restored */ + } + else { + /* TODO: Remove checking for every file --> although its only for the default files which + will probably be a low number. Still, unnecessary. Could make initialFiles a + Map object with fileType in it and switch(val.fileType) */ + if (val === '..') { + let file = new StrappPeerConnection() + /* Connect with host */ + } + else { + /* Each default file is going to have specific permissions, */ + let filedata = new File() + filedata.name = val + filedata.mode = + this.createFile(val,) + } + } + + }) + + }, + + fileExists(filename) { + return this.files[filename] === undefined ? false : true + }, + + /* Create a file in the file system, if specified overwrites any file that is already there + else does nothing */ + createFile(filename, filedata, overwrite = false){ + filedata.name = filename + if (this.files[filename] === undefined) { + this.files[filename] = filedata + } + else { + if (overwrite) { + console.log(`Overwriting ${filename}`) + this.files[filename] = filedata + } + else { + console.log(`Didn't overwrite file so nothing happened`) + } + } + }, + + /* Get a file from browser session memory */ + /* TODO: Option to get from localForage? */ + getFile(filename) { + return this.files[filename] + }, + + /* Save a file to file system*/ + saveFile(filename, filedata) { + /* TODO: Determine if file to be saved is of saveable nature e.g. SPC's cant really be saved */ + this.db.setItem(filename, filedata) + }, + + /* Delete file from localForage */ + removeFile(filename) { + this.db.removeItem(filename) + }, + + /* Delete a file from file system */ + killFile(filename) { + delete this.files[filename] + + }, + + /* Store file in file system to localForage */ + storeFile(filename) { + this.db.setItem(filename, this.files[filename].filedata) + }, + + /* Load file from localForage to file system */ + loadFile(filename, overwrite = false) { + let filedata = this.db.getItem(filename) + filedata = filedata.restore === undefined ? filedata : filedata.restore() + this.createFile(filename, filedata, overwrite) + }, + + saveFileSystem() { + /* TODO: save all files in filesystem to localforage */ + }, + db: {}, + files: {} + +} + +/* File System API */ + + +/* Load file system from localStorage/indexedDB if strapp.js is running on a host */ +function loadFileSystem(){ + +} + +/* Store file system before shutting down if strapp.js is running on a host- */ +function storeFileSystem(){} + + + +/* addFile - adds a created file to a file */ +//@arg fileType - what to set the type property +//@arg fileData - what to set the data property +//@arg filePos - where to create the file + +/* Determine if a publicKey has permissions to execute methods + +/* rm - Delete file */ + +/* ls - display file contents */ + //open file +//return all of file files + +/* cat - display file data */ +//return file data + +/* open - Open file */ + //traverse to file path +/* perm - display file permissions (if you have permissions) */ +/* find - find a file */ +//@arg fileToFind +/* stat - info about a file */ +//@arg path - path to the file +/* exists - determine if a file contains a file */ +//@arg {String} searchedFile - file to be searched +//@arg {String} fileName +//@arg {Number} Depth to look +//@return {Boolean} true if exists, false if doesn't + + + + + diff --git a/strappFileSystem.js b/strappFileSystem.js new file mode 100644 index 0000000..6f0b9d4 --- /dev/null +++ b/strappFileSystem.js @@ -0,0 +1,224 @@ +/** + * @file File System Interface + * @desc Provides basic file commands for interacting with Strapp + * file system as well as storage (and backups) of file system + * @author Jordan Lavatai and Ken Grimes + * @version 0.0.1 + * @license AGPL-3.0 + * @copyright Strapp.io + */ + +import localforage from "localforage" +import StrappPeerConnection from "strappPeerConnection" + +/* File constructor */ +class File extends Object { + constructor(...props) { + super() + return Object.assign(this, new.target.defaults, ...props) + } + get() { + return this.data + } + post(postedData) { + this.data += postedData + this.lastModified = new Date() + } + put(putData) { + this.data = putData + this.lastModified = new Date() + } + delete() { + this.data = '' + this.lastModified = new Date() + } + connect() { + + } + options(publicKey) { + return this.availPermissions(publicKey) + } +} +/* TODO: Continue to flesh this out */ +File.defaults = { + name: '', + data: '', + mode: {}, + size: 0 + //lastModified: new Date()? + //lastAccessed: new Date()? + +} + + +/* Filesystem maintains the current session memory for the strapp instance. Files can be + created and killed from the filesystem without leveraging localForage. Files that are + in the filesystem can be stored to localForage while files that are in localForage can be loaded + to the filesystem. When a Filesystem is first intialized, it attempts to get its strappID it populates itself from localForage and + overwrites any files in its current memory. Files that have restore() as a property (which will be some method needed to + make the file functions e.g. strappPeerConnections will restore() themselves lazily, i.e. when they are needed.*/ + + /* TODO: Should it be possible to create/preserve/destroy a file to both localForage and fileSystem? (same time) */ + /* TODO: Should initFileSystem not overwrite files? */ + +/* These are the default files on all file systems */ +let defaultFiles = [ "..", ".", "accounts", "ice", "log", "run" ] + + +/* TODO: Protect data via closures? */ +const fs = { + /* TODO: What if files are added to file system before init is is called? */ + initFileSystem(){ + this.db = localforage.createInstance({ name: "database" }) + /* Iterate through all files on localforage, adding them to FileSystem + and calling their restore methods */ + this.db.iterate( (value, key, n) => { + /* just btw, return !undefined to exit early */ + this.loadFile(key, true) + + }).catch( (err) => { + console.log(`error: ${err} when iterating through localForage during initFileSystem`) + }) + /* Add the hardcoded default files if they dont already exist */ + /* Restore these files --> need the private/public key*/ + initialFiles.map( (val, idx, array) => { + if (this.fileExists(val)) { + let file = this.getFile(val) + let restoreProp = file['restore'] + if (restoreProp === undefined && typeof restoreFx === 'function') { + //restore file + } + /* Else don't do anything, file exists in FS and doesnt need to be restored */ + } + else { + /* TODO: Remove checking for every file --> although its only for the default files which + will probably be a low number. Still, unnecessary. Could make initialFiles a + Map object with fileType in it and switch(val.fileType) */ + if (val === '..') { + let file = new StrappPeerConnection() + /* Connect with host */ + } + else { + /* Each default file is going to have specific permissions, */ + let filedata = new File() + filedata.name = val + filedata.mode = + this.createFile(val,) + } + } + + }) + + }, + + fileExists(filename) { + return this.files[filename] === undefined ? false : true + }, + + /* Create a file in the file system, if specified overwrites any file that is already there + else does nothing */ + createFile(filename, filedata, overwrite = false){ + filedata.name = filename + if (this.files[filename] === undefined) { + this.files[filename] = filedata + } + else { + if (overwrite) { + console.log(`Overwriting ${filename}`) + this.files[filename] = filedata + } + else { + console.log(`Didn't overwrite file so nothing happened`) + } + } + }, + + /* Get a file from browser session memory */ + /* TODO: Option to get from localForage? */ + getFile(filename) { + return this.files[filename] + }, + + /* Save a file to file system*/ + saveFile(filename, filedata) { + /* TODO: Determine if file to be saved is of saveable nature e.g. SPC's cant really be saved */ + this.db.setItem(filename, filedata) + }, + + /* Delete file from localForage */ + removeFile(filename) { + this.db.removeItem(filename) + }, + + /* Delete a file from file system */ + killFile(filename) { + delete this.files[filename] + + }, + + /* Store file in file system to localForage */ + storeFile(filename) { + this.db.setItem(filename, this.files[filename].filedata) + }, + + /* Load file from localForage to file system */ + loadFile(filename, overwrite = false) { + let filedata = this.db.getItem(filename) + filedata = filedata.restore === undefined ? filedata : filedata.restore() + this.createFile(filename, filedata, overwrite) + }, + + saveFileSystem() { + /* TODO: save all files in filesystem to localforage */ + }, + db: {}, + files: {} + +} + +/* File System API */ + + +/* Load file system from localStorage/indexedDB if strapp.js is running on a host */ +function loadFileSystem(){ + +} + +/* Store file system before shutting down if strapp.js is running on a host- */ +function storeFileSystem(){} + + + +/* addFile - adds a created file to a file */ +//@arg fileType - what to set the type property +//@arg fileData - what to set the data property +//@arg filePos - where to create the file + +/* Determine if a publicKey has permissions to execute methods + +/* rm - Delete file */ + +/* ls - display file contents */ + //open file +//return all of file files + +/* cat - display file data */ +//return file data + +/* open - Open file */ + //traverse to file path +/* perm - display file permissions (if you have permissions) */ +/* find - find a file */ +//@arg fileToFind +/* stat - info about a file */ +//@arg path - path to the file +/* exists - determine if a file contains a file */ +//@arg {String} searchedFile - file to be searched +//@arg {String} fileName +//@arg {Number} Depth to look +//@return {Boolean} true if exists, false if doesn't + + + + + diff --git a/strappPeerConnection.js b/strappPeerConnection.js new file mode 100644 index 0000000..8512bc2 --- /dev/null +++ b/strappPeerConnection.js @@ -0,0 +1,159 @@ +/** + * @file Messaging module providing capability for files to communicate with each other + * @author Jordan Lavatai and Ken Grimes + * @version 0.0.1 + * @license AGPL-3.0 + * @copyright Strapp.io + */ + +/** Remote Peer States: + unintialized + + +/* SPC default object */ +class StrappPeerConnection extends File { + GET() { + } + POST() { + } + CONNECT() { + } + OPTIONS() { + } + DELETE() { + } + +} + + +strappPeerConnection.defaults = Object.assign({}, File.defaults, { + state: '', + messageCount: 0, + messageHandler: new Map(), /* TODO: seperate handling? */ + remotePeer: { + bestConnectionType: '', + state: '', + peerID: '' /* clientID of Peer* / + } +} + +/* GET - gets the data (queued messages) from a spc */ +/* POST - sends a msg through a spc to the remote peer */ +/* CONNECT - Tries to establish a connection with an spc */ +/* local or remote peer of the spc? */ + /* Local is part of uri, make it remote*/ + +/* File System */ +/* Convert entire file system into json objects that can be read into memory */ + + +/* Send */ +/* Receive */ +/* Reconnect WebRTC */ +/* Reconnect WS */ +/* Determine remote peers bestConnectionType */ +/* Restablish connection */ + + +/** @func Send a message to the remote peer + * @desc + * + * @arg {Object} message - message in LMKID format, except no message ID yet + * @this {Object} - Refers to the SPC that is calling send + * @return {Object} - Promise for the response from the send message + */ +function send(message) { + /* Determine if 'this' is a SPC object */ + + + /* 'send data' means to create a strapp protocol message and send it over whichever + transmission channel is available */ + switch (this.state) { + case 'unintialized': + case 'offline': + if (this.establishConnection(clientKey, data)) { + send(clientKey, data) + } + else { + storeMessage(this, message) + } + + break + case 'disconnected': + this.reestablishConnection(clientKey, data) + /* Store the message */ + /* Fail if reconnection attempt fails */ + break + case 'polling': { + /* does remote peer have the ability to upgrade? */ + /* If so, attempt to upgrade */ + /* If upgrade fails, retry the send method else send via polling */ + break + } + case 'webSocket': { + /* does remote peer have the ability to upgrade? */ + /* If so, attempt to upgrade */ + /* If upgrade fails, retry the send method else send via polling */ + break + } + case 'webRTC': { + /* If remote peer is connected, send data */ + /* Else Attemp to fix connection, if connection is dead, set this.state and try this again. Will + Enter the 'disconnected' state */ + break + } + + } +} + + + +/** @func Attempts to connect the local peer to the remote peer + * @desc + * + * @ret {boolean} True if successful, false if not + */ +function establishSPC() { + +} + +/** @func Attempts to reconnect the local peer to the remote peer + * @desc This function assumes that the remotePeer property for the SPC object has + * been elevated to a status higher than uninitialized. + * + * @ret {boolean} True if successful, false if not + */ +function reestablishSPC() { + /* If successful, send all the cached messages that were saved when connection was down */ +} + + +/** @func Stores the message + * @desc + * + */ +function storeMessage() { +} + +/** @func Determines the multimedia signaling protocol for the given session description + * @desc Does some basic duck typing to determine the type of Session Description + * @arg {Object} Session Description Object + * @return {String} Returns 'planB' if Session Description is planB, 'Unified' if + * Session Description is Unified + */ +function determineSessionDescription(sessionDescription) { +} + +/** @func Converts a planB session description to Unified session description + * @arg {Object} Session Description Object + * @return {Object} PlanB Session Description + */ +function toUnified(sessionDescription) { +} + +/** @func Converts a Unified session description to planB session description + * @arg {Object} Session Description Object + * @return {Object} PlanB Session Description + */ +function toPlanB(sessionDescription) { +} diff --git a/strappProtocolManager.js b/strappProtocolManager.js new file mode 100644 index 0000000..8aa9bfb --- /dev/null +++ b/strappProtocolManager.js @@ -0,0 +1,35 @@ +/** + * @file Strapp Protocol Manager + * @author Jordan Lavatai and Ken Grimes + * @version 0.0.1 + * @license AGPL-3.0 + * @copyright Strapp.io + */ + +import send from 'spc' + +/* KLMID == Key Location Method msgID Data */ +/* Convert from KLMID to HTTP */ +/* Convert from HTTP to KLMID */ +/* Parse KLMID -> Execute method based on location */ +/* Convert Object with KLMID keys to a string */ + +/** @func Parse the strapp protocol string + * @desc KLMID stands for Location Key Method ID Data + * [Optional] Key is the clientKey that is used to authorize the method + * If not specified, this message will not have any authorizations + * [Optional] Location is the location of the target file that the method will execute upon + * If not specified, target file is the current file + * [Required] Method is one of the file commands available + * [Required] ID is the message ID, used when multiple messages are being sent and received + * [Optional] Data is a parameter for the method e.g. PUT will replace the target files data property + * + * An unincluded message part needs to be indicated by a whitespace, i.e. ' ' i.e. /u{0020}. + * This function is mainly called by a SPCs receive event that processes every message that a SPC + * receives + * @arg {String} msg - String conforming to the strapp protocol + * @return {Object} - Parsed object with keys corresponding to segments of strapp Protocol + */ +function parseSPM(msg) { + /* if no location, then call the method in LMKID on the local file, i.e. "." */ +} -- 2.18.0