From bfa95367e0508c675a0c9f032706090933df6c2d Mon Sep 17 00:00:00 2001 From: Arseni Lipinski Date: Wed, 4 Sep 2019 18:18:19 +0200 Subject: [PATCH] Upgrade monasca-kibana-plugin for Elkstack update Story: 2006376 Task: 36179 Besides the plugin update, tests should get remade as well as Zuul jobs due to plugin being built in a different way. Change-Id: Id5d0bb53d10fa8823ceda2c8e922ea36b9b27501 --- .zuul.yaml | 8 +- README.rst | 30 +++- gulpfile.js | 6 +- index.js | 19 ++- package.json | 73 ++++----- server/binding/index.js | 2 +- server/const.js | 11 +- server/healthcheck/index.js | 8 +- server/mt/auth/_authenticate.js | 41 ++++-- server/mt/auth/reload/index.js | 2 +- server/mt/auth/scheme.js | 6 +- server/mt/auth/strategy.js | 17 +-- server/mt/auth/token/index.js | 5 +- server/mt/auth/verify.js | 26 ++-- server/mt/index.js | 35 ++--- server/mt/kibana/_can_upgrade.js | 57 -------- server/mt/kibana/_configure.js | 107 -------------- server/mt/kibana/_create.js | 76 ---------- server/mt/kibana/_exists.js | 38 ----- .../mt/kibana/defaultIndexPattern/_create.js | 51 +++++++ .../mt/kibana/defaultIndexPattern/_delete.js | 33 +++++ .../defaultIndexPattern/_events_create.js | 58 -------- .../defaultIndexPattern/_events_delete.js | 30 ---- .../defaultIndexPattern/_events_exists.js | 33 ----- .../mt/kibana/defaultIndexPattern/_exists.js | 47 ++++++ .../_get_allowed_patterns.js | 31 ++++ .../defaultIndexPattern/_logs_create.js | 57 -------- .../defaultIndexPattern/_logs_delete.js | 30 ---- .../defaultIndexPattern/_logs_exists.js | 33 ----- .../_set_default_index_pattern.js | 46 ++++++ server/mt/kibana/defaultIndexPattern/index.js | 55 +++---- server/mt/kibana/index.js | 39 ----- server/mt/kibana/kibanaIndex.js | 29 ---- server/mt/kibana/savedObjectsToolkit/index.js | 138 ++++++++++++++++++ server/mt/projects/index.js | 2 +- server/mt/routing/_create_agent.js | 5 +- server/mt/routing/_create_proxy.js | 29 ++-- server/mt/routing/_map_uri.js | 6 +- server/mt/routing/_re_route.js | 15 +- server/mt/routing/_utils.js | 80 ++++++++-- server/mt/routing/index.js | 2 +- server/mt/routing/routes/bulk_get.js | 42 ++++++ server/mt/routing/routes/default.js | 13 +- server/mt/routing/routes/find.js | 51 +++++++ server/mt/routing/routes/kibana_index.js | 57 -------- server/mt/routing/routes/mget.js | 69 --------- server/mt/routing/routes/paths.js | 81 ---------- server/mt/routing/routes/pattern_search.js | 66 +++++++++ server/mt/routing/routes/search.js | 47 ++++++ server/mt/verify/_verify_index_pattern.js | 41 ------ server/mt/verify/index.js | 48 ------ server/session/index.js | 39 +++-- server/util/index.js | 26 ++-- 53 files changed, 874 insertions(+), 1122 deletions(-) delete mode 100644 server/mt/kibana/_can_upgrade.js delete mode 100644 server/mt/kibana/_configure.js delete mode 100644 server/mt/kibana/_create.js delete mode 100644 server/mt/kibana/_exists.js create mode 100644 server/mt/kibana/defaultIndexPattern/_create.js create mode 100644 server/mt/kibana/defaultIndexPattern/_delete.js delete mode 100644 server/mt/kibana/defaultIndexPattern/_events_create.js delete mode 100644 server/mt/kibana/defaultIndexPattern/_events_delete.js delete mode 100644 server/mt/kibana/defaultIndexPattern/_events_exists.js create mode 100644 server/mt/kibana/defaultIndexPattern/_exists.js create mode 100644 server/mt/kibana/defaultIndexPattern/_get_allowed_patterns.js delete mode 100644 server/mt/kibana/defaultIndexPattern/_logs_create.js delete mode 100644 server/mt/kibana/defaultIndexPattern/_logs_delete.js delete mode 100644 server/mt/kibana/defaultIndexPattern/_logs_exists.js create mode 100644 server/mt/kibana/defaultIndexPattern/_set_default_index_pattern.js delete mode 100644 server/mt/kibana/index.js delete mode 100644 server/mt/kibana/kibanaIndex.js create mode 100644 server/mt/kibana/savedObjectsToolkit/index.js create mode 100644 server/mt/routing/routes/bulk_get.js create mode 100644 server/mt/routing/routes/find.js delete mode 100644 server/mt/routing/routes/kibana_index.js delete mode 100644 server/mt/routing/routes/mget.js delete mode 100644 server/mt/routing/routes/paths.js create mode 100644 server/mt/routing/routes/pattern_search.js create mode 100644 server/mt/routing/routes/search.js delete mode 100644 server/mt/verify/_verify_index_pattern.js delete mode 100644 server/mt/verify/index.js diff --git a/.zuul.yaml b/.zuul.yaml index 8a93cec..6580506 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,7 +1,7 @@ - project: - templates: - - nodejs4-jobs check: jobs: - - monascalog-python3-tempest: - voting: false + - monasca-tempest-log-python3-influxdb + gate: + jobs: + - monasca-tempest-log-python3-influxdb diff --git a/README.rst b/README.rst index 3cd07f7..ea22adb 100644 --- a/README.rst +++ b/README.rst @@ -1,15 +1,26 @@ Monasca Kibana plugin ===================== -Keystone authentication support and multi-tenancy for Kibana 4.6.x +Keystone authentication support and multi-tenancy for Kibana 7.3.x Build ----- +After installing Node JS 10.15.2 and yarn, do the following to +initiate Kibana development environment. + :: - npm install - npm run package + git clone https://github.com/elastic/kibana --branch 7.3 + cd kibana + +Clone the plugin to plugins/ inside the environment and run. + +:: + + yarn kbn bootstrap + cd plugins/monasca-kibana-plugin + yarn build Installation ------------ @@ -35,9 +46,9 @@ Then install using the Kibana plugin manager tool: :: - $ /opt/kibana/bin/kibana plugin --install monasca-kibana-plugin --url file:///tmp/monasca-kibana-plugin-0.0.1.tar.gz + $ /opt/kibana/bin/kibana-plugin install file:///tmp/kibana/plugins/monasca-kibana-plugin/build/monasca-kibana-plugin-7.3.0.zip Installing monasca-kibana-plugin - Attempting to transfer from file:///tmp/monasca-kibana-plugin-0.0.1.tar.gz + Attempting to transfer from file:///tmp/kibana/plugins/monasca-kibana-plugin/build/monasca-kibana-plugin-7.3.0.zip Transferring 7567007 bytes.................... Transfer complete Extracting plugin archive @@ -45,11 +56,16 @@ Then install using the Kibana plugin manager tool: Optimizing and caching browser bundles... Plugin installation complete - $ /opt/kibana/bin/kibana plugin --list - monasca-kibana-plugin + $ /opt/kibana/bin/kibana-plugin list + monasca-kibana-plugin@7.3.0 Now start/restart your Kibana server by running: :: $ service kibana restart + + +Valuable resources: +- Kibana plugin notes - https://github.com/nreese/kibana-plugin-notes +- Elastic forum - https://discuss.elastic.co diff --git a/gulpfile.js b/gulpfile.js index bd4f286..aa39e1b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -32,12 +32,12 @@ var pkg = require('./package.json'); var packageName = pkg.name + '-' + pkg.version; // relative location of Kibana install -var pathToKibana = '../kibana'; +var pathToKibana = '../../kibana'; var buildDir = path.resolve(__dirname, 'build'); var targetDir = path.resolve(__dirname, 'target'); var buildTarget = path.resolve(buildDir, pkg.name); -var kibanaPluginDir = path.resolve(__dirname, pathToKibana, 'installedPlugins', pkg.name); +var kibanaPluginDir = path.resolve(__dirname, pathToKibana, 'plugins', pkg.name); var exclude = [ '.git', diff --git a/index.js b/index.js index fe8c9db..462d4ba 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -56,6 +56,11 @@ export default (kibana) => { }) .default(); + //Elasticsearch parameter added due to the fact that retrieving elasticsearch url via server.config() is impossible + const elasticsearch = Joi.object({ + url: Joi.string().uri({scheme: ['http', 'https']}) + }).default(); + return Joi .object({ enabled: Joi.boolean().default(true), @@ -65,7 +70,8 @@ export default (kibana) => { defaultEventsTimeField: Joi.string().default('@timestamp'), logsIndexPrefix: Joi.string().default('logs-'), eventsIndexPrefix: Joi.string().default('events-'), - cookie: cookie + cookie: cookie, + elasticsearch: elasticsearch }) .concat(deprecated_keystone) .concat(valid_keystone) @@ -74,12 +80,11 @@ export default (kibana) => { .default(); } - function init(server) { - server.log(['status', 'debug', 'keystone'], 'Initializing keystone plugin'); + async function init(server) { + server.log(['MKP','status', 'debug', 'keystone'], 'Initializing keystone plugin'); binding(server).start(); - session(server).start(); + await session(server).start(); healthCheck(this, server).start(); - server.log(['status', 'debug', 'keystone'], 'Initialized keystone plugin'); + server.log(['MKP','status', 'debug', 'keystone'], 'Initialized keystone plugin'); } - }; diff --git a/package.json b/package.json index 4790791..9ebcf99 100644 --- a/package.json +++ b/package.json @@ -1,45 +1,43 @@ { - "name": "monasca-kibana-plugin", - "version": "1.5.0", - "description": "Keystone authentication & multitenancy support for Kibana 4.6.x", - "author": "OpenStack", - "license": "Apache-2.0", - "keywords": [ - "kibana", - "authentication", - "keystone", - "multitenancy", - "plugin" - ], - "scripts": { - "start": "gulp dev", - "build": "gulp build", - "package": "gulp package", - "test": "gulp test", - "lint": "gulp lint" + "name": "monasca-kibana-plugin", + "version": "7.3.0", + "description": "Keystone authentication & multitenancy support for Kibana 7.3.x", + "main": "gulpfile.js", + "kibana": { + "version": "7.3.0", + "templateVersion": "1.0.0" }, - "engines": { - "node": "4.4.7", - "npm": "2.15.8" - }, - "main": "gulpfile.js", - "dependencies": { - "hoek": "^4.0.1", - "keystone-v3-client": "~0.0.8", - "yar": "^7.x.x" - }, - "repository": { - "type": "git", - "url": "https://github.com/openstack/monasca-kibana-plugin.git" + "scripts": { + "preinstall": "node ../../preinstall_check", + "kbn": "node ../../scripts/kbn", + "es": "node ../../scripts/es", + "lint": "eslint .", + "start": "plugin-helpers start", + "test:server": "plugin-helpers test:server", + "test:browser": "plugin-helpers test:browser", + "build": "plugin-helpers build" }, "devDependencies": { - "babel-eslint": "^4.1.8", - "babel-preset-es2015": "^6.3.13", - "babel-register": "^6.4.3", + "@elastic/eslint-config-kibana": "link:../../packages/eslint-config-kibana", + "@elastic/eslint-import-resolver-kibana": "link:../../packages/kbn-eslint-import-resolver-kibana", + "@kbn/expect": "link:../../packages/kbn-expect", + "@kbn/plugin-helpers": "link:../../packages/kbn-plugin-helpers" + }, + "dependencies": { + "babel-eslint": "^10.0.1", + "eslint": "^5.16.0", + "eslint-plugin-babel": "^5.3.0", + "eslint-plugin-import": "^2.16.0", + "eslint-plugin-jest": "^22.4.1", + "eslint-plugin-jsx-a11y": "^6.2.1", + "eslint-plugin-mocha": "^5.3.0", + "eslint-plugin-no-unsanitized": "^3.0.2", + "eslint-plugin-prefer-object-spread": "^1.2.1", + "eslint-plugin-react": "^7.12.4", + "@hapi/wreck": "^15.0.1", "bluebird": "^3.2.1", "boom": "^2.8.0", "chai": "^3.5.0", - "eslint-plugin-mocha": "^1.1.0", "gulp": "^3.9.0", "gulp-eslint": "^1.1.1", "gulp-gzip": "^1.2.0", @@ -54,6 +52,9 @@ "rsync": "^0.4.0", "semver": "^5.3.0", "sinon": "^1.17.3", - "wreck": "^8.0.0" + "hoek": "^4.0.1", + "keystone-v3-client": "~0.0.8", + "@hapi/yar": "^9.2.x", + "request": "^2.88.0" } } diff --git a/server/binding/index.js b/server/binding/index.js index b704954..a7c5d93 100644 --- a/server/binding/index.js +++ b/server/binding/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 diff --git a/server/const.js b/server/const.js index 2f6045e..bbc0e66 100644 --- a/server/const.js +++ b/server/const.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -21,7 +21,8 @@ export const SESSION_TOKEN_CHANGED = `monasca-kibana-plugin-token-changed-${NOW_ export const TOKEN_CHANGED_VALUE = Symbol('token-changed'); -export const RELOAD_MARKUP = ` - - reloading... - `; +export const RELOAD_MARKUP = ` + + +reloading... +`; diff --git a/server/healthcheck/index.js b/server/healthcheck/index.js index e663475..47cb61b 100644 --- a/server/healthcheck/index.js +++ b/server/healthcheck/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -36,7 +36,7 @@ module.exports = function healthcheck(plugin, server) { } }; - server.on('stop', stop); + server.events.on('stop', stop); return service; @@ -105,7 +105,7 @@ module.exports = function healthcheck(plugin, server) { function getRequest() { let required; - if (util.startsWith(keystoneUrl, 'https')) { + if (keystoneUrl.startsWith('https')) { required = require('https'); } else { required = require('http'); @@ -121,7 +121,7 @@ module.exports = function healthcheck(plugin, server) { port : getPort(), method : 'GET' }; - if (util.startsWith(keystoneUrl, 'https')) { + if (keystoneUrl.startsWith('https')) { params.rejectUnauthorized = false; } diff --git a/server/mt/auth/_authenticate.js b/server/mt/auth/_authenticate.js index 17d088d..62c3d98 100644 --- a/server/mt/auth/_authenticate.js +++ b/server/mt/auth/_authenticate.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -12,17 +12,16 @@ * the License. */ -import Boom from 'boom'; import Joi from 'joi'; -import { SESSION_USER_KEY, RELOAD_MARKUP } from '../../const'; +import {RELOAD_MARKUP, SESSION_USER_KEY} from '../../const'; import lookupToken from './token'; import RELOAD from './reload'; -const NOOP = ()=> { +const NOOP = () => { }; const SCHEMA = { - tokenOk : Joi.func().default(NOOP), + tokenOk: Joi.func().default(NOOP), tokenBad: Joi.func().default(NOOP) }; @@ -43,15 +42,23 @@ export default (server, opts) => { server.log(['status', 'debug', 'keystone'], 'Received error object from token lookup' ); - return reply(token); + return reply.unauthenticated(token); } else if (token === RELOAD) { + // TODO: this part is basically ineffective now due to Kibana rejecting + // any HTML injection server.log(['status', 'debug', 'keystone'], 'Received reload markup object from token lookup' ); - return reply(RELOAD_MARKUP).type('text/html'); + return reply.response(RELOAD_MARKUP).type('text/html').takeover(); } else if (userObj && 'project' in userObj) { - server.log(['status','info','keystone'], `${token} already authorized`); - return reply.continue({credentials:token}); + server.log(['status', 'info', 'keystone'], `${token} already authorized`); + return reply.authenticated( + { + credentials: token, + artifacts: { + project: userObj.project.id + } + }); } server.log(['status', 'debug', 'keystone'], @@ -61,7 +68,7 @@ export default (server, opts) => { return tokensApi .validate({ headers: { - 'X-Auth-Token' : token, + 'X-Auth-Token': token, 'X-Subject-Token': token } }) @@ -69,18 +76,24 @@ export default (server, opts) => { (data) => { userObj = data.data.token; return callbackOk(token, userObj, session) - .then(()=> { + .then(() => { server.log(['status', 'debug', 'keystone'], `Auth process completed for user ${userObj.user.id}`); - return reply.continue({credentials: token}); + return reply.authenticated({ + credentials: token, + artifacts: { + project: userObj.project.id + } + }); }); }) .catch((error) => { + server.log(['_auth-error'], error); return callbackBad(token, error, session) - .then((err)=> { + .then((err) => { server.log(['status', 'error', 'keystone'], `Auth process did not complete for token ${token}`); server.log(['status', 'error', 'keystone'], `${err}`); - return reply(Boom.wrap(err)); + return reply.unauthenticated(err); }); }); }; diff --git a/server/mt/auth/reload/index.js b/server/mt/auth/reload/index.js index f3d48d4..83c317e 100644 --- a/server/mt/auth/reload/index.js +++ b/server/mt/auth/reload/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 diff --git a/server/mt/auth/scheme.js b/server/mt/auth/scheme.js index fc56580..c5f6a6d 100644 --- a/server/mt/auth/scheme.js +++ b/server/mt/auth/scheme.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -13,9 +13,11 @@ */ import authenticateFactory from './_authenticate'; +import verify from './verify' export default (server, opts) => { return { - authenticate: authenticateFactory(server, opts) + authenticate: authenticateFactory(server, opts), + verify: verify(server) }; }; diff --git a/server/mt/auth/strategy.js b/server/mt/auth/strategy.js index 4871149..2271565 100644 --- a/server/mt/auth/strategy.js +++ b/server/mt/auth/strategy.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -15,7 +15,6 @@ import Boom from 'boom'; import Promise from 'bluebird'; -import kibanaIndex from '../kibana'; import defaultIndexPattern from '../kibana/defaultIndexPattern'; import userProjects from '../projects'; @@ -36,11 +35,7 @@ export default (server) => { session.set(SESSION_TOKEN_KEY, token); session.set(SESSION_USER_KEY, userObj); - return Promise - .all([ - userProjects(server, session, userObj), - kibanaIndex(server, userObj) - ]) + return userProjects(server, session, userObj) .then(defaultIndexPattern(server, userObj)) .then(() => { server.log(['status', 'info', 'keystone'], `User ${userObj.user.id} authorized with keystone`); @@ -49,7 +44,7 @@ export default (server) => { .catch(err => { server.log(['status', 'info', 'keystone'], `Error caught in process of authorization, err was ${err}`); - throw err; + throw new Error(err); }); } @@ -63,13 +58,11 @@ export default (server) => { if (error.statusCode === 401) { err = Boom.forbidden('\You\'re not logged in as a user who\'s authorized to access log information'); } else { - err = Boom.internal( - error.message || 'Unexpected error during Keystone communication', - {}, + err = Boom.internal(error.message || 'Unexpected error during Keystone communication', error.statusCode ); } - return resolve(err); + resolve(err); }); } diff --git a/server/mt/auth/token/index.js b/server/mt/auth/token/index.js index a327d75..c4c7ded 100644 --- a/server/mt/auth/token/index.js +++ b/server/mt/auth/token/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -38,7 +38,8 @@ const HEADER_NAME = 'x-auth-token'; */ module.exports = (server, request) => { - if (!request.yar || request.yar === null) { + + if (!request.yar) { server.log(['status', 'keystone', 'error'], 'Session is not enabled'); throw new Error('Session support is missing'); } diff --git a/server/mt/auth/verify.js b/server/mt/auth/verify.js index d4998cb..ed2123d 100644 --- a/server/mt/auth/verify.js +++ b/server/mt/auth/verify.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -15,26 +15,24 @@ import Boom from 'boom'; import { - SESSION_PROJECTS_KEY, SESSION_USER_KEY, SESSION_TOKEN_CHANGED, TOKEN_CHANGED_VALUE, RELOAD_MARKUP } from '../../const'; -export default () => { - return (request, reply) => { +export default (server) => { + return async (request, reply) => { let session = request.yar; let userObj = session.get(SESSION_USER_KEY); let tokenChanged = session.get(SESSION_TOKEN_CHANGED); if (tokenChanged === TOKEN_CHANGED_VALUE) { - request.log(['status', 'info', 'keystone'], - 'Detected that token has been changed, replaying the request' - ); + //TODO: This part doesn't really have any effect now because Kibana won't allow HTML injection + server.log(['status', 'info', 'keystone'], 'Detected that token has been changed, replaying the request'); session.clear(SESSION_TOKEN_CHANGED); - return reply(RELOAD_MARKUP).type('text/html'); + return reply.response(RELOAD_MARKUP).type('text/html'); } else if (userObj) { let expiresAt = new Date(userObj.expires_at).valueOf(); let now = new Date().valueOf(); @@ -44,19 +42,17 @@ export default () => { session.reset(); return reply(Boom.unauthorized('User token has expired')); } else { - return reply.continue({ + request.yar.set({ credentials: userObj, - artifacts : { - projects: session.get(SESSION_PROJECTS_KEY) - }, - log : { + log: { tags: 'keystone ok' } }); + return reply.continue; } } // TODO(trebskit) should actually throw error here I guess - return reply.continue(); - }; + return reply.continue; + } }; diff --git a/server/mt/index.js b/server/mt/index.js index 8ce8f7b..25535de 100644 --- a/server/mt/index.js +++ b/server/mt/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -12,28 +12,25 @@ * the License. */ -export default { +export default { bind: (server) => { server.log(['status', 'info', 'keystone'], 'Registering keystone-auth schema'); return Promise.all([ bindAuthScheme(server), - bindExt(server), bindRouting(server) ]); } }; +//I suppose the handler that would prevent user from deleting his default index pattern should be implemented as well function bindRouting(server) { - const kibanaIndex = server.config().get('kibana.index'); return require('./routing')(server) - .then((route)=> { - route(server, 'GET', '/{paths*}'); - route(server, 'POST', '/_mget'); - route(server, 'POST', '/{index}/_search'); - route(server, 'POST', '/{index}/_field_stats'); - route(server, 'POST', '/_msearch'); - route(server, 'POST', '/_search/scroll'); - route(server, ['PUT', 'POST', 'DELETE'], '/' + kibanaIndex + '/{paths*}'); + .then((route) => { + route(server, ['GET', 'POST'], '/{paths*}'); + route(server, ['GET', 'POST'], '/{search_pattern}/_search'); + route(server, 'GET', '/api/saved_objects/_find'); + route(server, 'GET', '/api/kibana/management/saved_objects/_find'); + route(server, 'POST', '/api/saved_objects/_bulk_get'); }); } @@ -44,21 +41,11 @@ function bindAuthScheme(server) { require('./auth/scheme') ), server.auth.strategy( - 'session', + 'keystone-session', 'keystone-token', - false, require('./auth/strategy')(server) ) ]); } -function bindExt(server) { - return Promise.all([ - server.ext( - 'onPreAuth', - require('./auth/verify')(server), - {after: ['yar']} - ), - server.ext('onRequest', require('./verify')(server)) - ]); -} +//bindExt is removed due to verify being a part of authentication scheme and the other one was no longer relevant diff --git a/server/mt/kibana/_can_upgrade.js b/server/mt/kibana/_can_upgrade.js deleted file mode 100644 index bfb85f6..0000000 --- a/server/mt/kibana/_can_upgrade.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import semver from 'semver'; - -const VERSION_REGEX = /(\d+\.\d+\.\d+)\-rc(\d+)/i; - -export default (server, doc) => { - const config = server.config(); - - if (/beta|snapshot/i.test(doc._id)) { - return false; - } - if (!doc._id) { - return false; - } - if (doc._id === config.get('pkg.version')) { - return false; - } - - let packageRcRelease = Infinity; - let rcRelease = Infinity; - let packageVersion = config.get('pkg.version'); - let version = doc._id; - let matches = doc._id.match(VERSION_REGEX); - let packageMatches = config.get('pkg.version').match(VERSION_REGEX); - - if (matches) { - version = matches[1]; - rcRelease = parseInt(matches[2], 10); - } - - if (packageMatches) { - packageVersion = packageMatches[1]; - packageRcRelease = parseInt(packageMatches[2], 10); - } - - try { - if (semver.gte(version, packageVersion) && rcRelease >= packageRcRelease) { - return false; - } - } catch (e) { - return false; - } - return true; -}; diff --git a/server/mt/kibana/_configure.js b/server/mt/kibana/_configure.js deleted file mode 100644 index d0cc1a5..0000000 --- a/server/mt/kibana/_configure.js +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import { find } from 'lodash'; -import Promise from 'bluebird'; - -import canUpgradeConfig from './_can_upgrade'; - -export default (server, indexName) => { - const config = server.config(); - const client = server.plugins.elasticsearch.client; - const options = { - index: indexName, - type : 'config', - body : { - size: 1000, - sort: [ - { - buildNum: { - order : 'desc', - ignore_unmapped: true - } - } - ] - } - }; - - server.log(['status', 'debug', 'keystone'], `Configuring index ${indexName}`); - - return client - .search(options) - .then(upgradeConfig(server, indexName)) - .then(()=>{ - return Promise - .delay(666) - .then(() => { - server.log(['status', 'debug', 'keystone'], `Index ${indexName} has been configured`); - return indexName; - }); - }) - .catch((err)=> { - throw new Error(`Configuring ${indexName} failed, error is ${err}`); - }); - - function upgradeConfig(server, indexName) { - const client = server.plugins.elasticsearch.client; - const config = server.config(); - - return (response) => { - if (response.hits.hits.length === 0) { - return client.create({ - index: indexName, - type : 'config', - body : { - buildNum: config.get('pkg.buildNum') - }, - id : config.get('pkg.version') - }); - } - - // if we already have a the current version in the index then we need to stop - var devConfig = find(response.hits.hits, function currentVersion(hit) { - return hit._id !== '@@version' && hit._id === config.get('pkg.version'); - }); - - if (devConfig) { - return Promise.resolve(); - } - - // Look for upgradeable configs. If none of them are upgradeable - // then resolve with null. - let body = find(response.hits.hits, canUpgradeConfig.bind(null, server)); - if (!body) { - return Promise.resolve(); - } - - // if the build number is still the template string (which it wil be in development) - // then we need to set it to the max interger. Otherwise we will set it to the build num - body._source.buildNum = config.get('pkg.buildNum'); - - server.log(['plugin', 'elasticsearch'], { - tmpl : 'Upgrade config from <%= prevVersion %> to <%= newVersion %>', - prevVersion: body._id, - newVersion : config.get('pkg.version') - }); - - return client.create({ - index: indexName, - type : 'config', - body : body._source, - id : config.get('pkg.version') - }); - }; - } - -}; diff --git a/server/mt/kibana/_create.js b/server/mt/kibana/_create.js deleted file mode 100644 index c563a6b..0000000 --- a/server/mt/kibana/_create.js +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import Boom from 'boom'; - -import { exists as indexExists } from './_exists'; - -export default (server, indexName) => { - const client = server.plugins.elasticsearch.client; - - server.log(['status', 'info', 'keystone'], `Creating user index ${indexName}`); - - return client.indices - .create({ - index: indexName, - body : { - settings: { - number_of_shards: 1 - }, - mappings: { - config: { - properties: { - buildNum: { - type : 'string', - index: 'not_analyzed' - } - } - }, - 'index-pattern': { - properties: { - title: { - type: 'string' - }, - timeFieldName: { - type: 'string' - }, - notExpandable: { - type: 'boolean' - }, - intervalName: { - type: 'string' - } - } - } - } - } - }) - .catch((err)=> { - throw Boom.wrap(err, 500, - `Failed to create index ${indexName}`); - }) - .then(() => { - return indexExists(server, indexName, 'yellow') - .catch((err)=> { - throw Boom.wrap(err, 500, - `Waiting for index ${indexName} to come online failed`); - }) - .then(()=> { - server.log(['status', 'info', 'keystone'], - `Index ${indexName} has been created`); - return Promise.resolve(indexName); - }); - }); - -}; diff --git a/server/mt/kibana/_exists.js b/server/mt/kibana/_exists.js deleted file mode 100644 index 947e5c9..0000000 --- a/server/mt/kibana/_exists.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import kibanaIndex from './kibanaIndex'; - -export default (server, userObj) => { - const indexName = kibanaIndex(server, userObj); - return exists(server, indexName) - .then((resp) => { - return {indexName, resp}; - }); -}; - -export function exists(server, indexName, status) { - const es = server.plugins.elasticsearch.client; - const opts = { - timeout : '5s', - index : indexName, - ignore : [408], - waitForActiveShards: 1 - }; - if (status) { - opts.waitForStatus = status; - } - return es.cluster.health(opts); -} - diff --git a/server/mt/kibana/defaultIndexPattern/_create.js b/server/mt/kibana/defaultIndexPattern/_create.js new file mode 100644 index 0000000..c28e7bd --- /dev/null +++ b/server/mt/kibana/defaultIndexPattern/_create.js @@ -0,0 +1,51 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + +import {createSavedObject} from '../savedObjectsToolkit'; + +export async function createLogsIndexPattern(server, userObj) { + server.log(['status', 'info', 'keystone'], + `Creating default logs-index pattern for ${userObj.project.id}`); + const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix') + .replace('', `${userObj.project.id}`) + '*'; + + return await createIndexPattern(server, pattern); +} + +export async function createEventsIndexPattern(server, userObj) { + server.log(['status', 'info', 'keystone'], + `Creating default events-index pattern for ${userObj.project.id}`); + const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix') + .replace('', `${userObj.project.id}`) + '*'; + + return await createIndexPattern(server, pattern); +} + +async function createIndexPattern(server, pattern) { + let timeFieldName = server.config().get('monasca-kibana-plugin.defaultTimeField'); + const type = 'index-pattern'; + const params = { + title: pattern, + timeFieldName: timeFieldName, + }; + const options = { + id: pattern, + overwrite: true + }; + + return createSavedObject(type, params, options) + .then(() => { + server.log(['status', 'info', 'keystone', 'create'], `Created ${type} ${pattern}`); + }); +} diff --git a/server/mt/kibana/defaultIndexPattern/_delete.js b/server/mt/kibana/defaultIndexPattern/_delete.js new file mode 100644 index 0000000..e63f5dc --- /dev/null +++ b/server/mt/kibana/defaultIndexPattern/_delete.js @@ -0,0 +1,33 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + +import {deleteSavedObject} from '../savedObjectsToolkit'; + +export async function deleteDefaultLogsIndex(server, userObj) { + const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix') + .replace('', `${userObj.project.id}`); + server.log(['status', 'info', 'keystone'], + `Attempting to delete logs-index pattern ${pattern}`); + + return await deleteSavedObject('index-pattern', pattern); +} + +export async function deleteDefaultEventsIndex(server, userObj) { + const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix') + .replace('', `${userObj.project.id}`); + server.log(['status', 'info', 'keystone'], + `Attempting to delete events-index pattern ${pattern}`); + + return await deleteSavedObject('index-pattern', pattern); +} diff --git a/server/mt/kibana/defaultIndexPattern/_events_create.js b/server/mt/kibana/defaultIndexPattern/_events_create.js deleted file mode 100644 index a907eb9..0000000 --- a/server/mt/kibana/defaultIndexPattern/_events_create.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import Promise from 'bluebird'; - -export default (server, indexName, userObj) => { - server.log(['status', 'info', 'keystone'], - `Creating default events-index pattern for ${indexName}`); - - const client = server.plugins.elasticsearch.client; - const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix') - .replace('', `${userObj.project.id}`) + '*'; - - return client.create({ - index: indexName, - type : 'index-pattern', - body : { - title: pattern, - timeFieldName : server.config().get('monasca-kibana-plugin.defaultEventsTimeField') - }, - id : pattern - }) - .then(() => { - return client.update({ - index: indexName, - type: 'config', - id: server.config().get('pkg.version'), - body: { - doc: { - defaultIndex: pattern - } - } - }); - }) - .then(() => { - return client.indices.refresh({ - index: indexName, - force: true - }); - }) - .then((response) => { - return Promise.resolve(response); - }) - .catch((err)=> { - throw new Error(`Unable to setup events-index pattern, error is ${err}`); - }); -}; diff --git a/server/mt/kibana/defaultIndexPattern/_events_delete.js b/server/mt/kibana/defaultIndexPattern/_events_delete.js deleted file mode 100644 index e4b5164..0000000 --- a/server/mt/kibana/defaultIndexPattern/_events_delete.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -export default (server, indexName, userObj) => { - const client = server.plugins.elasticsearch.client; - const options = { - index: indexName, - type : 'index-pattern', - id : server.config().get('monasca-kibana-plugin.eventsIndexPrefix') - .replace('', `${userObj.project.id}`) + '*', - }; - server.log(['status', 'info', 'keystone'], - `Deleting events-index pattern for ${indexName}...`); - return client - .delete(options) - .catch((err)=> { - throw new Error(`Deleting events-index pattern for ${indexName} failed, error is ${err}`); - }); -}; diff --git a/server/mt/kibana/defaultIndexPattern/_events_exists.js b/server/mt/kibana/defaultIndexPattern/_events_exists.js deleted file mode 100644 index 40be28d..0000000 --- a/server/mt/kibana/defaultIndexPattern/_events_exists.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -export default (server, indexName, userObj) => { - const client = server.plugins.elasticsearch.client; - const options = { - index: indexName, - type : 'index-pattern', - id : server.config().get('monasca-kibana-plugin.eventsIndexPrefix') - .replace('', `${userObj.project.id}`) + '*', - }; - server.log(['status', 'debug', 'keystone'], - `Checking if default events-index pattern for ${indexName} exists...`); - return client - .exists(options) - .then((resp) => { - return resp; - }) - .catch((err)=> { - throw new Error(`Getting events-index pattern for ${indexName} failed, error is ${err}`); - }); -}; diff --git a/server/mt/kibana/defaultIndexPattern/_exists.js b/server/mt/kibana/defaultIndexPattern/_exists.js new file mode 100644 index 0000000..5384d20 --- /dev/null +++ b/server/mt/kibana/defaultIndexPattern/_exists.js @@ -0,0 +1,47 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + +import {find} from "../savedObjectsToolkit"; + +export async function defaultLogsIndexExists(server, userObj) { + const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix') + .replace('', `${userObj.project.id}`) + '*'; + server.log(['status', 'debug', 'keystone'], + `Checking if default logs-index pattern for ${userObj.project.id} exists...`); + + return await patternExists(server, pattern); +} + +export async function defaultEventsIndexExists(server, userObj) { + const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix') + .replace('', `${userObj.project.id}`) + '*'; + server.log(['status', 'debug', 'keystone'], + `Checking if default events-index pattern for ${userObj.project.id} exists...`); + + return await patternExists(pattern); +} + +async function patternExists(pattern) { + const params = { + type: 'index-pattern', + fields: ['title'], + }; + + return find(params) + .then((response) => { + return response.saved_objects.some(el => el.attributes.title === pattern); + }).catch((err) => { + throw new Error(`Checking if ${pattern} exists failed, error is ${err}`); + }); +} diff --git a/server/mt/kibana/defaultIndexPattern/_get_allowed_patterns.js b/server/mt/kibana/defaultIndexPattern/_get_allowed_patterns.js new file mode 100644 index 0000000..6bdb0f7 --- /dev/null +++ b/server/mt/kibana/defaultIndexPattern/_get_allowed_patterns.js @@ -0,0 +1,31 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + +export default function (server, project) { + const logsEnabled = server.config().get('monasca-kibana-plugin.logs'); + const eventsEnabled = server.config().get('monasca-kibana-plugin.events'); + + let output = []; + if (logsEnabled) { + const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix') + .replace('', `${project}`) + '*'; + output += pattern; + } + if (eventsEnabled) { + const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix') + .replace('', `${project}`) + '*'; + output += pattern; + } + return output; +} diff --git a/server/mt/kibana/defaultIndexPattern/_logs_create.js b/server/mt/kibana/defaultIndexPattern/_logs_create.js deleted file mode 100644 index 76db6b3..0000000 --- a/server/mt/kibana/defaultIndexPattern/_logs_create.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import Promise from 'bluebird'; - -export default (server, indexName, userObj) => { - server.log(['status', 'info', 'keystone'], - `Creating default logs-index pattern for ${indexName}`); - - const client = server.plugins.elasticsearch.client; - const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix') - .replace('', `${userObj.project.id}`) + '*'; - return client.create({ - index: indexName, - type : 'index-pattern', - body : { - title: pattern, - timeFieldName : server.config().get('monasca-kibana-plugin.defaultTimeField') - }, - id : pattern - }) - .then(() => { - return client.update({ - index: indexName, - type: 'config', - id: server.config().get('pkg.version'), - body: { - doc: { - defaultIndex: pattern - } - } - }); - }) - .then(() => { - return client.indices.refresh({ - index: indexName, - force: true - }); - }) - .then((response) => { - return Promise.resolve(response); - }) - .catch((err)=> { - throw new Error(`Unable to setup logs-index pattern, error is ${err}`); - }); -}; diff --git a/server/mt/kibana/defaultIndexPattern/_logs_delete.js b/server/mt/kibana/defaultIndexPattern/_logs_delete.js deleted file mode 100644 index 9c44de8..0000000 --- a/server/mt/kibana/defaultIndexPattern/_logs_delete.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -export default (server, indexName, userObj) => { - const client = server.plugins.elasticsearch.client; - const options = { - index: indexName, - type : 'index-pattern', - id : server.config().get('monasca-kibana-plugin.logsIndexPrefix') - .replace('', `${userObj.project.id}`) + '*', - }; - server.log(['status', 'info', 'keystone'], - `Deleting logs-index pattern for ${indexName}...`); - return client - .delete(options) - .catch((err)=> { - throw new Error(`Deleting logs-index pattern for ${indexName} failed, error is ${err}`); - }); -}; diff --git a/server/mt/kibana/defaultIndexPattern/_logs_exists.js b/server/mt/kibana/defaultIndexPattern/_logs_exists.js deleted file mode 100644 index 9b4d7c6..0000000 --- a/server/mt/kibana/defaultIndexPattern/_logs_exists.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -export default (server, indexName, userObj) => { - const client = server.plugins.elasticsearch.client; - const options = { - index: indexName, - type : 'index-pattern', - id : server.config().get('monasca-kibana-plugin.logsIndexPrefix') - .replace('', `${userObj.project.id}`) + '*', - }; - server.log(['status', 'debug', 'keystone'], - `Checking if default logs-index pattern for ${indexName} exists...`); - return client - .exists(options) - .then((resp) => { - return resp; - }) - .catch((err)=> { - throw new Error(`Getting logs-index pattern for ${indexName} failed, error is ${err}`); - }); -}; diff --git a/server/mt/kibana/defaultIndexPattern/_set_default_index_pattern.js b/server/mt/kibana/defaultIndexPattern/_set_default_index_pattern.js new file mode 100644 index 0000000..470fee2 --- /dev/null +++ b/server/mt/kibana/defaultIndexPattern/_set_default_index_pattern.js @@ -0,0 +1,46 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + + +import {updateConfig} from "../savedObjectsToolkit"; + +export async function updateLogsConfig(server, userObj) { + const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix') + .replace('', `${userObj.project.id}`) + '*'; + + return await updateDefaultIndexPattern(server, pattern); +} + +export async function updateEventsConfig(server, userObj) { + const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix') + .replace('', `${userObj.project.id}`) + '*'; + + return await updateDefaultIndexPattern(server, pattern); +} + +async function updateDefaultIndexPattern(server, pattern) { + const type = 'config'; + const version = server.config().get('pkg.version'); + const changes = { + defaultIndex: pattern + }; + + return await updateConfig(type, version, changes) + .catch((err) => { + server.log(['updateConfig'], `Can't set ${pattern} as default index pattern, error is: ${err}`); + }) + .then(() => { + server.log(['updateConfig'], `Default index pattern is ${pattern}`); + }); +} diff --git a/server/mt/kibana/defaultIndexPattern/index.js b/server/mt/kibana/defaultIndexPattern/index.js index e5f4715..68ba0c5 100644 --- a/server/mt/kibana/defaultIndexPattern/index.js +++ b/server/mt/kibana/defaultIndexPattern/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -12,72 +12,73 @@ * the License. */ -import Promise from 'bluebird'; -import defaultLogsIndexExists from './_logs_exists'; -import defaultEventsIndexExists from './_events_exists'; -import createLogsDefaultIndex from './_logs_create'; -import createEventsDefaultIndex from './_events_create'; -import deleteLogsDefaultIndex from './_logs_delete'; -import deleteEventsDefaultIndex from './_events_delete'; -import kibanaIndex from '../kibanaIndex'; +import {createEventsIndexPattern, createLogsIndexPattern} from "./_create"; +import {defaultEventsIndexExists, defaultLogsIndexExists} from "./_exists"; +import {deleteDefaultEventsIndex, deleteDefaultLogsIndex} from "./_delete"; +import {updateEventsConfig, updateLogsConfig} from "./_set_default_index_pattern"; export default (server, userObj) => { return () => { - const indexName = kibanaIndex(server, userObj); - return defaultLogsIndexExists(server, indexName, userObj) + const project = userObj.project.id; + return defaultLogsIndexExists(server, userObj) .then((logsExists) => { if (server.config().get('monasca-kibana-plugin.logs')) { server.log(['status', 'info', 'keystone'], `Default logs-index pattern is enabled in kibana config file`); if (!logsExists) { server.log(['status', 'warning', 'keystone'], - `Default logs-index pattern for ${indexName} does not exist`); - return createLogsDefaultIndex(server, indexName, userObj); + `Default logs-index pattern for ${project} does not exist`); + return createLogsIndexPattern(server, userObj) + .then(() => { + return updateLogsConfig(server, userObj); + }); } else { server.log(['status', 'info', 'keystone'], - `Default logs-index pattern for ${indexName} already exists`); + `Default logs-index pattern for ${project} already exists`); } - } else { server.log(['status', 'info', 'keystone'], `Default logs-index pattern is disabled in kibana config file`); if (logsExists) { server.log(['status', 'warning', 'keystone'], - `Default logs-index pattern for ${indexName} exists, but it should not`); - return deleteLogsDefaultIndex(server, indexName, userObj); + `Default logs-index pattern for ${project} exists, but it should not`); + return deleteDefaultLogsIndex(server, userObj); } else { server.log(['status', 'info', 'keystone'], - `Default logs-index pattern for ${indexName} does not exist`); + `Default logs-index pattern for ${project} does not exist`); } } }) .then(() => { - defaultEventsIndexExists(server, indexName, userObj) + defaultEventsIndexExists(server, userObj) .then((eventsExists) => { if (server.config().get('monasca-kibana-plugin.events')) { server.log(['status', 'info', 'keystone'], `Default events-index pattern is enabled in kibana config file`); if (!eventsExists) { server.log(['status', 'warning', 'keystone'], - `Default events-index pattern for ${indexName} does not exist`); - return createEventsDefaultIndex(server, indexName, userObj); + `Default events-index pattern for ${project} does not exist`); + return createEventsIndexPattern(server, userObj) + .then(() => { + return updateEventsConfig(server, userObj) + }); } else { server.log(['status', 'info', 'keystone'], - `Default events-index pattern for ${indexName} already exists`); + `Default events-index pattern for ${project} already exists`); } } else { server.log(['status', 'info', 'keystone'], `Default events-index pattern is disabled in kibana config file`); if (eventsExists) { server.log(['status', 'warning', 'keystone'], - `Default events-index pattern for ${indexName} exists, but it should not`); - return deleteEventsDefaultIndex(server, indexName, userObj); + `Default events-index pattern for ${project} exists, but it should not`); + return deleteDefaultEventsIndex(server, userObj); } else { server.log(['status', 'info', 'keystone'], - `Default events-index pattern for ${indexName} does not exist`); + `Default events-index pattern for ${project} does not exist`); } } - }); - }); + }) + }) }; }; diff --git a/server/mt/kibana/index.js b/server/mt/kibana/index.js deleted file mode 100644 index 1971457..0000000 --- a/server/mt/kibana/index.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import indexExists from './_exists'; -import createIndex from './_create'; -import configureIndex from './_configure'; - -export default (server, userObj) => { - return doCheck(); - - function doCheck() { - return indexExists(server, userObj) - .then(({indexName, resp}) => { - if (!resp || resp.timed_out) { - server.log(['status', 'warning', 'keystone'], `Index ${indexName} does not exists`); - return createIndex(server, indexName); - } - if (resp.status === 'red') { - server.log(['status', 'warning', 'keystone'], `Shards not ready for index ${indexName}`); - return Promise.delay(2500).then(doCheck); - } - return Promise.resolve(indexName); - }) - .then((indexName)=> { - return configureIndex(server, indexName); - }); - } -}; diff --git a/server/mt/kibana/kibanaIndex.js b/server/mt/kibana/kibanaIndex.js deleted file mode 100644 index 8d914a2..0000000 --- a/server/mt/kibana/kibanaIndex.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -/** - * Returns tenant/project-aware kibana index - * - * @param server server object - * @param userObj user details as retrieved from keystone - * @returns {string} project aware kibana index - * - */ -export default (server, userObj) => { - return `${server.config().get('kibana.index')}-${getProjectId(userObj)}`; -}; - -function getProjectId(userObj) { - return userObj.project.id; -} diff --git a/server/mt/kibana/savedObjectsToolkit/index.js b/server/mt/kibana/savedObjectsToolkit/index.js new file mode 100644 index 0000000..81410df --- /dev/null +++ b/server/mt/kibana/savedObjectsToolkit/index.js @@ -0,0 +1,138 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + +let savedObjectsClient; + +export function initClients(server) { + const elasticsearchClient = server.plugins.elasticsearch.getCluster('admin').callWithInternalUser; + const {SavedObjectsClient, getSavedObjectsRepository} = server.savedObjects; + const internalRepository = getSavedObjectsRepository(elasticsearchClient); + + savedObjectsClient = new SavedObjectsClient(internalRepository); +} + +export function findWithMeta(server, params) { + const version = server.config().get('pkg.version'); + + params.fields ? params.fields.push('title') : params.fields = ['title']; + params.fields.push('visState'); + + return find(params) + .then((response) => { + response.saved_objects.forEach(el => _createMetaForSavedObject(el, version)); + return response; + }) + .catch((err) => { + throw new Error(err); + }); +} + +export async function bulkGetSavedObjects(params) { + return await savedObjectsClient.bulkGet(params) + .catch((err) => { + throw new Error(err); + }); +} + + +function _createMetaForSavedObject(object, version) { + const inAppPrefix = '/app/kibana#'; + const managementPrefix = '/management/kibana'; + switch (object.type) { + case 'config': + object.meta = { + title: `Advanced Settings [${version}]`, + inAppUrl: { + path: `${inAppPrefix}${managementPrefix}/settings`, + uiCapabilitiesPath: "advancedSettings.show" + } + }; + break; + case 'index-pattern': + object.meta = { + icon: 'indexPatternApp', + title: object.attributes.title, + editUrl: `${managementPrefix}/index_patterns/${object.title}`, + inAppUrl: { + path: `${inAppPrefix}${managementPrefix}/index_patterns/${object.id}`, + uiCapabilitiesPath: "management.kibana.index_patterns" + } + }; + break; + case 'search': + object.meta = { + icon: 'search', + title: object.attributes.title, + editUrl: `${managementPrefix}/objects/savedSearches/${object.id}`, + inAppUrl: { + path: `${inAppPrefix}/discover/${object.id}`, + uiCapabilitiesPath: "discover.show" + } + }; + break; + case 'visualization': + object.meta = { + icon: 'visualizeApp', + title: object.attributes.title, + editUrl: `${managementPrefix}/objects/savedVisualizations/${object.id}`, + inAppUrl: { + path: `${inAppPrefix}/visualize/edit/${object.id}`, + uiCapabilitiesPath: "visualize.show" + } + }; + break; + case 'dashboard': + object.meta = { + icon: 'dashboardApp', + title: object.attributes.title, + editUrl: `${managementPrefix}/objects/savedDashboards/${object.id}`, + inAppUrl: { + path: `${inAppPrefix}/dashboard/${object.id}`, + uiCapabilitiesPath: "dashboard.show" + } + }; + break; + default: + throw new Error(`Unknown saved object type ${object.type}`); + } + return object; +} + +export async function createSavedObject(type, options, params) { + return await savedObjectsClient.create(type, options, params) + .catch((err) => { + throw new Error(`Unable to create ${type} ${options}, error is ${err}`); + }); +} + +export async function find(params) { + return await savedObjectsClient.find(params) + .catch((err) => { + throw new Error(`Can't perform search ${params}, error is: ${err}`); + }); +} + +export async function updateConfig(type, version, changes) { + return await savedObjectsClient.update(type, version, changes) + .catch((err) => { + throw new Error(`Can't update ${type} with ${changes}, error is: ${err}`); + }); +} + +export async function deleteSavedObject(type, id) { + return await savedObjectsClient.delete(type, id) + .catch((err) => { + throw new Error(`Can't delete ${type} ${id}, error is: ${err}`); + }); +} diff --git a/server/mt/projects/index.js b/server/mt/projects/index.js index 84457c5..fb39a55 100644 --- a/server/mt/projects/index.js +++ b/server/mt/projects/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 diff --git a/server/mt/routing/_create_agent.js b/server/mt/routing/_create_agent.js index 4fd7d87..ba87478 100644 --- a/server/mt/routing/_create_agent.js +++ b/server/mt/routing/_create_agent.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -27,7 +27,8 @@ const readFile = (file) => fs.readFileSync(file, 'utf8'); module.exports = memoize(function (server) { const config = server.config(); - const target = url.parse(config.get('elasticsearch.url')); + + const target = url.parse(config.get('monasca-kibana-plugin.elasticsearch.url')); if (!/^https/.test(target.protocol)) { return new http.Agent(); diff --git a/server/mt/routing/_create_proxy.js b/server/mt/routing/_create_proxy.js index e6f962d..4263594 100644 --- a/server/mt/routing/_create_proxy.js +++ b/server/mt/routing/_create_proxy.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -16,27 +16,36 @@ import { PREFIX } from './_utils'; module.exports = (server, method, route) => { - const serverConfig = server.config(); const pre = '/elasticsearch'; const sep = route[0] === '/' ? '' : '/'; - const path = `${PREFIX}${pre}${sep}${route}`; + let path; + if (/\/api.*\//.test(route)) { + path = `${PREFIX}${sep}${route}`; + } else { + path = `${PREFIX}${pre}${sep}${route}`; + } + + server.log(['create-proxy-path'], path + '; ' + method); let options; switch (route) { - case '/_mget': - options = require('./routes/mget')(server, method, path); + case '/_search': + options = require('./routes/search')(server, method, path); break; - case '/{paths*}': - options = require('./routes/paths')(server, method, path); + case '/{search_pattern}/_search': + options = require('./routes/pattern_search')(server, method, path); + break; + case '/api/saved_objects/_bulk_get': + options = require('./routes/bulk_get')(server, method, path); break; default: - if (route === `/${serverConfig.get('kibana.index')}/{paths*}`) { - options = require('./routes/kibana_index')(server, method, path); + if (/\/api.*\/saved_objects\/_find/.test(route)) { + options = require('./routes/find')(server, method, path); } else { options = require('./routes/default')(server, method, path); } + break; } - return server.route(options); }; diff --git a/server/mt/routing/_map_uri.js b/server/mt/routing/_map_uri.js index 8a478ed..18026d7 100644 --- a/server/mt/routing/_map_uri.js +++ b/server/mt/routing/_map_uri.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -23,10 +23,10 @@ import { PREFIX } from './_utils'; export default (server, request) => { const config = server.config(); - const path = request.path.replace(`${PREFIX}/elasticsearch`, ''); + const path = request.path.replace(`${PREFIX}`, '').replace(`/elasticsearch`, ''); const query = querystring.stringify(request.query); - let url = config.get('elasticsearch.url'); + let url = config.get('monasca-kibana-plugin.elasticsearch.url'); if (path) { if (/\/$/.test(url)) { diff --git a/server/mt/routing/_re_route.js b/server/mt/routing/_re_route.js index 275d145..d03e267 100644 --- a/server/mt/routing/_re_route.js +++ b/server/mt/routing/_re_route.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -12,16 +12,17 @@ * the License. */ -import utils from '../../util'; +import {isRouted, requestPath} from '../../util'; import { PREFIX } from './_utils'; module.exports = function reRoute(server) { return (request, reply) => { - const requestPath = utils.requestPath(request); - if (utils.isESRequest(request)) { - server.log(['status', 'debug', 'keystone'], `Routing ${requestPath} onto ${PREFIX}${requestPath}`); - request.setUrl(`${PREFIX}${requestPath}`); + const path = requestPath(request); + if (isRouted(request)) { + server.log(['status', 'debug', 'keystone'], `Routing ${path} onto ${PREFIX}${path}`); + request.setUrl(`${PREFIX}${path}`); } - return reply.continue(); + return reply.continue; }; }; + diff --git a/server/mt/routing/_utils.js b/server/mt/routing/_utils.js index 2408003..944347d 100644 --- a/server/mt/routing/_utils.js +++ b/server/mt/routing/_utils.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -13,19 +13,20 @@ */ import createAgent from './_create_agent'; +import {bulkGetSavedObjects} from "../kibana/savedObjectsToolkit"; export const PREFIX = '/mt'; export function getOpts(server, request, url, payload) { let options = { - headers : {}, - redirects : true, - passThrough : true, - xforward : true, - timeout : 1000 * 60 * 3, + headers: {}, + redirects: true, + passThrough: true, + xforward: true, + search_timeout: '10s', localStatePassThrough: false, - agent : createAgent(server), + agent: createAgent(server), }; let protocol = url.split(':', 1)[0]; @@ -35,7 +36,7 @@ export function getOpts(server, request, url, payload) { if (options.passThrough) { options.headers = require('hoek').clone(request.headers); - delete options.headers.host; + //delete options.headers.host; if (options.acceptEncoding === false) { delete options.headers['accept-encoding']; } @@ -46,11 +47,11 @@ export function getOpts(server, request, url, payload) { request.info.remotePort) { options.headers['x-forwarded-for'] = (options.headers['x-forwarded-for'] ? - options.headers['x-forwarded-for'] + ',' : '') + request.info.remoteAddress; + options.headers['x-forwarded-for'] + ',' : '') + request.info.remoteAddress; options.headers['x-forwarded-port'] = (options.headers['x-forwarded-port'] ? - options.headers['x-forwarded-port'] + ',' : '') + request.info.remotePort; + options.headers['x-forwarded-port'] + ',' : '') + request.info.remotePort; options.headers['x-forwarded-proto'] = (options.headers['x-forwarded-proto'] ? - options.headers['x-forwarded-proto'] + ',' : '') + protocol; + options.headers['x-forwarded-proto'] + ',' : '') + protocol; } const contentType = request.headers['content-type']; @@ -68,3 +69,60 @@ export function parsePayload(request) { } return JSON.parse(payload.toString('utf-8')); } + + +function isAllowedSavedObject(element, patterns) { + let references = []; + switch (element.type) { + case 'index-pattern': + return patterns.includes(element.id); + case 'config': + return true; + case 'visualization': + case 'search': + references = element.references.filter(element => patterns.includes(element.id)); + return references.length > 0; + default: + return false; + } +} + +// We have to retrieve savedObjects once again because some of the searches may not contain the references of references +// Dashboard would be allowed only if it references saved objects accessible to the user +function isAllowedDashboard(element, patterns) { + + const dashboardRefs = element.references.map((el) => { + return {id: el.id, type: el.type} + }); + + return bulkGetSavedObjects(dashboardRefs) + .then((response) => { + let onlyAllowed = true; + const references = response.saved_objects.map((el) => el.references)[0]; + references.forEach((hit) => { + if (!patterns.includes(hit.id)) onlyAllowed = false; + }); + return onlyAllowed; + }); +} + +async function filterDashboards(object, patterns) { + let dashboards = []; + for (let element of object) { + if (element.type === 'dashboard' && await isAllowedDashboard(element, patterns)) { + dashboards.push(element); + } + } + return dashboards; +} + +// Dashboards have to be processed separately because it's references in saved_objects +// don't contain the references to index-pattern +// Dashboard is deemed allowed only if it contains allowed references only. +export async function filterResponse(data, patterns) { + let savedObjects = data.saved_objects.filter(el => isAllowedSavedObject(el, patterns)); + let savedDashboards = await filterDashboards(data.saved_objects, patterns); + + data.saved_objects = savedObjects.concat(savedDashboards); + return data; +} diff --git a/server/mt/routing/index.js b/server/mt/routing/index.js index 08d8f16..57e139c 100644 --- a/server/mt/routing/index.js +++ b/server/mt/routing/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 diff --git a/server/mt/routing/routes/bulk_get.js b/server/mt/routing/routes/bulk_get.js new file mode 100644 index 0000000..0260a59 --- /dev/null +++ b/server/mt/routing/routes/bulk_get.js @@ -0,0 +1,42 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + +import {bulkGetSavedObjects} from '../../kibana/savedObjectsToolkit'; +import getAllowedPatterns from '../../kibana/defaultIndexPattern/_get_allowed_patterns' +import {SESSION_USER_KEY} from '../../../const'; + +export default function (server, method, path) { + return { + method: method, + path: path, + config: { + auth: 'keystone-session', + }, + handler: handler + }; + + async function handler(request, reply) { + const userObj = request.yar.get(SESSION_USER_KEY); + const patterns = getAllowedPatterns(server, userObj.project.id); + const params = request.payload; + + if (params.type === 'index-pattern') { + if (!patterns.includes(params.id)) { + server.log(['error', 'api-bulk-get'], `Index-pattern ${params.id} is not allowed`); + throw new Error(`Index-pattern ${params.id} is not allowed`); + } + } + return bulkGetSavedObjects(params); + } +} diff --git a/server/mt/routing/routes/default.js b/server/mt/routing/routes/default.js index df5108d..72c8ce0 100644 --- a/server/mt/routing/routes/default.js +++ b/server/mt/routing/routes/default.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -20,14 +20,17 @@ module.exports = function defaultHandler(server, method, path) { method : method, path : path, config : { - auth : 'session' + auth : 'keystone-session' }, handler: { proxy: { - mapUri : (request, done) => { + mapUri : (request) => { + server.log(['route-handler-default'], request.url.path); server.log(['status', 'debug', 'keystone'], - `mapUri for path ${request.path}`); - done(null, mapUri(server, request)); + `mapUri for path ${request.url.path}`); + return { + uri: mapUri(server, request) + }; }, agent : createAgent(server), passThrough: true, diff --git a/server/mt/routing/routes/find.js b/server/mt/routing/routes/find.js new file mode 100644 index 0000000..077ad0a --- /dev/null +++ b/server/mt/routing/routes/find.js @@ -0,0 +1,51 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + +import {findWithMeta} from '../../kibana/savedObjectsToolkit'; +import {filterResponse} from "../_utils"; +import {SESSION_USER_KEY} from '../../../const'; +import getAllowedPatterns from '../../kibana/defaultIndexPattern/_get_allowed_patterns'; + +export default function (server, method, path) { + return { + method: method, + path: path, + config: { + auth: 'keystone-session', + }, + handler: handler + }; + + async function handler(request, reply) { + const userObj = request.yar.get(SESSION_USER_KEY); + const project = userObj.project.id; + const patterns = getAllowedPatterns(server, project); + const params = request.query; + + if (params.fields && !(params.fields instanceof Array)) { + params.fields = [params.fields]; + } + return findWithMeta(server, params) + .then((response) => { + return filterResponse(response, patterns, server) + }) + .then((response) => { + response.total = response.saved_objects.length; + return response; + }) + .catch((e) => { + throw new Error(`Find route failed, error: ${e}`); + }); + } +} diff --git a/server/mt/routing/routes/kibana_index.js b/server/mt/routing/routes/kibana_index.js deleted file mode 100644 index b72c535..0000000 --- a/server/mt/routing/routes/kibana_index.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import Wreck from 'wreck'; - -import { SESSION_USER_KEY } from '../../../const'; -import { getOpts, parsePayload } from '../_utils'; -import kibanaIndex from '../../kibana/kibanaIndex'; -import mapUri from '../_map_uri'; - -export default function (server, method, path) { - - const defaultKibanaIndex = server.config().get('kibana.index'); - - return { - method : method, - path : path, - config : { - auth : 'session', - payload: { - output: 'data', - parse : false - } - }, - handler: handler - }; - - function handler(request, reply) { - const url = getUrl(request); - const opts = getOpts(server, request, url, parsePayload(request)); - return Wreck.request(request.method, url, opts, (err, res) => { - return reply(res).code(res.statusCode).passThrough(!!opts.passThrough); - }); - } - - function getUrl(request) { - const session = request.yar._store; - - let url = mapUri(server, request).split('/'); - let indexPos = url.findIndex((item) => item === defaultKibanaIndex); - - url[indexPos] = kibanaIndex(server, session[SESSION_USER_KEY]); - - return url.join('/'); - } -} diff --git a/server/mt/routing/routes/mget.js b/server/mt/routing/routes/mget.js deleted file mode 100644 index a18d68c..0000000 --- a/server/mt/routing/routes/mget.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import Boom from 'boom'; -import Wreck from 'wreck'; - -import { SESSION_USER_KEY } from '../../../const'; -import { getOpts, parsePayload } from '../_utils'; -import kibanaIndex from '../../kibana/kibanaIndex'; -import mapUri from '../_map_uri'; - -export default function (server, method, path) { - - return { - method : method, - path : path, - config : { - auth : 'session', - payload: { - output: 'data', - parse : false - } - }, - handler: handler - }; - - function handler(request, reply) { - const url = mapUri(server, request); - const session = request.yar._store; - const payload = parsePayload(request); - - payload.docs.forEach((doc) => { - doc._index = kibanaIndex(server, session[SESSION_USER_KEY]); - }); - - const opts = getOpts(server, request, url, payload); - return Wreck.request(request.method, url, opts, (err, res) => { - if (err) { - server.log( - ['status', 'error', 'keystone'], - `Failed to request ${url}, error is ${err}`); - return reply(Boom.wrap(err)); - } - return Wreck.read(res, {json: true}, (err, body)=> { - if (err) { - server.log( - ['status', 'error', 'keystone'], - `Failed to read response from ${url}, error is ${err}`); - return reply(Boom.wrap(err)); - } - - return reply(body) - .code(res.statusCode) - .passThrough(!!opts.passThrough); - }); - }); - } -} diff --git a/server/mt/routing/routes/paths.js b/server/mt/routing/routes/paths.js deleted file mode 100644 index a41412a..0000000 --- a/server/mt/routing/routes/paths.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import Wreck from 'wreck'; -import Boom from 'boom'; - -import { SESSION_USER_KEY } from '../../../const'; -import { getOpts } from '../_utils'; -import kibanaIndex from '../../kibana/kibanaIndex'; -import mapUri from '../_map_uri'; - -export default function (server, method, path) { - const defaultKibanaIndex = server.config().get('kibana.index'); - const logIndexPostionInUrl = 3; - - return { - method : method, - path : path, - config : { - tags: ['elasticsearch', 'multitenancy'], - auth: 'session' - }, - handler: handler - }; - - function handler(request, reply) { - const session = request.yar._store; - - let url = mapUri(server, request).split('/'); - let kibanaIndexRequest = false; - - let indexPos = url.findIndex((item) => item === defaultKibanaIndex); - let logsIndexPref = server.config().get('monasca-kibana-plugin.logsIndexPrefix'); - let eventsIndexPref = server.config().get('monasca-kibana-plugin.eventsIndexPrefix'); - logsIndexPref = logsIndexPref.replace('', session[SESSION_USER_KEY].project.id); - eventsIndexPref = eventsIndexPref.replace('', session[SESSION_USER_KEY].project.id); - - server.log(['status', 'info', 'keystone'], - `Allowing only these Index-Prefix ${logsIndexPref}, ${eventsIndexPref}`); - - if (indexPos > -1) { - url[indexPos] = kibanaIndex(server, session[SESSION_USER_KEY]); - kibanaIndexRequest = true; - } else if (url.length > logIndexPostionInUrl - && !(url[logIndexPostionInUrl].startsWith(logsIndexPref) - || url[logIndexPostionInUrl].startsWith(eventsIndexPref))) { - return reply(Boom.unauthorized('User does not have access to this resource')); - } - - url = url.join('/'); - - const opts = getOpts(server, request, url); - return Wreck.request(request.method, url, opts, (err, res) => { - return Wreck.read(res, {json: true}, (err, body)=> { - let newData = {}; - - if (kibanaIndexRequest) { - let tenantAwareIndex = Object.keys(body)[0]; - newData[defaultKibanaIndex] = body[tenantAwareIndex]; - } else { - newData = body; - } - - return reply(newData) - .code(res.statusCode) - .passThrough(!!opts.passThrough); - }); - }); - } -} diff --git a/server/mt/routing/routes/pattern_search.js b/server/mt/routing/routes/pattern_search.js new file mode 100644 index 0000000..8b57eb4 --- /dev/null +++ b/server/mt/routing/routes/pattern_search.js @@ -0,0 +1,66 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + +import Wreck from '@hapi/wreck'; + +import {SESSION_USER_KEY} from '../../../const'; +import {getOpts} from '../_utils'; +import getAllowedPatterns from '../../kibana/defaultIndexPattern/_get_allowed_patterns'; +import mapUri from '../_map_uri'; + +export default function (server, method, path) { + return { + method: method, + path: path, + config: { + auth: 'keystone-session', + }, + handler: handler + }; + + async function handler(request, reply) { + + const session = request.yar; + let url = mapUri(server, request).split('/'); + let project = session.get(SESSION_USER_KEY).project.id; + + let logsIndexPref = server.config().get('monasca-kibana-plugin.logsIndexPrefix'); + let eventsIndexPref = server.config().get('monasca-kibana-plugin.eventsIndexPrefix'); + logsIndexPref = logsIndexPref.replace('', project); + eventsIndexPref = eventsIndexPref.replace('', project); + + server.log(['status', 'info', 'keystone'], + `Allowing only these Index-Prefix ${logsIndexPref}, ${eventsIndexPref}`); + + url = url.join('/'); + + const opts = getOpts(server, request, url, request.payload); + const patterns = getAllowedPatterns(server, project); + + try { + const rawResponse = await Wreck.request(request.method, url, opts); + const body = await Wreck.read(rawResponse, opts); + let response = JSON.parse(body.toString()); + + if (response._shards.total > 0) { + let indexHits = response.aggregations.indices.buckets; + response.aggregations.indices.buckets = + indexHits.filter(elem => patterns.includes(elem.key)); + } + return reply.response(response).code(rawResponse.statusCode).passThrough(!!opts.passThrough); + } catch (e) { + throw new Error(e); + } + } +} diff --git a/server/mt/routing/routes/search.js b/server/mt/routing/routes/search.js new file mode 100644 index 0000000..a71985b --- /dev/null +++ b/server/mt/routing/routes/search.js @@ -0,0 +1,47 @@ +/* + * Copyright 2020 FUJITSU LIMITED + * + * 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. + */ + +import Wreck from '@hapi/wreck'; + +import {getOpts, parsePayload} from '../_utils'; +import mapUri from '../_map_uri'; + +export default function (server, method, path) { + + return { + method: method, + path: path, + config: { + auth: 'keystone-session', + }, + handler: handler + }; + + async function handler(request, reply) { + const url = mapUri(server, request); + const payload = parsePayload(request); + const opts = getOpts(server, request, url, payload); + + try { + const rawResponse = await Wreck.request(request.method, url, opts); + const body = await Wreck.read(rawResponse, opts); + let response = JSON.parse(body.toString()); + + return reply.response(response).code(rawResponse.statusCode) + .passThrough(!!opts.passThrough); + } catch (e) { + throw new Error(e); + } + } +} diff --git a/server/mt/verify/_verify_index_pattern.js b/server/mt/verify/_verify_index_pattern.js deleted file mode 100644 index 2384c79..0000000 --- a/server/mt/verify/_verify_index_pattern.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import Boom from 'boom'; - -import { SESSION_PROJECTS_KEY } from '../../const'; -import util from '../../util'; - -const INDEX_PATTER_POS = 2; - -module.exports = (request, reply) => { - const session = request.yar._store; - const requestPath = util.requestPath(request); - const splittedPath = requestPath.split('/'); - - let pattern = splittedPath[INDEX_PATTER_POS]; - let projects = session[SESSION_PROJECTS_KEY]; - - if ('*' === pattern) { - return reply(Boom.badData('* as pattern is not supported at the moment')); - } else if (projects.filter(filter).length === 0) { - return reply(Boom.badData(`${pattern} do not match any project of current user`)); - } - - return reply.continue(); - - function filter(project) { - return new RegExp(`${project.id}.*`, 'gi').test(pattern); - } -}; diff --git a/server/mt/verify/index.js b/server/mt/verify/index.js deleted file mode 100644 index 0b009cb..0000000 --- a/server/mt/verify/index.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2016 FUJITSU LIMITED - * - * 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. - */ - -import { SESSION_USER_KEY } from '../../const'; -import util from '../../util'; - -module.exports = (server) => { - - return (request, reply) => { - const session = request.yar._store; - if (!(session && (SESSION_USER_KEY in session))) { - server.log(['status', 'warning', 'keystone'], 'Session not yet available'); - return reply.continue(); - } - - let requestPath = util.requestPath(request); - let requestMethod = request.method; - - if (util.isESRequest(request)) { - let handler; - if (isIndexPatternLookup()) { - handler = require('./_verify_index_pattern'); - } - if (handler) { - return handler(request, reply); - } - } - - return reply.continue(); - - function isIndexPatternLookup() { - let regExp = /\/elasticsearch\/.*\/_mapping\/field\/.*/; - return regExp.test(requestPath) && requestMethod.toLowerCase() === 'get'; - } - - }; -}; diff --git a/server/session/index.js b/server/session/index.js index 30edaf3..5c7380d 100644 --- a/server/session/index.js +++ b/server/session/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -11,40 +11,37 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - -import yarCookie from 'yar'; +import yarCookie from "@hapi/yar" import multiTenancy from '../mt'; +import {initClients} from "../mt/kibana/savedObjectsToolkit"; export default (server) => { const config = server.config(); return { - start: ()=> { - server.register({ - register: yarCookie, - options : { + start: async () => { + await server.register({ + plugin: yarCookie, + options: { maxCookieSize: 4096, - name : config.get('monasca-kibana-plugin.cookie.name'), - storeBlank : false, - cache : { + name: config.get('monasca-kibana-plugin.cookie.name'), + storeBlank: false, + cache: { expiresIn: config.get('monasca-kibana-plugin.cookie.expiresIn') }, cookieOptions: { - password : config.get('monasca-kibana-plugin.cookie.password'), - isSecure : config.get('monasca-kibana-plugin.cookie.isSecure'), + password: config.get('monasca-kibana-plugin.cookie.password'), + isSecure: config.get('monasca-kibana-plugin.cookie.isSecure'), ignoreErrors: config.get('monasca-kibana-plugin.cookie.ignoreErrors'), clearInvalid: false } } - }, (error) => { - if (!error) { - server.log(['status', 'info', 'keystone'], 'Session registered'); - multiTenancy.bind(server); - } else { - server.log(['status', 'error', 'keystone'], error); - throw error; - } + }).catch((err) => { + throw new Error(err); }); + server.log(['status', 'info', 'keystone'], 'Session registered'); + initClients(server); + await multiTenancy.bind(server); + } }; - }; diff --git a/server/util/index.js b/server/util/index.js index e686e27..ace003a 100644 --- a/server/util/index.js +++ b/server/util/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 FUJITSU LIMITED + * Copyright 2020 FUJITSU LIMITED * * 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 @@ -13,9 +13,8 @@ */ module.exports = { - startsWith: startsWith, requestPath: getRequestPath, - isESRequest: isESRequest, + isRouted: isRoutedRequest, keystoneUrl: keystoneUrl }; @@ -37,20 +36,19 @@ function keystoneUrl(config) { return url; } -function startsWith(str) { - var prefixes = Array.prototype.slice.call(arguments, 1); - for (var i = 0; i < prefixes.length; ++i) { - if (str.lastIndexOf(prefixes[i], 0) === 0) { - return true; - } - } - return false; -} - function getRequestPath(request) { return request.url.path; } function isESRequest(request) { - return startsWith(getRequestPath(request), '/elasticsearch'); + return getRequestPath(request).startsWith('/elasticsearch'); } + +function isSavedObjectsRequest(request) { + return /\/api.*\/saved_objects\/_/.test(getRequestPath(request)); +} + +function isRoutedRequest(request) { + return isESRequest(request) || isSavedObjectsRequest(request); +} +