From 2332a1c164a86567537d35d3c5e8e2329c7051b6 Mon Sep 17 00:00:00 2001 From: Jorge Lobo Date: Fri, 25 Jun 2021 11:21:47 +0200 Subject: [PATCH] F #5422: first steps serveradmin login Signed-off-by: Jorge Lobo --- .../server_cipher/server_cipher_auth.rb | 9 +- .../src/server/routes/api/auth/functions.js | 93 ++++++++++++++++++- .../src/server/utils/constants/defaults.js | 7 +- src/fireedge/src/server/utils/opennebula.js | 8 +- src/fireedge/src/server/utils/server.js | 47 +++++++++- 5 files changed, 152 insertions(+), 12 deletions(-) diff --git a/src/authm_mad/remotes/server_cipher/server_cipher_auth.rb b/src/authm_mad/remotes/server_cipher/server_cipher_auth.rb index 9b59f87f76..dbdf7aae0c 100644 --- a/src/authm_mad/remotes/server_cipher/server_cipher_auth.rb +++ b/src/authm_mad/remotes/server_cipher/server_cipher_auth.rb @@ -41,8 +41,10 @@ class OpenNebula::ServerCipherAuth if !srv_passwd.empty? # truncate token to 32-bytes for Ruby >= 2.4 @key = Digest::SHA256.hexdigest(@srv_passwd)[0..31] - else + @iv = @key[0..15] + else @key = "" + @iv = "" end @cipher = OpenSSL::Cipher.new(CIPHER) @@ -112,6 +114,7 @@ class OpenNebula::ServerCipherAuth begin # truncate token to 32-bytes for Ruby >= 2.4 @key = srv_pass[0..31] + @iv = srv_pass[0..15] token_array = decrypt(signed_text).split(':') @@ -133,7 +136,7 @@ class OpenNebula::ServerCipherAuth def encrypt(data) @cipher.encrypt @cipher.key = @key - + @cipher.iv = @iv rc = @cipher.update(data) rc << @cipher.final @@ -143,7 +146,7 @@ class OpenNebula::ServerCipherAuth def decrypt(data) @cipher.decrypt @cipher.key = @key - + @cipher.iv = @iv rc = @cipher.update(Base64::decode64(data)) rc << @cipher.final diff --git a/src/fireedge/src/server/routes/api/auth/functions.js b/src/fireedge/src/server/routes/api/auth/functions.js index 5cf959cf6f..6f10a4d373 100644 --- a/src/fireedge/src/server/routes/api/auth/functions.js +++ b/src/fireedge/src/server/routes/api/auth/functions.js @@ -23,7 +23,8 @@ const { defaultMethodZones, defaultMethodUserInfo, default2FAOpennebulaVar, - defaultNamespace + defaultNamespace, + defaultServerAdminID } = require('server/utils/constants/defaults') const { getConfig } = require('server/utils/yml') const { @@ -32,7 +33,7 @@ const { accepted } = require('server/utils/constants/http-codes') const { createToken } = require('server/utils/jwt') -const { httpResponse } = require('server/utils/server') +const { httpResponse, encrypt } = require('server/utils/server') const { responseOpennebula, checkOpennebulaCommand, @@ -46,12 +47,15 @@ const appConfig = getConfig() const namespace = appConfig.namespace || defaultNamespace const minimumExpirationTime = appConfig.minimun_opennebula_expiration || defaultOpennebulaMinimumExpiration +const serverAdmin = appConfig.server_admin || defaultServerAdminID const { POST } = httpMethod const getOpennebulaMethod = checkOpennebulaCommand(defaultMethodLogin, POST) let user = '' +let key = '' +let iv = '' let pass = '' let type = '' let tfatoken = '' @@ -67,10 +71,22 @@ let relativeTime = '' const dataSourceWithExpirateDate = () => Map(req).toObject() +const getKey = () => key +const getIV = () => iv const getUser = () => user const getPass = () => pass const getRelativeTime = () => relativeTime +const setKey = newKey => { + key = newKey + return key +} + +const setIV = newIV => { + iv = newIV + return iv +} + const setUser = newUser => { user = newUser return user @@ -143,6 +159,10 @@ const validate2faAuthentication = informationUser => { informationUser.TEMPLATE.SUNSTONE && informationUser.TEMPLATE.SUNSTONE[default2FAOpennebulaVar] ) { + /********************************************************* + * Validate 2FA + *********************************************************/ + if (tfatoken.length <= 0) { updaterResponse(httpResponse(accepted)) } else { @@ -154,7 +174,10 @@ const validate2faAuthentication = informationUser => { } } } else { - // without 2FA login + /********************************************************* + * Without 2FA + *********************************************************/ + rtn = true } return rtn @@ -223,17 +246,21 @@ const setZones = () => { } const login = userData => { - let rtn = true + let rtn = false if (userData) { const findTextError = `[${namespace + defaultMethodUserInfo}]` if (userData.indexOf && userData.indexOf(findTextError) >= 0) { updaterResponse(httpResponse(unauthorized)) + } else { + rtn = true } if (userData.USER) { setZones() if (validate2faAuthentication(userData.USER)) { rtn = false - checkOpennebulaToken(userData.USER) + setDates() + getServerAdmin() + // checkOpennebulaToken(userData.USER) // aca seria el call de la funcion serveradmin } } } @@ -242,6 +269,62 @@ 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) { diff --git a/src/fireedge/src/server/utils/constants/defaults.js b/src/fireedge/src/server/utils/constants/defaults.js index 05a23d0639..a0b5f90f2f 100644 --- a/src/fireedge/src/server/utils/constants/defaults.js +++ b/src/fireedge/src/server/utils/constants/defaults.js @@ -15,6 +15,7 @@ const appName = 'fireedge' const baseUrl = `${appName ? `/${appName}/` : '/'}` +const baseUrlWebsockets = 'websockets' const apps = { flow: { name: 'flow', @@ -30,6 +31,8 @@ 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, @@ -41,11 +44,11 @@ const defaults = { }, defaultFilesWebsockets: { hooks: { - path: `${baseUrl}websockets/hooks`, + path: `${baseUrl}${baseUrlWebsockets}hooks`, methods: ['GET', 'POST'] }, provision: { - path: `${baseUrl}websockets/provision`, + path: `${baseUrl}${baseUrlWebsockets}provision`, methods: ['GET', 'POST'] } }, diff --git a/src/fireedge/src/server/utils/opennebula.js b/src/fireedge/src/server/utils/opennebula.js index bedce24def..4f92f5c0e9 100644 --- a/src/fireedge/src/server/utils/opennebula.js +++ b/src/fireedge/src/server/utils/opennebula.js @@ -43,7 +43,13 @@ const fillResourceforHookConection = (username = '', action = '', parameters = ' let match // parameters[0] is the resource ID if (username && action && (match = action.match(regexInfoAction)) && match[1] && parameters[0] >= 0) { - if (global.users[username] && !global.users[username].resourcesHooks) { + if (global && !global.users) { + global.users = {} + } + if (!global.users[username]) { + global.users[username] = {} + } + if (!global.users[username].resourcesHooks) { global.users[username].resourcesHooks = {} } global.users[username].resourcesHooks[match[1]] = parameters[0] diff --git a/src/fireedge/src/server/utils/server.js b/src/fireedge/src/server/utils/server.js index 129ab89631..6581447d8c 100644 --- a/src/fireedge/src/server/utils/server.js +++ b/src/fireedge/src/server/utils/server.js @@ -15,6 +15,8 @@ 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 { existsSync, readFileSync, createWriteStream } = require('fs-extra') const { internalServerError } = require('./constants/http-codes') const { messageTerminal } = require('server/utils/general') @@ -30,7 +32,8 @@ const { defaultKeyFilename, defaultWebpackMode, defaultOpennebulaZones, - defaultEtcPath + defaultEtcPath, + defaultTypeCrypto } = require('./constants/defaults') let cert = '' @@ -117,6 +120,46 @@ const middlewareValidateAuthWebsocket = (server = {}, next = () => undefined) => } } +const encrypt = (data = '', key = '', iv = '') => { + let rtn + if (data && key) { + try { + const cipher = iv ? createCipheriv(defaultTypeCrypto, key, iv) : createCipher(defaultTypeCrypto, key) + let encryptData = cipher.update(data, 'ascii', 'base64') + encryptData += cipher.final('base64') + rtn = encryptData + } catch (err) { + const errorData = (err && err.message) || '' + messageTerminal({ + color: 'red', + message: 'Error: %s', + type: errorData + }) + } + } + return rtn +} + +const decrypt = (data = '', key = '', iv = '') => { + let rtn + if (data && key) { + try { + const cipher = iv ? createDecipheriv(defaultTypeCrypto, key, iv) : createDecipher(defaultTypeCrypto, key) + let decryptData = cipher.update(data, 'base64', 'ascii') + decryptData += cipher.final('ascii') + rtn = decryptData + } catch (err) { + const errorData = (err && err.message) || '' + messageTerminal({ + color: 'red', + message: 'Error: %s', + type: errorData + }) + } + } + return rtn +} + const existsFile = (path = '', success = () => undefined, error = () => undefined) => { let rtn = false let file @@ -262,6 +305,8 @@ const parsePostData = (postData = {}) => { return rtn } module.exports = { + encrypt, + decrypt, getDataZone, existsFile, createFile,