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:
parent
2332a1c164
commit
8ea8917292
@ -91,7 +91,8 @@ const auth = (req, res, next, connect) => {
|
||||
paramsDefaultByCommandOpennebula(defaultMethodUserInfo, GET),
|
||||
(err, value) => {
|
||||
responseOpennebula(updaterResponse, err, value, login, next)
|
||||
}
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
@ -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,
|
||||
|
@ -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`,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user