diff --git a/src/fireedge/config.yml b/src/fireedge/config.yml index 59a5d05776..a499f4f78a 100644 --- a/src/fireedge/config.yml +++ b/src/fireedge/config.yml @@ -20,4 +20,9 @@ TOKEN_SECRET: secret_token # JWT life time (days) LIMIT_TOKEN: MIN: 14 - MAX: 30 \ No newline at end of file + MAX: 30 + +# VMRC +VMRC: + TARGET: 'http://opennebula.io' + TOKENS_PATH: '/var/lib/one/sunstone_vnc_tokens/vmrc' diff --git a/src/fireedge/genPotFile.js b/src/fireedge/genPotFile.js deleted file mode 100644 index e274759488..0000000000 --- a/src/fireedge/genPotFile.js +++ /dev/null @@ -1,30 +0,0 @@ -const { createReadStream, generateFile } = require('fireedge-genpotfile'); -const constants = require('./src/utils/constants'); -const clientConstants = require('./src/public/constants'); - -const testFolder = './src/public'; -const exportFile = 'test.po'; -const definitions = { ...constants, ...clientConstants }; - -// function Tr() -const optsFunc = { - regex: /Tr(\("|\('|\()[a-zA-Z0-9_ ]*("\)|'\)|\))/g, - removeStart: /Tr(\()/g, - removeEnd: /(\))/g, - regexTextCaptureIndex: 0, - definitions -}; - -// React component -const optsComponent = { - regex: //g, - removeStart: //g, - regexTextCaptureIndex: 0, - definitions -}; - -createReadStream(testFolder, optsFunc); -createReadStream(testFolder, optsComponent); - -generateFile(exportFile); diff --git a/src/fireedge/package.json b/src/fireedge/package.json index 898b77ecc3..d0f0888827 100644 --- a/src/fireedge/package.json +++ b/src/fireedge/package.json @@ -12,7 +12,8 @@ "start": "node dist/index", "cypress:open": "cypress open", "cypress:run": "cypress run --headless --browser chrome --spec \"cypress/integration/**/*.spec.js\"", - "genPot": "node genPotFile.js", + "pot": "node potfile.js", + "po2json": "node po2json.js", "copy_static_assets": "node copyStaticAssets.js" }, "keywords": [ @@ -41,11 +42,11 @@ "concurrently": "^5.2.0", "cors": "^2.8.5", "express": "^4.17.1", - "fireedge-genpotfile": "^1.0.0", "fs-extra": "^9.0.1", "fuse.js": "^6.4.1", "helmet": "^3.23.3", "http": "0.0.1-security", + "http-proxy-middleware": "^1.0.5", "immutable": "^4.0.0-rc.12", "intersection-observer": "^0.11.0", "jsonschema": "^1.2.6", @@ -101,20 +102,17 @@ "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-cypress": "^2.11.1", "file-loader": "^6.0.0", + "fireedge-genpotfile": "^1.0.0", + "fireedge-pojson": "^1.0.1", "jloboescalona-eslint-config": "^1.1.0", "node-sass": "^4.12.0", "nodemon": "^1.18.10", - "once": "^1.4.0", + "path": "^0.12.7", "react-addons-test-utils": "^15.6.2", - "readdirp": "^3.4.0", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", "style-loader": "^1.0.0", - "through2": "^4.0.2", "webpack": "^4.43.0", "webpack-cli": "^3.3.7", "webpack-livereload-plugin": "^2.3.0", - "webpack-node-externals": "^1.7.2", - "xgettext-regex": "^0.3.0" + "webpack-node-externals": "^1.7.2" } } diff --git a/src/fireedge/po2json.js b/src/fireedge/po2json.js new file mode 100644 index 0000000000..6969fae4ac --- /dev/null +++ b/src/fireedge/po2json.js @@ -0,0 +1,22 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const { createReadStream, generateFile } = require('fireedge-pojson'); + +const testFolder = './src/public/assets/languages'; + +createReadStream(testFolder, { exportPath: testFolder }); + +generateFile(); diff --git a/src/fireedge/potfile.js b/src/fireedge/potfile.js new file mode 100644 index 0000000000..998ecdf38b --- /dev/null +++ b/src/fireedge/potfile.js @@ -0,0 +1,45 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const { createReadStream, generateFile } = require('fireedge-genpotfile'); +const constants = require('./src/utils/constants'); +const clientConstants = require('./src/public/constants'); + +const testFolder = './src/public'; +const exportFile = './src/public/assets/languages/messages.pot'; +const definitions = { ...constants, ...clientConstants }; + +// function Tr() +const optsFunc = { + regex: /Tr(\("|\('|\()[a-zA-Z0-9_ ]*("\)|'\)|\))/g, + removeStart: /Tr(\()/g, + removeEnd: /(\))/g, + regexTextCaptureIndex: 0, + definitions +}; + +// React component +const optsComponent = { + regex: //g, + removeStart: //g, + regexTextCaptureIndex: 0, + definitions +}; + +createReadStream(testFolder, optsFunc); +createReadStream(testFolder, optsComponent); + +generateFile(exportFile); diff --git a/src/fireedge/src/index.js b/src/fireedge/src/index.js index 5178be1dca..c8b4b52f56 100644 --- a/src/fireedge/src/index.js +++ b/src/fireedge/src/index.js @@ -19,7 +19,6 @@ const helmet = require('helmet'); const express = require('express'); const morgan = require('morgan'); const path = require('path'); -const socketIO = require('socket.io'); const cors = require('cors'); const { accessSync, @@ -33,7 +32,8 @@ const bodyParser = require('body-parser'); const { defaultConfigLogPath, defaultConfigLogFile, - defaultTypeLog + defaultTypeLog, + defaultPort } = require('./utils/constants/defaults'); const { validateServerIsSecure, getCert, getKey } = require('./utils/server'); const { @@ -41,13 +41,15 @@ const { entrypointApi, entrypointApp } = require('./routes/entrypoints'); -const { messageTerminal, addWsServer, getConfig } = require('./utils'); +const { oneHooks } = require('./routes/websockets/zeromq'); +const { vmrcUpgrade } = require('./routes/websockets/vmrc'); +const { messageTerminal, getConfig } = require('./utils'); const app = express(); // settings const appConfig = getConfig(); -const port = appConfig.PORT || 3000; +const port = appConfig.PORT || defaultPort; const userLog = appConfig.LOG || 'dev'; let log = morgan('dev'); @@ -102,9 +104,7 @@ const appServer = validateServerIsSecure() ) : unsecureServer(app); -// connect to websocket -const io = socketIO.listen(appServer); -addWsServer(io); +oneHooks(appServer); appServer.listen(port, () => { const config = { @@ -114,3 +114,4 @@ appServer.listen(port, () => { }; messageTerminal(config); }); +vmrcUpgrade(appServer); diff --git a/src/fireedge/src/public/containers/Websocket/index.js b/src/fireedge/src/public/containers/Webconsole/index.js similarity index 88% rename from src/fireedge/src/public/containers/Websocket/index.js rename to src/fireedge/src/public/containers/Webconsole/index.js index b3abc623a0..02e46e545f 100644 --- a/src/fireedge/src/public/containers/Websocket/index.js +++ b/src/fireedge/src/public/containers/Webconsole/index.js @@ -15,18 +15,20 @@ import React, { useState, useEffect } from 'react'; import io from 'socket.io-client'; -import { findStorageData } from '../../../utils'; +import { findStorageData } from 'client/utils'; import constants from 'client/constants'; +import { defaultPort } from 'server/utils/constants/defaults'; const { jwtName } = constants; -const ENDPOINT = 'http://127.0.0.1:3000'; +const ENDPOINT = `http://127.0.0.1:${defaultPort}`; -function Webconsole() { +const Webconsole = () => { const [response, setResponse] = useState({}); useEffect(() => { const socket = io(ENDPOINT, { + path: '/zeromq', query: { token: findStorageData(jwtName) } @@ -37,6 +39,5 @@ function Webconsole() { }, []); console.log('-->', response); return

; -} - +}; export default Webconsole; diff --git a/src/fireedge/src/public/containers/Websocket/WSConsole.js b/src/fireedge/src/public/containers/Websocket/WSConsole.js deleted file mode 100644 index 937f547bc0..0000000000 --- a/src/fireedge/src/public/containers/Websocket/WSConsole.js +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ -/* */ -/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ -/* not use this file except in compliance with the License. You may obtain */ -/* a copy of the License at */ -/* */ -/* http://www.apache.org/licenses/LICENSE-2.0 */ -/* */ -/* Unless required by applicable law or agreed to in writing, software */ -/* distributed under the License is distributed on an "AS IS" BASIS, */ -/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ -/* See the License for the specific language governing permissions and */ -/* limitations under the License. */ -/* -------------------------------------------------------------------------- */ - -import React, { Component } from 'react'; -import { w3cwebsocket as W3CWebSocket } from 'websocket'; -import JSONPretty from 'react-json-pretty'; -import { messageTerminal } from '../../../utils/general'; - -class WSConsole extends Component { - constructor(props) { - super(props); - this.state = { - data: '' - }; - } - - componentWillMount() { - const client = new W3CWebSocket( - 'ws://127.0.0.1:3000?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIwIiwiYXVkIjoib25lYWRtaW4iLCJqdGkiOiJkYzNjMmJkMDhlODM3MmU0NWY5MDVmNjE0ZjdkMDE0YmQ2ZmE3ZTVjYjlmNjU1OTRjNjRjYzMzZjNiYmYxY2IyIiwiaWF0IjoxNTY5MjIxOTgwLCJleHAiOjE1NzA0MzE1ODB9.ESxu8xeGNVQCAJ2T5-93y2cWofXCNxvsAdT0Jt5Qt5I' - ); - - client.onopen = () => { - const config = { - color: 'green', - type: 'ERROR', - message: 'WebSocket Client Connected' - }; - messageTerminal(config); - }; - - client.onmessage = message => { - if (message && message.data) { - this.setState({ - data: JSON.parse(message.data) - }); - } - }; - } - - render() { - const { data } = this.state; - return ; - } -} - -export default WSConsole; diff --git a/src/fireedge/src/public/router/endpoints.js b/src/fireedge/src/public/router/endpoints.js index 9fe66c77e6..a9d4401808 100644 --- a/src/fireedge/src/public/router/endpoints.js +++ b/src/fireedge/src/public/router/endpoints.js @@ -26,6 +26,7 @@ import Login from 'client/containers/Login'; import Dashboard from 'client/containers/Dashboard'; import Settings from 'client/containers/Settings'; import TestApi from 'client/containers/TestApi'; +import Webconsole from 'client/containers/Webconsole'; import { ApplicationCreate, ApplicationDeploy, @@ -74,6 +75,14 @@ const ENDPOINTS = [ icon: BallotIcon, component: TestApi }, + { + label: 'Webconsole', + path: '/webconsole', + authenticated: true, + devMode: true, + icon: BallotIcon, + component: Webconsole + }, { label: 'Create Application', path: PATH.APPLICATION.CREATE, diff --git a/src/fireedge/src/routes/api/routes/2fa/functions.js b/src/fireedge/src/routes/api/routes/2fa/functions.js new file mode 100644 index 0000000000..773463af1d --- /dev/null +++ b/src/fireedge/src/routes/api/routes/2fa/functions.js @@ -0,0 +1,279 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const speakeasy = require('speakeasy'); +const qrcode = require('qrcode'); +const { httpResponse } = require('../../../../utils/server'); +const { getConfig } = require('../../../../utils/yml'); +const { + httpMethod, + defaultMethodUserInfo, + defaultMethodUserUpdate, + default2FAIssuer, + default2FAOpennebulaVar, + default2FAOpennebulaTmpVar +} = require('../../../../utils/constants/defaults'); + +const { POST, GET } = httpMethod; +const { + responseOpennebula, + checkOpennebulaCommand, + generateNewTemplate, + check2Fa +} = require('../../../../utils/opennebula'); +const { from: fromData } = require('../../../../utils/constants/defaults'); +const { + ok, + unauthorized, + internalServerError +} = require('../../../../utils/constants/http-codes'); + +// user config +const appConfig = getConfig(); + +const twoFactorAuthIssuer = + appConfig.TWO_FACTOR_AUTH_ISSUER || default2FAIssuer; + +const getUserInfoAuthenticated = (connect, userId, callback, next) => { + if ( + connect && + !!userId && + callback && + next && + typeof connect === 'function' && + typeof callback === 'function' && + typeof next === 'function' && + defaultMethodUserInfo + ) { + const connectOpennebula = connect(); + const dataUser = {}; + // empty positions for validate... + dataUser[fromData.resource] = {}; + dataUser[fromData.query] = {}; + dataUser[fromData.postBody] = {}; + dataUser[fromData.resource].id = userId; + const getOpennebulaMethod = checkOpennebulaCommand( + defaultMethodUserInfo, + GET + ); + connectOpennebula( + defaultMethodUserInfo, + getOpennebulaMethod(dataUser), + (err, value) => { + responseOpennebula( + () => undefined, + err, + value, + info => { + if (info !== undefined && info !== null) { + callback(info); + } else { + next(); + } + }, + next + ); + } + ); + } +}; + +const generateQR = (req, res, next, connect, userId) => { + const secret = speakeasy.generateSecret({ + length: 10, + name: twoFactorAuthIssuer + }); + if (secret && secret.otpauth_url && secret.base32) { + const { otpauth_url: otpURL, base32 } = secret; + qrcode.toDataURL(otpURL, (err, dataURL) => { + if (err) { + res.locals.httpCode = httpResponse(internalServerError); + next(); + } else { + const connectOpennebula = connect(); + getUserInfoAuthenticated( + connect, + userId, + info => { + if (info && info.USER && info.USER.TEMPLATE && req) { + const dataUser = Map(req).toObject(); + const emptyTemplate = {}; + emptyTemplate[default2FAOpennebulaTmpVar] = base32; + + dataUser[fromData.resource].id = userId; + dataUser[fromData.postBody].template = generateNewTemplate( + info.USER.TEMPLATE.SUNSTONE || {}, + emptyTemplate, + [default2FAOpennebulaVar] + ); + const getOpennebulaMethod = checkOpennebulaCommand( + defaultMethodUserUpdate, + POST + ); + connectOpennebula( + defaultMethodUserUpdate, + getOpennebulaMethod(dataUser), + (error, value) => { + responseOpennebula( + () => undefined, + error, + value, + pass => { + if (pass !== undefined && pass !== null) { + res.locals.httpCode = httpResponse(ok, { + img: dataURL + }); + next(); + } else { + next(); + } + }, + next + ); + } + ); + } else { + next(); + } + }, + next + ); + } + }); + } else { + next(); + } +}; + +const twoFactorSetup = (req, res, next, connect, userId) => { + const connectOpennebula = connect(); + getUserInfoAuthenticated( + connect, + userId, + info => { + if ( + info && + info.USER && + info.USER.TEMPLATE && + info.USER.TEMPLATE.SUNSTONE && + info.USER.TEMPLATE.SUNSTONE[default2FAOpennebulaTmpVar] && + fromData && + fromData.postBody && + req && + req[fromData.postBody] && + req[fromData.postBody].token + ) { + const sunstone = info.USER.TEMPLATE.SUNSTONE; + const token = req[fromData.postBody].token; + const secret = sunstone[default2FAOpennebulaTmpVar]; + if (check2Fa(secret, token)) { + const emptyTemplate = {}; + emptyTemplate[default2FAOpennebulaVar] = secret; + + const dataUser = Map(req).toObject(); + dataUser[fromData.resource].id = userId; + dataUser[fromData.postBody].template = generateNewTemplate( + sunstone || {}, + emptyTemplate, + [default2FAOpennebulaTmpVar] + ); + const getOpennebulaMethodUpdate = checkOpennebulaCommand( + defaultMethodUserUpdate, + POST + ); + connectOpennebula( + defaultMethodUserUpdate, + getOpennebulaMethodUpdate(dataUser), + (err, value) => { + responseOpennebula( + () => undefined, + err, + value, + pass => { + if (pass !== undefined && pass !== null) { + res.locals.httpCode = httpResponse(ok); + } + next(); + }, + next + ); + } + ); + } else { + res.locals.httpCode = httpResponse(unauthorized); + next(); + } + } else { + next(); + } + }, + next + ); +}; +const twoFactorDelete = (req, res, next, connect, userId) => { + const connectOpennebula = connect(); + getUserInfoAuthenticated( + connect, + userId, + info => { + if ( + info && + info.USER && + info.USER.TEMPLATE && + info.USER.TEMPLATE.SUNSTONE + ) { + const emptyTemplate = {}; + const dataUser = Map(req).toObject(); + dataUser[fromData.resource].id = userId; + dataUser[fromData.postBody].template = generateNewTemplate( + info.USER.TEMPLATE.SUNSTONE || {}, + emptyTemplate, + [default2FAOpennebulaTmpVar, default2FAOpennebulaVar] + ); + const getOpennebulaMethodUpdate = checkOpennebulaCommand( + defaultMethodUserUpdate, + POST + ); + connectOpennebula( + defaultMethodUserUpdate, + getOpennebulaMethodUpdate(dataUser), + (err, value) => { + responseOpennebula( + () => undefined, + err, + value, + pass => { + if (pass !== undefined && pass !== null) { + res.locals.httpCode = httpResponse(ok); + } + next(); + }, + next + ); + } + ); + } else { + next(); + } + }, + next + ); +}; +module.exports = { + getUserInfoAuthenticated, + generateQR, + twoFactorSetup, + twoFactorDelete +}; diff --git a/src/fireedge/src/routes/api/routes/2fa/index.js b/src/fireedge/src/routes/api/routes/2fa/index.js index 730b366b5c..26a4f59e80 100644 --- a/src/fireedge/src/routes/api/routes/2fa/index.js +++ b/src/fireedge/src/routes/api/routes/2fa/index.js @@ -13,283 +13,31 @@ /* limitations under the License. */ /* -------------------------------------------------------------------------- */ -const { Map } = require('immutable'); -const speakeasy = require('speakeasy'); -const qrcode = require('qrcode'); -const { getConfig } = require('../../../../utils/yml'); -const { - httpMethod, - defaultMethodUserInfo, - defaultMethodUserUpdate, - default2FAIssuer, - default2FAOpennebulaVar, - default2FAOpennebulaTmpVar -} = require('../../../../utils/constants/defaults'); +const { generateQR, twoFactorSetup, twoFactorDelete } = require('./functions'); +const { httpMethod } = require('../../../../utils/constants/defaults'); const { TWO_FACTOR_QR, TWO_FACTOR_DELETE, TWO_FACTOR_SETUP } = require('./string-routes'); -const { - ok, - unauthorized, - internalServerError -} = require('../../../../utils/constants/http-codes'); -const { from: fromData } = require('../../../../utils/constants/defaults'); -const { - responseOpennebula, - checkOpennebulaCommand, - generateNewTemplate, - check2Fa -} = require('../../../../utils/opennebula'); -const { httpResponse } = require('../../../../utils/server'); -// user config -const appConfig = getConfig(); - -const twoFactorAuthIssuer = - appConfig.TWO_FACTOR_AUTH_ISSUER || default2FAIssuer; - -const { POST, GET, DELETE } = httpMethod; - -const getUserInfoAuthenticated = (connect, userId, callback, next) => { - if ( - connect && - !!userId && - callback && - next && - typeof connect === 'function' && - typeof callback === 'function' && - typeof next === 'function' && - defaultMethodUserInfo - ) { - const connectOpennebula = connect(); - const dataUser = {}; - // empty positions for validate... - dataUser[fromData.resource] = {}; - dataUser[fromData.query] = {}; - dataUser[fromData.postBody] = {}; - dataUser[fromData.resource].id = userId; - const getOpennebulaMethod = checkOpennebulaCommand( - defaultMethodUserInfo, - GET - ); - connectOpennebula( - defaultMethodUserInfo, - getOpennebulaMethod(dataUser), - (err, value) => { - responseOpennebula( - () => undefined, - err, - value, - info => { - if (info !== undefined && info !== null) { - callback(info); - } else { - next(); - } - }, - next - ); - } - ); - } -}; +const { POST, DELETE } = httpMethod; const privateRoutes = [ { httpMethod: POST, endpoint: TWO_FACTOR_QR, - action: (req, res, next, connect, userId) => { - const secret = speakeasy.generateSecret({ - length: 10, - name: twoFactorAuthIssuer - }); - if (secret && secret.otpauth_url && secret.base32) { - const { otpauth_url: otpURL, base32 } = secret; - qrcode.toDataURL(otpURL, (err, dataURL) => { - if (err) { - res.locals.httpCode = httpResponse(internalServerError); - next(); - } else { - const connectOpennebula = connect(); - getUserInfoAuthenticated( - connect, - userId, - info => { - if (info && info.USER && info.USER.TEMPLATE && req) { - const dataUser = Map(req).toObject(); - const emptyTemplate = {}; - emptyTemplate[default2FAOpennebulaTmpVar] = base32; - - dataUser[fromData.resource].id = userId; - dataUser[ - fromData.postBody - ].template = generateNewTemplate( - info.USER.TEMPLATE.SUNSTONE || {}, - emptyTemplate, - [default2FAOpennebulaVar] - ); - const getOpennebulaMethod = checkOpennebulaCommand( - defaultMethodUserUpdate, - POST - ); - connectOpennebula( - defaultMethodUserUpdate, - getOpennebulaMethod(dataUser), - (error, value) => { - responseOpennebula( - () => undefined, - error, - value, - pass => { - if (pass !== undefined && pass !== null) { - res.locals.httpCode = httpResponse(ok, { - img: dataURL - }); - next(); - } else { - next(); - } - }, - next - ); - } - ); - } else { - next(); - } - }, - next - ); - } - }); - } else { - next(); - } - } + action: generateQR }, { httpMethod: POST, endpoint: TWO_FACTOR_SETUP, - action: (req, res, next, connect, userId) => { - const connectOpennebula = connect(); - getUserInfoAuthenticated( - connect, - userId, - info => { - if ( - info && - info.USER && - info.USER.TEMPLATE && - info.USER.TEMPLATE.SUNSTONE && - info.USER.TEMPLATE.SUNSTONE[default2FAOpennebulaTmpVar] && - fromData && - fromData.postBody && - req && - req[fromData.postBody] && - req[fromData.postBody].token - ) { - const sunstone = info.USER.TEMPLATE.SUNSTONE; - const token = req[fromData.postBody].token; - const secret = sunstone[default2FAOpennebulaTmpVar]; - if (check2Fa(secret, token)) { - const emptyTemplate = {}; - emptyTemplate[default2FAOpennebulaVar] = secret; - - const dataUser = Map(req).toObject(); - dataUser[fromData.resource].id = userId; - dataUser[ - fromData.postBody - ].template = generateNewTemplate(sunstone || {}, emptyTemplate, [ - default2FAOpennebulaTmpVar - ]); - const getOpennebulaMethodUpdate = checkOpennebulaCommand( - defaultMethodUserUpdate, - POST - ); - connectOpennebula( - defaultMethodUserUpdate, - getOpennebulaMethodUpdate(dataUser), - (err, value) => { - responseOpennebula( - () => undefined, - err, - value, - pass => { - if (pass !== undefined && pass !== null) { - res.locals.httpCode = httpResponse(ok); - } - next(); - }, - next - ); - } - ); - } else { - res.locals.httpCode = httpResponse(unauthorized); - next(); - } - } else { - next(); - } - }, - next - ); - } + action: twoFactorSetup }, { httpMethod: DELETE, endpoint: TWO_FACTOR_DELETE, - action: (req, res, next, connect, userId) => { - const connectOpennebula = connect(); - getUserInfoAuthenticated( - connect, - userId, - info => { - if ( - info && - info.USER && - info.USER.TEMPLATE && - info.USER.TEMPLATE.SUNSTONE - ) { - const emptyTemplate = {}; - const dataUser = Map(req).toObject(); - dataUser[fromData.resource].id = userId; - dataUser[fromData.postBody].template = generateNewTemplate( - info.USER.TEMPLATE.SUNSTONE || {}, - emptyTemplate, - [default2FAOpennebulaTmpVar, default2FAOpennebulaVar] - ); - const getOpennebulaMethodUpdate = checkOpennebulaCommand( - defaultMethodUserUpdate, - POST - ); - connectOpennebula( - defaultMethodUserUpdate, - getOpennebulaMethodUpdate(dataUser), - (err, value) => { - responseOpennebula( - () => undefined, - err, - value, - pass => { - if (pass !== undefined && pass !== null) { - res.locals.httpCode = httpResponse(ok); - } - next(); - }, - next - ); - } - ); - } else { - next(); - } - }, - next - ); - } + action: twoFactorDelete } ]; diff --git a/src/fireedge/src/routes/api/routes/2fa/string-routes.js b/src/fireedge/src/routes/api/routes/2fa/string-routes.js index 2474f4a544..9995646411 100644 --- a/src/fireedge/src/routes/api/routes/2fa/string-routes.js +++ b/src/fireedge/src/routes/api/routes/2fa/string-routes.js @@ -1,3 +1,18 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + const TWO_FACTOR_SETUP = '2fsetup'; const TWO_FACTOR_QR = '2fqr'; const TWO_FACTOR_DELETE = '2fdelete'; diff --git a/src/fireedge/src/routes/api/routes/auth/index.js b/src/fireedge/src/routes/api/routes/auth/index.js index efcb84d01e..7847e5f6d0 100644 --- a/src/fireedge/src/routes/api/routes/auth/index.js +++ b/src/fireedge/src/routes/api/routes/auth/index.js @@ -14,9 +14,7 @@ /* -------------------------------------------------------------------------- */ const { Map } = require('immutable'); -const { - AUTH, -} = require('./string-routes'); +const { AUTH } = require('./string-routes'); const { httpMethod, defaultMethodLogin diff --git a/src/fireedge/src/routes/api/routes/auth/string-routes.js b/src/fireedge/src/routes/api/routes/auth/string-routes.js index 485ce0dcd0..8c294217dd 100644 --- a/src/fireedge/src/routes/api/routes/auth/string-routes.js +++ b/src/fireedge/src/routes/api/routes/auth/string-routes.js @@ -1,3 +1,18 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + const AUTH = 'auth'; const Actions = { diff --git a/src/fireedge/src/routes/api/routes/oneflow/string-routes.js b/src/fireedge/src/routes/api/routes/oneflow/string-routes.js index 5d3f59da23..259a60db97 100644 --- a/src/fireedge/src/routes/api/routes/oneflow/string-routes.js +++ b/src/fireedge/src/routes/api/routes/oneflow/string-routes.js @@ -1,3 +1,18 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + const SERVICE = 'service'; const SERVICE_ACTION = 'service-action'; const SERVICE_SCALE = 'service-scale'; diff --git a/src/fireedge/src/routes/websockets/vmrc/index.js b/src/fireedge/src/routes/websockets/vmrc/index.js new file mode 100644 index 0000000000..c336c928af --- /dev/null +++ b/src/fireedge/src/routes/websockets/vmrc/index.js @@ -0,0 +1,74 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ +const { createProxyMiddleware } = require('http-proxy-middleware'); +const { readFileSync } = require('fs-extra'); +const { getConfig } = require('../../../utils/yml'); +const { messageTerminal } = require('../../../utils/general'); +const { console } = require('window-or-global'); + +const appConfig = getConfig(); +const vmrcData = appConfig.VMRC || {}; + +const endpoint = '/vmrc'; +const url = vmrcData.TARGET || ''; +const vmrcProxy = createProxyMiddleware(endpoint, { + target: url, + changeOrigin: false, + ws: true, + secure: /^(https):\/\/[^ "]+$/.test(url), + logLevel: 'debug', + pathRewrite: path => path.replace(endpoint, '/ticket'), + router: req => { + if (req && req.url) { + const ticket = req.url.split('/')[2]; + try { + const esxi = readFileSync( + `${vmrcData.TOKENS_PATH || ''}/${ticket}` + ).toString(); + return esxi; + } catch (error) { + const config = { + color: 'red', + type: error.message, + message: 'Error read vmrc token: %s' + }; + messageTerminal(config); + } + } + } +}); + +const vmrc = appServer => { + if (appServer) { + appServer(endpoint, vmrcProxy); + } +}; +const vmrcUpgrade = appServer => { + if ( + appServer && + appServer.on && + appServer.constructor && + appServer.constructor.name && + appServer.constructor.name === 'Server' + ) { + appServer.on('upgrade', vmrcProxy.upgrade); + } +}; + +module.exports = { + endpoint, + vmrc, + vmrcUpgrade +}; diff --git a/src/fireedge/src/utils/ws-zeromq.js b/src/fireedge/src/routes/websockets/zeromq/index.js similarity index 85% rename from src/fireedge/src/utils/ws-zeromq.js rename to src/fireedge/src/routes/websockets/zeromq/index.js index 2a22c2e01d..3c34b40294 100644 --- a/src/fireedge/src/utils/ws-zeromq.js +++ b/src/fireedge/src/routes/websockets/zeromq/index.js @@ -14,14 +14,13 @@ /* -------------------------------------------------------------------------- */ const atob = require('atob'); - +const socketIO = require('socket.io'); const { socket: socketZeroMQ } = require('zeromq'); const xml2js = require('xml2js'); -const { messageTerminal } = require('./general'); -const { getConfig } = require('./yml'); -const { validateAuth } = require('./jwt'); -// const { unauthorized } = require('./constants/http-codes'); +const { messageTerminal } = require('../../../utils/general'); +const { getConfig } = require('../../../utils/yml'); +const { validateAuth } = require('../../../utils/jwt'); // user config const appConfig = getConfig(); @@ -30,7 +29,8 @@ const zeromqType = appConfig.ZEROTYPE || 'tcp'; const zeromqPort = appConfig.ZEROPORT || 2101; const zeromqHost = appConfig.ZEROHOST || '127.0.0.1'; -const addWsServer = appServer => { +const endpoint = '/zeromq'; +const oneHooksEmits = appServer => { // connect to zeromq const zeromqSock = socketZeroMQ('sub'); const address = `${zeromqType}://${zeromqHost}:${zeromqPort}`; @@ -86,11 +86,6 @@ const addWsServer = appServer => { ); } }); - - /* socketServer.on('disconnect', () => { - console.log('Client disconnected'); - clearInterval(interval); - }); */ }); } catch (error) { const configErrorZeroMQ = { @@ -102,6 +97,19 @@ const addWsServer = appServer => { } }; -module.exports = { - addWsServer +const oneHooks = appServer => { + if ( + appServer && + appServer.constructor && + appServer.constructor.name && + appServer.constructor.name === 'Server' + ) { + const io = socketIO({ path: endpoint }).listen(appServer); + oneHooksEmits(io); + } +}; + +module.exports = { + endpoint, + oneHooks }; diff --git a/src/fireedge/src/utils/constants/defaults.js b/src/fireedge/src/utils/constants/defaults.js index 6bd072ce3a..f7ae577a92 100644 --- a/src/fireedge/src/utils/constants/defaults.js +++ b/src/fireedge/src/utils/constants/defaults.js @@ -63,6 +63,7 @@ const defaults = { defaultLang: 'en_US', defaultIP: defaultIp, defaultProtocolHotReload: 'http', + defaultPort: 2616, defaultPortHotReload: '3001', translations: { en_US: 'English', diff --git a/src/fireedge/src/utils/index.js b/src/fireedge/src/utils/index.js index c9cdec2b7e..19289e2413 100644 --- a/src/fireedge/src/utils/index.js +++ b/src/fireedge/src/utils/index.js @@ -20,7 +20,6 @@ const functionRoutes = require('../routes/api'); const { validateAuth } = require('./jwt'); const { httpResponse } = require('./server'); const { messageTerminal, addPrintf } = require('./general'); -const { addWsServer } = require('./ws-zeromq'); const { getConfig } = require('./yml'); // user config @@ -127,7 +126,6 @@ const checkIfIsARouteFunction = (route, httpMethod) => { }; module.exports = { - addWsServer, validateAuth, createParamsState, getAllowedQueryParams,