UNPKG

node-gotapi

Version:

The node-gotapi is a Node.js implementation of the Generic Open Terminal API Framework (GotAPI) developed by the Open Mobile Alliance (OMA).

440 lines (408 loc) 13.1 kB
/* ------------------------------------------------------------------ * node-gotapi - gotapi-server.js * * Copyright (c) 2017-2019, Futomi Hatano, All rights reserved. * Released under the MIT license * Date: 2019-10-19 * ---------------------------------------------------------------- */ 'use strict'; process.chdir(__dirname); let mCrypto = require('crypto'); /* ------------------------------------------------------------------ * Constructor: GotapiServer(config) * ---------------------------------------------------------------- */ let GotapiServer = function (config) { this.config = config; this.gotapiInterface1 = null; this.gotapiInterface4 = null; this.gotapiInterface5 = null; this.httpServer = null; this.authorizing_client = null; this.clients = {}; this.requests = {}; // For debug this.oncommunication = null; this.debug_access_token = '0123456789'; this.debug_client_id = '0123456789'; this.error_code_map = require('./error-code.json'); }; /* ------------------------------------------------------------------ * Method: start([optons, [callback]]) * ---------------------------------------------------------------- */ GotapiServer.prototype.start = function (options, callback) { if (options && typeof (options) === 'object') { for (let k in options) { this.config[k] = options[k]; } } if (!this.config['enable_console']) { console.log = () => { }; console.dir = () => { }; console.error = () => { }; } if (this.config['disable_auth']) { this.clients[this.debug_access_token] = { key: '', client_id: this.debug_client_id, access_token: this.debug_access_token, scope: [] }; } let this_module_info = require('../package.json'); this.config.version = this_module_info.version; this.config.product = this_module_info.name; // Create the GotAPI-Interface-1 (HTTP server) let GotapiInterface1 = require('./gotapi-interface-1.js'); this.gotapiInterface1 = new GotapiInterface1(this.config, this._receiveMessageOnIf1.bind(this)); // Create the GotAPI-Interface-4 let GotapiInterface4 = require('./gotapi-interface-4.js'); this.gotapiInterface4 = new GotapiInterface4(this.config, this._receiveMessageOnIf4.bind(this)); // Create the GotAPI-Interface-5 (WebSocket server) let GotapiInterface5 = require('./gotapi-interface-5.js'); this.gotapiInterface5 = new GotapiInterface5(this.config, this.gotapiInterface1.server, this._receiveMessageOnIf5.bind(this)); // Create a HTTP Server for applications let HttpServer = require('./http-server.js'); this.httpServer = new HttpServer(this.config); // Monitor callback if (this.oncommunication && typeof (this.oncommunication) === 'function') { this.gotapiInterface1.oncommunication = this.oncommunication; //this.gotapiInterface4.oncommunication = this.oncommunication; this.gotapiInterface5.oncommunication = this.oncommunication; } // Run the servers this.gotapiInterface1.start().then(() => { console.log('- The GotAPI Interface-1 has been woken up.'); return this.gotapiInterface4.start(); }).then((plugin_list) => { console.log('- The GotAPI Interface-4 has been woken up.'); if (plugin_list.length === 0) { console.log(' - No Plug-In was found.'); } else if (plugin_list.length === 1) { console.log(' - A Plug-In was found:'); } else { console.log(' - ' + plugin_list.length + ' Plug-Ins were found:'); } plugin_list.forEach((p) => { console.log(' - ' + p['info']['name'] + ' (' + p['id'] + ')'); }); return this.gotapiInterface5.start(); }).then(() => { console.log('- The GotAPI Interface-5 has been woken up.'); let port1 = 0; let port2 = 0; let scheme = ''; if (this.config['ssl_engine'] === true) { port1 = this.config['gotapi_if_ssl_port']; port2 = this.config['https_server_port']; scheme = 'https:'; } else { port1 = this.config['gotapi_if_port']; port2 = this.config['http_server_port']; scheme = 'http:'; } if (port1 === port2) { console.log('The GotAPI Server has been started successfully.') console.log('Your web application can be accessed at:'); console.log('- ' + scheme + '//localhost:' + port1); if (callback && typeof (callback) === 'function') { callback(); } } else { this.httpServer.start().then(() => { console.log('- The HTTP server has been woken up.'); console.log('The GotAPI Server has been started successfully.') console.log('Your web application can be accessed at:'); console.log('- [1] ' + scheme + '//localhost:' + port1 + ' (RECOMMENDED)'); console.log('- [2] ' + scheme + '//localhost:' + port2 + ' (DEPRECATED)'); if (callback && typeof (callback) === 'function') { callback(); } }).catch((error) => { console.log(error.message); return; }); } }).catch((error) => { console.log(error.message); return; }); }; /* ------------------------------------------------------------------ * Methods for the GotAPI-1 Interface (HTTP channel for web apps) * ---------------------------------------------------------------- */ GotapiServer.prototype._receiveMessageOnIf1 = function (data) { /* * data = { * if_type : 1, * request_id : 12345678, * request_url : "/gotapi/availability?key=01234567", * params : { * key: 01234567 * }, * package : "http://localhost:8080", // The origin of the web app * api : "gotapi", * profile : "authorization", * attribute : "accesstoken", * method : "get" * } */ let request_id = data['request_id']; this.requests[request_id] = data; let prof = data['profile']; let attr = data['attribute']; if (prof === 'availability') { this._availabilityApi(data); /* } else if(prof === 'authorization' && attr === 'grant') { this._authorizationGrant(data); } else if(prof === 'authorization' && attr === 'accesstoken') { this._authorizationAccesstoken(data); */ } else if (prof === 'authorization') { if (attr === 'grant') { this._authorizationGrant(data); } else if (attr === 'accesstoken') { this._authorizationAccesstoken(data); } else { this._returnErrorMessageToIf1( this.error_code_map['INVALID_ATTRIBUTE'], 'Unknow attribute was specified.', data ); } // } else if (prof === 'servicediscovery') { // this._serviceDiscovery(data); } else if (prof) { this._requestToPlugin(data); } else { this._returnErrorMessageToIf1( this.error_code_map['INVALID_PROFILE'], 'No profile was specified.', data ); } }; GotapiServer.prototype._availabilityApi = function (data) { let p = data.params; if (p.key) { this.authorizing_client = { key: p.key, client_id: '', access_token: '', scope: [] }; this._returnMessageToIf1(data); } else { this._returnErrorMessageToIf1( this.error_code_map['INVALID_PARAMETER'], 'The parameter `key` is required.', data ); } }; GotapiServer.prototype._authorizationGrant = function (data) { let p = data.params; if (this.authorizing_client) { let client_id = this._createUniqueRandomString(); this.authorizing_client['client_id'] = client_id; data['clientId'] = client_id; data['_client'] = JSON.parse(JSON.stringify(this.authorizing_client)); this._returnMessageToIf1(data); } else { this._returnErrorMessageToIf1( this.error_code_map['NOT_AUTHORIZED'], 'The availability API has not been called yet.', data ); } }; GotapiServer.prototype._createUniqueRandomString = function () { let id = ''; id += mCrypto.randomBytes(32).toString('hex') + '_'; id += Date.now(); let sha256 = mCrypto.createHash('sha256'); sha256.update(id); id = sha256.digest('hex'); return id; }; GotapiServer.prototype._authorizationAccesstoken = function (data) { let p = data.params; if (!this.authorizing_client) { this._returnErrorMessageToIf1( this.error_code_map['NOT_AUTHORIZED'], 'The availability API has not been called yet.', data ); return; } if (p.clientId !== this.authorizing_client['client_id']) { this._returnErrorMessageToIf1( this.error_code_map['INVALID_CLIENT_ID'], 'The specified client ID is invalid.', data ); return; } if (!p.scope) { this._returnErrorMessageToIf1( this.error_code_map['INVALID_PARAMETER'], 'The parameter "scope" is required.', data ); return; } let access_token = this._createUniqueRandomString(); this.authorizing_client['access_token'] = access_token; this.authorizing_client['scope'] = p.scope.split(/\,\s*/); this.clients[access_token] = this.authorizing_client; data['accessToken'] = access_token; data['_client'] = JSON.parse(JSON.stringify(this.authorizing_client)); this._returnMessageToIf1(data); this.authorizing_client = null; }; /* GotapiServer.prototype._serviceDiscovery = function (data) { let p = data.params; let access_token = p['accessToken']; if (access_token) { if (!this.clients[access_token]) { if (this.config['disable_auth']) { access_token = this.debug_access_token; } else { this._returnErrorMessageToIf1( this.error_code_map['INVALID_TOKEN'], 'The specified access token is invalid.', data ); return; } } } else { if (this.config['disable_auth']) { access_token = this.debug_access_token; } else { this._returnErrorMessageToIf1( this.error_code_map['INVALID_TOKEN'], 'The access token is required.', data ); return; } } let client = this.clients[access_token]; data['_client'] = JSON.parse(JSON.stringify(client)); data['receiver'] = this.config.gotapi_server_app_id; this.gotapiInterface4.postMessage(data); }; */ GotapiServer.prototype._requestToPlugin = function (data) { let p = data.params; let access_token = p['accessToken']; if (access_token) { if (!this.clients[access_token]) { if (this.config['disable_auth']) { access_token = this.debug_access_token; } else { this._returnErrorMessageToIf1( this.error_code_map['INVALID_TOKEN'], 'The specified access token is invalid.', data ); return; } } } else { if (this.config['disable_auth']) { access_token = this.debug_access_token; } else { this._returnErrorMessageToIf1( this.error_code_map['INVALID_TOKEN'], 'The access token is required.', data ); return; } } let client = this.clients[access_token]; data['_client'] = JSON.parse(JSON.stringify(client)); data['receiver'] = this.config.gotapi_server_app_id; this.gotapiInterface4.postMessage(data); }; GotapiServer.prototype._returnErrorMessageToIf1 = function (error_code_obj, message, data) { data['result'] = error_code_obj['result']; data['errorCode'] = error_code_obj['errorCode']; data['errorMessage'] = '[' + error_code_obj['errorName'] + '] ' + message; data['statusCode'] = error_code_obj['statusCode']; this._returnMessageToIf1(data); }; GotapiServer.prototype._returnMessageToIf1 = function (data) { data['product'] = this.config.product; data['version'] = this.config.version; if (!data['result']) { data['result'] = 0; } let request_id = data['request_id']; this.gotapiInterface1.postMessage(data); if (this.requests[request_id]) { delete this.requests[request_id]; } }; /* ------------------------------------------------------------------ * Methods for the GotAPI-5 Interface (WebSocket channel for web apps) * ---------------------------------------------------------------- */ GotapiServer.prototype._receiveMessageOnIf5 = function (data) { /* * data = { * if_type : 5, * ws_conn_id : "e8188012c2420bc036ba69461d7c23bc61321e687179c7159c79697b5b1435e4", * accessToken : "0c68d31ffd58bd691c101a9e8d8ae02276bab905c161e092534d3e80f527427b", * profile : "authorization", * attribute : "verify", * } */ if (data['profile'] === 'authorization' && data['attribute'] === 'verify') { let access_token = data['accessToken']; if (this._verifyAccessToken(access_token)) { data['result'] = 0; data['errorCode'] = 0; data['errorMessage'] = ''; } else { data['result'] = 401; data['errorCode'] = 401; data['errorMessage'] = 'The posted access token is not authorized.'; } this.gotapiInterface5.postMessage(data); } }; GotapiServer.prototype._verifyAccessToken = function (access_token) { if (!this.config['disable_auth']) { return true; } if (!access_token) { return false; } let is_valid = false; for (let id in this.clients) { let client = this.clients[id]; if (client['access_token'] === access_token) { is_valid = true; break; } } return is_valid; }; GotapiServer.prototype._returnMessageToIf5 = function (data) { this.gotapiInterface5.postMessage(data); }; /* ------------------------------------------------------------------ * Methods for the GotAPI-4 Interface (Plug-Ins) * ---------------------------------------------------------------- */ GotapiServer.prototype._receiveMessageOnIf4 = function (data) { // Pass the response coming from a Plug-In to the Web app if (data['action'] === 'EVENT') { this._returnMessageToIf5(data); } else { this._returnMessageToIf1(data); } }; module.exports = GotapiServer;