1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-16 22:50:10 +03:00

F #5422: login with serveradmin

Signed-off-by: Jorge Lobo <jlobo@opennebula.io>
This commit is contained in:
Jorge Lobo 2021-06-28 16:19:29 +02:00
parent 2332a1c164
commit 8ea8917292
No known key found for this signature in database
GPG Key ID: 9C21660F7B06905E
5 changed files with 151 additions and 155 deletions

View File

@ -91,7 +91,8 @@ const auth = (req, res, next, connect) => {
paramsDefaultByCommandOpennebula(defaultMethodUserInfo, GET),
(err, value) => {
responseOpennebula(updaterResponse, err, value, login, next)
}
},
false
)
}
} else {

View File

@ -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,

View File

@ -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`,

View File

@ -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

View File

@ -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,