diff --git a/src/fireedge/src/server/routes/api/auth/auth.js b/src/fireedge/src/server/routes/api/auth/auth.js index 66d474d6b7..593d560432 100644 --- a/src/fireedge/src/server/routes/api/auth/auth.js +++ b/src/fireedge/src/server/routes/api/auth/auth.js @@ -91,7 +91,8 @@ const auth = (req, res, next, connect) => { paramsDefaultByCommandOpennebula(defaultMethodUserInfo, GET), (err, value) => { responseOpennebula(updaterResponse, err, value, login, next) - } + }, + false ) } } else { diff --git a/src/fireedge/src/server/routes/api/auth/functions.js b/src/fireedge/src/server/routes/api/auth/functions.js index 6f10a4d373..4955b4731f 100644 --- a/src/fireedge/src/server/routes/api/auth/functions.js +++ b/src/fireedge/src/server/routes/api/auth/functions.js @@ -14,40 +14,38 @@ /* -------------------------------------------------------------------------- */ const { DateTime } = require('luxon') const { Map } = require('immutable') +// eslint-disable-next-line node/no-deprecated-api +const { parse } = require('url') +const { global, Array } = require('window-or-global') +const { createHash } = require('crypto') + const { - from, httpMethod, defaultOpennebulaExpiration, - defaultOpennebulaMinimumExpiration, defaultMethodLogin, defaultMethodZones, defaultMethodUserInfo, default2FAOpennebulaVar, - defaultNamespace, - defaultServerAdminID + defaultNamespace } = require('server/utils/constants/defaults') const { getConfig } = require('server/utils/yml') const { ok, unauthorized, - accepted + accepted, + internalServerError } = require('server/utils/constants/http-codes') const { createToken } = require('server/utils/jwt') -const { httpResponse, encrypt } = require('server/utils/server') +const { httpResponse, encrypt, getSunstoneAuth } = require('server/utils/server') const { responseOpennebula, checkOpennebulaCommand, check2Fa } = require('server/utils/opennebula') -// eslint-disable-next-line node/no-deprecated-api -const { parse } = require('url') -const { global, Array } = require('window-or-global') const appConfig = getConfig() const namespace = appConfig.namespace || defaultNamespace -const minimumExpirationTime = appConfig.minimun_opennebula_expiration || defaultOpennebulaMinimumExpiration -const serverAdmin = appConfig.server_admin || defaultServerAdminID const { POST } = httpMethod @@ -138,7 +136,11 @@ const setDates = () => { relativeTime = diff.seconds } -const connectOpennebula = () => nodeConnect(user, pass) +const connectOpennebula = (usr = '', pss = '') => { + const connectUser = usr || user + const connectPass = pss || pass + return nodeConnect(connectUser, connectPass) +} const updaterResponse = code => { if ( @@ -184,8 +186,8 @@ const validate2faAuthentication = informationUser => { } const genJWT = (token, informationUser) => { - if (token && token.token && informationUser && informationUser.ID && informationUser.PASSWORD) { - const { ID: id, TEMPLATE: userTemplate } = informationUser + if (token && token.token && informationUser && informationUser.ID && informationUser.NAME) { + const { ID: id, TEMPLATE: userTemplate, NAME: user } = informationUser const dataJWT = { id, user, token: token.token } const addTime = token.expiration_time || nowWithMinutes.toSeconds() const jwt = createToken(dataJWT, nowUnix, addTime) @@ -240,7 +242,93 @@ const setZones = () => { }, next ) - } + }, + false + ) + } +} + +const createTokenServerAdmin = (serverAdmin = '', username = '') => { + let rtn + const key = getKey() + const iv = getIV() + if (serverAdmin && username && key && iv) { + rtn = encrypt( + `${serverAdmin}:${username}:${parseInt(nowWithMinutes.toSeconds())}`, + key, + iv + ) + } + return rtn +} + +const wrapUserWithServerAdmin = (serverAdminData = {}, userData = {}) => { + const relativeTime = getRelativeTime() + let serverAdminName = '' + let serverAdminPassword = '' + let userName = '' + + if ( + relativeTime && + serverAdminData && + serverAdminData.USER && + (serverAdminName = serverAdminData.USER.NAME) && + (serverAdminPassword = serverAdminData.USER.PASSWORD) && + userData && + (userName = userData.NAME) && + userData.ID && + userData.TEMPLATE + ) { + /********************************************************* + * equals what is placed in: + * src/authm_mad/remotes/server_cipher/server_cipher_auth.rb:44 + *********************************************************/ + setKey(serverAdminPassword.substring(0, 32)) + setIV(serverAdminPassword.substring(0, 16)) + + const tokenWithServerAdmin = createTokenServerAdmin(serverAdminName, userName) + if (tokenWithServerAdmin) { + genJWT( + { + token: tokenWithServerAdmin + }, + { + NAME: `${serverAdminName}:${userName}`, + ID: userData.ID, + TEMPLATE: userData.TEMPLATE + } + ) + next() + } + } else { + updaterResponse(httpResponse(internalServerError)) + next() + } +} + +const getServerAdminAndWrapUser = (userData = {}) => { + const serverAdminData = getSunstoneAuth() + if (serverAdminData && + serverAdminData.username && + serverAdminData.key && + serverAdminData.iv + ) { + setKey(serverAdminData.key) + setIV(serverAdminData.iv) + const tokenWithServerAdmin = createTokenServerAdmin(serverAdminData.username, serverAdminData.username) + const oneConnect = connectOpennebula(`${serverAdminData.username}:${serverAdminData.username}`, tokenWithServerAdmin) + oneConnect( + defaultMethodUserInfo, + [-1, false], + (err, value) => { + responseOpennebula( + updaterResponse, + err, + value, + (serverAdminData = {}) => wrapUserWithServerAdmin(serverAdminData, userData), + next) + }, + false ) } } @@ -259,8 +347,7 @@ const login = userData => { if (validate2faAuthentication(userData.USER)) { rtn = false setDates() - getServerAdmin() - // checkOpennebulaToken(userData.USER) // aca seria el call de la funcion serveradmin + getServerAdminAndWrapUser(userData.USER) } } } @@ -269,135 +356,6 @@ const login = userData => { } } -const createTokenServerAdmin = (serverAdmin = '', username = '') => { - let rtn - const key = getKey() - const iv = getIV() - if (serverAdmin && username && key && iv) { - rtn = encrypt( - `${serverAdmin}:${username}:${parseInt(nowWithMinutes.toSeconds())}`, - key, - iv - ) - } - return rtn -} - -const wrapUserWithServerAdmin = (serverAdminData = {}) => { - const relativeTime = getRelativeTime() - const user = getUser() - let userPassword = '' - if ( - relativeTime && - serverAdminData && - serverAdminData.USER && - serverAdminData.USER.NAME && - (userPassword = serverAdminData.USER.PASSWORD) && - user - ) { - /********************************************************* - * equals what is placed in: - * src/authm_mad/remotes/server_cipher/server_cipher_auth.rb:44 - *********************************************************/ - - setKey(userPassword.substring(0, 32)) - setIV(userPassword.substring(0, 16)) - const tokenWithServerAdmin = createTokenServerAdmin(serverAdminData.USER.NAME, user) - if (tokenWithServerAdmin) { - console.log('FINAL --> ', `${serverAdminData.USER.NAME}:${user}:${tokenWithServerAdmin}`) - } - } -} - -const getServerAdmin = () => { - const oneConnect = connectOpennebula() - oneConnect( - defaultMethodUserInfo, - [parseInt(serverAdmin, 10), false], - (err, value) => { - responseOpennebula( - updaterResponse, - err, - value, - wrapUserWithServerAdmin, - next) - } - ) -} - -const checkOpennebulaToken = userData => { - setDates() - if (userData && userData.LOGIN_TOKEN) { - const loginTokens = Array.isArray(userData.LOGIN_TOKEN) ? userData.LOGIN_TOKEN : [userData.LOGIN_TOKEN] - const token = getValidOpennebulaToken(loginTokens) - if (token) { - genJWT(token, userData) - next() - } else { - createOpennebulaToken(userData) - } - } else { - createOpennebulaToken(userData) - } -} - -const getValidOpennebulaToken = userDataTokens => { - let rtn - if (Array.isArray(userDataTokens)) { - const validToken = userDataTokens.find(token => { - now = DateTime.local() - nowUnix = now.toSeconds() - return ( - token && - token.TOKEN && - token.EGID && - token.EGID === '-1' && - token.EXPIRATION_TIME && - parseInt(token.EXPIRATION_TIME, 10) >= nowUnix + (parseInt(minimumExpirationTime, 10) * 60) - ) - }) - if (validToken && validToken.TOKEN && validToken.EXPIRATION_TIME) { - rtn = { - token: validToken.TOKEN, - expiration_time: validToken.EXPIRATION_TIME - } - } - } - return rtn -} - -const createOpennebulaToken = userData => { - const relativeTime = getRelativeTime() - const dataSourceWithExpirateDate = Map(req).toObject() - // add expire time unix for opennebula creation token - dataSourceWithExpirateDate[from.postBody].expire = relativeTime - dataSourceWithExpirateDate[from.postBody].token = '' - - const oneConnect = connectOpennebula() - oneConnect( - defaultMethodLogin, - getOpennebulaMethod(dataSourceWithExpirateDate), - (err, value) => { - responseOpennebula( - updaterResponse, - err, - value, - token => authenticate(token, userData), - next) - } - ) -} - -const authenticate = (token, userData) => { - const findTextError = `[${namespace + defaultMethodLogin}]` - if (token && userData) { - if (token.indexOf(findTextError) < 0) { - genJWT({ token }, userData) - } - } - next() -} - const functionRoutes = { login, getUser, diff --git a/src/fireedge/src/server/utils/constants/defaults.js b/src/fireedge/src/server/utils/constants/defaults.js index a0b5f90f2f..53e16a0e0f 100644 --- a/src/fireedge/src/server/utils/constants/defaults.js +++ b/src/fireedge/src/server/utils/constants/defaults.js @@ -15,7 +15,7 @@ const appName = 'fireedge' const baseUrl = `${appName ? `/${appName}/` : '/'}` -const baseUrlWebsockets = 'websockets' +const baseUrlWebsockets = 'websockets/' const apps = { flow: { name: 'flow', @@ -31,12 +31,10 @@ const default2FAOpennebulaVar = 'TWO_FACTOR_AUTH_SECRET' const defaultIp = 'localhost' const protocol = 'http' const defaults = { - defaultServerAdminID: 1, defaultTypeCrypto: 'aes-256-cbc', defaultEmptyFunction: () => undefined, defaultErrorTemplate: 'ERROR_FIREEDGE="%1$s"', defaultOpennebulaExpiration: 180, - defaultOpennebulaMinimumExpiration: 30, defaultAppName: appName, defaultConfigErrorMessage: { color: 'red', @@ -100,6 +98,7 @@ const defaults = { defaultEtcPath: '/etc/one', defaultLogFilename: `${appName}.log`, defaultKeyFilename: `${appName}_key`, + defaultSunstoneAuth: 'sunstone_auth', defaultVmrcTokens: 'sunstone_vmrc_tokens/', defaultBaseURL: '', endpointVmrc: `${baseUrl}vmrc`, diff --git a/src/fireedge/src/server/utils/opennebula.js b/src/fireedge/src/server/utils/opennebula.js index 4f92f5c0e9..199db22f77 100644 --- a/src/fireedge/src/server/utils/opennebula.js +++ b/src/fireedge/src/server/utils/opennebula.js @@ -68,7 +68,7 @@ const opennebulaConnect = (username = '', password = '', path = '') => { xmlClient = rpc.createClient(path) } if (xmlClient && xmlClient.methodCall) { - rtn = (action = '', parameters = [], callback = () => undefined) => { + rtn = (action = '', parameters = [], callback = () => undefined, fillHookResource = true) => { if (action && parameters && Array.isArray(parameters) && callback) { const xmlParameters = [`${username}:${password}`, ...parameters] xmlClient.methodCall( @@ -116,7 +116,7 @@ const opennebulaConnect = (username = '', password = '', path = '') => { errorData[0].STRING ) { // success - fillResourceforHookConection(username, action, parameters) + fillHookResource && fillResourceforHookConection(username, action, parameters) callback(undefined, errorData[0].STRING) } } @@ -124,7 +124,12 @@ const opennebulaConnect = (username = '', password = '', path = '') => { ) return } else if (value && value[0] && value[1]) { - const messageCall = value[1] + let messageCall + if (Array.isArray(value) && value[0] && value[1]) { + messageCall = value[1] + } else if (value.length > 0) { + messageCall = value + } if (typeof messageCall === 'string' && messageCall.length > 0) { xml2js.parseString( messageCall, @@ -135,7 +140,7 @@ const opennebulaConnect = (username = '', password = '', path = '') => { return } // success - fillResourceforHookConection(username, action, parameters) + fillHookResource && fillResourceforHookConection(username, action, parameters) callback( undefined, error === null && result === null ? messageCall : result diff --git a/src/fireedge/src/server/utils/server.js b/src/fireedge/src/server/utils/server.js index 6581447d8c..65aebe4e50 100644 --- a/src/fireedge/src/server/utils/server.js +++ b/src/fireedge/src/server/utils/server.js @@ -16,7 +16,7 @@ const { env } = require('process') const { Map } = require('immutable') const { global } = require('window-or-global') // eslint-disable-next-line node/no-deprecated-api -const { createCipheriv, createCipher, createDecipheriv, createDecipher } = require('crypto') +const { createCipheriv, createCipher, createDecipheriv, createDecipher, createHash } = require('crypto') const { existsSync, readFileSync, createWriteStream } = require('fs-extra') const { internalServerError } = require('./constants/http-codes') const { messageTerminal } = require('server/utils/general') @@ -30,6 +30,7 @@ const { defaultVmrcTokens, defaultVarPath, defaultKeyFilename, + defaultSunstoneAuth, defaultWebpackMode, defaultOpennebulaZones, defaultEtcPath, @@ -228,6 +229,34 @@ const genFireedgeKey = () => { } } +const getSunstoneAuth = () => { + let rtn + if (global && global.SUNSTONE_AUTH_PATH) { + existsFile(global.SUNSTONE_AUTH_PATH, + filedata => { + if (filedata) { + const serverAdminData = filedata.split(':') + const regexReplaceSpaces = /\r|\n/g + if (serverAdminData[0] && serverAdminData[1]) { + const username = serverAdminData[0].replace(regexReplaceSpaces, '') + const password = createHash('sha256').update(serverAdminData[1].replace(regexReplaceSpaces, '')).digest('hex') + const key = password.substring(0, 32) + const iv = key.substring(0, 16) + rtn = { username, key, iv } + } + } + }, err => { + const config = { + color: 'red', + message: 'Error: %s', + type: err.message || '' + } + messageTerminal(config) + }) + } + return rtn +} + const getDataZone = (zone = '0', configuredZones) => { let rtn const zones = (global && global.zones) || configuredZones || defaultOpennebulaZones @@ -260,6 +289,9 @@ const genPathResources = () => { if (!global.FIREEDGE_LOG) { global.FIREEDGE_LOG = `${LOG_LOCATION}/${defaultLogFilename}` } + if (!global.SUNSTONE_AUTH_PATH) { + global.SUNSTONE_AUTH_PATH = `${VAR_LOCATION}/.one/${defaultSunstoneAuth}` + } if (!global.FIREEDGE_KEY_PATH) { global.FIREEDGE_KEY_PATH = `${VAR_LOCATION}/.one/${defaultKeyFilename}` } @@ -309,6 +341,7 @@ module.exports = { decrypt, getDataZone, existsFile, + getSunstoneAuth, createFile, httpResponse, validateServerIsSecure,