Commit bf19d9b1 authored by Gabriele De Blasi's avatar Gabriele De Blasi
Browse files

extract IPMIConsole handling from DimonServer

parent 9527c08d
Pipeline #3037071 passed with stages
in 11 minutes and 41 seconds
// @ts-check
const
IPMIConsole = require('express').Router(),
path = require('path'),
pug = require('pug'),
{ get, isEmpty, toString } = require('lodash'),
superagent = require('superagent'),
{ DOMParser } = require('xmldom'),
ipmiPath = '/dist/ipmi-console', // IPMI-JavaWS resources path
iKVMTemplate = pug.compileFile(
path.join(__dirname, '../../www', `${ipmiPath}/iKVM.pug`),
{ pretty: true });
IPMIConsole.get('/ipmi-console/:host', async (req, res, next) => {
const credentials = get(req, [ 'locals', 'credentials' ]);
const host = req.params.host;
try {
// fetch session credentials
const loginUsr = get(credentials, [ `ipmi://${host}`, 'user' ]);
if (!loginUsr) {
throw new Error(`'${host}' login error: user missing.`);
}
const loginPsw = get(credentials, [ `ipmi://${host}`, 'password' ]);
if (!loginPsw) {
throw new Error(`'${host}' login error: password missing.`);
}
const agent = superagent.agent(); // to save cookies
// login
await agent.post(`https://${host}/cgi/login.cgi`)
.disableTLSCerts() /* FIXME: update certs and remove this line */
.send(`name=${loginUsr}&pwd=${loginPsw}`);
// check Session ID
const SID = agent.jar.getCookie('SID',
{ path: '/', domain: host, secure: true, script: false });
if (SID && isEmpty(SID.value)) {
throw new Error(`'${host}' login error: cannot open a session. ` +
`Please check credentials.`);
}
// generate the 'iKVM.jnlp' file
const iKVM = await agent
.get(`https://${host}/cgi/url_redirect.cgi?url_name=ikvm&url_type=jwsk`)
.disableTLSCerts() /* FIXME: update certs and remove this line */
.then((/** @type {any} */res) => {
const doc = new DOMParser().parseFromString(toString(res.body));
const args = doc.getElementsByTagName('argument');
return iKVMTemplate({
basePath: `${req.protocol}://${req.get('host')}` + ipmiPath,
host,
username: args[1].textContent,
password: args[2].textContent
});
});
return res
.set('Content-Disposition', `attachment; filename="iKVM.jnlp"`)
.set('Content-Type', 'application/x-java-jnlp-file')
.status(200)
.send(iKVM);
}
catch (err) {
return next(err);
}
});
module.exports = IPMIConsole;
......@@ -3,13 +3,10 @@
const
{ Dimon, Status } = require('./Dimon'),
{ ActionRunner, ActAllowed } = require('./Actions/ActionRunner'),
IPMIConsole = require('./Actions/IPMIConsole'),
path = require('path'),
pug = require('pug'),
superagent = require('superagent'),
{ DOMParser } = require('xmldom'),
{ get, forEach, throttle, has, find, includes, isEmpty,
toString } = require('lodash');
bodyParserJson = require('express').json,
{ get, forEach, throttle, has, find, includes, set } = require('lodash');
/**
* @typedef {import('./types').Dimon.Zone} Zone
......@@ -27,6 +24,8 @@ const WS_NORMAL_CLOSURE = 1000;
const WS_GOING_AWAY = 1001;
const WS_INTERNAL_ERROR = 1011;
const ActionRouters = [ IPMIConsole ];
class DimonServer {
/**
* @param {Config} config
......@@ -58,10 +57,6 @@ class DimonServer {
throw new Error('WebSocket not supported');
}
const iKVMTemplate = pug.compileFile(
path.join(__dirname, '..', 'www/dist/ipmi-console/iKVM.pug'),
{ pretty: true });
router.ws('/monitoring', (ws) => {
try {
/* send menus and current zones */
......@@ -137,71 +132,21 @@ class DimonServer {
ws.once('close', () => ws.removeAllListeners());
});
router.get('/ipmi-console/:host', async (req, res, next) => {
router.use('/action', bodyParserJson(), (req, res, next) => {
try {
const action = this._getAction(/** @type {ActionReq} */(req.query));
const action = this._getAction(
/** @type {ActionReq} */(req.query || req.body));
if (!action) { throw new Error('Action not found'); }
this._checkPermission(action, get(req.user, [ 'cern_roles' ]));
// fetch session credentials
const host = req.params.host;
const loginUsr = get(this.credentials, [ `ipmi://${host}`, 'user' ]);
if (!loginUsr) {
throw new Error(`'${host}' login error: user missing.`);
}
const loginPsw =
get(this.credentials, [ `ipmi://${host}`, 'password' ]);
if (!loginPsw) {
throw new Error(`'${host}' login error: password missing.`);
}
const agent = superagent.agent(); // to save cookies
// login
await agent.post(`https://${host}/cgi/login.cgi`)
.disableTLSCerts() /* FIXME: update certs and remove this line */
.send(`name=${loginUsr}&pwd=${loginPsw}`);
// check Session ID
const SID = agent.jar.getCookie('SID',
{ path: '/', domain: host, secure: true, script: false });
if (SID && isEmpty(SID.value)) {
throw new Error(`'${host}' login error: cannot open a session. ` +
`Please check credentials.`);
}
// generate the 'iKVM.jnlp' file
const iKVM = await agent
.get(`https://${host}/cgi/url_redirect.cgi?url_name=ikvm&url_type=jwsk`)
.disableTLSCerts() /* FIXME: update certs and remove this line */
.then((/** @type {any} */res) => {
const doc = new DOMParser().parseFromString(toString(res.body));
const args = doc.getElementsByTagName('argument');
return iKVMTemplate({
basePath: `${req.protocol}://${req.get('host')}/dist/ipmi-console/`,
host,
username: args[1].textContent,
password: args[2].textContent
});
});
res
.set('Content-Disposition', `attachment; filename="iKVM.jnlp"`)
.set('Content-Type', 'application/x-java-jnlp-file')
.status(200)
.send(iKVM);
set(req, [ 'locals', 'credentials' ], this.credentials);
set(req, [ 'locals', 'action' ], action);
return next();
}
catch (err) {
// forward to the server error handler
next({ status: 500, message: err.message });
return;
return next(err);
}
});
}, ...ActionRouters);
}
/**
......
......@@ -18,5 +18,5 @@ export type ExtendedActionInfo = AppServer.Dimon.ActionInfo &
declare class Action {
getConfig(info?: ExtendedActionInfo): Promise<ActionConfig>;
runOnServer(): boolean;
useWS(): boolean;
}
......@@ -21,7 +21,7 @@ BaseDialog(ref='dialog' @hidden='onHidden')
| {{ out.data }}
pre.m-1.h6(v-for='(lnk, lnkIdx) in extLinks')
| Open
a.x-link.mx-2.text-primary(:href='lnk.url' target="_blank" rel="noopener" :key='lnkIdx')
a.x-link.mx-2.text-primary(:href='lnk.url' target="_blank" :key='lnkIdx')
| {{lnk.descr || 'external link'}} #[i.fas.fa-external-link-alt]
template(v-slot:footer='{ resolve }')
.d-flex.justify-content-end
......
......@@ -87,7 +87,7 @@ const component = /** @type {V.Constructor<typeof options, Refs>} */(Vue).extend
if (config.output) { this.output.push(config.output); }
if (config.extLink) { this.extLinks.push(config.extLink); }
this.sendActReq = this.sendActReq || Action.runOnServer();
this.sendActReq = this.sendActReq || Action.useWS();
}
if (isEmpty(this.warnings) && (action.allowed !== Allowed.ALWAYS)) {
......
......@@ -9,7 +9,7 @@ class BashScript {
static async getConfig() { return {}; } // no config
/** @return {boolean} */
static runOnServer() { return true; }
static useWS() { return true; }
}
export default BashScript;
......@@ -35,13 +35,13 @@ class IPMIConsole {
return {
extLink: {
descr: 'Remote Console',
url: `${currentUrl()}/ipmi-console/${info.host}` + query
url: `${currentUrl()}/action/ipmi-console/${info.host}` + query
}
}
};
}
/** @return {boolean} */
static runOnServer() { return false; }
static useWS() { return false; }
}
export default IPMIConsole;
......@@ -9,7 +9,7 @@ class OCRollout {
static async getConfig() { return {}; } // no config
/** @return {boolean} */
static runOnServer() { return true; }
static useWS() { return true; }
}
export default OCRollout;
......@@ -9,7 +9,7 @@ class SSHCmd {
static async getConfig() { return {}; } // no config
/** @return {boolean} */
static runOnServer() { return true; }
static useWS() { return true; }
}
export default SSHCmd;
......@@ -9,7 +9,7 @@ class Systemctl {
static async getConfig() { return {}; } // no config
/** @return {boolean} */
static runOnServer() { return true; }
static useWS() { return true; }
}
export default Systemctl;
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment