MAJOR: Updates for Authenticated Web UI and CLI #30
							
								
								
									
										448
									
								
								bin/telebitd.js
									
									
									
									
									
								
							
							
						
						
									
										448
									
								
								bin/telebitd.js
									
									
									
									
									
								
							| @ -549,254 +549,254 @@ function handleApi() { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|     function initOrConfig(req, res) { |   function initOrConfig(req, res) { | ||||||
|       var conf = {}; |     var conf = {}; | ||||||
|       if (!req.body) { |     if (!req.body) { | ||||||
|         res.statusCode = 422; |       res.statusCode = 422; | ||||||
|         res.end('{"error":{"message":"module \'init\' needs more arguments"}}'); |       res.end('{"error":{"message":"module \'init\' needs more arguments"}}'); | ||||||
|         return; |       return; | ||||||
|       } |     } | ||||||
| 
 | 
 | ||||||
|       if (Array.isArray(req.body)) { |     if (Array.isArray(req.body)) { | ||||||
|         // relay, email, agree_tos, servernames, ports
 |       // relay, email, agree_tos, servernames, ports
 | ||||||
|         //
 |       //
 | ||||||
|         req.body.forEach(function (opt) { |       req.body.forEach(function (opt) { | ||||||
|           var parts = opt.split(/:/); |         var parts = opt.split(/:/); | ||||||
|           if ('true' === parts[1]) { |         if ('true' === parts[1]) { | ||||||
|             parts[1] = true; |           parts[1] = true; | ||||||
|           } else if ('false' === parts[1]) { |         } else if ('false' === parts[1]) { | ||||||
|             parts[1] = false; |           parts[1] = false; | ||||||
|           } else if ('null' === parts[1]) { |         } else if ('null' === parts[1]) { | ||||||
|             parts[1] = null; |           parts[1] = null; | ||||||
|           } else if ('undefined' === parts[1]) { |         } else if ('undefined' === parts[1]) { | ||||||
|             parts[1] = undefined; |           parts[1] = undefined; | ||||||
|           } |  | ||||||
|           conf[parts[0]] = parts[1]; |  | ||||||
|         }); |  | ||||||
|       } else { |  | ||||||
|         conf = req.body; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       conf = camelCopy(conf); |  | ||||||
| 
 |  | ||||||
|       // TODO deep merge
 |  | ||||||
|       // greenlock config
 |  | ||||||
|       if (!state.config.greenlock) { state.config.greenlock = {}; } |  | ||||||
|       if (conf.greenlock) { |  | ||||||
|         if ('undefined' !== typeof conf.greenlock.agree) { |  | ||||||
|           state.config.greenlock.agree = conf.greenlock.agree; |  | ||||||
|         } |         } | ||||||
|         if (conf.greenlock.server) { state.config.greenlock.server = conf.greenlock.server; } |         conf[parts[0]] = parts[1]; | ||||||
|         if (conf.greenlock.version) { state.config.greenlock.version = conf.greenlock.version; } |       }); | ||||||
|       } |     } else { | ||||||
|  |       conf = req.body; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|       // main config
 |     conf = camelCopy(conf); | ||||||
|       if (conf.email) { state.config.email = conf.email; } |  | ||||||
|       if (conf.relay) { state.config.relay = conf.relay; } |  | ||||||
|       if (conf.token) { state.config.token = conf.token; } |  | ||||||
|       if (conf.secret) { state.config.secret = conf.secret; } |  | ||||||
|       if ('undefined' !== typeof conf.agreeTos) { |  | ||||||
|         state.config.agreeTos = conf.agreeTos; |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       // to state
 |     // TODO deep merge
 | ||||||
|       if (conf.pretoken) { state.pretoken = conf.pretoken; } |     // greenlock config
 | ||||||
|       if (conf._otp) { |     if (!state.config.greenlock) { state.config.greenlock = {}; } | ||||||
|         state.otp = conf._otp; // TODO should this only be done on the client side?
 |     if (conf.greenlock) { | ||||||
|         delete conf._otp; |       if ('undefined' !== typeof conf.greenlock.agree) { | ||||||
|  |         state.config.greenlock.agree = conf.greenlock.agree; | ||||||
|       } |       } | ||||||
|  |       if (conf.greenlock.server) { state.config.greenlock.server = conf.greenlock.server; } | ||||||
|  |       if (conf.greenlock.version) { state.config.greenlock.version = conf.greenlock.version; } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|       console.log(); |     // main config
 | ||||||
|       console.log('conf.token', typeof conf.token, conf.token); |     if (conf.email) { state.config.email = conf.email; } | ||||||
|       console.log('state.config.token', typeof state.config.token, state.config.token); |     if (conf.relay) { state.config.relay = conf.relay; } | ||||||
|  |     if (conf.token) { state.config.token = conf.token; } | ||||||
|  |     if (conf.secret) { state.config.secret = conf.secret; } | ||||||
|  |     if ('undefined' !== typeof conf.agreeTos) { | ||||||
|  |       state.config.agreeTos = conf.agreeTos; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|       if (state.secret) { console.log('state.secret'); state.token = common.signToken(state); } |     // to state
 | ||||||
|       if (!state.token) { console.log('!state.token'); state.token = conf._token; } |     if (conf.pretoken) { state.pretoken = conf.pretoken; } | ||||||
|  |     if (conf._otp) { | ||||||
|  |       state.otp = conf._otp; // TODO should this only be done on the client side?
 | ||||||
|  |       delete conf._otp; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|       console.log(); |     console.log(); | ||||||
|       console.log('JSON.stringify(conf)'); |     console.log('conf.token', typeof conf.token, conf.token); | ||||||
|       console.log(JSON.stringify(conf)); |     console.log('state.config.token', typeof state.config.token, state.config.token); | ||||||
|       console.log(); |  | ||||||
|       console.log('JSON.stringify(state)'); |  | ||||||
|       console.log(JSON.stringify(state)); |  | ||||||
|       console.log(); |  | ||||||
|       if ('undefined' !== typeof conf.newsletter) { |  | ||||||
|         state.config.newsletter = conf.newsletter; |  | ||||||
|       } |  | ||||||
|       if ('undefined' !== typeof conf.communityMember |  | ||||||
|         || 'undefined' !== typeof conf.community_member) { |  | ||||||
|         state.config.communityMember = conf.communityMember || conf.community_member; |  | ||||||
|       } |  | ||||||
|       if ('undefined' !== typeof conf.telemetry) { |  | ||||||
|         state.config.telemetry = conf.telemetry; |  | ||||||
|       } |  | ||||||
|       if (conf._servernames) { |  | ||||||
|         (conf._servernames||'').split(/,/g).forEach(function (key) { |  | ||||||
|           if (!state.config.servernames[key]) { |  | ||||||
|             state.config.servernames[key] = { sub: undefined }; |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|       if (conf._ports) { |  | ||||||
|         (conf._ports||'').split(/,/g).forEach(function (key) { |  | ||||||
|           if (!state.config.ports[key]) { |  | ||||||
|             state.config.ports[key] = {}; |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       if (!state.config.relay || !state.config.email || !state.config.agreeTos) { |     if (state.secret) { console.log('state.secret'); state.token = common.signToken(state); } | ||||||
|         console.warn('missing config'); |     if (!state.token) { console.log('!state.token'); state.token = conf._token; } | ||||||
|         res.statusCode = 400; |  | ||||||
| 
 | 
 | ||||||
|  |     console.log(); | ||||||
|  |     console.log('JSON.stringify(conf)'); | ||||||
|  |     console.log(JSON.stringify(conf)); | ||||||
|  |     console.log(); | ||||||
|  |     console.log('JSON.stringify(state)'); | ||||||
|  |     console.log(JSON.stringify(state)); | ||||||
|  |     console.log(); | ||||||
|  |     if ('undefined' !== typeof conf.newsletter) { | ||||||
|  |       state.config.newsletter = conf.newsletter; | ||||||
|  |     } | ||||||
|  |     if ('undefined' !== typeof conf.communityMember | ||||||
|  |       || 'undefined' !== typeof conf.community_member) { | ||||||
|  |       state.config.communityMember = conf.communityMember || conf.community_member; | ||||||
|  |     } | ||||||
|  |     if ('undefined' !== typeof conf.telemetry) { | ||||||
|  |       state.config.telemetry = conf.telemetry; | ||||||
|  |     } | ||||||
|  |     if (conf._servernames) { | ||||||
|  |       (conf._servernames||'').split(/,/g).forEach(function (key) { | ||||||
|  |         if (!state.config.servernames[key]) { | ||||||
|  |           state.config.servernames[key] = { sub: undefined }; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |     if (conf._ports) { | ||||||
|  |       (conf._ports||'').split(/,/g).forEach(function (key) { | ||||||
|  |         if (!state.config.ports[key]) { | ||||||
|  |           state.config.ports[key] = {}; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!state.config.relay || !state.config.email || !state.config.agreeTos) { | ||||||
|  |       console.warn('missing config'); | ||||||
|  |       res.statusCode = 400; | ||||||
|  | 
 | ||||||
|  |       res.setHeader('Content-Type', 'application/json'); | ||||||
|  |       res.end(JSON.stringify({ | ||||||
|  |         error: { | ||||||
|  |           code: "E_INIT" | ||||||
|  |         , message: "Missing important config file params" | ||||||
|  |         , _params: JSON.stringify(conf) | ||||||
|  |         , _config: JSON.stringify(state.config) | ||||||
|  |         , _body: JSON.stringify(req.body) | ||||||
|  |         } | ||||||
|  |       })); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // init also means enable
 | ||||||
|  |     delete state.config.disable; | ||||||
|  |     safeStartTelebitRemote(true).then(saveAndReport).catch(handleError); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function restart(req, res) { | ||||||
|  |     console.info("[telebitd.js] server closing..."); | ||||||
|  |     state.keepAlive.state = false; | ||||||
|  |     if (myRemote) { | ||||||
|  |       myRemote.end(); | ||||||
|  |       myRemote.on('end', respondAndClose); | ||||||
|  |       // failsafe
 | ||||||
|  |       setTimeout(function () { | ||||||
|  |         console.info("[telebitd.js] closing too slowly, force quit"); | ||||||
|  |         respondAndClose(); | ||||||
|  |       }, 5 * 1000); | ||||||
|  |     } else { | ||||||
|  |       respondAndClose(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function respondAndClose() { | ||||||
|  |       res.setHeader('Content-Type', 'application/json'); | ||||||
|  |       res.end(JSON.stringify({ success: true })); | ||||||
|  |       controlServer.close(function () { | ||||||
|  |         console.info("[telebitd.js] server closed"); | ||||||
|  |         setTimeout(function () { | ||||||
|  |           // system daemon will restart the process
 | ||||||
|  |           process.exit(22); // use non-success exit code
 | ||||||
|  |         }, 100); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function mustHaveValidConfig(req, res, next) { | ||||||
|  |     //
 | ||||||
|  |     // Check for proper config
 | ||||||
|  |     //
 | ||||||
|  |     if (state.config.relay && state.config.email && state.config.agreeTos) { | ||||||
|  |       next(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     res.statusCode = 400; | ||||||
|  |     res.setHeader('Content-Type', 'application/json'); | ||||||
|  |     res.end(JSON.stringify({ | ||||||
|  |       error: { code: "E_CONFIG", message: "Invalid config file. Please run 'telebit init'" } | ||||||
|  |     })); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function saveAndCommit(req, res) { | ||||||
|  |     state.config.servernames = state.servernames; | ||||||
|  |     state.config.ports = state.ports; | ||||||
|  |     fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { | ||||||
|  |       if (err) { | ||||||
|  |         res.statusCode = 500; | ||||||
|         res.setHeader('Content-Type', 'application/json'); |         res.setHeader('Content-Type', 'application/json'); | ||||||
|         res.end(JSON.stringify({ |         res.end(JSON.stringify({ | ||||||
|           error: { |           "error":{"message":"Could not save config file. Perhaps you're not running as root?"} | ||||||
|             code: "E_INIT" |  | ||||||
|           , message: "Missing important config file params" |  | ||||||
|           , _params: JSON.stringify(conf) |  | ||||||
|           , _config: JSON.stringify(state.config) |  | ||||||
|           , _body: JSON.stringify(req.body) |  | ||||||
|           } |  | ||||||
|         })); |         })); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|  |       listSuccess(); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|       // init also means enable
 |   function handleError(err, req, res) { | ||||||
|       delete state.config.disable; |     res.statusCode = 500; | ||||||
|       safeStartTelebitRemote(true).then(saveAndReport).catch(handleError); |     res.setHeader('Content-Type', 'application/json'); | ||||||
|     } |     res.end(JSON.stringify({ | ||||||
|  |       error: { message: err.message, code: err.code } | ||||||
|  |     })); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|     function restart(req, res) { |   function enable(req, res) { | ||||||
|       console.info("[telebitd.js] server closing..."); |     delete state.config.disable;// = undefined;
 | ||||||
|       state.keepAlive.state = false; |     state.keepAlive.state = true; | ||||||
|       if (myRemote) { |  | ||||||
|         myRemote.end(); |  | ||||||
|         myRemote.on('end', respondAndClose); |  | ||||||
|         // failsafe
 |  | ||||||
|         setTimeout(function () { |  | ||||||
|           console.info("[telebitd.js] closing too slowly, force quit"); |  | ||||||
|           respondAndClose(); |  | ||||||
|         }, 5 * 1000); |  | ||||||
|       } else { |  | ||||||
|         respondAndClose(); |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       function respondAndClose() { |     fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { | ||||||
|         res.setHeader('Content-Type', 'application/json'); |       if (err) { | ||||||
|         res.end(JSON.stringify({ success: true })); |         err.message = "Could not save config file. Perhaps you're user doesn't have permission?"; | ||||||
|         controlServer.close(function () { |         handleError(err, req, res); | ||||||
|           console.info("[telebitd.js] server closed"); |  | ||||||
|           setTimeout(function () { |  | ||||||
|             // system daemon will restart the process
 |  | ||||||
|             process.exit(22); // use non-success exit code
 |  | ||||||
|           }, 100); |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function mustHaveValidConfig(req, res, next) { |  | ||||||
|       //
 |  | ||||||
|       // Check for proper config
 |  | ||||||
|       //
 |  | ||||||
|       if (state.config.relay && state.config.email && state.config.agreeTos) { |  | ||||||
|         next(); |  | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|  |       // TODO XXX myRemote.active
 | ||||||
|  |       if (myRemote) { | ||||||
|  |         listSuccess(req, res); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       safeStartTelebitRemote(true).then(listSuccess).catch(function () { | ||||||
|  |         handleError(err, req, res); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|       res.statusCode = 400; |   function disable(req, res) { | ||||||
|  |     state.config.disable = true; | ||||||
|  |     state.keepAlive.state = false; | ||||||
|  | 
 | ||||||
|  |     if (myRemote) { myRemote.end(); myRemote = null; } | ||||||
|  |     fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { | ||||||
|       res.setHeader('Content-Type', 'application/json'); |       res.setHeader('Content-Type', 'application/json'); | ||||||
|       res.end(JSON.stringify({ |       if (err) { | ||||||
|         error: { code: "E_CONFIG", message: "Invalid config file. Please run 'telebit init'" } |         err.message = "Could not save config file. Perhaps you're user doesn't have permission?"; | ||||||
|       })); |         handleError(err); | ||||||
|     } |         return; | ||||||
|  |       } | ||||||
|  |       res.end('{"success":true}'); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|     function saveAndCommit(req, res) { |   function getStatus(req, res) { | ||||||
|       state.config.servernames = state.servernames; |     var now = Date.now(); | ||||||
|       state.config.ports = state.ports; |     res.setHeader('Content-Type', 'application/json'); | ||||||
|       fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { |     require('../lib/ssh.js').checkSecurity().then(function (ssh) { | ||||||
|         if (err) { |       res.end(JSON.stringify( | ||||||
|           res.statusCode = 500; |         { module: 'status' | ||||||
|           res.setHeader('Content-Type', 'application/json'); |         , version: pkg.version | ||||||
|           res.end(JSON.stringify({ |         , port: (state.config.ipc && state.config.ipc.port || state._ipc.port || undefined) | ||||||
|             "error":{"message":"Could not save config file. Perhaps you're not running as root?"} |         , enabled: !state.config.disable | ||||||
|           })); |         , active: !!myRemote | ||||||
|           return; |         , initialized: (state.config.relay && state.config.token && state.config.agreeTos) ? true : false | ||||||
|  |         , connected: isConnected | ||||||
|  |         //, proctime: Math.round(process.uptime() * 1000)
 | ||||||
|  |         , uptime: now - startTime | ||||||
|  |         , runtime: isConnected && connectTimes.length && (now - connectTimes[0]) || 0 | ||||||
|  |         , reconnects: connectTimes.length | ||||||
|  |         , servernames: state.servernames | ||||||
|  |         , ssh: state.config.sshAuto | ||||||
|  |         , ssh_permit_root_login: ssh.permit_root_login | ||||||
|  |         , ssh_password_authentication: ssh.password_authentication | ||||||
|  |         , ssh_requests_password: ssh.requests_password | ||||||
|         } |         } | ||||||
|         listSuccess(); |       )); | ||||||
|       }); |     }); | ||||||
|     } |   } | ||||||
| 
 |  | ||||||
|     function handleError(err, req, res) { |  | ||||||
|       res.statusCode = 500; |  | ||||||
|       res.setHeader('Content-Type', 'application/json'); |  | ||||||
|       res.end(JSON.stringify({ |  | ||||||
|         error: { message: err.message, code: err.code } |  | ||||||
|       })); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function enable(req, res) { |  | ||||||
|       delete state.config.disable;// = undefined;
 |  | ||||||
|       state.keepAlive.state = true; |  | ||||||
| 
 |  | ||||||
|       fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { |  | ||||||
|         if (err) { |  | ||||||
|           err.message = "Could not save config file. Perhaps you're user doesn't have permission?"; |  | ||||||
|           handleError(err, req, res); |  | ||||||
|           return; |  | ||||||
|         } |  | ||||||
|         // TODO XXX myRemote.active
 |  | ||||||
|         if (myRemote) { |  | ||||||
|           listSuccess(req, res); |  | ||||||
|           return; |  | ||||||
|         } |  | ||||||
|         safeStartTelebitRemote(true).then(listSuccess).catch(function () { |  | ||||||
|           handleError(err, req, res); |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function disable(req, res) { |  | ||||||
|       state.config.disable = true; |  | ||||||
|       state.keepAlive.state = false; |  | ||||||
| 
 |  | ||||||
|       if (myRemote) { myRemote.end(); myRemote = null; } |  | ||||||
|       fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { |  | ||||||
|         res.setHeader('Content-Type', 'application/json'); |  | ||||||
|         if (err) { |  | ||||||
|           err.message = "Could not save config file. Perhaps you're user doesn't have permission?"; |  | ||||||
|           handleError(err); |  | ||||||
|           return; |  | ||||||
|         } |  | ||||||
|         res.end('{"success":true}'); |  | ||||||
|       }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function getStatus(req, res) { |  | ||||||
|       var now = Date.now(); |  | ||||||
|       res.setHeader('Content-Type', 'application/json'); |  | ||||||
|       require('../lib/ssh.js').checkSecurity().then(function (ssh) { |  | ||||||
|         res.end(JSON.stringify( |  | ||||||
|           { module: 'status' |  | ||||||
|           , version: pkg.version |  | ||||||
|           , port: (state.config.ipc && state.config.ipc.port || state._ipc.port || undefined) |  | ||||||
|           , enabled: !state.config.disable |  | ||||||
|           , active: !!myRemote |  | ||||||
|           , initialized: (state.config.relay && state.config.token && state.config.agreeTos) ? true : false |  | ||||||
|           , connected: isConnected |  | ||||||
|           //, proctime: Math.round(process.uptime() * 1000)
 |  | ||||||
|           , uptime: now - startTime |  | ||||||
|           , runtime: isConnected && connectTimes.length && (now - connectTimes[0]) || 0 |  | ||||||
|           , reconnects: connectTimes.length |  | ||||||
|           , servernames: state.servernames |  | ||||||
|           , ssh: state.config.sshAuto |  | ||||||
|           , ssh_permit_root_login: ssh.permit_root_login |  | ||||||
|           , ssh_password_authentication: ssh.password_authentication |  | ||||||
|           , ssh_requests_password: ssh.requests_password |  | ||||||
|           } |  | ||||||
|         )); |  | ||||||
|       }); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|   // TODO turn strings into regexes to match beginnings
 |   // TODO turn strings into regexes to match beginnings
 | ||||||
|   app.use(/\b(relay)\b/, controllers.relay); |   app.use(/\b(relay)\b/, controllers.relay); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user