Revert "Revert "Add Multi-Tenancy for keystone plugin""
This reverts commit 4752d35777
.
Change-Id: Ie377d6675c901eb922b4e07e800e391805f69361
This commit is contained in:
parent
4752d35777
commit
0d84a5afa6
25
index.js
25
index.js
@ -12,11 +12,12 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = (kibana) => {
|
import session from './server/session';
|
||||||
|
import binding from './server/binding';
|
||||||
|
import healthCheck from './server/healthcheck';
|
||||||
|
|
||||||
const session = require('./server/session');
|
export default (kibana) => {
|
||||||
const proxy = require('./server/proxy');
|
const COOKIE_PASSWORD_SIZE = 32;
|
||||||
const healthCheck = require('./server/healthcheck');
|
|
||||||
|
|
||||||
return new kibana.Plugin({
|
return new kibana.Plugin({
|
||||||
require: ['elasticsearch'],
|
require: ['elasticsearch'],
|
||||||
@ -27,17 +28,19 @@ module.exports = (kibana) => {
|
|||||||
function config(Joi) {
|
function config(Joi) {
|
||||||
|
|
||||||
const cookie = Joi.object({
|
const cookie = Joi.object({
|
||||||
|
name : Joi.string()
|
||||||
|
.default('keystone'),
|
||||||
password : Joi.string()
|
password : Joi.string()
|
||||||
.min(16)
|
.min(COOKIE_PASSWORD_SIZE)
|
||||||
.default(require('crypto').randomBytes(16).toString('hex')),
|
.default(require('crypto').randomBytes(COOKIE_PASSWORD_SIZE).toString('hex')),
|
||||||
isSecure : Joi.boolean()
|
isSecure : Joi.boolean()
|
||||||
.default(false),
|
.default(process.env.NODE_ENV !== 'development'),
|
||||||
ignoreErrors: Joi.boolean()
|
ignoreErrors: Joi.boolean()
|
||||||
.default(true),
|
.default(true),
|
||||||
expiresIn : Joi.number()
|
expiresIn : Joi.number()
|
||||||
.positive()
|
.positive()
|
||||||
.integer()
|
.integer()
|
||||||
.default(24 * 60 * 60 * 1000) // 1 day
|
.default(60 * 60 * 1000) // 1 hour
|
||||||
}).default();
|
}).default();
|
||||||
|
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
@ -51,9 +54,11 @@ module.exports = (kibana) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function init(server) {
|
function init(server) {
|
||||||
session(server);
|
server.log(['status', 'debug', 'keystone'], 'Initializing keystone plugin');
|
||||||
proxy(server);
|
binding(server).start();
|
||||||
|
session(server).start();
|
||||||
healthCheck(this, server).start();
|
healthCheck(this, server).start();
|
||||||
|
server.log(['status', 'debug', 'keystone'], 'Initialized keystone plugin');
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
17
package.json
17
package.json
@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "fts-keystone",
|
"name": "fts-keystone",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"description": "Keystone authentication support for Kibana 4.4.x",
|
"description": "Keystone authentication & multitenancy support for Kibana 4.4.x",
|
||||||
"author": "Fujitsu Enabling Software Technology GmbH",
|
"author": "Fujitsu Enabling Software Technology GmbH",
|
||||||
"licenses": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"kibana",
|
"kibana",
|
||||||
"authentication",
|
"authentication",
|
||||||
"keystone",
|
"keystone",
|
||||||
|
"multitenancy",
|
||||||
"plugin"
|
"plugin"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -22,8 +23,9 @@
|
|||||||
},
|
},
|
||||||
"main": "gulpfile.js",
|
"main": "gulpfile.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"yar": "^4.2.0",
|
"hoek": "^4.0.1",
|
||||||
"keystone-v3-client": "^0.0.7"
|
"keystone-v3-client": "^0.0.7",
|
||||||
|
"yar": "^7.x.x"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -43,11 +45,14 @@
|
|||||||
"gulp-mocha": "^2.2.0",
|
"gulp-mocha": "^2.2.0",
|
||||||
"gulp-tar": "^1.8.0",
|
"gulp-tar": "^1.8.0",
|
||||||
"gulp-util": "^3.0.7",
|
"gulp-util": "^3.0.7",
|
||||||
|
"joi": "^9.0.4",
|
||||||
"lodash": "^4.2.1",
|
"lodash": "^4.2.1",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"proxyquire": "^1.7.4",
|
"proxyquire": "^1.7.4",
|
||||||
"rimraf": "^2.5.1",
|
"rimraf": "^2.5.1",
|
||||||
"rsync": "^0.4.0",
|
"rsync": "^0.4.0",
|
||||||
"sinon": "^1.17.3"
|
"semver": "^5.3.0",
|
||||||
|
"sinon": "^1.17.3",
|
||||||
|
"wreck": "^8.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
server/__tests__/binding.spec.js
Normal file
45
server/__tests__/binding.spec.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const sinon = require('sinon');
|
||||||
|
const chai = require('chai');
|
||||||
|
const proxyRequire = require('proxyquire');
|
||||||
|
|
||||||
|
describe('fts-keystone', () => {
|
||||||
|
describe('binding', () => {
|
||||||
|
|
||||||
|
it('should expose tokens & users', () => {
|
||||||
|
|
||||||
|
let tokens = sinon.spy();
|
||||||
|
let users = sinon.spy();
|
||||||
|
|
||||||
|
let server = {
|
||||||
|
config: sinon.stub().returns({
|
||||||
|
get: sinon.spy()
|
||||||
|
}),
|
||||||
|
expose: sinon.spy()
|
||||||
|
};
|
||||||
|
|
||||||
|
proxyRequire('../binding', {
|
||||||
|
'keystone-v3-client/lib/keystone/tokens': tokens,
|
||||||
|
'keystone-v3-client/lib/keystone/users' : users
|
||||||
|
})(server).start();
|
||||||
|
|
||||||
|
chai.expect(server.expose.callCount).to.be.eq(2);
|
||||||
|
chai.expect(server.expose.calledWith('tokens', tokens));
|
||||||
|
chai.expect(server.expose.calledWith('users', users));
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -52,6 +52,7 @@ describe('plugins/fts-keystone', ()=> {
|
|||||||
|
|
||||||
server = {
|
server = {
|
||||||
log : sinon.stub(),
|
log : sinon.stub(),
|
||||||
|
on : sinon.stub(),
|
||||||
config: function () {
|
config: function () {
|
||||||
return {
|
return {
|
||||||
get: configGet
|
get: configGet
|
||||||
@ -63,6 +64,7 @@ describe('plugins/fts-keystone', ()=> {
|
|||||||
|
|
||||||
it('should set status to green if keystone available', (done)=> {
|
it('should set status to green if keystone available', (done)=> {
|
||||||
let expectedCode = 200;
|
let expectedCode = 200;
|
||||||
|
let expectedStatus = true;
|
||||||
let healthcheck = proxyRequire('../healthcheck', {
|
let healthcheck = proxyRequire('../healthcheck', {
|
||||||
'http': {
|
'http': {
|
||||||
request: (_, callback)=> {
|
request: (_, callback)=> {
|
||||||
@ -82,8 +84,8 @@ describe('plugins/fts-keystone', ()=> {
|
|||||||
|
|
||||||
check
|
check
|
||||||
.run()
|
.run()
|
||||||
.then((code) => {
|
.then((status) => {
|
||||||
chai.expect(expectedCode).to.be.equal(code);
|
chai.expect(expectedStatus).to.be.equal(status);
|
||||||
chai.expect(plugin.status.green.calledWith('Ready')).to.be.ok;
|
chai.expect(plugin.status.green.calledWith('Ready')).to.be.ok;
|
||||||
})
|
})
|
||||||
.finally(done);
|
.finally(done);
|
||||||
@ -92,6 +94,7 @@ describe('plugins/fts-keystone', ()=> {
|
|||||||
|
|
||||||
it('should set status to red if keystone not available', (done) => {
|
it('should set status to red if keystone not available', (done) => {
|
||||||
let expectedCode = 500;
|
let expectedCode = 500;
|
||||||
|
let expectedStatus = false;
|
||||||
let healthcheck = proxyRequire('../healthcheck', {
|
let healthcheck = proxyRequire('../healthcheck', {
|
||||||
'http': {
|
'http': {
|
||||||
request: (_, callback)=> {
|
request: (_, callback)=> {
|
||||||
@ -111,8 +114,8 @@ describe('plugins/fts-keystone', ()=> {
|
|||||||
|
|
||||||
check
|
check
|
||||||
.run()
|
.run()
|
||||||
.catch((code) => {
|
.catch((status) => {
|
||||||
chai.expect(expectedCode).to.be.equal(code);
|
chai.expect(expectedStatus).to.be.equal(status);
|
||||||
chai.expect(plugin.status.red.calledWith('Unavailable')).to.be.ok;
|
chai.expect(plugin.status.red.calledWith('Unavailable')).to.be.ok;
|
||||||
})
|
})
|
||||||
.finally(done);
|
.finally(done);
|
||||||
|
@ -1,174 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const proxyRequire = require('proxyquire');
|
|
||||||
const Promise = require('bluebird');
|
|
||||||
const sinon = require('sinon');
|
|
||||||
const chai = require('chai');
|
|
||||||
|
|
||||||
describe('plugins/fts-keystone', ()=> {
|
|
||||||
describe('proxy', ()=> {
|
|
||||||
describe('proxy_check', ()=> {
|
|
||||||
|
|
||||||
const keystoneUrl = 'http://localhost'; // mocking http
|
|
||||||
const keystonePort = 9000;
|
|
||||||
|
|
||||||
let server;
|
|
||||||
let configGet;
|
|
||||||
|
|
||||||
beforeEach(()=> {
|
|
||||||
configGet = sinon.stub();
|
|
||||||
configGet.withArgs('fts-keystone.url').returns(keystoneUrl);
|
|
||||||
configGet.withArgs('fts-keystone.port').returns(keystonePort);
|
|
||||||
|
|
||||||
server = {
|
|
||||||
log : sinon.stub(),
|
|
||||||
config: function () {
|
|
||||||
return {
|
|
||||||
get: configGet
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should do nothing if not /elasticsearch call', ()=> {
|
|
||||||
let checkSpy = sinon.spy();
|
|
||||||
let retrieveTokenSpy = sinon.spy();
|
|
||||||
let proxy = proxyRequire('../proxy/proxy', {
|
|
||||||
'keystone-v3-client/lib/keystone/tokens': () => {
|
|
||||||
return {check: checkSpy};
|
|
||||||
},
|
|
||||||
'./retrieveToken' : retrieveTokenSpy
|
|
||||||
})(server);
|
|
||||||
let request = {
|
|
||||||
url: {
|
|
||||||
path: '/bundles/styles.css'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let reply = {
|
|
||||||
'continue': sinon.spy()
|
|
||||||
};
|
|
||||||
|
|
||||||
proxy(request, reply);
|
|
||||||
|
|
||||||
chai.expect(reply.continue.calledOnce).to.be.ok;
|
|
||||||
chai.expect(checkSpy.called).to.not.be.ok;
|
|
||||||
chai.expect(retrieveTokenSpy.called).to.not.be.ok;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should authenticate with keystone', (done)=> {
|
|
||||||
|
|
||||||
let token = '1234567890';
|
|
||||||
let checkStub = sinon.stub().returns(Promise.resolve());
|
|
||||||
let retrieveTokenStub = sinon.stub().returns(token);
|
|
||||||
|
|
||||||
let proxy = proxyRequire('../proxy/proxy', {
|
|
||||||
'keystone-v3-client/lib/keystone/tokens': () => {
|
|
||||||
return {check: checkStub};
|
|
||||||
},
|
|
||||||
'./retrieveToken' : retrieveTokenStub
|
|
||||||
})(server);
|
|
||||||
let request = {
|
|
||||||
session: {
|
|
||||||
'get' : sinon.stub(),
|
|
||||||
'set' : sinon.stub()
|
|
||||||
},
|
|
||||||
url : {
|
|
||||||
path: '/elasticsearch/.kibana'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let reply = {
|
|
||||||
'continue': sinon.spy()
|
|
||||||
};
|
|
||||||
let replyCall;
|
|
||||||
|
|
||||||
proxy(request, reply)
|
|
||||||
.finally(verifyStubs)
|
|
||||||
.done(done);
|
|
||||||
|
|
||||||
function verifyStubs() {
|
|
||||||
chai.expect(reply.continue.calledOnce).to.be.ok;
|
|
||||||
replyCall = reply.continue.firstCall.args;
|
|
||||||
|
|
||||||
chai.expect(replyCall).to.be.empty;
|
|
||||||
|
|
||||||
// other stubs
|
|
||||||
chai.expect(checkStub.calledOnce).to.be.ok;
|
|
||||||
chai.expect(checkStub.calledWithExactly({
|
|
||||||
headers: {
|
|
||||||
'X-Auth-Token' : token,
|
|
||||||
'X-Subject-Token': token
|
|
||||||
}
|
|
||||||
})).to.be.ok;
|
|
||||||
|
|
||||||
chai.expect(retrieveTokenStub.calledOnce).to.be.ok;
|
|
||||||
chai.expect(retrieveTokenStub.calledWithExactly(server, request))
|
|
||||||
.to.be.ok;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not authenticate with keystone', (done)=> {
|
|
||||||
let token = '1234567890';
|
|
||||||
let checkStub = sinon.stub().returns(Promise.reject({
|
|
||||||
statusCode: 666
|
|
||||||
}));
|
|
||||||
let retrieveTokenStub = sinon.stub().returns(token);
|
|
||||||
let proxy = proxyRequire('../proxy/proxy', {
|
|
||||||
'keystone-v3-client/lib/keystone/tokens': () => {
|
|
||||||
return {check: checkStub};
|
|
||||||
},
|
|
||||||
'./retrieveToken' : retrieveTokenStub
|
|
||||||
})(server);
|
|
||||||
let request = {
|
|
||||||
session: {
|
|
||||||
'get' : sinon.stub(),
|
|
||||||
'set' : sinon.stub()
|
|
||||||
},
|
|
||||||
url : {
|
|
||||||
path: '/elasticsearch/.kibana'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let reply = sinon.spy();
|
|
||||||
let replyCall;
|
|
||||||
|
|
||||||
proxy(request, reply)
|
|
||||||
.finally(verifyStubs)
|
|
||||||
.done(done);
|
|
||||||
|
|
||||||
function verifyStubs() {
|
|
||||||
chai.expect(reply.calledOnce).to.be.ok;
|
|
||||||
replyCall = reply.firstCall.args[0];
|
|
||||||
|
|
||||||
chai.expect(replyCall.isBoom).to.be.ok;
|
|
||||||
|
|
||||||
// other stubs
|
|
||||||
chai.expect(checkStub.calledOnce).to.be.ok;
|
|
||||||
chai.expect(checkStub.calledWithExactly({
|
|
||||||
headers: {
|
|
||||||
'X-Auth-Token' : token,
|
|
||||||
'X-Subject-Token': token
|
|
||||||
}
|
|
||||||
})).to.be.ok;
|
|
||||||
|
|
||||||
chai.expect(retrieveTokenStub.calledOnce).to.be.ok;
|
|
||||||
chai.expect(retrieveTokenStub.calledWithExactly(server, request))
|
|
||||||
.to.be.ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -15,11 +15,14 @@
|
|||||||
const sinon = require('sinon');
|
const sinon = require('sinon');
|
||||||
const chai = require('chai');
|
const chai = require('chai');
|
||||||
|
|
||||||
const retrieveToken = require('../proxy/retrieveToken');
|
const retrieveToken = require('../mt/auth/token');
|
||||||
|
const CONSTANTS = require('../const');
|
||||||
|
const RELOAD_SYMBOL = require('../mt/auth/reload');
|
||||||
|
|
||||||
describe('plugins/fts-keystone', ()=> {
|
describe('plugins/fts-keystone', ()=> {
|
||||||
describe('proxy', ()=> {
|
describe('mt', ()=> {
|
||||||
describe('retrieveToken', ()=> {
|
describe('auth', () => {
|
||||||
|
describe('token', ()=> {
|
||||||
|
|
||||||
let server;
|
let server;
|
||||||
|
|
||||||
@ -38,7 +41,7 @@ describe('plugins/fts-keystone', ()=> {
|
|||||||
}).to.throw(errMsg);
|
}).to.throw(errMsg);
|
||||||
|
|
||||||
request = {
|
request = {
|
||||||
session: undefined
|
yar: undefined
|
||||||
};
|
};
|
||||||
chai.expect(()=> {
|
chai.expect(()=> {
|
||||||
retrieveToken(server, request);
|
retrieveToken(server, request);
|
||||||
@ -55,10 +58,10 @@ describe('plugins/fts-keystone', ()=> {
|
|||||||
it('should Boom with unauthorized if token not in header or session', function () {
|
it('should Boom with unauthorized if token not in header or session', function () {
|
||||||
let expectedMsg = 'You\'re not logged into the OpenStack. Please login via Horizon Dashboard';
|
let expectedMsg = 'You\'re not logged into the OpenStack. Please login via Horizon Dashboard';
|
||||||
let request = {
|
let request = {
|
||||||
session: {
|
yar : {
|
||||||
'get': sinon
|
'get': sinon
|
||||||
.stub()
|
.stub()
|
||||||
.withArgs('keystone_token')
|
.withArgs(CONSTANTS.SESSION_TOKEN_KEY)
|
||||||
.returns(undefined)
|
.returns(undefined)
|
||||||
},
|
},
|
||||||
headers: {}
|
headers: {}
|
||||||
@ -73,99 +76,115 @@ describe('plugins/fts-keystone', ()=> {
|
|||||||
it('should use session token if requested does not have it', () => {
|
it('should use session token if requested does not have it', () => {
|
||||||
let expectedToken = 'SOME_RANDOM_TOKEN';
|
let expectedToken = 'SOME_RANDOM_TOKEN';
|
||||||
let yar = {
|
let yar = {
|
||||||
'set': sinon
|
'reset': sinon.spy(),
|
||||||
.spy(),
|
'set' : sinon.spy(),
|
||||||
'get': sinon
|
'get' : sinon.stub()
|
||||||
.stub()
|
|
||||||
.withArgs('keystone_token')
|
|
||||||
.returns(expectedToken)
|
|
||||||
};
|
};
|
||||||
let request = {
|
let request = {
|
||||||
session: yar,
|
yar : yar,
|
||||||
headers: {}
|
headers: {}
|
||||||
};
|
};
|
||||||
let token;
|
let token;
|
||||||
|
|
||||||
|
yar.get.returns(expectedToken);
|
||||||
|
|
||||||
token = retrieveToken(server, request);
|
token = retrieveToken(server, request);
|
||||||
chai.expect(token).not.to.be.undefined;
|
chai.expect(token).not.to.be.undefined;
|
||||||
chai.expect(token).to.be.eql(expectedToken);
|
chai.expect(token).to.be.eql(expectedToken);
|
||||||
|
|
||||||
|
console.log(yar.get.callCount);
|
||||||
|
|
||||||
|
chai.expect(yar.get.callCount).to.be.eq(2);
|
||||||
|
chai.expect(yar.set.calledOnce).not.to.be.ok;
|
||||||
chai.expect(
|
chai.expect(
|
||||||
yar.get.calledOnce
|
yar.set.calledWithExactly(CONSTANTS.SESSION_TOKEN_KEY, expectedToken)
|
||||||
).to.be.ok;
|
|
||||||
chai.expect(
|
|
||||||
yar.set.calledOnce
|
|
||||||
).not.to.be.ok;
|
|
||||||
chai.expect(
|
|
||||||
yar.set.calledWithExactly('keystone_token', expectedToken)
|
|
||||||
).not.to.be.ok;
|
).not.to.be.ok;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set token in session if not there and request has it', () => {
|
it('should set token in session if not there and request has it', () => {
|
||||||
let expectedToken = 'SOME_RANDOM_TOKEN';
|
let expectedToken = 'SOME_RANDOM_TOKEN';
|
||||||
let yar = {
|
let yar = {
|
||||||
'set': sinon
|
'reset': sinon.spy(),
|
||||||
.spy(),
|
'set' : sinon.spy(),
|
||||||
'get': sinon
|
'get' : sinon.stub()
|
||||||
.stub()
|
|
||||||
.withArgs('keystone_token')
|
|
||||||
.returns(undefined)
|
|
||||||
};
|
};
|
||||||
let request = {
|
let request = {
|
||||||
session: yar,
|
yar : yar,
|
||||||
headers: {
|
headers: {
|
||||||
'x-auth-token': expectedToken
|
'x-auth-token': expectedToken
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let token;
|
let token;
|
||||||
|
|
||||||
|
yar.get
|
||||||
|
.withArgs(CONSTANTS.SESSION_TOKEN_KEY)
|
||||||
|
.onCall(0).returns(undefined)
|
||||||
|
.onCall(1).returns(expectedToken);
|
||||||
|
|
||||||
token = retrieveToken(server, request);
|
token = retrieveToken(server, request);
|
||||||
chai.expect(token).to.not.be.undefined;
|
chai.expect(token).to.not.be.undefined;
|
||||||
chai.expect(token).to.be.eql(expectedToken);
|
chai.expect(token).to.be.eql(expectedToken);
|
||||||
|
|
||||||
|
chai.expect(yar.get.callCount).to.be.eq(2);
|
||||||
|
chai.expect(yar.set.calledOnce).to.be.ok;
|
||||||
|
|
||||||
chai.expect(
|
chai.expect(
|
||||||
yar.get.calledOnce
|
yar.set.calledWithExactly(
|
||||||
|
CONSTANTS.SESSION_TOKEN_KEY,
|
||||||
|
expectedToken
|
||||||
|
)
|
||||||
).to.be.ok;
|
).to.be.ok;
|
||||||
chai.expect(
|
chai.expect(
|
||||||
yar.set.calledOnce
|
yar.set.calledWithExactly(
|
||||||
).to.be.ok;
|
CONSTANTS.SESSION_TOKEN_CHANGED,
|
||||||
chai.expect(
|
CONSTANTS.TOKEN_CHANGED_VALUE
|
||||||
yar.set.calledWithExactly('keystone_token', expectedToken)
|
)
|
||||||
).to.be.ok;
|
).to.not.be.ok;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update token in session if request\'s token is different', ()=> {
|
it('should update token in session if request\'s token is different', ()=> {
|
||||||
let expectedToken = 'SOME_RANDOM_TOKEN';
|
let expectedToken = 'SOME_RANDOM_TOKEN';
|
||||||
|
let oldToken = 'OLD_TOKEN';
|
||||||
|
|
||||||
let headers = {
|
let headers = {
|
||||||
'x-auth-token': expectedToken
|
'x-auth-token': expectedToken
|
||||||
};
|
};
|
||||||
let yar = {
|
let yar = {
|
||||||
|
'reset': sinon.stub(),
|
||||||
'get' : sinon
|
'get' : sinon
|
||||||
.stub()
|
.stub()
|
||||||
.withArgs('keystone_token')
|
.withArgs(CONSTANTS.SESSION_TOKEN_KEY)
|
||||||
.returns('SOME_OLD_TOKEN'),
|
.returns(oldToken),
|
||||||
'set': sinon
|
'set' : sinon.spy()
|
||||||
.spy()
|
|
||||||
};
|
};
|
||||||
let token;
|
let token;
|
||||||
let request = {
|
let request = {
|
||||||
session: yar,
|
yar : yar,
|
||||||
headers: headers
|
headers: headers
|
||||||
};
|
};
|
||||||
|
|
||||||
token = retrieveToken(server, request);
|
token = retrieveToken(server, request);
|
||||||
chai.expect(token).to.not.be.undefined;
|
chai.expect(token).to.not.be.undefined;
|
||||||
chai.expect(token).to.be.eql(expectedToken);
|
chai.expect(token).to.be.eql(RELOAD_SYMBOL);
|
||||||
|
|
||||||
|
chai.expect(yar.reset.calledOnce).to.be.ok;
|
||||||
|
chai.expect(yar.get.calledOnce).to.be.ok;
|
||||||
|
|
||||||
|
chai.expect(yar.set.callCount).to.be.eq(2);
|
||||||
chai.expect(
|
chai.expect(
|
||||||
yar.get.calledOnce
|
yar.set.calledWithExactly(
|
||||||
|
CONSTANTS.SESSION_TOKEN_KEY,
|
||||||
|
expectedToken
|
||||||
|
)
|
||||||
).to.be.ok;
|
).to.be.ok;
|
||||||
chai.expect(
|
chai.expect(
|
||||||
yar.set.calledOnce
|
yar.set.calledWithExactly(
|
||||||
).to.be.ok;
|
CONSTANTS.SESSION_TOKEN_CHANGED,
|
||||||
chai.expect(
|
CONSTANTS.TOKEN_CHANGED_VALUE
|
||||||
yar.set.calledWithExactly('keystone_token', expectedToken)
|
)
|
||||||
).to.be.ok;
|
).to.be.ok;
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
132
server/__tests__/routing.createProxy.spec.js
Normal file
132
server/__tests__/routing.createProxy.spec.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const sinon = require('sinon');
|
||||||
|
const chai = require('chai');
|
||||||
|
const proxyRequire = require('proxyquire');
|
||||||
|
|
||||||
|
describe('plugins/fts-keystone', () => {
|
||||||
|
describe('mt', () => {
|
||||||
|
describe('routing', () => {
|
||||||
|
describe('createProxy', () => {
|
||||||
|
const kibanaIndex = '.kibana';
|
||||||
|
const server = {
|
||||||
|
log : sinon.spy(),
|
||||||
|
config: sinon.stub().returns({
|
||||||
|
get: sinon.stub().withArgs('kibana.index').returns(kibanaIndex)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let mgetHandler;
|
||||||
|
let pathsHandler;
|
||||||
|
let kibanaIndexHandler;
|
||||||
|
let defaultHandler;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mgetHandler = sinon.stub().returns({});
|
||||||
|
pathsHandler = sinon.stub().returns({});
|
||||||
|
kibanaIndexHandler = sinon.stub().returns({});
|
||||||
|
defaultHandler = sinon.stub().returns({});
|
||||||
|
|
||||||
|
server.route = sinon.spy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load mget handler if that is the route', () => {
|
||||||
|
const route = '/_mget';
|
||||||
|
const method = sinon.spy();
|
||||||
|
|
||||||
|
proxyRequire('../mt/routing/_create_proxy', {
|
||||||
|
'./routes/mget' : mgetHandler,
|
||||||
|
'./routes/paths' : pathsHandler,
|
||||||
|
'./routes/kibana_index': kibanaIndexHandler,
|
||||||
|
'./routes/default' : defaultHandler
|
||||||
|
})(server, method, route);
|
||||||
|
|
||||||
|
chai.expect(mgetHandler.calledOnce).to.be.ok;
|
||||||
|
chai.expect(mgetHandler.calledWith(server, method, sinon.match.string)).to.be.ok;
|
||||||
|
|
||||||
|
chai.expect(pathsHandler.calledOnce).to.not.be.ok;
|
||||||
|
chai.expect(kibanaIndexHandler.calledOnce).to.not.be.ok;
|
||||||
|
chai.expect(defaultHandler.calledOnce).to.not.be.ok;
|
||||||
|
|
||||||
|
chai.expect(server.route.calledWith(sinon.match.object)).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load paths handler if that is the route', () => {
|
||||||
|
const route = '/{paths*}';
|
||||||
|
const method = sinon.spy();
|
||||||
|
|
||||||
|
proxyRequire('../mt/routing/_create_proxy', {
|
||||||
|
'./routes/mget' : mgetHandler,
|
||||||
|
'./routes/paths' : pathsHandler,
|
||||||
|
'./routes/kibana_index': kibanaIndexHandler,
|
||||||
|
'./routes/default' : defaultHandler
|
||||||
|
})(server, method, route);
|
||||||
|
|
||||||
|
chai.expect(pathsHandler.calledOnce).to.be.ok;
|
||||||
|
chai.expect(pathsHandler.calledWith(server, method, sinon.match.string)).to.be.ok;
|
||||||
|
|
||||||
|
chai.expect(mgetHandler.calledOnce).to.not.be.ok;
|
||||||
|
chai.expect(kibanaIndexHandler.calledOnce).to.not.be.ok;
|
||||||
|
chai.expect(defaultHandler.calledOnce).to.not.be.ok;
|
||||||
|
|
||||||
|
chai.expect(server.route.calledWith(sinon.match.object)).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load default handler if that is the route', () => {
|
||||||
|
const route = '/other';
|
||||||
|
const method = sinon.spy();
|
||||||
|
|
||||||
|
proxyRequire('../mt/routing/_create_proxy', {
|
||||||
|
'./routes/mget' : mgetHandler,
|
||||||
|
'./routes/paths' : pathsHandler,
|
||||||
|
'./routes/kibana_index': kibanaIndexHandler,
|
||||||
|
'./routes/default' : defaultHandler
|
||||||
|
})(server, method, route);
|
||||||
|
|
||||||
|
chai.expect(defaultHandler.calledOnce).to.be.ok;
|
||||||
|
chai.expect(defaultHandler.calledWith(server, method, sinon.match.string)).to.be.ok;
|
||||||
|
|
||||||
|
chai.expect(mgetHandler.calledOnce).to.not.be.ok;
|
||||||
|
chai.expect(kibanaIndexHandler.calledOnce).to.not.be.ok;
|
||||||
|
chai.expect(pathsHandler.calledOnce).to.not.be.ok;
|
||||||
|
|
||||||
|
chai.expect(server.route.calledWith(sinon.match.object)).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load kibana handler if that is the route', () => {
|
||||||
|
const route = `/${kibanaIndex}/{paths*}`;
|
||||||
|
const method = sinon.spy();
|
||||||
|
|
||||||
|
proxyRequire('../mt/routing/_create_proxy', {
|
||||||
|
'./routes/mget' : mgetHandler,
|
||||||
|
'./routes/paths' : pathsHandler,
|
||||||
|
'./routes/kibana_index': kibanaIndexHandler,
|
||||||
|
'./routes/default' : defaultHandler
|
||||||
|
})(server, method, route);
|
||||||
|
|
||||||
|
chai.expect(kibanaIndexHandler.calledOnce).to.be.ok;
|
||||||
|
chai.expect(kibanaIndexHandler.calledWith(server, method, sinon.match.string)).to.be.ok;
|
||||||
|
|
||||||
|
chai.expect(mgetHandler.calledOnce).to.not.be.ok;
|
||||||
|
chai.expect(defaultHandler.calledOnce).to.not.be.ok;
|
||||||
|
chai.expect(pathsHandler.calledOnce).to.not.be.ok;
|
||||||
|
|
||||||
|
chai.expect(server.route.calledWith(sinon.match.object)).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
86
server/__tests__/routing.reRouting.spec.js
Normal file
86
server/__tests__/routing.reRouting.spec.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const sinon = require('sinon');
|
||||||
|
const chai = require('chai');
|
||||||
|
const proxyRequire = require('proxyquire');
|
||||||
|
|
||||||
|
describe('plugins/fts-keystone', () => {
|
||||||
|
describe('mt', () => {
|
||||||
|
describe('routing', () => {
|
||||||
|
describe('reRoute', () => {
|
||||||
|
const prefix = '/test';
|
||||||
|
const server = {
|
||||||
|
log: sinon.spy()
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should re-route ElasticSearch request', () => {
|
||||||
|
const requestPath = '/elasticsearch/something/a/b/c';
|
||||||
|
|
||||||
|
let request = {
|
||||||
|
setUrl: sinon.spy()
|
||||||
|
};
|
||||||
|
let reply = {
|
||||||
|
continue: sinon.spy()
|
||||||
|
};
|
||||||
|
let utils = {
|
||||||
|
requestPath: sinon.stub().withArgs(request).returns(requestPath),
|
||||||
|
isESRequest: sinon.stub().withArgs(request).returns(true)
|
||||||
|
};
|
||||||
|
|
||||||
|
proxyRequire('../mt/routing/_re_route', {
|
||||||
|
'../../util': utils,
|
||||||
|
'./_utils' : {
|
||||||
|
PREFIX: prefix
|
||||||
|
}
|
||||||
|
})(server)(request, reply);
|
||||||
|
|
||||||
|
chai.expect(request.setUrl.calledOnce).to.be.ok;
|
||||||
|
chai.expect(request.setUrl.calledWith(`${prefix}${requestPath}`)).to.be.ok;
|
||||||
|
chai.expect(reply.continue.calledOnce).to.be.ok;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not re-route non-ElasticSearch request', () => {
|
||||||
|
const requestPath = '/resources/cool.ico';
|
||||||
|
|
||||||
|
let request = {
|
||||||
|
setUrl: sinon.spy()
|
||||||
|
};
|
||||||
|
let reply = {
|
||||||
|
continue: sinon.spy()
|
||||||
|
};
|
||||||
|
let utils = {
|
||||||
|
requestPath: sinon.stub().withArgs(request).returns(requestPath),
|
||||||
|
isESRequest: sinon.stub().withArgs(request).returns(false)
|
||||||
|
};
|
||||||
|
|
||||||
|
proxyRequire('../mt/routing/_re_route', {
|
||||||
|
'../../util': utils,
|
||||||
|
'./_utils' : {
|
||||||
|
PREFIX: prefix
|
||||||
|
}
|
||||||
|
})(server)(request, reply);
|
||||||
|
|
||||||
|
chai.expect(request.setUrl.calledOnce).to.not.be.ok;
|
||||||
|
chai.expect(request.setUrl.calledWith(`${prefix}${requestPath}`)).to.not.be.ok;
|
||||||
|
chai.expect(reply.continue.calledOnce).to.be.ok;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
57
server/__tests__/routing.spec.js
Normal file
57
server/__tests__/routing.spec.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const sinon = require('sinon');
|
||||||
|
const chai = require('chai');
|
||||||
|
const proxyRequire = require('proxyquire');
|
||||||
|
|
||||||
|
describe('plugins/fts-keystone', ()=> {
|
||||||
|
describe('mt', ()=> {
|
||||||
|
describe('routing', () => {
|
||||||
|
|
||||||
|
const createProxy = sinon.spy();
|
||||||
|
const serverLog = sinon.spy();
|
||||||
|
|
||||||
|
let serverExt;
|
||||||
|
let server;
|
||||||
|
let reRoute;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
serverExt = sinon.spy();
|
||||||
|
server = {
|
||||||
|
log: serverLog,
|
||||||
|
ext: serverExt
|
||||||
|
};
|
||||||
|
reRoute = sinon.spy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load re-route logic', (done) => {
|
||||||
|
|
||||||
|
proxyRequire('../mt/routing', {
|
||||||
|
'./_create_proxy': createProxy,
|
||||||
|
'./_re_route' : reRoute
|
||||||
|
})(server).then(verify);
|
||||||
|
|
||||||
|
function verify(route) {
|
||||||
|
chai.expect(serverExt.calledOnce).to.be.ok;
|
||||||
|
chai.expect(serverExt.calledWith('onRequest', reRoute));
|
||||||
|
chai.expect(createProxy).to.be.eq(route);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
114
server/__tests__/verify.spec.js
Normal file
114
server/__tests__/verify.spec.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const sinon = require('sinon');
|
||||||
|
const chai = require('chai');
|
||||||
|
const proxyRequire = require('proxyquire');
|
||||||
|
|
||||||
|
const CONSTANTS = require('../const');
|
||||||
|
|
||||||
|
describe('plugins/fts-keystone', ()=> {
|
||||||
|
describe('mt', ()=> {
|
||||||
|
describe('verify', () => {
|
||||||
|
|
||||||
|
it('should skip if session not available', () => {
|
||||||
|
let server = {
|
||||||
|
log: sinon.spy()
|
||||||
|
};
|
||||||
|
let request = {
|
||||||
|
yar: {
|
||||||
|
_store: undefined
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let reply = {
|
||||||
|
continue: sinon.spy()
|
||||||
|
};
|
||||||
|
|
||||||
|
require('../mt/verify')(server)(request, reply);
|
||||||
|
chai.expect(reply.continue.calledOnce).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip if session available but user object not found', () => {
|
||||||
|
let server = {
|
||||||
|
log: sinon.spy()
|
||||||
|
};
|
||||||
|
let request = {
|
||||||
|
yar: {
|
||||||
|
_store: {
|
||||||
|
'1': 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let reply = {
|
||||||
|
continue: sinon.spy()
|
||||||
|
};
|
||||||
|
|
||||||
|
require('../mt/verify')(server)(request, reply);
|
||||||
|
chai.expect(reply.continue.calledOnce).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip non ElasticSearch requests', () => {
|
||||||
|
let store = {};
|
||||||
|
|
||||||
|
store[CONSTANTS.SESSION_USER_KEY] = {'id': 1};
|
||||||
|
|
||||||
|
let server = {
|
||||||
|
log: sinon.spy()
|
||||||
|
};
|
||||||
|
let request = {
|
||||||
|
url : {
|
||||||
|
path: '/some/other/path'
|
||||||
|
},
|
||||||
|
yar: {
|
||||||
|
_store: store
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let reply = {
|
||||||
|
continue: sinon.spy()
|
||||||
|
};
|
||||||
|
|
||||||
|
require('../mt/verify')(server)(request, reply);
|
||||||
|
chai.expect(reply.continue.calledOnce).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call verify indexPattern', () => {
|
||||||
|
let store = {};
|
||||||
|
let indexPattern = '*';
|
||||||
|
|
||||||
|
store[CONSTANTS.SESSION_USER_KEY] = {'id': 1};
|
||||||
|
|
||||||
|
let server = {
|
||||||
|
log: sinon.spy()
|
||||||
|
};
|
||||||
|
let request = {
|
||||||
|
method: 'GET',
|
||||||
|
url : {
|
||||||
|
path: `/elasticsearch/${indexPattern}/_mapping/field/`
|
||||||
|
},
|
||||||
|
yar: {
|
||||||
|
_store: store
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let verifyIndexPattern = sinon.spy();
|
||||||
|
|
||||||
|
proxyRequire('../mt/verify', {
|
||||||
|
'./_verify_index_pattern': verifyIndexPattern
|
||||||
|
})(server)(request);
|
||||||
|
|
||||||
|
chai.expect(verifyIndexPattern.calledOnce).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
103
server/__tests__/verifyIndexPattern.spec.js
Normal file
103
server/__tests__/verifyIndexPattern.spec.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const sinon = require('sinon');
|
||||||
|
const chai = require('chai');
|
||||||
|
|
||||||
|
const CONSTANTS = require('../const');
|
||||||
|
const verifyIndexPattern = require('../mt/verify/_verify_index_pattern');
|
||||||
|
|
||||||
|
describe('plugins/fts-keystone', ()=> {
|
||||||
|
describe('mt', ()=> {
|
||||||
|
describe('verify', () => {
|
||||||
|
describe('verify_index_pattern', () => {
|
||||||
|
|
||||||
|
it('should reject * as index-pattern', () => {
|
||||||
|
let indexPattern = '*';
|
||||||
|
let request = {
|
||||||
|
url: {
|
||||||
|
path: `/a/${indexPattern}/`
|
||||||
|
},
|
||||||
|
yar: {
|
||||||
|
_store: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
verifyIndexPattern(request, (result) => {
|
||||||
|
chai.expect(result.isBoom)
|
||||||
|
.to.be.true;
|
||||||
|
chai.expect(result.output.payload.message)
|
||||||
|
.to.be.eq('* as pattern is not supported at the moment');
|
||||||
|
chai.expect(result.output.statusCode)
|
||||||
|
.to.be.eq(422);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject index-pattern if it does not match user`s projects', () => {
|
||||||
|
let projects = [
|
||||||
|
{ id: 'project_1'}, { id: 'project_2' }, {id: 'project_3'}
|
||||||
|
];
|
||||||
|
let indexPattern = 'test';
|
||||||
|
let store = {};
|
||||||
|
|
||||||
|
store[CONSTANTS.SESSION_PROJECTS_KEY] = projects;
|
||||||
|
|
||||||
|
let request = {
|
||||||
|
url: {
|
||||||
|
path: `/a/${indexPattern}/`
|
||||||
|
},
|
||||||
|
yar: {
|
||||||
|
_store: store
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let reply = sinon.spy();
|
||||||
|
|
||||||
|
verifyIndexPattern(request, (result) => {
|
||||||
|
chai.expect(result.isBoom)
|
||||||
|
.to.be.true;
|
||||||
|
chai.expect(result.output.payload.message)
|
||||||
|
.to.be.eq(`${indexPattern} do not match any project of current user`);
|
||||||
|
chai.expect(result.output.statusCode)
|
||||||
|
.to.be.eq(422);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should accept index-pattern it it constaint project id', () => {
|
||||||
|
let projects = [
|
||||||
|
{ id: 'project_1'}, { id: 'project_2' }, {id: 'project_3'}
|
||||||
|
];
|
||||||
|
let indexPattern = projects[1].id;
|
||||||
|
let store = {};
|
||||||
|
|
||||||
|
store[CONSTANTS.SESSION_PROJECTS_KEY] = projects;
|
||||||
|
|
||||||
|
let request = {
|
||||||
|
url: {
|
||||||
|
path: `/a/${indexPattern}/`
|
||||||
|
},
|
||||||
|
yar: {
|
||||||
|
_store: store
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let reply = {
|
||||||
|
continue: sinon.spy()
|
||||||
|
};
|
||||||
|
|
||||||
|
verifyIndexPattern(request, reply);
|
||||||
|
chai.expect(reply.continue.calledOnce).to.be.ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
31
server/binding/index.js
Normal file
31
server/binding/index.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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 TokensApi from 'keystone-v3-client/lib/keystone/tokens';
|
||||||
|
import UsersApi from 'keystone-v3-client/lib/keystone/users';
|
||||||
|
|
||||||
|
module.exports = function binding(server) {
|
||||||
|
const config = server.config();
|
||||||
|
const keystoneCfg = {
|
||||||
|
url: `${config.get('fts-keystone.url')}:${config.get('fts-keystone.port')}`
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: () => {
|
||||||
|
server.expose('tokens', new TokensApi(keystoneCfg));
|
||||||
|
server.expose('users', new UsersApi(keystoneCfg));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
22
server/const.js
Normal file
22
server/const.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const NOW_TIME = (new Date().valueOf() / 1000);
|
||||||
|
|
||||||
|
export const SESSION_USER_KEY = `fts-keystone-user-${NOW_TIME}`;
|
||||||
|
export const SESSION_TOKEN_KEY = `fts-keystone-token-${NOW_TIME}`;
|
||||||
|
export const SESSION_PROJECTS_KEY = `fts-keystone-projects-${NOW_TIME}`;
|
||||||
|
export const SESSION_TOKEN_CHANGED = `fts-keystone-token-changed-${NOW_TIME}`;
|
||||||
|
|
||||||
|
export const TOKEN_CHANGED_VALUE = Symbol('token-changed');
|
@ -12,18 +12,19 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Promise = require('bluebird');
|
import url from 'url';
|
||||||
const url = require('url');
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
const util = require('../util/');
|
import util from '../util';
|
||||||
|
|
||||||
module.exports = function (plugin, server) {
|
|
||||||
let timeoutId;
|
|
||||||
|
|
||||||
|
module.exports = function healthcheck(plugin, server) {
|
||||||
const config = server.config();
|
const config = server.config();
|
||||||
const keystoneUrl = config.get('fts-keystone.url');
|
const keystoneUrl = config.get('fts-keystone.url');
|
||||||
const keystonePort = config.get('fts-keystone.port');
|
const keystonePort = config.get('fts-keystone.port');
|
||||||
const request = getRequest();
|
const request = getRequest();
|
||||||
|
|
||||||
|
let timeoutId;
|
||||||
|
|
||||||
const service = {
|
const service = {
|
||||||
run : check,
|
run : check,
|
||||||
start : start,
|
start : start,
|
||||||
@ -33,22 +34,12 @@ module.exports = function (plugin, server) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
server.on('stop', stop);
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
|
|
||||||
function getRequest() {
|
|
||||||
let required;
|
|
||||||
if (util.startsWith(keystoneUrl, 'https')) {
|
|
||||||
required = require('https');
|
|
||||||
} else {
|
|
||||||
required = require('http');
|
|
||||||
}
|
|
||||||
return required.request;
|
|
||||||
}
|
|
||||||
|
|
||||||
function check() {
|
function check() {
|
||||||
|
|
||||||
return new Promise((resolve, reject)=> {
|
return new Promise((resolve, reject)=> {
|
||||||
|
|
||||||
const req = request({
|
const req = request({
|
||||||
hostname: getHostname(),
|
hostname: getHostname(),
|
||||||
port : keystonePort,
|
port : keystonePort,
|
||||||
@ -57,12 +48,13 @@ module.exports = function (plugin, server) {
|
|||||||
const statusCode = res.statusCode;
|
const statusCode = res.statusCode;
|
||||||
if (statusCode >= 400) {
|
if (statusCode >= 400) {
|
||||||
plugin.status.red('Unavailable');
|
plugin.status.red('Unavailable');
|
||||||
reject(statusCode);
|
reject(false);
|
||||||
} else {
|
} else {
|
||||||
plugin.status.green('Ready');
|
plugin.status.green('Ready');
|
||||||
resolve(statusCode);
|
resolve(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('error', (error)=> {
|
req.on('error', (error)=> {
|
||||||
plugin.status.red('Unavailable: Failed to communicate with Keystone');
|
plugin.status.red('Unavailable: Failed to communicate with Keystone');
|
||||||
server.log(['keystone', 'healthcheck', 'error'], `${error.message}`);
|
server.log(['keystone', 'healthcheck', 'error'], `${error.message}`);
|
||||||
@ -70,14 +62,9 @@ module.exports = function (plugin, server) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
req.end();
|
req.end();
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHostname() {
|
|
||||||
return url.parse(keystoneUrl).hostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
scheduleCheck(service.stop() ? 10000 : 1);
|
scheduleCheck(service.stop() ? 10000 : 1);
|
||||||
}
|
}
|
||||||
@ -109,4 +96,18 @@ module.exports = function (plugin, server) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getHostname() {
|
||||||
|
return url.parse(keystoneUrl).hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRequest() {
|
||||||
|
let required;
|
||||||
|
if (util.startsWith(keystoneUrl, 'https')) {
|
||||||
|
required = require('https');
|
||||||
|
} else {
|
||||||
|
required = require('http');
|
||||||
|
}
|
||||||
|
return required.request;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
91
server/mt/auth/_authenticate.js
Normal file
91
server/mt/auth/_authenticate.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 Joi from 'joi';
|
||||||
|
|
||||||
|
import { SESSION_USER_KEY } from '../../const';
|
||||||
|
import lookupToken from './token';
|
||||||
|
import RELOAD from './reload';
|
||||||
|
|
||||||
|
const RELOAD_MARKUP = `<html>
|
||||||
|
<head><script type="text/javascript">window.location.reload();</script></head>
|
||||||
|
<body>reloading...</body>
|
||||||
|
</html>`;
|
||||||
|
const NOOP = ()=> {
|
||||||
|
};
|
||||||
|
const SCHEMA = {
|
||||||
|
tokenOk : Joi.func().default(NOOP),
|
||||||
|
tokenBad: Joi.func().default(NOOP)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (server, opts) => {
|
||||||
|
Joi.assert(opts, SCHEMA, 'Invalid keystone auth options');
|
||||||
|
|
||||||
|
const tokensApi = server.plugins['fts-keystone'].tokens;
|
||||||
|
const callbackOk = opts.tokenOk;
|
||||||
|
const callbackBad = opts.tokenBad;
|
||||||
|
|
||||||
|
return (request, reply) => {
|
||||||
|
const token = lookupToken(server, request);
|
||||||
|
const session = request.yar;
|
||||||
|
|
||||||
|
let userObj = session.get(SESSION_USER_KEY);
|
||||||
|
|
||||||
|
if (token.isBoom) {
|
||||||
|
server.log(['status', 'debug', 'keystone'],
|
||||||
|
'Received error object from token lookup'
|
||||||
|
);
|
||||||
|
return reply(token);
|
||||||
|
} else if (token === RELOAD) {
|
||||||
|
server.log(['status', 'debug', 'keystone'],
|
||||||
|
'Received reload markup object from token lookup'
|
||||||
|
);
|
||||||
|
return reply(RELOAD_MARKUP).type('text/html');
|
||||||
|
} else if (userObj && 'project' in userObj) {
|
||||||
|
server.log(['status','info','keystone'], `${token} already authorized`);
|
||||||
|
return reply.continue({credentials:token});
|
||||||
|
}
|
||||||
|
|
||||||
|
server.log(['status', 'debug', 'keystone'],
|
||||||
|
'About to validate token with keystone'
|
||||||
|
);
|
||||||
|
|
||||||
|
return tokensApi
|
||||||
|
.validate({
|
||||||
|
headers: {
|
||||||
|
'X-Auth-Token' : token,
|
||||||
|
'X-Subject-Token': token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
(data) => {
|
||||||
|
userObj = data.data.token;
|
||||||
|
return callbackOk(token, userObj, session)
|
||||||
|
.then(()=> {
|
||||||
|
server.log(['status', 'debug', 'keystone'],
|
||||||
|
`Auth process completed for user ${userObj.user.id}`);
|
||||||
|
return reply.continue({credentials: token});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
return callbackBad(token, error, session)
|
||||||
|
.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));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
17
server/mt/auth/reload/index.js
Normal file
17
server/mt/auth/reload/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const RELOAD = Symbol('reload-me');
|
||||||
|
|
||||||
|
module.exports = RELOAD;
|
@ -12,14 +12,10 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const proxy = require('./proxy');
|
import authenticateFactory from './_authenticate';
|
||||||
|
|
||||||
module.exports = function createProxy(server) {
|
export default (server, opts) => {
|
||||||
server.ext(
|
return {
|
||||||
'onPreAuth',
|
authenticate: authenticateFactory(server, opts)
|
||||||
proxy(server),
|
};
|
||||||
{
|
|
||||||
after: ['yar']
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
74
server/mt/auth/strategy.js
Normal file
74
server/mt/auth/strategy.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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 Promise from 'bluebird';
|
||||||
|
|
||||||
|
import kibanaIndex from '../kibana';
|
||||||
|
import userProjects from '../projects';
|
||||||
|
|
||||||
|
import {
|
||||||
|
SESSION_TOKEN_KEY,
|
||||||
|
SESSION_USER_KEY
|
||||||
|
} from '../../const';
|
||||||
|
|
||||||
|
export default (server) => {
|
||||||
|
|
||||||
|
return {
|
||||||
|
tokenOk : tokenOk,
|
||||||
|
tokenBad: tokenBad
|
||||||
|
};
|
||||||
|
|
||||||
|
function tokenOk(token, userObj, session) {
|
||||||
|
session.reset();
|
||||||
|
session.set(SESSION_TOKEN_KEY, token);
|
||||||
|
session.set(SESSION_USER_KEY, userObj);
|
||||||
|
|
||||||
|
return Promise
|
||||||
|
.all([
|
||||||
|
userProjects(server, session, userObj),
|
||||||
|
kibanaIndex(server, userObj)
|
||||||
|
])
|
||||||
|
.then(() => {
|
||||||
|
server.log(['status', 'info', 'keystone'], `User ${userObj.user.id} authorized with keystone`);
|
||||||
|
return token;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
server.log(['status', 'info', 'keystone'],
|
||||||
|
`Error caught in process of authorization, err was ${err}`);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenBad(token, error, session) {
|
||||||
|
return new Promise((resolve)=> {
|
||||||
|
server.log(['keystone', 'error'], `Failed to authenticate token ${token} with keystone, error is ${error.statusCode}.`);
|
||||||
|
session.reset();
|
||||||
|
|
||||||
|
let err;
|
||||||
|
|
||||||
|
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',
|
||||||
|
{},
|
||||||
|
error.statusCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return resolve(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -12,10 +12,15 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Boom = require('boom');
|
import Boom from 'boom';
|
||||||
|
import {
|
||||||
|
SESSION_TOKEN_KEY,
|
||||||
|
SESSION_TOKEN_CHANGED,
|
||||||
|
TOKEN_CHANGED_VALUE
|
||||||
|
} from '../../../const';
|
||||||
|
import RELOAD_SYMBOL from '../reload';
|
||||||
|
|
||||||
/** @module */
|
const HEADER_NAME = 'x-auth-token';
|
||||||
module.exports = retrieveToken;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves token from the response header using key <b>X-Keystone-Token</b>.
|
* Retrieves token from the response header using key <b>X-Keystone-Token</b>.
|
||||||
@ -31,21 +36,21 @@ module.exports = retrieveToken;
|
|||||||
*
|
*
|
||||||
* @returns {string} current token value
|
* @returns {string} current token value
|
||||||
*/
|
*/
|
||||||
|
module.exports = (server, request) => {
|
||||||
|
|
||||||
const HEADER_NAME = 'x-auth-token';
|
if (!request.yar || request.yar === null) {
|
||||||
|
server.log(['status', 'keystone', 'error'], 'Session is not enabled');
|
||||||
function retrieveToken(server, request) {
|
|
||||||
|
|
||||||
if (!request.session || request.session === null) {
|
|
||||||
server.log(['keystone', 'error'], 'Session is not enabled');
|
|
||||||
throw new Error('Session support is missing');
|
throw new Error('Session support is missing');
|
||||||
}
|
}
|
||||||
|
|
||||||
let tokenFromSession = request.session.get('keystone_token');
|
// DEV PURPOSE ONLY
|
||||||
|
// request.yar.set(SESSION_TOKEN_KEY, 'a60e832483c34526a0c2bc3c6f8fa320');
|
||||||
|
|
||||||
|
let tokenFromSession = request.yar.get(SESSION_TOKEN_KEY);
|
||||||
let token = request.headers[HEADER_NAME];
|
let token = request.headers[HEADER_NAME];
|
||||||
|
|
||||||
if (!token && !tokenFromSession) {
|
if (!token && !tokenFromSession) {
|
||||||
server.log(['keystone', 'error'],
|
server.log(['status', 'keystone', 'error'],
|
||||||
'Token hasn\'t been located, looked in headers and session');
|
'Token hasn\'t been located, looked in headers and session');
|
||||||
return Boom.unauthorized(
|
return Boom.unauthorized(
|
||||||
'You\'re not logged into the OpenStack. Please login via Horizon Dashboard'
|
'You\'re not logged into the OpenStack. Please login via Horizon Dashboard'
|
||||||
@ -54,15 +59,28 @@ function retrieveToken(server, request) {
|
|||||||
|
|
||||||
if (!token && tokenFromSession) {
|
if (!token && tokenFromSession) {
|
||||||
token = tokenFromSession;
|
token = tokenFromSession;
|
||||||
server.log(['keystone', 'debug'],
|
server.log(['status', 'debug', 'keystone'],
|
||||||
'Token lookup status: Found token in session'
|
'Token lookup status: Found token in session'
|
||||||
);
|
);
|
||||||
} else if ((token && !tokenFromSession) || (token !== tokenFromSession)) {
|
} else if ((token && !tokenFromSession) || (token !== tokenFromSession)) {
|
||||||
server.log(['keystone', 'debug'],
|
server.log(['status', 'debug', 'keystone'],
|
||||||
'Token lookup status: Token located in header/session or token changed'
|
'Token lookup status: Token located in header/session or token changed'
|
||||||
);
|
);
|
||||||
request.session.set('keystone_token', token);
|
|
||||||
|
if ((token !== tokenFromSession) && (token && tokenFromSession)) {
|
||||||
|
server.log(['status', 'info', 'keystone'],
|
||||||
|
'Reseting session because token has changed'
|
||||||
|
);
|
||||||
|
request.yar.reset();
|
||||||
|
|
||||||
|
request.yar.set(SESSION_TOKEN_CHANGED, TOKEN_CHANGED_VALUE);
|
||||||
|
request.yar.set(SESSION_TOKEN_KEY, token);
|
||||||
|
|
||||||
|
return RELOAD_SYMBOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
request.yar.set(SESSION_TOKEN_KEY, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return request.yar.get(SESSION_TOKEN_KEY);
|
||||||
|
};
|
62
server/mt/auth/verify.js
Normal file
62
server/mt/auth/verify.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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,
|
||||||
|
SESSION_USER_KEY,
|
||||||
|
SESSION_TOKEN_CHANGED,
|
||||||
|
TOKEN_CHANGED_VALUE
|
||||||
|
} from '../../const';
|
||||||
|
import reload from './reload';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (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'
|
||||||
|
);
|
||||||
|
session.clear(SESSION_TOKEN_CHANGED);
|
||||||
|
return reply(reload.markup).type('text/html');
|
||||||
|
} else if (userObj) {
|
||||||
|
let expiresAt = new Date(userObj.expires_at).valueOf();
|
||||||
|
let now = new Date().valueOf();
|
||||||
|
let diff = now - expiresAt;
|
||||||
|
|
||||||
|
if (diff >= 0) {
|
||||||
|
session.reset();
|
||||||
|
return reply(Boom.unauthorized('User token has expired')).takeover();
|
||||||
|
} else {
|
||||||
|
return reply.continue({
|
||||||
|
credentials: userObj,
|
||||||
|
artifacts : {
|
||||||
|
projects: session.get(SESSION_PROJECTS_KEY)
|
||||||
|
},
|
||||||
|
log : {
|
||||||
|
tags: 'keystone ok'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(trebskit) should actually throw error here I guess
|
||||||
|
return reply.continue();
|
||||||
|
};
|
||||||
|
};
|
64
server/mt/index.js
Normal file
64
server/mt/index.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
bind: (server) => {
|
||||||
|
server.log(['status', 'info', 'keystone'], 'Registering keystone-auth schema');
|
||||||
|
return Promise.all([
|
||||||
|
bindAuthScheme(server),
|
||||||
|
bindExt(server),
|
||||||
|
bindRouting(server)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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*}');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindAuthScheme(server) {
|
||||||
|
return Promise.all([
|
||||||
|
server.auth.scheme(
|
||||||
|
'keystone-token',
|
||||||
|
require('./auth/scheme')
|
||||||
|
),
|
||||||
|
server.auth.strategy(
|
||||||
|
'session',
|
||||||
|
'keystone-token',
|
||||||
|
true,
|
||||||
|
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))
|
||||||
|
]);
|
||||||
|
}
|
57
server/mt/kibana/_can_upgrade.js
Normal file
57
server/mt/kibana/_can_upgrade.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
};
|
107
server/mt/kibana/_configure.js
Normal file
107
server/mt/kibana/_configure.js
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* 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')
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
60
server/mt/kibana/_create.js
Normal file
60
server/mt/kibana/_create.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
38
server/mt/kibana/_exists.js
Normal file
38
server/mt/kibana/_exists.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.status = status;
|
||||||
|
}
|
||||||
|
return es.cluster.health(opts);
|
||||||
|
}
|
||||||
|
|
39
server/mt/kibana/index.js
Normal file
39
server/mt/kibana/index.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
29
server/mt/kibana/kibanaIndex.js
Normal file
29
server/mt/kibana/kibanaIndex.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
52
server/mt/projects/index.js
Normal file
52
server/mt/projects/index.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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';
|
||||||
|
import { pick, sortBy } from 'lodash';
|
||||||
|
|
||||||
|
import { SESSION_PROJECTS_KEY, SESSION_TOKEN_KEY } from '../../const';
|
||||||
|
|
||||||
|
export default (server, session, userObj) => {
|
||||||
|
|
||||||
|
const usersApi = server.plugins['fts-keystone'].users;
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
return usersApi
|
||||||
|
.allProjects({
|
||||||
|
params : {
|
||||||
|
user_id: userObj.user.id
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'X-Auth-Token' : session.get(SESSION_TOKEN_KEY),
|
||||||
|
'X-Subject-Token': session.get(SESSION_TOKEN_KEY)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const data = response.data;
|
||||||
|
const projects = data.projects;
|
||||||
|
|
||||||
|
return sortBy(
|
||||||
|
projects.map(
|
||||||
|
project=>pick(project, ['id', 'name', 'description', 'domain_id'])
|
||||||
|
),
|
||||||
|
'name'
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then((projects) => {
|
||||||
|
session.set(SESSION_PROJECTS_KEY, projects);
|
||||||
|
return resolve(projects);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
58
server/mt/routing/_create_agent.js
Normal file
58
server/mt/routing/_create_agent.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file was copied and modified for this plugin needs.
|
||||||
|
Original file can be found here => https://github.com/elastic/kibana/blob/4.4/src/plugins/elasticsearch/lib/create_agent.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
import url from 'url';
|
||||||
|
import {memoize, size} from 'lodash';
|
||||||
|
import fs from 'fs';
|
||||||
|
import http from 'http';
|
||||||
|
import https from 'https';
|
||||||
|
|
||||||
|
const readFile = (file) => fs.readFileSync(file, 'utf8');
|
||||||
|
|
||||||
|
module.exports = memoize(function (server) {
|
||||||
|
const config = server.config();
|
||||||
|
const target = url.parse(config.get('elasticsearch.url'));
|
||||||
|
|
||||||
|
if (!/^https/.test(target.protocol)) {
|
||||||
|
return new http.Agent();
|
||||||
|
}
|
||||||
|
|
||||||
|
const agentOptions = {
|
||||||
|
rejectUnauthorized: config.get('elasticsearch.ssl.verify')
|
||||||
|
};
|
||||||
|
|
||||||
|
if (size(config.get('elasticsearch.ssl.ca'))) {
|
||||||
|
agentOptions.ca = config.get('elasticsearch.ssl.ca').map(readFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasSSLEnabled()) {
|
||||||
|
agentOptions.cert = readFile(config.get('elasticsearch.ssl.cert'));
|
||||||
|
agentOptions.key = readFile(config.get('elasticsearch.ssl.key'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new https.Agent(agentOptions);
|
||||||
|
|
||||||
|
function hasSSLEnabled() {
|
||||||
|
return config.get('elasticsearch.ssl.cert')
|
||||||
|
&& config.get('elasticsearch.ssl.key');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.cache = new Map();
|
42
server/mt/routing/_create_proxy.js
Normal file
42
server/mt/routing/_create_proxy.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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 { 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 options;
|
||||||
|
|
||||||
|
switch (route) {
|
||||||
|
case '/_mget':
|
||||||
|
options = require('./routes/mget')(server, method, path);
|
||||||
|
break;
|
||||||
|
case '/{paths*}':
|
||||||
|
options = require('./routes/paths')(server, method, path);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (route === `/${serverConfig.get('kibana.index')}/{paths*}`) {
|
||||||
|
options = require('./routes/kibana_index')(server, method, path);
|
||||||
|
} else {
|
||||||
|
options = require('./routes/default')(server, method, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.route(options);
|
||||||
|
};
|
43
server/mt/routing/_map_uri.js
Normal file
43
server/mt/routing/_map_uri.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file was copied and modified for this plugin needs.
|
||||||
|
Original file can be found here => https://github.com/elastic/kibana/blob/4.4/src/plugins/elasticsearch/lib/map_uri.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
import querystring from 'querystring';
|
||||||
|
|
||||||
|
import { PREFIX } from './_utils';
|
||||||
|
|
||||||
|
export default (server, request) => {
|
||||||
|
const config = server.config();
|
||||||
|
const path = request.path.replace(`${PREFIX}/elasticsearch`, '');
|
||||||
|
const query = querystring.stringify(request.query);
|
||||||
|
|
||||||
|
let url = config.get('elasticsearch.url');
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
if (/\/$/.test(url)) {
|
||||||
|
url = url.substring(0, url.length - 1);
|
||||||
|
}
|
||||||
|
url += path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
url += '?' + query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
27
server/mt/routing/_re_route.js
Normal file
27
server/mt/routing/_re_route.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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 utils 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}`);
|
||||||
|
}
|
||||||
|
return reply.continue();
|
||||||
|
};
|
||||||
|
};
|
70
server/mt/routing/_utils.js
Normal file
70
server/mt/routing/_utils.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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 createAgent from './_create_agent';
|
||||||
|
|
||||||
|
export const PREFIX = '/mt';
|
||||||
|
|
||||||
|
export function getOpts(server, request, url, payload) {
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
headers : {},
|
||||||
|
redirects : true,
|
||||||
|
passThrough : true,
|
||||||
|
xforward : true,
|
||||||
|
timeout : 1000 * 60 * 3,
|
||||||
|
localStatePassThrough: false,
|
||||||
|
agent : createAgent(server),
|
||||||
|
};
|
||||||
|
let protocol = url.split(':', 1)[0];
|
||||||
|
|
||||||
|
if (payload) {
|
||||||
|
options.payload = JSON.stringify(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.passThrough) {
|
||||||
|
options.headers = require('hoek').clone(request.headers);
|
||||||
|
delete options.headers.host;
|
||||||
|
if (options.acceptEncoding === false) {
|
||||||
|
delete options.headers['accept-encoding'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.xforward &&
|
||||||
|
request.info.remoteAddress &&
|
||||||
|
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-port'] = (options.headers['x-forwarded-port'] ?
|
||||||
|
options.headers['x-forwarded-port'] + ',' : '') + request.info.remotePort;
|
||||||
|
options.headers['x-forwarded-proto'] = (options.headers['x-forwarded-proto'] ?
|
||||||
|
options.headers['x-forwarded-proto'] + ',' : '') + protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType = request.headers['content-type'];
|
||||||
|
if (contentType) {
|
||||||
|
options.headers['content-type'] = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parsePayload(request) {
|
||||||
|
let payload = request.payload;
|
||||||
|
if (!payload || payload.length <= 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return JSON.parse(payload.toString('utf-8'));
|
||||||
|
}
|
23
server/mt/routing/index.js
Normal file
23
server/mt/routing/index.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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';
|
||||||
|
import reRoute from './_re_route';
|
||||||
|
|
||||||
|
module.exports = function routing(server) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
server.ext('onRequest', reRoute(server));
|
||||||
|
resolve(require('./_create_proxy'));
|
||||||
|
});
|
||||||
|
};
|
35
server/mt/routing/routes/default.js
Normal file
35
server/mt/routing/routes/default.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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 mapUri from '../_map_uri';
|
||||||
|
import createAgent from '../_create_agent';
|
||||||
|
|
||||||
|
module.exports = function defaultHandler(server, method, path) {
|
||||||
|
return {
|
||||||
|
method : method,
|
||||||
|
path : path,
|
||||||
|
handler: {
|
||||||
|
proxy: {
|
||||||
|
mapUri : (request, done) => {
|
||||||
|
server.log(['status', 'debug', 'keystone'],
|
||||||
|
`mapUri for path ${request.path}`);
|
||||||
|
done(null, mapUri(server, request));
|
||||||
|
},
|
||||||
|
agent : createAgent(server),
|
||||||
|
passThrough: true,
|
||||||
|
xforward : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
57
server/mt/routing/routes/kibana_index.js
Normal file
57
server/mt/routing/routes/kibana_index.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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 : false,
|
||||||
|
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('/');
|
||||||
|
}
|
||||||
|
}
|
69
server/mt/routing/routes/mget.js
Normal file
69
server/mt/routing/routes/mget.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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 : false,
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
66
server/mt/routing/routes/paths.js
Normal file
66
server/mt/routing/routes/paths.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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 } from '../_utils';
|
||||||
|
import kibanaIndex from '../../kibana/kibanaIndex';
|
||||||
|
import mapUri from '../_map_uri';
|
||||||
|
|
||||||
|
export default function (server, method, path) {
|
||||||
|
const defaultKibanaIndex = defaultKibanaIndex;
|
||||||
|
|
||||||
|
return {
|
||||||
|
method : method,
|
||||||
|
path : path,
|
||||||
|
config : {
|
||||||
|
tags: ['elasticsearch', 'multitenancy'],
|
||||||
|
auth: false
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
if (indexPos > -1) {
|
||||||
|
url[indexPos] = kibanaIndex(server, session[SESSION_USER_KEY]);
|
||||||
|
kibanaIndexRequest = true;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
41
server/mt/verify/_verify_index_pattern.js
Normal file
41
server/mt/verify/_verify_index_pattern.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
};
|
48
server/mt/verify/index.js
Normal file
48
server/mt/verify/index.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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';
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
@ -1,93 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Boom = require('boom');
|
|
||||||
const retrieveToken = require('./retrieveToken');
|
|
||||||
const TokensApi = require('keystone-v3-client/lib/keystone/tokens');
|
|
||||||
|
|
||||||
const util = require('../util/');
|
|
||||||
|
|
||||||
module.exports = function (server) {
|
|
||||||
const config = server.config();
|
|
||||||
const tokensApi = new TokensApi({
|
|
||||||
url: `${config.get('fts-keystone.url')}:${config.get('fts-keystone.port')}`
|
|
||||||
});
|
|
||||||
|
|
||||||
return (request, reply) => {
|
|
||||||
const requestPath = getRequestPath(request);
|
|
||||||
let token;
|
|
||||||
|
|
||||||
if (shouldCallKeystone(requestPath)) {
|
|
||||||
server.log(
|
|
||||||
['keystone', 'debug'],
|
|
||||||
`Call for ${requestPath} detected, authenticating with keystone`
|
|
||||||
);
|
|
||||||
|
|
||||||
token = retrieveToken(server, request);
|
|
||||||
if (token.isBoom) {
|
|
||||||
return reply(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokensApi
|
|
||||||
.check({
|
|
||||||
headers: {
|
|
||||||
'X-Auth-Token' : token,
|
|
||||||
'X-Subject-Token': token
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(onFulfilled, onFailed);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return reply.continue();
|
|
||||||
|
|
||||||
function onFulfilled() {
|
|
||||||
reply.continue();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onFailed(error) {
|
|
||||||
|
|
||||||
server.log(
|
|
||||||
['keystone', 'error'],
|
|
||||||
`Failed to authenticate token ${token} with keystone,
|
|
||||||
error is ${error.statusCode}.`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error.statusCode === 401) {
|
|
||||||
request.session.clear('keystone_token');
|
|
||||||
reply(Boom.forbidden(
|
|
||||||
`
|
|
||||||
You\'re not logged in as a
|
|
||||||
user who\'s authenticated to access log information
|
|
||||||
`
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
reply(Boom.internal(
|
|
||||||
error.message || 'Unexpected error during Keystone communication',
|
|
||||||
{},
|
|
||||||
error.statusCode
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function getRequestPath(request) {
|
|
||||||
return request.url.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldCallKeystone(path) {
|
|
||||||
return util.startsWith(path, '/elasticsearch');
|
|
||||||
}
|
|
@ -12,13 +12,18 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = function initSession(server) {
|
import yarCookie from 'yar';
|
||||||
|
import multiTenancy from '../mt';
|
||||||
|
|
||||||
|
export default (server) => {
|
||||||
const config = server.config();
|
const config = server.config();
|
||||||
const registerOpts = {
|
return {
|
||||||
register: require('yar'),
|
start: ()=> {
|
||||||
|
server.register({
|
||||||
|
register: yarCookie,
|
||||||
options : {
|
options : {
|
||||||
name : 'kibana_session',
|
maxCookieSize: 4096,
|
||||||
|
name : config.get('fts-keystone.cookie.name'),
|
||||||
storeBlank : false,
|
storeBlank : false,
|
||||||
cache : {
|
cache : {
|
||||||
expiresIn: config.get('fts-keystone.cookie.expiresIn')
|
expiresIn: config.get('fts-keystone.cookie.expiresIn')
|
||||||
@ -27,19 +32,19 @@ module.exports = function initSession(server) {
|
|||||||
password : config.get('fts-keystone.cookie.password'),
|
password : config.get('fts-keystone.cookie.password'),
|
||||||
isSecure : config.get('fts-keystone.cookie.isSecure'),
|
isSecure : config.get('fts-keystone.cookie.isSecure'),
|
||||||
ignoreErrors: config.get('fts-keystone.cookie.ignoreErrors'),
|
ignoreErrors: config.get('fts-keystone.cookie.ignoreErrors'),
|
||||||
clearInvalid: true
|
clearInvalid: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}, (error) => {
|
||||||
|
|
||||||
const callback = (error) => {
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
server.log(['session', 'debug'], 'Session registered');
|
server.log(['status', 'info', 'keystone'], 'Session registered');
|
||||||
|
multiTenancy.bind(server);
|
||||||
} else {
|
} else {
|
||||||
server.log(['session', 'error'], error);
|
server.log(['status', 'error', 'keystone'], error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
server.register(registerOpts, callback);
|
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
startsWith: startsWith
|
startsWith: startsWith,
|
||||||
|
requestPath: getRequestPath,
|
||||||
|
isESRequest: isESRequest
|
||||||
};
|
};
|
||||||
|
|
||||||
function startsWith(str) {
|
function startsWith(str) {
|
||||||
@ -25,3 +27,11 @@ function startsWith(str) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRequestPath(request) {
|
||||||
|
return request.url.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isESRequest(request) {
|
||||||
|
return startsWith(getRequestPath(request), '/elasticsearch');
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user