mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
(cherry picked from commit 7e1ac044e318b1562cce8c21a489ae136a7fa458)
This commit is contained in:
parent
364105a498
commit
0424a1daa4
@ -28,7 +28,7 @@ export const applicationService = {
|
||||
*/
|
||||
getApplication: async ({ id }) => {
|
||||
const res = await RestClient.request({
|
||||
url: `/api/${SERVICE}/list/${id}`,
|
||||
url: `/api/${SERVICE}/${id}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -44,7 +44,7 @@ export const applicationService = {
|
||||
*/
|
||||
getApplications: async () => {
|
||||
const res = await RestClient.request({
|
||||
url: `/api/${SERVICE}/list`,
|
||||
url: `/api/${SERVICE}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
|
@ -30,7 +30,7 @@ export const applicationTemplateService = {
|
||||
*/
|
||||
getApplicationTemplate: ({ id }) => {
|
||||
const res = RestClient.request({
|
||||
url: `/api/${SERVICE_TEMPLATE}/list/${id}`,
|
||||
url: `/api/${SERVICE_TEMPLATE}/${id}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -44,7 +44,7 @@ export const applicationTemplateService = {
|
||||
*/
|
||||
getApplicationsTemplates: async () => {
|
||||
const res = await RestClient.request({
|
||||
url: `/api/${SERVICE_TEMPLATE}/list`,
|
||||
url: `/api/${SERVICE_TEMPLATE}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -64,7 +64,7 @@ export const applicationTemplateService = {
|
||||
const res = await RestClient.request({
|
||||
data,
|
||||
method: POST,
|
||||
url: `/api/${SERVICE_TEMPLATE}/create`,
|
||||
url: `/api/${SERVICE_TEMPLATE}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -85,7 +85,7 @@ export const applicationTemplateService = {
|
||||
const res = RestClient.request({
|
||||
data,
|
||||
method: PUT,
|
||||
url: `/api/${SERVICE_TEMPLATE}/update/${id}`,
|
||||
url: `/api/${SERVICE_TEMPLATE}/${id}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { PROVIDER } from 'server/routes/api/provision/string-routes'
|
||||
import { PROVIDER } from 'server/routes/api/oneprovision/string-routes'
|
||||
import { httpCodes, defaults } from 'server/utils/constants'
|
||||
import { RestClient } from 'client/utils'
|
||||
|
||||
@ -30,7 +30,7 @@ export const providerService = {
|
||||
*/
|
||||
getProvider: async ({ id }) => {
|
||||
const res = await RestClient.request({
|
||||
url: `/api/${PROVIDER}/list/${id}`,
|
||||
url: `/api/${PROVIDER}/${id}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -46,7 +46,7 @@ export const providerService = {
|
||||
*/
|
||||
getProviders: async () => {
|
||||
const res = await RestClient.request({
|
||||
url: `/api/${PROVIDER}/list`,
|
||||
url: `/api/${PROVIDER}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -86,7 +86,7 @@ export const providerService = {
|
||||
const res = await RestClient.request({
|
||||
data,
|
||||
method: POST,
|
||||
url: `/api/${PROVIDER}/create`,
|
||||
url: `/api/${PROVIDER}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -107,7 +107,7 @@ export const providerService = {
|
||||
const res = await RestClient.request({
|
||||
data,
|
||||
method: PUT,
|
||||
url: `/api/${PROVIDER}/update/${id}`,
|
||||
url: `/api/${PROVIDER}/${id}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -126,7 +126,7 @@ export const providerService = {
|
||||
deleteProvider: async ({ id }) => {
|
||||
const res = await RestClient.request({
|
||||
method: DELETE,
|
||||
url: `/api/${PROVIDER}/delete/${id}`,
|
||||
url: `/api/${PROVIDER}/${id}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { PROVISION } from 'server/routes/api/provision/string-routes'
|
||||
import { PROVISION } from 'server/routes/api/oneprovision/string-routes'
|
||||
import { httpCodes, defaults } from 'server/utils/constants'
|
||||
import { RestClient } from 'client/utils'
|
||||
|
||||
@ -63,7 +63,7 @@ export const provisionService = {
|
||||
*/
|
||||
getProvision: async ({ id }) => {
|
||||
const res = await RestClient.request({
|
||||
url: `/api/${PROVISION}/list/${id}`,
|
||||
url: `/api/${PROVISION}/${id}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -79,7 +79,7 @@ export const provisionService = {
|
||||
*/
|
||||
getProvisions: async () => {
|
||||
const res = await RestClient.request({
|
||||
url: `/api/${PROVISION}/list`,
|
||||
url: `/api/${PROVISION}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
@ -99,7 +99,7 @@ export const provisionService = {
|
||||
const res = await RestClient.request({
|
||||
data,
|
||||
method: POST,
|
||||
url: `/api/${PROVISION}/create`,
|
||||
url: `/api/${PROVISION}`,
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) {
|
||||
@ -146,7 +146,7 @@ export const provisionService = {
|
||||
deleteProvision: async ({ id, ...data }) => {
|
||||
const res = await RestClient.request({
|
||||
method: DELETE,
|
||||
url: `/api/${PROVISION}/delete/${id}`,
|
||||
url: `/api/${PROVISION}/${id}`,
|
||||
data,
|
||||
})
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { setApiRoutes } = require('server/utils/server')
|
||||
const { routes: tfaRoutes } = require('./tfa')
|
||||
const { TFA } = require('./string-routes')
|
||||
const { routes: tfaRoutes } = require('server/routes/api/2fa/routes')
|
||||
const { TFA } = require('server/routes/api/2fa/string-routes')
|
||||
|
||||
const functionRoutes = {
|
||||
private: setApiRoutes(tfaRoutes, TFA),
|
||||
|
@ -18,7 +18,7 @@ const {
|
||||
httpMethod,
|
||||
from: fromData,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { setup, qr, del } = require('./tfa-functions')
|
||||
const { setup, qr, del } = require('server/routes/api/2fa/functions')
|
||||
const { POST, DELETE, GET } = httpMethod
|
||||
|
||||
const routes = {
|
@ -1,134 +0,0 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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 { Map } = require('immutable')
|
||||
const {
|
||||
login,
|
||||
setUser,
|
||||
setPass,
|
||||
setType,
|
||||
setTfaToken,
|
||||
setRemember,
|
||||
setNext,
|
||||
setRes,
|
||||
setNodeConnect,
|
||||
connectOpennebula,
|
||||
updaterResponse,
|
||||
} = require('./functions')
|
||||
|
||||
const {
|
||||
internalServerError,
|
||||
unauthorized,
|
||||
} = require('server/utils/constants/http-codes')
|
||||
const { Actions } = require('server/utils/constants/commands/user')
|
||||
const {
|
||||
httpMethod,
|
||||
defaultEmptyFunction,
|
||||
} = require('server/utils/constants/defaults')
|
||||
|
||||
const { GET } = httpMethod
|
||||
|
||||
const {
|
||||
getDefaultParamsOfOpennebulaCommand,
|
||||
} = require('server/utils/opennebula')
|
||||
|
||||
const { writeInLogger } = require('server/utils/logger')
|
||||
|
||||
/**
|
||||
* Login user.
|
||||
*
|
||||
* @param {error} err - run if no have user data
|
||||
* @param {string} value - opennebula information
|
||||
* @param {Function} success - success
|
||||
* @param {Function} error - error
|
||||
*/
|
||||
const loginUser = (
|
||||
err = '',
|
||||
value = '',
|
||||
success = defaultEmptyFunction,
|
||||
error = defaultEmptyFunction
|
||||
) => {
|
||||
if (value && value.USER && !err) {
|
||||
success(value)
|
||||
} else {
|
||||
error()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fireedge user auth.
|
||||
*
|
||||
* @param {object} res - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {object} userData - user of http request
|
||||
* @param {Function} oneConnection - function of xmlrpc
|
||||
*/
|
||||
const auth = (
|
||||
res = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {},
|
||||
oneConnection = defaultEmptyFunction
|
||||
) => {
|
||||
const { user, token, type, token2fa, remember } = params
|
||||
setRes(res)
|
||||
setNext(next)
|
||||
setNodeConnect(oneConnection)
|
||||
updaterResponse(new Map(internalServerError).toObject())
|
||||
if (user && token) {
|
||||
const oneConnect = connectOpennebula(user, token)
|
||||
|
||||
/**
|
||||
* Run if have information.
|
||||
*
|
||||
* @param {object} oneValue - opennebula value
|
||||
*/
|
||||
const success = (oneValue) => {
|
||||
setUser(user || '')
|
||||
setPass(token || '')
|
||||
setType(type || '')
|
||||
setTfaToken(token2fa || '')
|
||||
setRemember(remember || false)
|
||||
login(oneValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Run if no have information.
|
||||
*/
|
||||
const error = () => {
|
||||
updaterResponse(new Map(unauthorized).toObject())
|
||||
writeInLogger(unauthorized)
|
||||
next()
|
||||
}
|
||||
|
||||
oneConnect(
|
||||
Actions.USER_INFO,
|
||||
getDefaultParamsOfOpennebulaCommand(Actions.USER_INFO, GET),
|
||||
(err, value) => {
|
||||
loginUser(err, value, success, error)
|
||||
},
|
||||
false
|
||||
)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const authApi = {
|
||||
auth,
|
||||
}
|
||||
module.exports = authApi
|
@ -14,611 +14,121 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { DateTime } = require('luxon')
|
||||
// eslint-disable-next-line node/no-deprecated-api
|
||||
const { parse } = require('url')
|
||||
const { global, Array } = require('window-or-global')
|
||||
const { Actions: ActionUsers } = require('server/utils/constants/commands/user')
|
||||
const { Actions: ActionZones } = require('server/utils/constants/commands/zone')
|
||||
const { Map } = require('immutable')
|
||||
const {
|
||||
httpMethod,
|
||||
defaultSessionExpiration,
|
||||
default2FAOpennebulaVar,
|
||||
defaultNamespace,
|
||||
defaultEmptyFunction,
|
||||
defaultSessionLimitExpiration,
|
||||
defaultRememberSessionExpiration,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { getFireedgeConfig } = require('server/utils/yml')
|
||||
const {
|
||||
ok,
|
||||
unauthorized,
|
||||
accepted,
|
||||
internalServerError,
|
||||
} = require('server/utils/constants/http-codes')
|
||||
const { createJWT, check2Fa } = require('server/utils/jwt')
|
||||
const {
|
||||
httpResponse,
|
||||
encrypt,
|
||||
getSunstoneAuth,
|
||||
} = require('server/utils/server')
|
||||
const {
|
||||
responseOpennebula,
|
||||
getDefaultParamsOfOpennebulaCommand,
|
||||
} = require('server/utils/opennebula')
|
||||
|
||||
const appConfig = getFireedgeConfig()
|
||||
|
||||
const namespace = appConfig.namespace || defaultNamespace
|
||||
|
||||
const { GET } = httpMethod
|
||||
|
||||
let user = ''
|
||||
let key = ''
|
||||
let iv = ''
|
||||
let pass = ''
|
||||
let type = ''
|
||||
let tfatoken = ''
|
||||
let remember = false
|
||||
let next = defaultEmptyFunction
|
||||
let req = {}
|
||||
let res = {}
|
||||
let nodeConnect = defaultEmptyFunction
|
||||
let now = ''
|
||||
let nowUnix = ''
|
||||
let expireTime = ''
|
||||
let relativeTime = ''
|
||||
let limitToken = defaultSessionExpiration
|
||||
let limitExpirationReuseToken = defaultSessionLimitExpiration
|
||||
|
||||
/**
|
||||
* Get key opennebula.
|
||||
*
|
||||
* @returns {string} get key
|
||||
*/
|
||||
const getKey = () => key
|
||||
|
||||
/**
|
||||
* Get initialization vector.
|
||||
*
|
||||
* @returns {string} get initialization vector
|
||||
*/
|
||||
const getIV = () => iv
|
||||
|
||||
/**
|
||||
* Get user opennebula.
|
||||
*
|
||||
* @returns {string} user opennebula
|
||||
*/
|
||||
const getUser = () => user
|
||||
|
||||
/**
|
||||
* Get user password opennebula.
|
||||
*
|
||||
* @returns {string} get password user opennebula
|
||||
*/
|
||||
const getPass = () => pass
|
||||
|
||||
/**
|
||||
* Get relative time.
|
||||
*
|
||||
* @returns {string} date
|
||||
*/
|
||||
const getRelativeTime = () => relativeTime
|
||||
|
||||
/**
|
||||
* Opennebula encode-decode key.
|
||||
*
|
||||
* @param {string} newKey - new key
|
||||
* @returns {string} get key
|
||||
*/
|
||||
const setKey = (newKey) => {
|
||||
key = newKey
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization vector (encrypt).
|
||||
*
|
||||
* @param {string} newIV - //16 characters
|
||||
* @returns {string} get IV
|
||||
*/
|
||||
const setIV = (newIV) => {
|
||||
iv = newIV
|
||||
|
||||
return iv
|
||||
}
|
||||
|
||||
/**
|
||||
* Username opennebula.
|
||||
*
|
||||
* @param {string} newUser - new user data
|
||||
* @returns {string} get user
|
||||
*/
|
||||
const setUser = (newUser) => {
|
||||
user = newUser
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
/**
|
||||
* User password opennebula.
|
||||
*
|
||||
* @param {string} newPass - set new opennebula password user
|
||||
* @returns {string} password user
|
||||
*/
|
||||
const setPass = (newPass) => {
|
||||
pass = newPass
|
||||
|
||||
return pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Type app.
|
||||
*
|
||||
* @param {string} newtype - new type (application)
|
||||
* @returns {string} get type
|
||||
*/
|
||||
const setType = (newtype) => {
|
||||
type = newtype
|
||||
|
||||
return type
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2FA token.
|
||||
*
|
||||
* @param {string} newTfaToken - new TFA token
|
||||
* @returns {string} get TFA token
|
||||
*/
|
||||
const setTfaToken = (newTfaToken) => {
|
||||
tfatoken = newTfaToken
|
||||
|
||||
return tfatoken
|
||||
}
|
||||
|
||||
/**
|
||||
* Set remember.
|
||||
*
|
||||
* @param {boolean} newRemember - new remember
|
||||
* @returns {boolean} remember
|
||||
*/
|
||||
const setRemember = (newRemember) => {
|
||||
remember = newRemember
|
||||
|
||||
return remember
|
||||
}
|
||||
|
||||
/**
|
||||
* Set express stepper.
|
||||
*
|
||||
* @param {Function} newNext - new stepper
|
||||
* @returns {Function} http response
|
||||
*/
|
||||
const setNext = (newNext = defaultEmptyFunction) => {
|
||||
next = newNext
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
/**
|
||||
* Set http resquest.
|
||||
*
|
||||
* @param {object} newReq - new request
|
||||
* @returns {object} http resquest
|
||||
*/
|
||||
const setReq = (newReq = {}) => {
|
||||
req = newReq
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
/**
|
||||
* Set xlmrpc connection function.
|
||||
*
|
||||
* @param {Function} newConnect - new connect opennebula
|
||||
* @returns {Function} xmlrpc function
|
||||
*/
|
||||
const setNodeConnect = (newConnect = defaultEmptyFunction) => {
|
||||
nodeConnect = newConnect
|
||||
|
||||
return nodeConnect
|
||||
}
|
||||
|
||||
/**
|
||||
* Set http response.
|
||||
*
|
||||
* @param {object} newRes - new response
|
||||
* @returns {object} http response
|
||||
*/
|
||||
const setRes = (newRes = {}) => {
|
||||
res = newRes
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Set dates.
|
||||
*/
|
||||
const setDates = () => {
|
||||
limitToken = remember
|
||||
? appConfig.session__remember_expiration || defaultRememberSessionExpiration
|
||||
: appConfig.session_expiration || defaultSessionExpiration
|
||||
limitExpirationReuseToken =
|
||||
parseInt(appConfig.session_reuse_token_time, 10) ||
|
||||
defaultSessionLimitExpiration
|
||||
now = DateTime.local()
|
||||
nowUnix = now.toSeconds()
|
||||
expireTime = now.plus({ minutes: limitToken })
|
||||
const diff = expireTime.diff(now, 'seconds')
|
||||
relativeTime = diff.seconds
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to function xmlrpc.
|
||||
*
|
||||
* @param {string} usr - user
|
||||
* @param {string} pss - password
|
||||
* @returns {Function} xmlrpc function
|
||||
*/
|
||||
const connectOpennebula = (usr = '', pss = '') => {
|
||||
const connectUser = usr || user
|
||||
const connectPass = pss || pass
|
||||
|
||||
return nodeConnect(connectUser, connectPass)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updater http request.
|
||||
*
|
||||
* @param {string} code - http code
|
||||
*/
|
||||
const updaterResponse = (code) => {
|
||||
if (
|
||||
'id' in code &&
|
||||
'message' in code &&
|
||||
res &&
|
||||
res.locals &&
|
||||
res.locals.httpCode
|
||||
) {
|
||||
res.locals.httpCode = code
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate 2FA.
|
||||
*
|
||||
* @param {object} informationUser - user data
|
||||
* @returns {boolean} return if data is valid
|
||||
*/
|
||||
const validate2faAuthentication = (informationUser) => {
|
||||
let rtn = false
|
||||
if (
|
||||
informationUser.TEMPLATE &&
|
||||
informationUser.TEMPLATE.SUNSTONE &&
|
||||
informationUser.TEMPLATE.SUNSTONE[default2FAOpennebulaVar]
|
||||
) {
|
||||
/*********************************************************
|
||||
* Validate 2FA
|
||||
*********************************************************/
|
||||
|
||||
if (tfatoken.length <= 0) {
|
||||
updaterResponse(httpResponse(accepted))
|
||||
} else {
|
||||
const secret = informationUser.TEMPLATE.SUNSTONE[default2FAOpennebulaVar]
|
||||
if (!check2Fa(secret, tfatoken)) {
|
||||
updaterResponse(httpResponse(unauthorized, '', 'invalid 2fa token'))
|
||||
} else {
|
||||
rtn = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*********************************************************
|
||||
* Without 2FA
|
||||
*********************************************************/
|
||||
|
||||
rtn = true
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a JWT.
|
||||
*
|
||||
* @param {string} token - opennebula token
|
||||
* @param {object} informationUser - user data
|
||||
*/
|
||||
const genJWT = (token, informationUser) => {
|
||||
if (
|
||||
token &&
|
||||
token.token &&
|
||||
token.time &&
|
||||
informationUser &&
|
||||
informationUser.ID &&
|
||||
informationUser.NAME
|
||||
) {
|
||||
const { ID: id, TEMPLATE: userTemplate, NAME: username } = informationUser
|
||||
const dataJWT = { id, user: username, token: token.token }
|
||||
const expire = token.time || expireTime.toSeconds()
|
||||
const jwt = createJWT(dataJWT, nowUnix, expire)
|
||||
if (jwt) {
|
||||
const rtn = { token: jwt, id }
|
||||
if (userTemplate && userTemplate.SUNSTONE && userTemplate.SUNSTONE.LANG) {
|
||||
rtn.language = userTemplate.SUNSTONE.LANG
|
||||
}
|
||||
updaterResponse(httpResponse(ok, rtn))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get created user tokens.
|
||||
*
|
||||
* @param {string} username - username
|
||||
* @returns {object} - user token
|
||||
*/
|
||||
const getCreatedTokenOpennebula = (username = '') => {
|
||||
if (
|
||||
global &&
|
||||
global.users &&
|
||||
username &&
|
||||
global.users[username] &&
|
||||
global.users[username].tokens
|
||||
) {
|
||||
let acc = { token: '', time: 0 }
|
||||
global.users[username].tokens.forEach((curr = {}, index = 0) => {
|
||||
const currentTime = parseInt(curr.time, 10)
|
||||
|
||||
// this delete expired tokens of global.users[username]
|
||||
if (currentTime < nowUnix) {
|
||||
delete global.users[username].tokens[index]
|
||||
}
|
||||
|
||||
// this select a valid token
|
||||
if (
|
||||
DateTime.fromSeconds(currentTime).minus({
|
||||
minutes: limitExpirationReuseToken,
|
||||
}) >= now &&
|
||||
currentTime >= acc.time
|
||||
) {
|
||||
acc = { token: curr.token, time: curr.time }
|
||||
}
|
||||
})
|
||||
|
||||
if (acc.token && acc.time) {
|
||||
return acc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get zones.
|
||||
*/
|
||||
const setZones = () => {
|
||||
if (global && !global.zones) {
|
||||
const oneConnect = connectOpennebula()
|
||||
oneConnect(
|
||||
ActionZones.ZONEPOOL_INFO,
|
||||
getDefaultParamsOfOpennebulaCommand(ActionZones.ZONEPOOL_INFO, GET),
|
||||
(err, value) => {
|
||||
// res, err, value, response, next
|
||||
responseOpennebula(
|
||||
() => undefined,
|
||||
err,
|
||||
value,
|
||||
(zonesOpennebula) => {
|
||||
if (
|
||||
zonesOpennebula &&
|
||||
zonesOpennebula.ZONE_POOL &&
|
||||
zonesOpennebula.ZONE_POOL.ZONE
|
||||
) {
|
||||
const oneZones = !Array.isArray(zonesOpennebula.ZONE_POOL.ZONE)
|
||||
? [zonesOpennebula.ZONE_POOL.ZONE]
|
||||
: zonesOpennebula.ZONE_POOL.ZONE
|
||||
global.zones = oneZones.map((oneZone) => {
|
||||
const rpc =
|
||||
(oneZone && oneZone.TEMPLATE && oneZone.TEMPLATE.ENDPOINT) ||
|
||||
''
|
||||
const parsedURL = rpc && parse(rpc)
|
||||
const parsedHost = parsedURL.hostname || ''
|
||||
|
||||
return {
|
||||
id: oneZone.ID || '',
|
||||
name: oneZone.NAME || '',
|
||||
rpc: rpc,
|
||||
zeromq: `tcp://${parsedHost}:2101`,
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
next
|
||||
)
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create token server admin.
|
||||
*
|
||||
* @param {string} serverAdmin - serveradmin name
|
||||
* @param {string} username - user name
|
||||
* @returns {string} data encrypted serveradmin
|
||||
*/
|
||||
const createTokenServerAdmin = (serverAdmin = '', username = '') => {
|
||||
let rtn
|
||||
const keyGet = getKey()
|
||||
const ivGet = getIV()
|
||||
if (serverAdmin && username && key && iv) {
|
||||
const expire = parseInt(expireTime.toSeconds(), 10)
|
||||
rtn = {
|
||||
token: encrypt(`${serverAdmin}:${username}:${expire}`, keyGet, ivGet),
|
||||
time: expire,
|
||||
}
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap user with serveradmin.
|
||||
*
|
||||
* @param {object} serverAdminData - opennebula serveradmin data
|
||||
* @param {object} userData - opennebula user data
|
||||
*/
|
||||
const wrapUserWithServerAdmin = (serverAdminData = {}, userData = {}) => {
|
||||
let serverAdminName = ''
|
||||
let serverAdminPassword = ''
|
||||
let userName = ''
|
||||
|
||||
if (
|
||||
getRelativeTime() &&
|
||||
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 JWTusername = `${serverAdminName}:${userName}`
|
||||
|
||||
let tokenWithServerAdmin
|
||||
let setGlobalNewToken = false
|
||||
const validToken = getCreatedTokenOpennebula(JWTusername)
|
||||
if (validToken) {
|
||||
tokenWithServerAdmin = validToken
|
||||
} else {
|
||||
setGlobalNewToken = true
|
||||
tokenWithServerAdmin = createTokenServerAdmin(serverAdminName, userName)
|
||||
}
|
||||
|
||||
if (tokenWithServerAdmin) {
|
||||
genJWT(tokenWithServerAdmin, {
|
||||
NAME: JWTusername,
|
||||
ID: userData.ID,
|
||||
TEMPLATE: userData.TEMPLATE,
|
||||
})
|
||||
|
||||
// set global state
|
||||
if (setGlobalNewToken) {
|
||||
if (!global.users) {
|
||||
global.users = {}
|
||||
}
|
||||
if (!global.users[JWTusername]) {
|
||||
global.users[JWTusername] = { tokens: [] }
|
||||
}
|
||||
global.users[JWTusername].tokens.push({
|
||||
token: tokenWithServerAdmin.token,
|
||||
time: parseInt(expireTime.toSeconds(), 10),
|
||||
})
|
||||
}
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
updaterResponse(httpResponse(internalServerError))
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server admin and wrap user.
|
||||
*
|
||||
* @param {object} userData - opennebula user data
|
||||
*/
|
||||
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
|
||||
)
|
||||
if (tokenWithServerAdmin.token) {
|
||||
const oneConnect = connectOpennebula(
|
||||
`${serverAdminData.username}:${serverAdminData.username}`,
|
||||
tokenWithServerAdmin.token
|
||||
)
|
||||
oneConnect(
|
||||
ActionUsers.USER_INFO,
|
||||
getDefaultParamsOfOpennebulaCommand(ActionUsers.USER_INFO, GET),
|
||||
(err, value) => {
|
||||
responseOpennebula(
|
||||
updaterResponse,
|
||||
err,
|
||||
value,
|
||||
(serverAdmin = {}) =>
|
||||
wrapUserWithServerAdmin(serverAdmin, userData),
|
||||
next
|
||||
)
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Login route function.
|
||||
*
|
||||
* @param {object} userData - opennebula user data
|
||||
*/
|
||||
const login = (userData) => {
|
||||
let rtn = false
|
||||
if (userData) {
|
||||
const findTextError = `[${namespace}.${ActionUsers.USER_INFO}]`
|
||||
if (userData.indexOf && userData.indexOf(findTextError) >= 0) {
|
||||
updaterResponse(httpResponse(unauthorized))
|
||||
} else {
|
||||
rtn = true
|
||||
}
|
||||
if (userData.USER) {
|
||||
setZones()
|
||||
if (validate2faAuthentication(userData.USER)) {
|
||||
rtn = false
|
||||
setDates()
|
||||
getServerAdminAndWrapUser(userData.USER)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rtn) {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const functionRoutes = {
|
||||
login,
|
||||
getUser,
|
||||
getPass,
|
||||
setType,
|
||||
setUser,
|
||||
setPass,
|
||||
setType,
|
||||
setTfaToken,
|
||||
setRemember,
|
||||
setNext,
|
||||
setReq,
|
||||
setRes,
|
||||
updaterResponse,
|
||||
setNodeConnect,
|
||||
connectOpennebula,
|
||||
getCreatedTokenOpennebula,
|
||||
updaterResponse,
|
||||
} = require('server/routes/api/auth/utils')
|
||||
|
||||
const {
|
||||
internalServerError,
|
||||
unauthorized,
|
||||
} = require('server/utils/constants/http-codes')
|
||||
const { Actions } = require('server/utils/constants/commands/user')
|
||||
const {
|
||||
httpMethod,
|
||||
defaultEmptyFunction,
|
||||
} = require('server/utils/constants/defaults')
|
||||
|
||||
const { GET } = httpMethod
|
||||
|
||||
const {
|
||||
getDefaultParamsOfOpennebulaCommand,
|
||||
} = require('server/utils/opennebula')
|
||||
|
||||
const { writeInLogger } = require('server/utils/logger')
|
||||
|
||||
/**
|
||||
* Login user.
|
||||
*
|
||||
* @param {error} err - run if no have user data
|
||||
* @param {string} value - opennebula information
|
||||
* @param {Function} success - success
|
||||
* @param {Function} error - error
|
||||
*/
|
||||
const loginUser = (
|
||||
err = '',
|
||||
value = '',
|
||||
success = defaultEmptyFunction,
|
||||
error = defaultEmptyFunction
|
||||
) => {
|
||||
if (value && value.USER && !err) {
|
||||
success(value)
|
||||
} else {
|
||||
error()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = functionRoutes
|
||||
/**
|
||||
* Fireedge user auth.
|
||||
*
|
||||
* @param {object} res - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {object} userData - user of http request
|
||||
* @param {Function} oneConnection - function of xmlrpc
|
||||
*/
|
||||
const auth = (
|
||||
res = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {},
|
||||
oneConnection = defaultEmptyFunction
|
||||
) => {
|
||||
const { user, token, type, token2fa, remember } = params
|
||||
setRes(res)
|
||||
setNext(next)
|
||||
setNodeConnect(oneConnection)
|
||||
updaterResponse(new Map(internalServerError).toObject())
|
||||
if (user && token) {
|
||||
const oneConnect = connectOpennebula(user, token)
|
||||
|
||||
/**
|
||||
* Run if have information.
|
||||
*
|
||||
* @param {object} oneValue - opennebula value
|
||||
*/
|
||||
const success = (oneValue) => {
|
||||
setUser(user || '')
|
||||
setPass(token || '')
|
||||
setType(type || '')
|
||||
setTfaToken(token2fa || '')
|
||||
setRemember(remember || false)
|
||||
login(oneValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Run if no have information.
|
||||
*/
|
||||
const error = () => {
|
||||
updaterResponse(new Map(unauthorized).toObject())
|
||||
writeInLogger(unauthorized)
|
||||
next()
|
||||
}
|
||||
|
||||
oneConnect(
|
||||
Actions.USER_INFO,
|
||||
getDefaultParamsOfOpennebulaCommand(Actions.USER_INFO, GET),
|
||||
(err, value) => {
|
||||
loginUser(err, value, success, error)
|
||||
},
|
||||
false
|
||||
)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const authApi = {
|
||||
auth,
|
||||
}
|
||||
module.exports = authApi
|
||||
|
@ -15,8 +15,8 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { setApiRoutes } = require('server/utils/server')
|
||||
const { routes: authRoutes } = require('./auth')
|
||||
const { AUTH } = require('./string-routes')
|
||||
const { routes: authRoutes } = require('server/routes/api/auth/routes')
|
||||
const { AUTH } = require('server/routes/api/auth/string-routes')
|
||||
|
||||
const functionRoutes = {
|
||||
private: [],
|
||||
|
@ -18,7 +18,7 @@ const {
|
||||
httpMethod,
|
||||
from: fromData,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { auth } = require('./auth-functions')
|
||||
const { auth } = require('server/routes/api/auth/functions')
|
||||
const { POST } = httpMethod
|
||||
|
||||
const routes = {
|
624
src/fireedge/src/server/routes/api/auth/utils.js
Normal file
624
src/fireedge/src/server/routes/api/auth/utils.js
Normal file
@ -0,0 +1,624 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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 { DateTime } = require('luxon')
|
||||
// eslint-disable-next-line node/no-deprecated-api
|
||||
const { parse } = require('url')
|
||||
const { global, Array } = require('window-or-global')
|
||||
const { Actions: ActionUsers } = require('server/utils/constants/commands/user')
|
||||
const { Actions: ActionZones } = require('server/utils/constants/commands/zone')
|
||||
const {
|
||||
httpMethod,
|
||||
defaultSessionExpiration,
|
||||
default2FAOpennebulaVar,
|
||||
defaultNamespace,
|
||||
defaultEmptyFunction,
|
||||
defaultSessionLimitExpiration,
|
||||
defaultRememberSessionExpiration,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { getFireedgeConfig } = require('server/utils/yml')
|
||||
const {
|
||||
ok,
|
||||
unauthorized,
|
||||
accepted,
|
||||
internalServerError,
|
||||
} = require('server/utils/constants/http-codes')
|
||||
const { createJWT, check2Fa } = require('server/utils/jwt')
|
||||
const {
|
||||
httpResponse,
|
||||
encrypt,
|
||||
getSunstoneAuth,
|
||||
} = require('server/utils/server')
|
||||
const {
|
||||
responseOpennebula,
|
||||
getDefaultParamsOfOpennebulaCommand,
|
||||
} = require('server/utils/opennebula')
|
||||
|
||||
const appConfig = getFireedgeConfig()
|
||||
|
||||
const namespace = appConfig.namespace || defaultNamespace
|
||||
|
||||
const { GET } = httpMethod
|
||||
|
||||
let user = ''
|
||||
let key = ''
|
||||
let iv = ''
|
||||
let pass = ''
|
||||
let type = ''
|
||||
let tfatoken = ''
|
||||
let remember = false
|
||||
let next = defaultEmptyFunction
|
||||
let req = {}
|
||||
let res = {}
|
||||
let nodeConnect = defaultEmptyFunction
|
||||
let now = ''
|
||||
let nowUnix = ''
|
||||
let expireTime = ''
|
||||
let relativeTime = ''
|
||||
let limitToken = defaultSessionExpiration
|
||||
let limitExpirationReuseToken = defaultSessionLimitExpiration
|
||||
|
||||
/**
|
||||
* Get key opennebula.
|
||||
*
|
||||
* @returns {string} get key
|
||||
*/
|
||||
const getKey = () => key
|
||||
|
||||
/**
|
||||
* Get initialization vector.
|
||||
*
|
||||
* @returns {string} get initialization vector
|
||||
*/
|
||||
const getIV = () => iv
|
||||
|
||||
/**
|
||||
* Get user opennebula.
|
||||
*
|
||||
* @returns {string} user opennebula
|
||||
*/
|
||||
const getUser = () => user
|
||||
|
||||
/**
|
||||
* Get user password opennebula.
|
||||
*
|
||||
* @returns {string} get password user opennebula
|
||||
*/
|
||||
const getPass = () => pass
|
||||
|
||||
/**
|
||||
* Get relative time.
|
||||
*
|
||||
* @returns {string} date
|
||||
*/
|
||||
const getRelativeTime = () => relativeTime
|
||||
|
||||
/**
|
||||
* Opennebula encode-decode key.
|
||||
*
|
||||
* @param {string} newKey - new key
|
||||
* @returns {string} get key
|
||||
*/
|
||||
const setKey = (newKey) => {
|
||||
key = newKey
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization vector (encrypt).
|
||||
*
|
||||
* @param {string} newIV - //16 characters
|
||||
* @returns {string} get IV
|
||||
*/
|
||||
const setIV = (newIV) => {
|
||||
iv = newIV
|
||||
|
||||
return iv
|
||||
}
|
||||
|
||||
/**
|
||||
* Username opennebula.
|
||||
*
|
||||
* @param {string} newUser - new user data
|
||||
* @returns {string} get user
|
||||
*/
|
||||
const setUser = (newUser) => {
|
||||
user = newUser
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
/**
|
||||
* User password opennebula.
|
||||
*
|
||||
* @param {string} newPass - set new opennebula password user
|
||||
* @returns {string} password user
|
||||
*/
|
||||
const setPass = (newPass) => {
|
||||
pass = newPass
|
||||
|
||||
return pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Type app.
|
||||
*
|
||||
* @param {string} newtype - new type (application)
|
||||
* @returns {string} get type
|
||||
*/
|
||||
const setType = (newtype) => {
|
||||
type = newtype
|
||||
|
||||
return type
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2FA token.
|
||||
*
|
||||
* @param {string} newTfaToken - new TFA token
|
||||
* @returns {string} get TFA token
|
||||
*/
|
||||
const setTfaToken = (newTfaToken) => {
|
||||
tfatoken = newTfaToken
|
||||
|
||||
return tfatoken
|
||||
}
|
||||
|
||||
/**
|
||||
* Set remember.
|
||||
*
|
||||
* @param {boolean} newRemember - new remember
|
||||
* @returns {boolean} remember
|
||||
*/
|
||||
const setRemember = (newRemember) => {
|
||||
remember = newRemember
|
||||
|
||||
return remember
|
||||
}
|
||||
|
||||
/**
|
||||
* Set express stepper.
|
||||
*
|
||||
* @param {Function} newNext - new stepper
|
||||
* @returns {Function} http response
|
||||
*/
|
||||
const setNext = (newNext = defaultEmptyFunction) => {
|
||||
next = newNext
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
/**
|
||||
* Set http resquest.
|
||||
*
|
||||
* @param {object} newReq - new request
|
||||
* @returns {object} http resquest
|
||||
*/
|
||||
const setReq = (newReq = {}) => {
|
||||
req = newReq
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
/**
|
||||
* Set xlmrpc connection function.
|
||||
*
|
||||
* @param {Function} newConnect - new connect opennebula
|
||||
* @returns {Function} xmlrpc function
|
||||
*/
|
||||
const setNodeConnect = (newConnect = defaultEmptyFunction) => {
|
||||
nodeConnect = newConnect
|
||||
|
||||
return nodeConnect
|
||||
}
|
||||
|
||||
/**
|
||||
* Set http response.
|
||||
*
|
||||
* @param {object} newRes - new response
|
||||
* @returns {object} http response
|
||||
*/
|
||||
const setRes = (newRes = {}) => {
|
||||
res = newRes
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Set dates.
|
||||
*/
|
||||
const setDates = () => {
|
||||
limitToken = remember
|
||||
? appConfig.session__remember_expiration || defaultRememberSessionExpiration
|
||||
: appConfig.session_expiration || defaultSessionExpiration
|
||||
limitExpirationReuseToken =
|
||||
parseInt(appConfig.session_reuse_token_time, 10) ||
|
||||
defaultSessionLimitExpiration
|
||||
now = DateTime.local()
|
||||
nowUnix = now.toSeconds()
|
||||
expireTime = now.plus({ minutes: limitToken })
|
||||
const diff = expireTime.diff(now, 'seconds')
|
||||
relativeTime = diff.seconds
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to function xmlrpc.
|
||||
*
|
||||
* @param {string} usr - user
|
||||
* @param {string} pss - password
|
||||
* @returns {Function} xmlrpc function
|
||||
*/
|
||||
const connectOpennebula = (usr = '', pss = '') => {
|
||||
const connectUser = usr || user
|
||||
const connectPass = pss || pass
|
||||
|
||||
return nodeConnect(connectUser, connectPass)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updater http request.
|
||||
*
|
||||
* @param {string} code - http code
|
||||
*/
|
||||
const updaterResponse = (code) => {
|
||||
if (
|
||||
'id' in code &&
|
||||
'message' in code &&
|
||||
res &&
|
||||
res.locals &&
|
||||
res.locals.httpCode
|
||||
) {
|
||||
res.locals.httpCode = code
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate 2FA.
|
||||
*
|
||||
* @param {object} informationUser - user data
|
||||
* @returns {boolean} return if data is valid
|
||||
*/
|
||||
const validate2faAuthentication = (informationUser) => {
|
||||
let rtn = false
|
||||
if (
|
||||
informationUser.TEMPLATE &&
|
||||
informationUser.TEMPLATE.SUNSTONE &&
|
||||
informationUser.TEMPLATE.SUNSTONE[default2FAOpennebulaVar]
|
||||
) {
|
||||
/*********************************************************
|
||||
* Validate 2FA
|
||||
*********************************************************/
|
||||
|
||||
if (tfatoken.length <= 0) {
|
||||
updaterResponse(httpResponse(accepted))
|
||||
} else {
|
||||
const secret = informationUser.TEMPLATE.SUNSTONE[default2FAOpennebulaVar]
|
||||
if (!check2Fa(secret, tfatoken)) {
|
||||
updaterResponse(httpResponse(unauthorized, '', 'invalid 2fa token'))
|
||||
} else {
|
||||
rtn = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*********************************************************
|
||||
* Without 2FA
|
||||
*********************************************************/
|
||||
|
||||
rtn = true
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a JWT.
|
||||
*
|
||||
* @param {string} token - opennebula token
|
||||
* @param {object} informationUser - user data
|
||||
*/
|
||||
const genJWT = (token, informationUser) => {
|
||||
if (
|
||||
token &&
|
||||
token.token &&
|
||||
token.time &&
|
||||
informationUser &&
|
||||
informationUser.ID &&
|
||||
informationUser.NAME
|
||||
) {
|
||||
const { ID: id, TEMPLATE: userTemplate, NAME: username } = informationUser
|
||||
const dataJWT = { id, user: username, token: token.token }
|
||||
const expire = token.time || expireTime.toSeconds()
|
||||
const jwt = createJWT(dataJWT, nowUnix, expire)
|
||||
if (jwt) {
|
||||
const rtn = { token: jwt, id }
|
||||
if (userTemplate && userTemplate.SUNSTONE && userTemplate.SUNSTONE.LANG) {
|
||||
rtn.language = userTemplate.SUNSTONE.LANG
|
||||
}
|
||||
updaterResponse(httpResponse(ok, rtn))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get created user tokens.
|
||||
*
|
||||
* @param {string} username - username
|
||||
* @returns {object} - user token
|
||||
*/
|
||||
const getCreatedTokenOpennebula = (username = '') => {
|
||||
if (
|
||||
global &&
|
||||
global.users &&
|
||||
username &&
|
||||
global.users[username] &&
|
||||
global.users[username].tokens
|
||||
) {
|
||||
let acc = { token: '', time: 0 }
|
||||
global.users[username].tokens.forEach((curr = {}, index = 0) => {
|
||||
const currentTime = parseInt(curr.time, 10)
|
||||
|
||||
// this delete expired tokens of global.users[username]
|
||||
if (currentTime < nowUnix) {
|
||||
delete global.users[username].tokens[index]
|
||||
}
|
||||
|
||||
// this select a valid token
|
||||
if (
|
||||
DateTime.fromSeconds(currentTime).minus({
|
||||
minutes: limitExpirationReuseToken,
|
||||
}) >= now &&
|
||||
currentTime >= acc.time
|
||||
) {
|
||||
acc = { token: curr.token, time: curr.time }
|
||||
}
|
||||
})
|
||||
|
||||
if (acc.token && acc.time) {
|
||||
return acc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get zones.
|
||||
*/
|
||||
const setZones = () => {
|
||||
if (global && !global.zones) {
|
||||
const oneConnect = connectOpennebula()
|
||||
oneConnect(
|
||||
ActionZones.ZONEPOOL_INFO,
|
||||
getDefaultParamsOfOpennebulaCommand(ActionZones.ZONEPOOL_INFO, GET),
|
||||
(err, value) => {
|
||||
// res, err, value, response, next
|
||||
responseOpennebula(
|
||||
() => undefined,
|
||||
err,
|
||||
value,
|
||||
(zonesOpennebula) => {
|
||||
if (
|
||||
zonesOpennebula &&
|
||||
zonesOpennebula.ZONE_POOL &&
|
||||
zonesOpennebula.ZONE_POOL.ZONE
|
||||
) {
|
||||
const oneZones = !Array.isArray(zonesOpennebula.ZONE_POOL.ZONE)
|
||||
? [zonesOpennebula.ZONE_POOL.ZONE]
|
||||
: zonesOpennebula.ZONE_POOL.ZONE
|
||||
global.zones = oneZones.map((oneZone) => {
|
||||
const rpc =
|
||||
(oneZone && oneZone.TEMPLATE && oneZone.TEMPLATE.ENDPOINT) ||
|
||||
''
|
||||
const parsedURL = rpc && parse(rpc)
|
||||
const parsedHost = parsedURL.hostname || ''
|
||||
|
||||
return {
|
||||
id: oneZone.ID || '',
|
||||
name: oneZone.NAME || '',
|
||||
rpc: rpc,
|
||||
zeromq: `tcp://${parsedHost}:2101`,
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
next
|
||||
)
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create token server admin.
|
||||
*
|
||||
* @param {string} serverAdmin - serveradmin name
|
||||
* @param {string} username - user name
|
||||
* @returns {string} data encrypted serveradmin
|
||||
*/
|
||||
const createTokenServerAdmin = (serverAdmin = '', username = '') => {
|
||||
let rtn
|
||||
const keyGet = getKey()
|
||||
const ivGet = getIV()
|
||||
if (serverAdmin && username && key && iv) {
|
||||
const expire = parseInt(expireTime.toSeconds(), 10)
|
||||
rtn = {
|
||||
token: encrypt(`${serverAdmin}:${username}:${expire}`, keyGet, ivGet),
|
||||
time: expire,
|
||||
}
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap user with serveradmin.
|
||||
*
|
||||
* @param {object} serverAdminData - opennebula serveradmin data
|
||||
* @param {object} userData - opennebula user data
|
||||
*/
|
||||
const wrapUserWithServerAdmin = (serverAdminData = {}, userData = {}) => {
|
||||
let serverAdminName = ''
|
||||
let serverAdminPassword = ''
|
||||
let userName = ''
|
||||
|
||||
if (
|
||||
getRelativeTime() &&
|
||||
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 JWTusername = `${serverAdminName}:${userName}`
|
||||
|
||||
let tokenWithServerAdmin
|
||||
let setGlobalNewToken = false
|
||||
const validToken = getCreatedTokenOpennebula(JWTusername)
|
||||
if (validToken) {
|
||||
tokenWithServerAdmin = validToken
|
||||
} else {
|
||||
setGlobalNewToken = true
|
||||
tokenWithServerAdmin = createTokenServerAdmin(serverAdminName, userName)
|
||||
}
|
||||
|
||||
if (tokenWithServerAdmin) {
|
||||
genJWT(tokenWithServerAdmin, {
|
||||
NAME: JWTusername,
|
||||
ID: userData.ID,
|
||||
TEMPLATE: userData.TEMPLATE,
|
||||
})
|
||||
|
||||
// set global state
|
||||
if (setGlobalNewToken) {
|
||||
if (!global.users) {
|
||||
global.users = {}
|
||||
}
|
||||
if (!global.users[JWTusername]) {
|
||||
global.users[JWTusername] = { tokens: [] }
|
||||
}
|
||||
global.users[JWTusername].tokens.push({
|
||||
token: tokenWithServerAdmin.token,
|
||||
time: parseInt(expireTime.toSeconds(), 10),
|
||||
})
|
||||
}
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
updaterResponse(httpResponse(internalServerError))
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server admin and wrap user.
|
||||
*
|
||||
* @param {object} userData - opennebula user data
|
||||
*/
|
||||
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
|
||||
)
|
||||
if (tokenWithServerAdmin.token) {
|
||||
const oneConnect = connectOpennebula(
|
||||
`${serverAdminData.username}:${serverAdminData.username}`,
|
||||
tokenWithServerAdmin.token
|
||||
)
|
||||
oneConnect(
|
||||
ActionUsers.USER_INFO,
|
||||
getDefaultParamsOfOpennebulaCommand(ActionUsers.USER_INFO, GET),
|
||||
(err, value) => {
|
||||
responseOpennebula(
|
||||
updaterResponse,
|
||||
err,
|
||||
value,
|
||||
(serverAdmin = {}) =>
|
||||
wrapUserWithServerAdmin(serverAdmin, userData),
|
||||
next
|
||||
)
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Login route function.
|
||||
*
|
||||
* @param {object} userData - opennebula user data
|
||||
*/
|
||||
const login = (userData) => {
|
||||
let rtn = false
|
||||
if (userData) {
|
||||
const findTextError = `[${namespace}.${ActionUsers.USER_INFO}]`
|
||||
if (userData.indexOf && userData.indexOf(findTextError) >= 0) {
|
||||
updaterResponse(httpResponse(unauthorized))
|
||||
} else {
|
||||
rtn = true
|
||||
}
|
||||
if (userData.USER) {
|
||||
setZones()
|
||||
if (validate2faAuthentication(userData.USER)) {
|
||||
rtn = false
|
||||
setDates()
|
||||
getServerAdminAndWrapUser(userData.USER)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rtn) {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const functionRoutes = {
|
||||
login,
|
||||
getUser,
|
||||
getPass,
|
||||
setType,
|
||||
setUser,
|
||||
setPass,
|
||||
setTfaToken,
|
||||
setRemember,
|
||||
setNext,
|
||||
setReq,
|
||||
setRes,
|
||||
updaterResponse,
|
||||
setNodeConnect,
|
||||
connectOpennebula,
|
||||
getCreatedTokenOpennebula,
|
||||
}
|
||||
|
||||
module.exports = functionRoutes
|
@ -18,8 +18,8 @@ const { setApiRoutes } = require('server/utils/server')
|
||||
const {
|
||||
privateRoutes: filePrivateRoutes,
|
||||
publicRoutes: filePublicRoutes,
|
||||
} = require('./files')
|
||||
const { FILES } = require('./string-routes')
|
||||
} = require('server/routes/api/files/routes')
|
||||
const { FILES } = require('server/routes/api/files/string-routes')
|
||||
|
||||
const functionRoutes = {
|
||||
private: setApiRoutes(filePrivateRoutes, FILES),
|
||||
|
@ -18,7 +18,13 @@ const {
|
||||
from: fromData,
|
||||
httpMethod,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { show, list, upload, update, deleteFile } = require('./functions')
|
||||
const {
|
||||
show,
|
||||
list,
|
||||
upload,
|
||||
update,
|
||||
deleteFile,
|
||||
} = require('server/routes/api/files/functions')
|
||||
const { GET, POST, PUT, DELETE } = httpMethod
|
||||
|
||||
const publicRoutes = {
|
@ -14,35 +14,37 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { readdirSync } = require('fs-extra')
|
||||
const { resolve } = require('path')
|
||||
const { messageTerminal } = require('server/utils/general')
|
||||
const { getRouteForOpennebulaCommand } = require('server/utils/opennebula')
|
||||
const {
|
||||
defaultFilesRoutes,
|
||||
defaultConfigErrorMessage,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { defaultConfigErrorMessage } = require('server/utils/constants/defaults')
|
||||
|
||||
const filesDataPrivate = []
|
||||
const filesDataPublic = []
|
||||
|
||||
defaultFilesRoutes.forEach((file) => {
|
||||
try {
|
||||
// eslint-disable-next-line global-require
|
||||
const fileInfo = require(`./${file}`)
|
||||
readdirSync(resolve(__dirname), { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.forEach((file) => {
|
||||
try {
|
||||
const { name } = file
|
||||
// eslint-disable-next-line global-require
|
||||
const fileInfo = require(`./${name}`)
|
||||
|
||||
if (fileInfo.private && fileInfo.private.length) {
|
||||
filesDataPrivate.push(...fileInfo.private)
|
||||
if (fileInfo.private && fileInfo.private.length) {
|
||||
filesDataPrivate.push(...fileInfo.private)
|
||||
}
|
||||
if (fileInfo.public && fileInfo.public.length) {
|
||||
filesDataPublic.push(...fileInfo.public)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.code === 'MODULE_NOT_FOUND') {
|
||||
const config = defaultConfigErrorMessage
|
||||
config.error = error.message
|
||||
messageTerminal(config)
|
||||
}
|
||||
}
|
||||
if (fileInfo.public && fileInfo.public.length) {
|
||||
filesDataPublic.push(...fileInfo.public)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.code === 'MODULE_NOT_FOUND') {
|
||||
const config = defaultConfigErrorMessage
|
||||
config.error = error.message
|
||||
messageTerminal(config)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const opennebulaActions = getRouteForOpennebulaCommand()
|
||||
const routes = {
|
||||
|
@ -15,7 +15,9 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { setApiRoutes } = require('server/utils/server')
|
||||
const { routes: marketappRoutes } = require('./marketapp')
|
||||
const {
|
||||
routes: marketappRoutes,
|
||||
} = require('server/routes/api/marketapp/routes')
|
||||
const { MARKETAPP } = require('./string-routes')
|
||||
|
||||
const functionRoutes = {
|
||||
|
@ -18,7 +18,11 @@ const {
|
||||
httpMethod,
|
||||
from: fromData,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { exportApp, importMarket, getDockerTags } = require('./functions')
|
||||
const {
|
||||
exportApp,
|
||||
importMarket,
|
||||
getDockerTags,
|
||||
} = require('server/routes/api/marketapp/functions')
|
||||
const { POST, GET } = httpMethod
|
||||
|
||||
const routes = {
|
@ -16,7 +16,7 @@
|
||||
|
||||
const { setApiRoutes } = require('server/utils/server')
|
||||
const { routes: serviceRoutes } = require('./service')
|
||||
const { routes: serviceTemplateRoutes } = require('./service_template')
|
||||
const { routes: serviceTemplateRoutes } = require('./template')
|
||||
|
||||
const { SERVICE, SERVICE_TEMPLATE } = require('./string-routes')
|
||||
|
||||
|
@ -15,8 +15,11 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { Validator } = require('jsonschema')
|
||||
const { action } = require('./schemas')
|
||||
const { oneFlowConnection } = require('./functions')
|
||||
const { action } = require('server/routes/api/oneflow/schemas')
|
||||
const {
|
||||
oneFlowConnection,
|
||||
returnSchemaError,
|
||||
} = require('server/routes/api/oneflow/utils')
|
||||
const {
|
||||
httpMethod,
|
||||
defaultEmptyFunction,
|
||||
@ -27,7 +30,6 @@ const {
|
||||
internalServerError,
|
||||
methodNotAllowed,
|
||||
} = require('server/utils/constants/http-codes')
|
||||
const { returnSchemaError } = require('./functions')
|
||||
const { generateNewResourceTemplate } = require('server/utils/opennebula')
|
||||
const { Actions: ActionVM } = require('server/utils/constants/commands/vm')
|
||||
const { GET, POST, DELETE } = httpMethod
|
||||
@ -312,7 +314,7 @@ const serviceAddRoleAction = (
|
||||
path: '/service/{0}/role/{1}',
|
||||
user,
|
||||
password,
|
||||
request: [role, id],
|
||||
request: [id, role],
|
||||
post: postAction,
|
||||
}
|
||||
oneFlowConnection(
|
@ -27,15 +27,15 @@ const {
|
||||
serviceAddSchedAction,
|
||||
serviceUpdateSchedAction,
|
||||
serviceDeleteSchedAction,
|
||||
} = require('./service-functions')
|
||||
} = require('server/routes/api/oneflow/service/functions')
|
||||
const { GET, POST, DELETE, PUT } = httpMethod
|
||||
|
||||
const routes = {
|
||||
[GET]: {
|
||||
list: {
|
||||
null: {
|
||||
action: service,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -54,11 +54,11 @@ const routes = {
|
||||
action: { from: fromData.postBody },
|
||||
},
|
||||
},
|
||||
'role-action': {
|
||||
role_action: {
|
||||
action: serviceAddRoleAction,
|
||||
params: {
|
||||
role: { from: fromData.resource, name: 'id' },
|
||||
id: { from: fromData.resource, name: 'id2' },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
role: { from: fromData.resource, name: 'id2' },
|
||||
action: { from: fromData.postBody },
|
||||
},
|
||||
},
|
||||
@ -81,15 +81,15 @@ const routes = {
|
||||
},
|
||||
},
|
||||
[DELETE]: {
|
||||
delete: {
|
||||
null: {
|
||||
action: serviceDelete,
|
||||
params: { id: { from: fromData.resource, name: 'id', front: true } },
|
||||
params: { id: { from: fromData.resource, name: 'method' } },
|
||||
},
|
||||
sched_action: {
|
||||
action: serviceDeleteSchedAction,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
id_sched: { from: fromData.resource, name: 'id2' },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
id_sched: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
},
|
@ -15,8 +15,11 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { Validator } = require('jsonschema')
|
||||
const { role, service, action } = require('./schemas')
|
||||
const { oneFlowConection } = require('./functions')
|
||||
const { role, service, action } = require('server/routes/api/oneflow/schemas')
|
||||
const {
|
||||
oneFlowConection,
|
||||
returnSchemaError,
|
||||
} = require('server/routes/api/oneflow/utils')
|
||||
const {
|
||||
httpMethod,
|
||||
defaultEmptyFunction,
|
||||
@ -27,7 +30,6 @@ const {
|
||||
internalServerError,
|
||||
methodNotAllowed,
|
||||
} = require('server/utils/constants/http-codes')
|
||||
const { returnSchemaError } = require('./functions')
|
||||
const { GET, POST, DELETE, PUT } = httpMethod
|
||||
|
||||
/**
|
@ -24,47 +24,47 @@ const {
|
||||
serviceTemplateCreate,
|
||||
serviceTemplateUpdate,
|
||||
serviceTemplateAction,
|
||||
} = require('./service_template-functions')
|
||||
} = require('server/routes/api/oneflow/template/functions')
|
||||
const { GET, POST, DELETE, PUT } = httpMethod
|
||||
|
||||
const routes = {
|
||||
[GET]: {
|
||||
list: {
|
||||
null: {
|
||||
action: serviceTemplate,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
},
|
||||
[POST]: {
|
||||
create: {
|
||||
null: {
|
||||
action: serviceTemplateCreate,
|
||||
params: {
|
||||
template: { from: fromData.postBody, front: true },
|
||||
template: { from: fromData.postBody },
|
||||
},
|
||||
},
|
||||
action: {
|
||||
action: serviceTemplateAction,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
template: { from: fromData.postBody, front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
template: { from: fromData.postBody },
|
||||
},
|
||||
},
|
||||
},
|
||||
[PUT]: {
|
||||
update: {
|
||||
null: {
|
||||
action: serviceTemplateUpdate,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
template: { from: fromData.postBody, front: true },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
template: { from: fromData.postBody },
|
||||
},
|
||||
},
|
||||
},
|
||||
[DELETE]: {
|
||||
delete: {
|
||||
null: {
|
||||
action: serviceTemplateDelete,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
},
|
@ -15,11 +15,21 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { setApiRoutes } = require('server/utils/server')
|
||||
const { routes: provisionRoutes } = require('./provision')
|
||||
const { routes: provisionTemplateRoutes } = require('./provision_template')
|
||||
const { routes: providerRoutes } = require('./provider')
|
||||
const {
|
||||
routes: provisionRoutes,
|
||||
} = require('server/routes/api/oneprovision/provision')
|
||||
const {
|
||||
routes: provisionTemplateRoutes,
|
||||
} = require('server/routes/api/oneprovision/template')
|
||||
const {
|
||||
routes: providerRoutes,
|
||||
} = require('server/routes/api/oneprovision/provider')
|
||||
|
||||
const { PROVIDER, PROVISION, PROVISION_TEMPLATE } = require('./string-routes')
|
||||
const {
|
||||
PROVIDER,
|
||||
PROVISION,
|
||||
PROVISION_TEMPLATE,
|
||||
} = require('server/routes/api/oneprovision/string-routes')
|
||||
|
||||
/**
|
||||
* Add routes.
|
@ -39,7 +39,7 @@ const {
|
||||
createYMLContent,
|
||||
getEndpoint,
|
||||
getSpecificConfig,
|
||||
} = require('./functions')
|
||||
} = require('server/routes/api/oneprovision/utils')
|
||||
|
||||
const httpInternalError = httpResponse(internalServerError, '', '')
|
||||
|
@ -14,7 +14,10 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { from: fromData } = require('server/utils/constants/defaults')
|
||||
const {
|
||||
from: fromData,
|
||||
httpMethod,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const {
|
||||
getListProviders,
|
||||
getConnectionProviders,
|
||||
@ -22,23 +25,22 @@ const {
|
||||
updateProviders,
|
||||
deleteProvider,
|
||||
getProviderConfig,
|
||||
} = require('./provider-functions')
|
||||
const { httpMethod } = require('server/utils/constants/defaults')
|
||||
} = require('server/routes/api/oneprovision/provider/functions')
|
||||
|
||||
const { GET, POST, PUT, DELETE } = httpMethod
|
||||
|
||||
const routes = {
|
||||
[GET]: {
|
||||
list: {
|
||||
null: {
|
||||
action: getListProviders,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
connection: {
|
||||
action: getConnectionProviders,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
config: {
|
||||
@ -47,27 +49,27 @@ const routes = {
|
||||
},
|
||||
},
|
||||
[POST]: {
|
||||
create: {
|
||||
null: {
|
||||
action: createProviders,
|
||||
params: {
|
||||
resource: { from: fromData.postBody, front: true },
|
||||
resource: { from: fromData.postBody },
|
||||
},
|
||||
},
|
||||
},
|
||||
[PUT]: {
|
||||
update: {
|
||||
null: {
|
||||
action: updateProviders,
|
||||
params: {
|
||||
resource: { from: fromData.postBody, front: true },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
resource: { from: fromData.postBody },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
},
|
||||
[DELETE]: {
|
||||
delete: {
|
||||
null: {
|
||||
action: deleteProvider,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
},
|
@ -58,8 +58,8 @@ const {
|
||||
getEndpoint,
|
||||
addOptionalCreateCommand,
|
||||
getSpecificConfig,
|
||||
} = require('./functions')
|
||||
const { provision } = require('./schemas')
|
||||
} = require('server/routes/api/oneprovision/utils')
|
||||
const { provision } = require('server/routes/api/oneprovision/schemas')
|
||||
|
||||
const httpInternalError = httpResponse(internalServerError, '', '')
|
||||
|
@ -31,63 +31,63 @@ const {
|
||||
configureHost,
|
||||
validate,
|
||||
getProvisionDefaults,
|
||||
} = require('./provision-functions')
|
||||
} = require('server/routes/api/oneprovision/provision/functions')
|
||||
const { GET, POST, DELETE, PUT } = httpMethod
|
||||
|
||||
const routes = {
|
||||
[GET]: {
|
||||
list: {
|
||||
null: {
|
||||
action: getListProvisions,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
cluster: {
|
||||
action: getListResourceProvision,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
resource: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
datastore: {
|
||||
action: getListResourceProvision,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
resource: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
host: {
|
||||
action: getListResourceProvision,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
resource: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
image: {
|
||||
action: getListResourceProvision,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
resource: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
network: {
|
||||
action: getListResourceProvision,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
resource: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
template: {
|
||||
action: getListResourceProvision,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
resource: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
vntemplate: {
|
||||
action: getListResourceProvision,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
resource: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
log: {
|
||||
action: getLogProvisions,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
defaults: {
|
||||
@ -96,17 +96,17 @@ const routes = {
|
||||
},
|
||||
},
|
||||
[POST]: {
|
||||
create: {
|
||||
null: {
|
||||
action: createProvision,
|
||||
params: {
|
||||
resource: { from: fromData.postBody, front: true },
|
||||
resource: { from: fromData.postBody },
|
||||
},
|
||||
websocket: true,
|
||||
},
|
||||
validate: {
|
||||
action: validate,
|
||||
params: {
|
||||
resource: { from: fromData.postBody, front: true },
|
||||
resource: { from: fromData.postBody },
|
||||
},
|
||||
},
|
||||
host: {
|
||||
@ -114,39 +114,39 @@ const routes = {
|
||||
action: hostCommand,
|
||||
params: {
|
||||
action: { from: fromData.resource, name: 'id' },
|
||||
id: { from: fromData.resource, name: 'id2', front: true },
|
||||
id: { from: fromData.resource, name: 'id2' },
|
||||
},
|
||||
},
|
||||
reboot: {
|
||||
action: hostCommand,
|
||||
params: {
|
||||
action: { from: fromData.resource, name: 'id' },
|
||||
id: { from: fromData.resource, name: 'id2', front: true },
|
||||
id: { from: fromData.resource, name: 'id2' },
|
||||
},
|
||||
},
|
||||
resume: {
|
||||
action: hostCommand,
|
||||
params: {
|
||||
action: { from: fromData.resource, name: 'id' },
|
||||
id: { from: fromData.resource, name: 'id2', front: true },
|
||||
id: { from: fromData.resource, name: 'id2' },
|
||||
},
|
||||
},
|
||||
ssh: {
|
||||
action: hostCommandSSH,
|
||||
params: {
|
||||
action: { from: fromData.resource, name: 'id' },
|
||||
id: { from: fromData.resource, name: 'id2', front: true },
|
||||
command: { from: fromData.postBody, name: 'command', front: true },
|
||||
id: { from: fromData.resource, name: 'id2' },
|
||||
command: { from: fromData.postBody, name: 'command' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
[DELETE]: {
|
||||
delete: {
|
||||
null: {
|
||||
action: deleteProvision,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
cleanup: { from: fromData.postBody, name: 'cleanup', front: true },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
cleanup: { from: fromData.postBody, name: 'cleanup' },
|
||||
},
|
||||
websocket: true,
|
||||
},
|
||||
@ -154,56 +154,56 @@ const routes = {
|
||||
action: deleteResource,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
flowtemplate: {
|
||||
action: deleteResource,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
host: {
|
||||
action: deleteResource,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
image: {
|
||||
action: deleteResource,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
network: {
|
||||
action: deleteResource,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
vntemplate: {
|
||||
action: deleteResource,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
template: {
|
||||
action: deleteResource,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
cluster: {
|
||||
action: deleteResource,
|
||||
params: {
|
||||
resource: { from: fromData.resource, name: 'method' },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
},
|
@ -32,8 +32,8 @@ const {
|
||||
createTemporalFile,
|
||||
getEndpoint,
|
||||
getSpecificConfig,
|
||||
} = require('./functions')
|
||||
const { provider } = require('./schemas')
|
||||
} = require('server/routes/api/oneprovision/utils')
|
||||
const { provider } = require('server/routes/api/oneprovision/schemas')
|
||||
|
||||
const httpInternalError = httpResponse(internalServerError, '', '')
|
||||
|
@ -21,48 +21,48 @@ const {
|
||||
instantiateProvisionTemplate,
|
||||
updateProvisionTemplate,
|
||||
deleteProvisionTemplate,
|
||||
} = require('./provision_template-functions')
|
||||
} = require('server/routes/api/oneprovision/template/functions')
|
||||
const { httpMethod } = require('server/utils/constants/defaults')
|
||||
|
||||
const { GET, POST, PUT, DELETE } = httpMethod
|
||||
|
||||
const routes = {
|
||||
[GET]: {
|
||||
list: {
|
||||
null: {
|
||||
action: getListProvisionTemplates,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
},
|
||||
[POST]: {
|
||||
create: {
|
||||
null: {
|
||||
action: createProvisionTemplate,
|
||||
params: {
|
||||
resource: { from: fromData.postBody, front: true },
|
||||
resource: { from: fromData.postBody },
|
||||
},
|
||||
},
|
||||
instantiate: {
|
||||
action: instantiateProvisionTemplate,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'id' },
|
||||
},
|
||||
},
|
||||
},
|
||||
[PUT]: {
|
||||
update: {
|
||||
null: {
|
||||
action: updateProvisionTemplate,
|
||||
params: {
|
||||
resource: { from: fromData.postBody, front: true },
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
resource: { from: fromData.postBody },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
},
|
||||
[DELETE]: {
|
||||
delete: {
|
||||
null: {
|
||||
action: deleteProvisionTemplate,
|
||||
params: {
|
||||
id: { from: fromData.resource, name: 'id', front: true },
|
||||
id: { from: fromData.resource, name: 'method' },
|
||||
},
|
||||
},
|
||||
},
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { setApiRoutes } = require('server/utils/server')
|
||||
const { routes: sunstoneRoutes } = require('./sunstone')
|
||||
const { routes: sunstoneRoutes } = require('./routes')
|
||||
|
||||
const { SUNSTONE } = require('./string-routes')
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { httpMethod } = require('server/utils/constants/defaults')
|
||||
const { getConfig, getViews } = require('./sunstone-functions')
|
||||
const { getConfig, getViews } = require('server/routes/api/sunstone/functions')
|
||||
const { GET } = httpMethod
|
||||
|
||||
const routes = {
|
@ -29,7 +29,7 @@ const {
|
||||
consoleParseToString,
|
||||
consoleParseToJSON,
|
||||
} = require('server/utils/opennebula')
|
||||
const { resources } = require('./command-flags')
|
||||
const { resources } = require('server/routes/api/vcenter/command-flags')
|
||||
|
||||
const { getSunstoneConfig } = require('server/utils/yml')
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { setApiRoutes } = require('server/utils/server')
|
||||
const { routes: vcenterRoutes } = require('./vcenter')
|
||||
const { VCENTER } = require('./string-routes')
|
||||
const { routes: vcenterRoutes } = require('server/routes/api/vcenter/routes')
|
||||
const { VCENTER } = require('server/routes/api/vcenter/string-routes')
|
||||
|
||||
const functionRoutes = {
|
||||
private: setApiRoutes(vcenterRoutes, VCENTER),
|
||||
|
@ -24,7 +24,7 @@ const {
|
||||
listAll,
|
||||
cleartags,
|
||||
hosts,
|
||||
} = require('./functions')
|
||||
} = require('server/routes/api/vcenter/functions')
|
||||
const { POST, GET } = httpMethod
|
||||
|
||||
const routes = {
|
||||
@ -82,12 +82,12 @@ const routes = {
|
||||
},
|
||||
},
|
||||
[GET]: {
|
||||
list: {
|
||||
null: {
|
||||
action: list,
|
||||
params: {
|
||||
vobject: {
|
||||
from: fromData.resource,
|
||||
name: 'id',
|
||||
name: 'method',
|
||||
},
|
||||
host: {
|
||||
from: fromData.query,
|
@ -15,8 +15,8 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { setApiRoutes } = require('server/utils/server')
|
||||
const { routes: vmRoutes } = require('./vm')
|
||||
const { VM } = require('./string-routes')
|
||||
const { routes: vmRoutes } = require('server/routes/api/vm/routes')
|
||||
const { VM } = require('server/routes/api/vm/string-routes')
|
||||
|
||||
const functionRoutes = {
|
||||
private: setApiRoutes(vmRoutes, VM),
|
||||
|
@ -18,7 +18,7 @@ const {
|
||||
httpMethod,
|
||||
from: fromData,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { saveAsTemplate } = require('./functions')
|
||||
const { saveAsTemplate } = require('server/routes/api/vm/functions')
|
||||
const { POST } = httpMethod
|
||||
|
||||
const routes = {
|
@ -13,35 +13,422 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
const { global } = require('window-or-global')
|
||||
|
||||
const { env } = require('process')
|
||||
const zendesk = require('node-zendesk')
|
||||
const { getSunstoneConfig } = require('server/utils/yml')
|
||||
const {
|
||||
defaultEmptyFunction,
|
||||
defaultSeverities,
|
||||
defaultWebpackMode,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { httpResponse } = require('server/utils/server')
|
||||
const { getSession } = require('server/routes/api/zendesk/utils')
|
||||
|
||||
const {
|
||||
ok,
|
||||
internalServerError,
|
||||
badRequest,
|
||||
unauthorized,
|
||||
} = require('server/utils/constants/http-codes')
|
||||
|
||||
const formatCreate = ({
|
||||
subject = '',
|
||||
body = '',
|
||||
version = '',
|
||||
severity = '',
|
||||
}) => {
|
||||
let rtn
|
||||
if (subject && body && version && severity) {
|
||||
rtn = {
|
||||
request: {
|
||||
subject,
|
||||
comment: {
|
||||
body,
|
||||
},
|
||||
custom_fields: [
|
||||
{ id: 391130, value: version }, // version
|
||||
{ id: 391197, value: severity }, // severity
|
||||
],
|
||||
can_be_solved_by_me: false,
|
||||
tags: [severity],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
const formatComment = ({ body = '', solved = '', attachments = [] }) => {
|
||||
let rtn
|
||||
if (body) {
|
||||
rtn = {
|
||||
request: {
|
||||
comment: {
|
||||
body,
|
||||
public: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
if (solved) {
|
||||
rtn.solved = 'true'
|
||||
}
|
||||
if (attachments && Array.isArray(attachments) && attachments.length > 0) {
|
||||
rtn.request.comment.uploads = attachments.filter((att) => att)
|
||||
}
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
const parseBufferError = (err) => {
|
||||
let rtn = ''
|
||||
let errorJson = {}
|
||||
if (err && err.result) {
|
||||
try {
|
||||
errorJson = JSON.parse(err.result.toString())
|
||||
} catch {}
|
||||
if (errorJson && errorJson.error) {
|
||||
rtn = errorJson.error.title ? `${errorJson.error.title}: ` : ''
|
||||
rtn += errorJson.error.message ? errorJson.error.message : ''
|
||||
}
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
const httpBadRequest = httpResponse(badRequest, '', '')
|
||||
|
||||
/**
|
||||
* Get data fireedge session.
|
||||
* Login on Zendesk.
|
||||
*
|
||||
* @param {string} username - username
|
||||
* @param {string} token - pass
|
||||
* @returns {object} user session
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {string} params.user - zendesk user
|
||||
* @param {string} params.pass - zendesk.pass
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const getSession = (username = '', token = '') => {
|
||||
if (
|
||||
username &&
|
||||
token &&
|
||||
global &&
|
||||
global.users &&
|
||||
username &&
|
||||
global.users[username] &&
|
||||
global.users[username].tokens
|
||||
) {
|
||||
const session = global.users[username].tokens.find(
|
||||
(curr = {}, index = 0) => curr.token && curr.token === token
|
||||
)
|
||||
const login = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const sunstoneConfig = getSunstoneConfig()
|
||||
const remoteUri = sunstoneConfig.support_url || ''
|
||||
const { user, password } = userData
|
||||
const { user: zendeskUser, pass } = params
|
||||
if (remoteUri && zendeskUser && pass && user && password) {
|
||||
const zendeskData = {
|
||||
username: zendeskUser,
|
||||
password: pass,
|
||||
remoteUri,
|
||||
debug: env.NODE_ENV === defaultWebpackMode,
|
||||
}
|
||||
const session = getSession(userData.user, userData.password)
|
||||
/** ZENDESK AUTH */
|
||||
const zendeskClient = zendesk.createClient(zendeskData)
|
||||
zendeskClient.users.auth((err, res, result) => {
|
||||
let method = ok
|
||||
let data = result
|
||||
if (err) {
|
||||
if (session.zendesk) {
|
||||
delete session.zendesk
|
||||
}
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
}
|
||||
if (result && result.authenticity_token) {
|
||||
const zendeskUserData = {
|
||||
...zendeskData,
|
||||
id: result.id,
|
||||
}
|
||||
session.zendesk = zendeskUserData
|
||||
}
|
||||
|
||||
return session
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
})
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const functions = {
|
||||
getSession,
|
||||
/**
|
||||
* List on Zendesk.
|
||||
*
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const list = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const { user, password } = userData
|
||||
if (user && password) {
|
||||
const session = getSession(user, password)
|
||||
if (session.zendesk && session.zendesk.id) {
|
||||
/** LIST ZENDESK */
|
||||
const zendeskClient = zendesk.createClient(session.zendesk)
|
||||
zendeskClient.requests.getRequest(
|
||||
{ status: 'open,pending' },
|
||||
(err, req, result) => {
|
||||
let method = ok
|
||||
let data = ''
|
||||
|
||||
if (err) {
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
} else if (result) {
|
||||
let pendings = 0
|
||||
let opens = 0
|
||||
const tickets = Array.isArray(result) ? result : result
|
||||
tickets.forEach((ticket) => {
|
||||
if (ticket && ticket.status) {
|
||||
switch (ticket.status) {
|
||||
case 'pending':
|
||||
pendings += 1
|
||||
break
|
||||
default:
|
||||
opens += 1
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
data = {
|
||||
tickets: result,
|
||||
pendings,
|
||||
opens,
|
||||
}
|
||||
}
|
||||
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
response.locals.httpCode = httpResponse(unauthorized)
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = functions
|
||||
/**
|
||||
* Get Comments on ticket.
|
||||
*
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {number} params.id - comment id
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const comments = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const { id } = params
|
||||
const { user, password } = userData
|
||||
if (Number.isInteger(parseInt(id, 10)) && user && password) {
|
||||
const session = getSession(user, password)
|
||||
if (session.zendesk) {
|
||||
/** GET COMMENTS ON TICKET ZENDESK */
|
||||
const zendeskClient = zendesk.createClient(session.zendesk)
|
||||
zendeskClient.requests.listComments(params.id, (err, req, result) => {
|
||||
let method = ok
|
||||
let data = ''
|
||||
|
||||
if (err) {
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
} else if (result) {
|
||||
data = result
|
||||
}
|
||||
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
})
|
||||
} else {
|
||||
response.locals.httpCode = httpResponse(unauthorized)
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ticket.
|
||||
*
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {string} params.subject - subject
|
||||
* @param {string} params.body - body
|
||||
* @param {string} params.version - version
|
||||
* @param {string} params.severity - severity
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const create = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const { subject, body, version, severity } = params
|
||||
const { user, password } = userData
|
||||
if (
|
||||
subject &&
|
||||
body &&
|
||||
version &&
|
||||
severity &&
|
||||
defaultSeverities.includes(severity) &&
|
||||
user &&
|
||||
password
|
||||
) {
|
||||
const session = getSession(user, password)
|
||||
if (session.zendesk && session.zendesk.id) {
|
||||
/** CREATE TICKET ZENDESK */
|
||||
const zendeskClient = zendesk.createClient(session.zendesk)
|
||||
const ticket = formatCreate(params)
|
||||
zendeskClient.requests.create(ticket, (err, req, result) => {
|
||||
let method = ok
|
||||
let data = ''
|
||||
if (err) {
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
} else if (result) {
|
||||
data = result
|
||||
}
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
})
|
||||
} else {
|
||||
response.locals.httpCode = httpResponse(unauthorized)
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Ticket.
|
||||
*
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {string} params.id - ticket id
|
||||
* @param {string} params.body - ticket body
|
||||
* @param {object[]} params.attachments - files
|
||||
* @param {string} params.attachments.originalname - original name
|
||||
* @param {string} params.attachments.path - path file
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const update = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const { id, body, attachments } = params
|
||||
const { user, password } = userData
|
||||
if (Number.isInteger(parseInt(id, 10)) && body && user && password) {
|
||||
const session = getSession(userData.user, userData.password)
|
||||
if (session.zendesk && session.zendesk.id) {
|
||||
const zendeskClient = zendesk.createClient(session.zendesk)
|
||||
|
||||
const sendRequest = (requestParams = {}) => {
|
||||
/** UPDATE TICKET ZENDESK */
|
||||
const ticket = formatComment(requestParams)
|
||||
zendeskClient.requests.update(id, ticket, (err, req, result) => {
|
||||
let method = ok
|
||||
let data = ''
|
||||
|
||||
if (err) {
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
} else if (result) {
|
||||
data = result
|
||||
}
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
/** UPLOAD FILES */
|
||||
let uploadedAttachments
|
||||
if (
|
||||
attachments &&
|
||||
zendeskClient.attachments &&
|
||||
typeof zendeskClient.attachments.upload === 'function'
|
||||
) {
|
||||
attachments.forEach((att = {}) => {
|
||||
if (att && att.originalname && att.path) {
|
||||
zendeskClient.attachments.upload(
|
||||
att.path,
|
||||
{
|
||||
filename: att.originalname,
|
||||
},
|
||||
(err, req, result) => {
|
||||
const token =
|
||||
(result && result.upload && result.upload.token) || ''
|
||||
if (uploadedAttachments) {
|
||||
uploadedAttachments.push(token)
|
||||
} else {
|
||||
uploadedAttachments = [token]
|
||||
}
|
||||
if (
|
||||
!err &&
|
||||
token &&
|
||||
uploadedAttachments.length === attachments.length
|
||||
) {
|
||||
sendRequest({ ...params, attachments: uploadedAttachments })
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
sendRequest({ ...params, attachments })
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpResponse(unauthorized)
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const functionRoutes = {
|
||||
login,
|
||||
list,
|
||||
comments,
|
||||
create,
|
||||
update,
|
||||
}
|
||||
module.exports = functionRoutes
|
||||
|
@ -15,8 +15,8 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { setApiRoutes } = require('server/utils/server')
|
||||
const { routes: zendeskRoutes } = require('./zendesk')
|
||||
const { ZENDESK } = require('./string-routes')
|
||||
const { routes: zendeskRoutes } = require('server/routes/api/zendesk/routes')
|
||||
const { ZENDESK } = require('server/routes/api/zendesk/string-routes')
|
||||
|
||||
const functionRoutes = {
|
||||
private: setApiRoutes(zendeskRoutes, ZENDESK),
|
||||
|
@ -18,7 +18,13 @@ const {
|
||||
httpMethod,
|
||||
from: fromData,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { login, list, comments, create, update } = require('./zendesk-functions')
|
||||
const {
|
||||
login,
|
||||
list,
|
||||
comments,
|
||||
create,
|
||||
update,
|
||||
} = require('server/routes/api/zendesk/functions')
|
||||
const { POST, GET, PUT } = httpMethod
|
||||
|
||||
const routes = {
|
||||
@ -36,7 +42,7 @@ const routes = {
|
||||
},
|
||||
},
|
||||
},
|
||||
create: {
|
||||
null: {
|
||||
action: create,
|
||||
params: {
|
||||
subject: {
|
||||
@ -59,12 +65,12 @@ const routes = {
|
||||
},
|
||||
},
|
||||
[PUT]: {
|
||||
update: {
|
||||
null: {
|
||||
action: update,
|
||||
params: {
|
||||
id: {
|
||||
from: fromData.resource,
|
||||
name: 'id',
|
||||
name: 'method',
|
||||
},
|
||||
body: {
|
||||
from: fromData.postBody,
|
||||
@ -82,7 +88,7 @@ const routes = {
|
||||
},
|
||||
},
|
||||
[GET]: {
|
||||
list: {
|
||||
null: {
|
||||
action: list,
|
||||
params: {},
|
||||
},
|
@ -13,14 +13,35 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
const { global } = require('window-or-global')
|
||||
|
||||
const privateRoutes = []
|
||||
/**
|
||||
* Get data fireedge session.
|
||||
*
|
||||
* @param {string} username - username
|
||||
* @param {string} token - pass
|
||||
* @returns {object} user session
|
||||
*/
|
||||
const getSession = (username = '', token = '') => {
|
||||
if (
|
||||
username &&
|
||||
token &&
|
||||
global &&
|
||||
global.users &&
|
||||
username &&
|
||||
global.users[username] &&
|
||||
global.users[username].tokens
|
||||
) {
|
||||
const session = global.users[username].tokens.find(
|
||||
(curr = {}, index = 0) => curr.token && curr.token === token
|
||||
)
|
||||
|
||||
const publicRoutes = []
|
||||
|
||||
const functionRoutes = {
|
||||
private: privateRoutes,
|
||||
public: publicRoutes,
|
||||
return session
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = functionRoutes
|
||||
const functions = {
|
||||
getSession,
|
||||
}
|
||||
|
||||
module.exports = functions
|
@ -1,434 +0,0 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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 { env } = require('process')
|
||||
const zendesk = require('node-zendesk')
|
||||
const { getSunstoneConfig } = require('server/utils/yml')
|
||||
const {
|
||||
defaultEmptyFunction,
|
||||
defaultSeverities,
|
||||
defaultWebpackMode,
|
||||
} = require('server/utils/constants/defaults')
|
||||
const { httpResponse } = require('server/utils/server')
|
||||
const { getSession } = require('./functions')
|
||||
|
||||
const {
|
||||
ok,
|
||||
internalServerError,
|
||||
badRequest,
|
||||
unauthorized,
|
||||
} = require('server/utils/constants/http-codes')
|
||||
|
||||
const formatCreate = ({
|
||||
subject = '',
|
||||
body = '',
|
||||
version = '',
|
||||
severity = '',
|
||||
}) => {
|
||||
let rtn
|
||||
if (subject && body && version && severity) {
|
||||
rtn = {
|
||||
request: {
|
||||
subject,
|
||||
comment: {
|
||||
body,
|
||||
},
|
||||
custom_fields: [
|
||||
{ id: 391130, value: version }, // version
|
||||
{ id: 391197, value: severity }, // severity
|
||||
],
|
||||
can_be_solved_by_me: false,
|
||||
tags: [severity],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
const formatComment = ({ body = '', solved = '', attachments = [] }) => {
|
||||
let rtn
|
||||
if (body) {
|
||||
rtn = {
|
||||
request: {
|
||||
comment: {
|
||||
body,
|
||||
public: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
if (solved) {
|
||||
rtn.solved = 'true'
|
||||
}
|
||||
if (attachments && Array.isArray(attachments) && attachments.length > 0) {
|
||||
rtn.request.comment.uploads = attachments.filter((att) => att)
|
||||
}
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
const parseBufferError = (err) => {
|
||||
let rtn = ''
|
||||
let errorJson = {}
|
||||
if (err && err.result) {
|
||||
try {
|
||||
errorJson = JSON.parse(err.result.toString())
|
||||
} catch {}
|
||||
if (errorJson && errorJson.error) {
|
||||
rtn = errorJson.error.title ? `${errorJson.error.title}: ` : ''
|
||||
rtn += errorJson.error.message ? errorJson.error.message : ''
|
||||
}
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
const httpBadRequest = httpResponse(badRequest, '', '')
|
||||
|
||||
/**
|
||||
* Login on Zendesk.
|
||||
*
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {string} params.user - zendesk user
|
||||
* @param {string} params.pass - zendesk.pass
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const login = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const sunstoneConfig = getSunstoneConfig()
|
||||
const remoteUri = sunstoneConfig.support_url || ''
|
||||
const { user, password } = userData
|
||||
const { user: zendeskUser, pass } = params
|
||||
if (remoteUri && zendeskUser && pass && user && password) {
|
||||
const zendeskData = {
|
||||
username: zendeskUser,
|
||||
password: pass,
|
||||
remoteUri,
|
||||
debug: env.NODE_ENV === defaultWebpackMode,
|
||||
}
|
||||
const session = getSession(userData.user, userData.password)
|
||||
/** ZENDESK AUTH */
|
||||
const zendeskClient = zendesk.createClient(zendeskData)
|
||||
zendeskClient.users.auth((err, res, result) => {
|
||||
let method = ok
|
||||
let data = result
|
||||
if (err) {
|
||||
if (session.zendesk) {
|
||||
delete session.zendesk
|
||||
}
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
}
|
||||
if (result && result.authenticity_token) {
|
||||
const zendeskUserData = {
|
||||
...zendeskData,
|
||||
id: result.id,
|
||||
}
|
||||
session.zendesk = zendeskUserData
|
||||
}
|
||||
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
})
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List on Zendesk.
|
||||
*
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const list = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const { user, password } = userData
|
||||
if (user && password) {
|
||||
const session = getSession(user, password)
|
||||
if (session.zendesk && session.zendesk.id) {
|
||||
/** LIST ZENDESK */
|
||||
const zendeskClient = zendesk.createClient(session.zendesk)
|
||||
zendeskClient.requests.getRequest(
|
||||
{ status: 'open,pending' },
|
||||
(err, req, result) => {
|
||||
let method = ok
|
||||
let data = ''
|
||||
|
||||
if (err) {
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
} else if (result) {
|
||||
let pendings = 0
|
||||
let opens = 0
|
||||
const tickets = Array.isArray(result) ? result : result
|
||||
tickets.forEach((ticket) => {
|
||||
if (ticket && ticket.status) {
|
||||
switch (ticket.status) {
|
||||
case 'pending':
|
||||
pendings += 1
|
||||
break
|
||||
default:
|
||||
opens += 1
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
data = {
|
||||
tickets: result,
|
||||
pendings,
|
||||
opens,
|
||||
}
|
||||
}
|
||||
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
response.locals.httpCode = httpResponse(unauthorized)
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Comments on ticket.
|
||||
*
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {number} params.id - comment id
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const comments = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const { id } = params
|
||||
const { user, password } = userData
|
||||
if (Number.isInteger(parseInt(id, 10)) && user && password) {
|
||||
const session = getSession(user, password)
|
||||
if (session.zendesk) {
|
||||
/** GET COMMENTS ON TICKET ZENDESK */
|
||||
const zendeskClient = zendesk.createClient(session.zendesk)
|
||||
zendeskClient.requests.listComments(params.id, (err, req, result) => {
|
||||
let method = ok
|
||||
let data = ''
|
||||
|
||||
if (err) {
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
} else if (result) {
|
||||
data = result
|
||||
}
|
||||
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
})
|
||||
} else {
|
||||
response.locals.httpCode = httpResponse(unauthorized)
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ticket.
|
||||
*
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {string} params.subject - subject
|
||||
* @param {string} params.body - body
|
||||
* @param {string} params.version - version
|
||||
* @param {string} params.severity - severity
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const create = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const { subject, body, version, severity } = params
|
||||
const { user, password } = userData
|
||||
if (
|
||||
subject &&
|
||||
body &&
|
||||
version &&
|
||||
severity &&
|
||||
defaultSeverities.includes(severity) &&
|
||||
user &&
|
||||
password
|
||||
) {
|
||||
const session = getSession(user, password)
|
||||
if (session.zendesk && session.zendesk.id) {
|
||||
/** CREATE TICKET ZENDESK */
|
||||
const zendeskClient = zendesk.createClient(session.zendesk)
|
||||
const ticket = formatCreate(params)
|
||||
zendeskClient.requests.create(ticket, (err, req, result) => {
|
||||
let method = ok
|
||||
let data = ''
|
||||
if (err) {
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
} else if (result) {
|
||||
data = result
|
||||
}
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
})
|
||||
} else {
|
||||
response.locals.httpCode = httpResponse(unauthorized)
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Ticket.
|
||||
*
|
||||
* @param {object} response - http response
|
||||
* @param {Function} next - express stepper
|
||||
* @param {object} params - params of http request
|
||||
* @param {string} params.id - ticket id
|
||||
* @param {string} params.body - ticket body
|
||||
* @param {object[]} params.attachments - files
|
||||
* @param {string} params.attachments.originalname - original name
|
||||
* @param {string} params.attachments.path - path file
|
||||
* @param {object} userData - user of http request
|
||||
* @param {string} userData.user - username
|
||||
* @param {string} userData.password - user password
|
||||
*/
|
||||
const update = (
|
||||
response = {},
|
||||
next = defaultEmptyFunction,
|
||||
params = {},
|
||||
userData = {}
|
||||
) => {
|
||||
const { id, body, attachments } = params
|
||||
const { user, password } = userData
|
||||
if (Number.isInteger(parseInt(id, 10)) && body && user && password) {
|
||||
const session = getSession(userData.user, userData.password)
|
||||
if (session.zendesk && session.zendesk.id) {
|
||||
const zendeskClient = zendesk.createClient(session.zendesk)
|
||||
|
||||
const sendRequest = (requestParams = {}) => {
|
||||
/** UPDATE TICKET ZENDESK */
|
||||
const ticket = formatComment(requestParams)
|
||||
zendeskClient.requests.update(id, ticket, (err, req, result) => {
|
||||
let method = ok
|
||||
let data = ''
|
||||
|
||||
if (err) {
|
||||
method = internalServerError
|
||||
data = parseBufferError(err)
|
||||
} else if (result) {
|
||||
data = result
|
||||
}
|
||||
response.locals.httpCode = httpResponse(method, data)
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
/** UPLOAD FILES */
|
||||
let uploadedAttachments
|
||||
if (
|
||||
attachments &&
|
||||
zendeskClient.attachments &&
|
||||
typeof zendeskClient.attachments.upload === 'function'
|
||||
) {
|
||||
attachments.forEach((att = {}) => {
|
||||
if (att && att.originalname && att.path) {
|
||||
zendeskClient.attachments.upload(
|
||||
att.path,
|
||||
{
|
||||
filename: att.originalname,
|
||||
},
|
||||
(err, req, result) => {
|
||||
const token =
|
||||
(result && result.upload && result.upload.token) || ''
|
||||
if (uploadedAttachments) {
|
||||
uploadedAttachments.push(token)
|
||||
} else {
|
||||
uploadedAttachments = [token]
|
||||
}
|
||||
if (
|
||||
!err &&
|
||||
token &&
|
||||
uploadedAttachments.length === attachments.length
|
||||
) {
|
||||
sendRequest({ ...params, attachments: uploadedAttachments })
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
sendRequest({ ...params, attachments })
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpResponse(unauthorized)
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
response.locals.httpCode = httpBadRequest
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const functionRoutes = {
|
||||
login,
|
||||
list,
|
||||
comments,
|
||||
create,
|
||||
update,
|
||||
}
|
||||
module.exports = functionRoutes
|
@ -16,7 +16,7 @@
|
||||
|
||||
const { middlewareValidateAuthWebsocket } = require('server/utils/server')
|
||||
const { messageTerminal } = require('server/utils/general')
|
||||
const { subscriber } = require('server/routes/api/provision/functions')
|
||||
const { subscriber } = require('server/routes/api/oneprovision/utils')
|
||||
|
||||
/**
|
||||
* Object http error.
|
||||
|
@ -67,19 +67,6 @@ const defaults = {
|
||||
methods: ['GET', 'POST'],
|
||||
},
|
||||
},
|
||||
defaultFilesRoutes: [
|
||||
'2fa',
|
||||
'auth',
|
||||
'files',
|
||||
'marketapp',
|
||||
'oneflow',
|
||||
'support',
|
||||
'vcenter',
|
||||
'vm',
|
||||
'zendesk',
|
||||
appNameProvision,
|
||||
appNameSunstone,
|
||||
],
|
||||
defaultApps: apps,
|
||||
httpMethod: {
|
||||
GET: 'GET',
|
||||
|
@ -188,16 +188,16 @@ const addFunctionAsRoute = (
|
||||
if (req && req.serverDataSource && res && next && routes) {
|
||||
const serverDataSource = req.serverDataSource
|
||||
const resources = Object.keys(serverDataSource[fromData.resource])
|
||||
const route =
|
||||
let route =
|
||||
routes[
|
||||
`${serverDataSource[fromData.resource][resources[index]]}`.toLowerCase()
|
||||
]
|
||||
if (
|
||||
fromData &&
|
||||
fromData.resource &&
|
||||
serverDataSource[fromData.resource] &&
|
||||
route
|
||||
) {
|
||||
|
||||
if (!route && index === 0 && routes.null) {
|
||||
route = routes.null
|
||||
}
|
||||
|
||||
if (route) {
|
||||
if (Object.keys(route).length > 0 && route.constructor === Object) {
|
||||
if (
|
||||
route.action &&
|
||||
|
Loading…
x
Reference in New Issue
Block a user