diff --git a/README.md b/README.md index 185ef706..e2194dce 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,6 @@ Navigate to the `cra-client` and `cra-server` directories. Run `npm install` ins After a couple of minutes while it builds, for tenant aaa, you should be able to login to the user interface using `http://localhost:3000/aaa/login`. - ### Demo login In your web browser go to https://localhost:9443/aaa/ : diff --git a/cra-client/src/components/GlossaryAuthor/components/GlossaryQuickTerms.js b/cra-client/src/components/GlossaryAuthor/components/GlossaryQuickTerms.js index dba5f7e0..3485ab97 100644 --- a/cra-client/src/components/GlossaryAuthor/components/GlossaryQuickTerms.js +++ b/cra-client/src/components/GlossaryAuthor/components/GlossaryQuickTerms.js @@ -46,7 +46,8 @@ export default function GlossaryQuickTerms(props) { e.preventDefault(); setRestCallInProgress(true); if (terms.length > 0) { - console.log("issueUpdate " + url); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("issueUpdate " + url); issueRestCreate(url, terms, onSuccessfulCreate, onErrorCreate); } else { alert("Nothing to create"); diff --git a/cra-client/src/components/GlossaryAuthor/components/update/UpdateNode.js b/cra-client/src/components/GlossaryAuthor/components/update/UpdateNode.js index 75019df4..d1e1d86f 100644 --- a/cra-client/src/components/GlossaryAuthor/components/update/UpdateNode.js +++ b/cra-client/src/components/GlossaryAuthor/components/update/UpdateNode.js @@ -45,8 +45,8 @@ export default function UpdateNode(props) { // TODO consider moving this up to a node controller as per the CRUD pattern. // in the meantime this will be self contained. - - console.log("issueUpdate " + url); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("issueUpdate " + url); issueRestUpdate(url, body, onSuccessfulUpdate, onErrorUpdate); }; const onSuccessfulGet = (json) => { diff --git a/cra-server/db/users.js b/cra-server/db/users.js index 0b8e423d..516a3338 100644 --- a/cra-server/db/users.js +++ b/cra-server/db/users.js @@ -39,17 +39,18 @@ var records = [ ]; -exports.findById = function(id, cb) { -process.nextTick(function() { -console.log("findByUserId"); -var idx = id - 1; -if (records[idx]) { - cb(null, Object.assign({}, records[idx])); -} else { - cb(new Error('User ' + id + ' does not exist')); -} -}); -} +exports.findById = function (id, cb) { + process.nextTick(function () { + console.log("findByUserId"); + for (var i = 0, len = records.length; i < len; i++) { + var record = records[i]; + if (record.id === id) { + return cb(null, Object.assign({}, record)); + } + } + return cb(new Error("User " + id + " does not exist")); + }); +}; exports.findByUsername = function(username, cb) { process.nextTick(function() { diff --git a/cra-server/functions/getAxiosInstance.js b/cra-server/functions/getAxiosInstance.js index 11c304ff..bb7c15f1 100644 --- a/cra-server/functions/getAxiosInstance.js +++ b/cra-server/functions/getAxiosInstance.js @@ -27,7 +27,6 @@ const getAxiosInstance = (url) => { remoteServerName + "/open-metadata/view-services/" + remainingURL; - console.log("downstream url " + downStreamURL); const instance = axios.create({ baseURL: downStreamURL, httpsAgent: new https.Agent({ diff --git a/cra-server/functions/getServerInfoFromEnv.js b/cra-server/functions/getServerInfoFromEnv.js index 8372ce59..e18b54f9 100644 --- a/cra-server/functions/getServerInfoFromEnv.js +++ b/cra-server/functions/getServerInfoFromEnv.js @@ -2,12 +2,9 @@ /* Copyright Contributors to the ODPi Egeria project. */ const getServerInfoFromEnv = () => { - - console.log(" getServerInfoFromEnv() 1"); let modifiableServers = {}; // capitals as Windows can be case sensitive. const env_prefix = "EGERIA_PRESENTATIONSERVER_SERVER_"; - // console.log(process.env); const env = process.env; @@ -15,7 +12,6 @@ const getServerInfoFromEnv = () => { try { if (envVariable.startsWith(env_prefix)) { // Found an environment variable with out prefix - console.log(" getServerInfoFromEnv() 2"); if (envVariable.length == env_prefix.length - 1) { console.log( "there is no server name specified in the environment Variable envVariable.length=" + diff --git a/cra-server/functions/passportConfiguration.js b/cra-server/functions/passportConfiguration.js index cc7ea2c4..d874e4b4 100644 --- a/cra-server/functions/passportConfiguration.js +++ b/cra-server/functions/passportConfiguration.js @@ -44,7 +44,8 @@ const passportConfiguration = (passport) => { passport.deserializeUser(function (id, cb) { console.log("deserializeUser called with id " + id); db.users.findById(id, function (err, user) { - console.log("passport.deserializeUser user is " + user + ",err is" + err); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("passport.deserializeUser user is " + user + ",err is" + err); if (err) { return cb(err); } diff --git a/cra-server/functions/serverNameMiddleware.js b/cra-server/functions/serverNameMiddleware.js index 587f51f3..0fff8aa6 100644 --- a/cra-server/functions/serverNameMiddleware.js +++ b/cra-server/functions/serverNameMiddleware.js @@ -26,13 +26,15 @@ const serverNameMiddleWare = (req, res, next) => { if (segmentNumber > 1) { const segment1 = segmentArray.slice(1, 2).join("/"); - console.log("segment1 " + segment1); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("segment1 " + segment1); if (segment1 != "servers" && segment1 != "open-metadata" && segment1 != "user") { // in a production scenario we are looking at login, favicon.ico and bundle.js for for now look for those in the last segment // TODO once we have development webpack, maybe the client should send a /js/ or a /static/ segment after the servername so we know to keep the subsequent segments. const lastSegment = segmentArray.slice(-1); + // Disabling logging as CodeQL does not like user supplied values being logged. // console.log("Last segment is " + lastSegment); if ( lastSegment == "bundle.js" || @@ -47,6 +49,7 @@ const serverNameMiddleWare = (req, res, next) => { req.query.serverName = segment1; } } + // Disabling logging as CodeQL does not like user supplied values being logged. // console.log("after " + req.url); next(); diff --git a/cra-server/package.json b/cra-server/package.json index bbc1da79..2de08672 100644 --- a/cra-server/package.json +++ b/cra-server/package.json @@ -23,6 +23,7 @@ "body-parser": "^1.19.0", "dotenv": "^8.2.0", "express": "^4.17.1", + "express-rate-limit": "^5.2.3", "express-session": "^1.17.1", "passport": "^0.4.1", "passport-local": "^1.0.0" diff --git a/cra-server/router/routes.js b/cra-server/router/routes.js index e3aa8b4c..d788d090 100644 --- a/cra-server/router/routes.js +++ b/cra-server/router/routes.js @@ -1,28 +1,39 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* Copyright Contributors to the ODPi Egeria project. */ -const express = require('express'); +const express = require("express"); const router = express.Router(); -const fs = require('fs'); -const path = require('path'); -const axios = require('axios'); -const https = require('https'); +const fs = require("fs"); +const path = require("path"); +const axios = require("axios"); +const https = require("https"); +const rateLimit = require("express-rate-limit"); -const getAxiosInstance = require('../functions/getAxiosInstance'); -const validateURL = require('../validations/validateURL'); -const validateAdminURL = require('../validations/validateAdminURL'); +const getAxiosInstance = require("../functions/getAxiosInstance"); +const validateURL = require("../validations/validateURL"); +const validateAdminURL = require("../validations/validateAdminURL"); -const cert = fs.readFileSync(path.join(__dirname, '../../') + "ssl/keys/server.cert"); -const key = fs.readFileSync(path.join(__dirname, '../../') + "ssl/keys/server.key"); +// required for codeQL to ensure that logins are rate limitted +const loginLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // limit each IP to 100 requests per windowMs +}); + +const cert = fs.readFileSync( + path.join(__dirname, "../../") + "ssl/keys/server.cert" +); +const key = fs.readFileSync( + path.join(__dirname, "../../") + "ssl/keys/server.key" +); /** - * Middleware to handle post requests that start with /login i.e. the login request. The tenant segment has been removed by previous middleware. - * The login is performed using passport' local authentication (http://www.passportjs.org/docs/authenticate/). + * Middleware to handle post requests that start with /login i.e. the login request. The tenant segment has been removed by previous middleware. + * The login is performed using passport' local authentication (http://www.passportjs.org/docs/authenticate/). * TODO support other authentication style e.g oauth and ldap both of which passport supports. */ -router.post("/login", function (req, res, next) { - console.log("/login"); +router.post("/login", loginLimiter, function (req, res, next) { + console.debug("/login"); // get passport instance from app - const passport = (req.app.get('passport')); + const passport = req.app.get("passport"); passport.authenticate("local", function (err, user, next) { if (err) { return next(err); @@ -48,7 +59,7 @@ router.post("/login", function (req, res, next) { * logout - destroy the session */ router.get("/logout", function (req, res) { - console.log("/logout"); + console.debug("/logout"); req.session.destroy(function (err) { // https://stackoverflow.com/questions/13758207/why-is-passportjs-in-node-not-removing-session-on-logout // explicity clear the cookie. @@ -58,8 +69,9 @@ router.get("/logout", function (req, res) { }); router.get("/user", (req, res) => { - console.log('/user'); - console.log(req.user); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log('/user'); + // console.log(req.user); if (req.user) { res.json({ user: req.user }); } else { @@ -67,30 +79,40 @@ router.get("/user", (req, res) => { } }); -const staticJoinedPath = path.join(__dirname, "../../cra-client/public/index.html"); +const staticJoinedPath = path.join( + __dirname, + "../../cra-client/public/index.html" +); router.use(express.static(staticJoinedPath, { index: false })); -const joinedPath = path.join(__dirname, "../../cra-client/public/", "index.html"); +const joinedPath = path.join( + __dirname, + "../../cra-client/public/", + "index.html" +); /** * Process login url, */ -router.get("/login", (req, res) => { - console.log("/login called " + joinedPath); +router.get("/login", loginLimiter, (req, res) => { + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("/login called " + joinedPath); res.sendFile(joinedPath); }); router.post("/servers/*", (req, res) => { const incomingUrl = req.url; - console.log("/servers/* post called " + incomingUrl); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("/servers/* post called " + incomingUrl); const body = req.body; - console.log("Got body:", body); - const servers = (req.app.get('servers')); + // Disabling logging as CodeQL does not like user supplied values being logged. + //console.log("Got body:", body); + const servers = req.app.get("servers"); if (validateURL(incomingUrl, servers)) { const instance = getAxiosInstance(incomingUrl); instance .post("", body) .then(function (response) { - console.log("response.data"); - console.log(response.data); + console.debug("response.data"); + console.debug(response.data); const resBody = response.data; res.setHeader("Content-Type", "application/json"); res.json(resBody); @@ -109,21 +131,23 @@ router.post("/servers/*", (req, res) => { /** * Middleware to proxy put requests that start with /servers. - * The outbound call is made with https. + * The outbound call is made with https. */ router.put("/servers/*", (req, res) => { const incomingUrl = req.url; - console.log("/servers/* put called " + incomingUrl); + // Disabling logging as CodeQL does not like user supplied values being logged. + //console.log("/servers/* put called " + incomingUrl); const body = req.body; - console.log("Got body:", body); - const servers = (req.app.get('servers')); + // Disabling logging as CodeQL does not like user supplied values being logged. + //console.log("Got body:", body); + const servers = req.app.get("servers"); if (validateURL(incomingUrl, servers)) { const instance = getAxiosInstance(incomingUrl); instance .put("", body) .then(function (response) { - console.log("response.data"); - console.log(response.data); + console.debug("response.data"); + console.debug(response.data); const resBody = response.data; res.setHeader("Content-Type", "application/json"); res.json(resBody); @@ -142,19 +166,20 @@ router.put("/servers/*", (req, res) => { /** * Middleware to proxy delete requests that start with /servers. - * The outbound call is made with https. + * The outbound call is made with https. */ router.delete("/servers/*", (req, res) => { const incomingUrl = req.url; - console.log("/servers/* delete called " + incomingUrl); - const servers = (req.app.get('servers')); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("/servers/* delete called " + incomingUrl); + const servers = req.app.get("servers"); if (validateURL(incomingUrl, servers)) { const instance = getAxiosInstance(incomingUrl); instance .delete() .then(function (response) { - console.log("response.data"); - console.log(response.data); + console.debug("response.data"); + console.debug(response.data); const resBody = response.data; res.setHeader("Content-Type", "application/json"); res.json(resBody); @@ -173,12 +198,13 @@ router.delete("/servers/*", (req, res) => { /** * Middleware to proxy get requests that start with /servers. - * The outbound call is made with https. + * The outbound call is made with https. */ router.get("/servers/*", (req, res) => { const url = req.url; - console.log("/servers/* get called " + url); - const servers = (req.app.get('servers')); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("/servers/* get called " + url); + const servers = req.app.get("servers"); if (validateURL(url, servers)) { const instance = getAxiosInstance(url); instance @@ -203,15 +229,16 @@ router.get("/servers/*", (req, res) => { // Handle admin services router.get("/open-metadata/admin-services/*", (req, res) => { const incomingPath = req.path; - console.log("/open-metadata/admin-services/* get called " + incomingPath); - if (!(validateAdminURL(incomingPath))) { + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("/open-metadata/admin-services/* get called " + incomingPath); + if (!validateAdminURL(incomingPath)) { res.status(400).send("Error, invalid supplied URL: " + incomingPath); return; } - const servers = (req.app.get('servers')); + const servers = req.app.get("servers"); const urlRoot = servers[req.query.tenantId].remoteURL; const apiReq = { - method: 'get', + method: "get", url: urlRoot + incomingPath, httpsAgent: new https.Agent({ // ca: - at some stage add the certificate authority @@ -221,8 +248,8 @@ router.get("/open-metadata/admin-services/*", (req, res) => { }), headers: { "Access-Control-Allow-Origin": "*", - } - } + }, + }; axios(apiReq) .then(function (response) { // console.log({response}); @@ -235,29 +262,27 @@ router.get("/open-metadata/admin-services/*", (req, res) => { } }) .catch(function (error) { - console.error({error}); + console.error({ error }); res.status(400).send(error); - }) + }); }); router.post("/open-metadata/admin-services/*", (req, res) => { const incomingUrl = req.url; - console.log("/open-metadata/admin-services/* post called " + incomingUrl); - if (!(validateAdminURL(incomingUrl))) { + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("/open-metadata/admin-services/* post called " + incomingUrl); + if (!validateAdminURL(incomingUrl)) { res.status(400).send("Error, invalid supplied URL: " + incomingUrl); return; } - const { - config, - tenantId, - } = req.body; - const servers = (req.app.get('servers')); + const { config, tenantId } = req.body; + const servers = req.app.get("servers"); const urlRoot = servers[tenantId].remoteURL; const apiReq = { - method: 'post', + method: "post", url: urlRoot + incomingUrl, - headers: { - 'Content-Type': 'application/json', + headers: { + "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", }, httpsAgent: new https.Agent({ @@ -279,29 +304,27 @@ router.post("/open-metadata/admin-services/*", (req, res) => { res.json(resBody); }) .catch(function (error) { - console.log(error); + console.debug(error); res.status(400).send(error); }); }); router.delete("/open-metadata/admin-services/*", (req, res) => { const incomingUrl = req.url; - console.log("/open-metadata/admin-services/* delete called " + incomingUrl); - if (!(validateAdminURL(incomingUrl))) { + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.debug("/open-metadata/admin-services/* delete called " + incomingUrl); + if (!validateAdminURL(incomingUrl)) { res.status(400).send("Error, invalid supplied URL: " + incomingUrl); return; } - const { - config, - tenantId, - } = req.body; - const servers = (req.app.get('servers')); + const { config, tenantId } = req.body; + const servers = req.app.get("servers"); const urlRoot = servers[tenantId].remoteURL; const apiReq = { - method: 'delete', + method: "delete", url: urlRoot + incomingUrl, - headers: { - 'Content-Type': 'application/json', + headers: { + "Content-Type": "application/json", }, httpsAgent: new https.Agent({ // ca: - at some stage add the certificate authority @@ -330,16 +353,17 @@ router.delete("/open-metadata/admin-services/*", (req, res) => { // Handle platform services router.get("/open-metadata/platform-services/*", (req, res) => { const incomingPath = req.path; - console.log("/open-metadata/platform-services/* get called " + incomingPath); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("/open-metadata/platform-services/* get called " + incomingPath); // TODO: Add validator for platform url // if (!(validatePlatformURL(incomingPath))) { // res.status(400).send("Error, invalid supplied URL: " + incomingPath); // return; // } - const servers = (req.app.get('servers')); + const servers = req.app.get("servers"); const urlRoot = servers[req.query.tenantId].remoteURL; const apiReq = { - method: 'get', + method: "get", url: urlRoot + incomingPath, httpsAgent: new https.Agent({ // ca: - at some stage add the certificate authority @@ -347,20 +371,20 @@ router.get("/open-metadata/platform-services/*", (req, res) => { key, rejectUnauthorized: false, }), - } + }; axios(apiReq) .then(function (response) { const resBody = response.data; if (resBody.relatedHTTPCode == 200) { res.json(resBody); } else { - throw new Error(resBody.exceptionErrorMessage) + throw new Error(resBody.exceptionErrorMessage); } }) .catch(function (error) { - console.error({error}); + console.error({ error }); res.status(400).send(error); - }) + }); }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/cra-server/validations/validateAdminURL.js b/cra-server/validations/validateAdminURL.js index f245dc5c..1f76a8d7 100644 --- a/cra-server/validations/validateAdminURL.js +++ b/cra-server/validations/validateAdminURL.js @@ -1,28 +1,33 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* Copyright Contributors to the ODPi Egeria project. */ -const validateAdminURL = (url) => { +const validateAdminURL = (url) => { const urlArray = url.split("/"); let isValid = true; if (url.length < 7) { - console.log("Supplied url not long enough " + url); - console.debug(urlArray.length); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.debug("Supplied url not long enough " + url); + // console.debug(urlArray.length); isValid = false; } else if (urlArray[3] != "users") { - console.log("Users expected in url " + url); - console.debug(urlArray[3]); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.debug("Users expected in url " + url); + // console.debug(urlArray[3]); isValid = false; } else if (urlArray[4].length == 0) { - console.log("No user supplied"); - console.debug(urlArray[4]); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.debug("No user supplied"); + // console.debug(urlArray[4]); isValid = false; } else if (urlArray[5] != "servers") { - console.log("Servers expected in url"); - console.debug(urlArray[5]); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.debug("Servers expected in url"); + // console.debug(urlArray[5]); isValid = false; } else if (urlArray[6].length == 0) { - console.log("No server supplied"); - console.debug(urlArray[6]); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.debug("No server supplied"); + // console.debug(urlArray[6]); isValid = false; } return isValid; diff --git a/cra-server/validations/validateURL.js b/cra-server/validations/validateURL.js index afa6ee19..743560e2 100644 --- a/cra-server/validations/validateURL.js +++ b/cra-server/validations/validateURL.js @@ -5,10 +5,12 @@ const validateURL = (url, servers) => { const urlArray = url.split("/"); let isValid = true; if (url.length < 5) { - console.log("Supplied url not long enough " + url); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("Supplied url not long enough " + url); isValid = false; } else if (urlArray[4] != "users") { - console.log("Users expected in url " + url); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log("Users expected in url " + url); isValid = false; } else if (urlArray[5].length == 0) { console.log("No user supplied"); @@ -25,18 +27,20 @@ const validateURL = (url, servers) => { console.log("ServerName not configured"); isValid = false; } else if (serverDetails.remoteURL == undefined) { - console.log( - "ServerName " + - suppliedserverName + - " found but there was no associated remoteURL" - ); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log( + // "ServerName " + + // suppliedserverName + + // " found but there was no associated remoteURL" + // ); isValid = false; } else if (serverDetails.remoteServerName == undefined) { - console.log( - "ServerName " + - suppliedserverName + - " found but there was no associated remoteServerName" - ); + // Disabling logging as CodeQL does not like user supplied values being logged. + // console.log( + // "ServerName " + + // suppliedserverName + + // " found but there was no associated remoteServerName" + // ); isValid = false; } } @@ -44,4 +48,4 @@ const validateURL = (url, servers) => { return isValid; }; -module.exports = validateURL; \ No newline at end of file +module.exports = validateURL;