mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
F OpenNebula/one#2312: Fireedge VMRC proxy (#225)
* add vmrc proxy * fix zeromq and vmrc * organizate code websockets * catch error vmrc Signed-off-by: Jorge Lobo <jlobo@opennebula.io>
This commit is contained in:
parent
22debd99e3
commit
169b072a32
@ -20,4 +20,9 @@ TOKEN_SECRET: secret_token
|
||||
# JWT life time (days)
|
||||
LIMIT_TOKEN:
|
||||
MIN: 14
|
||||
MAX: 30
|
||||
MAX: 30
|
||||
|
||||
# VMRC
|
||||
VMRC:
|
||||
TARGET: 'http://opennebula.io'
|
||||
TOKENS_PATH: '/var/lib/one/sunstone_vnc_tokens/vmrc'
|
||||
|
@ -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 <Translate word="word"/>
|
||||
const optsComponent = {
|
||||
regex: /<Translate word=('|"|{|{'|{")[a-zA-Z0-9_ ]*('|"|}|'}|"}) \/>/g,
|
||||
removeStart: /<Translate word=('|"|{|{'|{")/g,
|
||||
removeEnd: /('|"|}|'}|"}) \/>/g,
|
||||
regexTextCaptureIndex: 0,
|
||||
definitions
|
||||
};
|
||||
|
||||
createReadStream(testFolder, optsFunc);
|
||||
createReadStream(testFolder, optsComponent);
|
||||
|
||||
generateFile(exportFile);
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
22
src/fireedge/po2json.js
Normal file
22
src/fireedge/po2json.js
Normal file
@ -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();
|
45
src/fireedge/potfile.js
Normal file
45
src/fireedge/potfile.js
Normal file
@ -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 <Translate word="word"/>
|
||||
const optsComponent = {
|
||||
regex: /<Translate word=('|"|{|{'|{")[a-zA-Z0-9_ ]*('|"|}|'}|"}) \/>/g,
|
||||
removeStart: /<Translate word=('|"|{|{'|{")/g,
|
||||
removeEnd: /('|"|}|'}|"}) \/>/g,
|
||||
regexTextCaptureIndex: 0,
|
||||
definitions
|
||||
};
|
||||
|
||||
createReadStream(testFolder, optsFunc);
|
||||
createReadStream(testFolder, optsComponent);
|
||||
|
||||
generateFile(exportFile);
|
@ -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);
|
||||
|
@ -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 <p />;
|
||||
}
|
||||
|
||||
};
|
||||
export default Webconsole;
|
@ -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 <JSONPretty id="json-pretty" data={data} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default WSConsole;
|
@ -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,
|
||||
|
279
src/fireedge/src/routes/api/routes/2fa/functions.js
Normal file
279
src/fireedge/src/routes/api/routes/2fa/functions.js
Normal file
@ -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
|
||||
};
|
@ -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
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -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';
|
||||
|
@ -14,9 +14,7 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
const { Map } = require('immutable');
|
||||
const {
|
||||
AUTH,
|
||||
} = require('./string-routes');
|
||||
const { AUTH } = require('./string-routes');
|
||||
const {
|
||||
httpMethod,
|
||||
defaultMethodLogin
|
||||
|
@ -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 = {
|
||||
|
@ -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';
|
||||
|
74
src/fireedge/src/routes/websockets/vmrc/index.js
Normal file
74
src/fireedge/src/routes/websockets/vmrc/index.js
Normal file
@ -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
|
||||
};
|
@ -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
|
||||
};
|
@ -63,6 +63,7 @@ const defaults = {
|
||||
defaultLang: 'en_US',
|
||||
defaultIP: defaultIp,
|
||||
defaultProtocolHotReload: 'http',
|
||||
defaultPort: 2616,
|
||||
defaultPortHotReload: '3001',
|
||||
translations: {
|
||||
en_US: 'English',
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user