Fix problems with multitenancy
This commit fixes the following problems: - Kibana is not auto-detected by monasca-agent (Bug-Id: 13118) - Kibana renders error page when token expires - Keystone-token can be 'stolen' due to sharing of default session in Kibana - Some endpoints were not secured Related change for monasca-ui: https://review.openstack.org/#/c/387269/ Bug-Id: 13118 Change-Id: I2a363ebabcf593469943f7d071a92a680344ec73
This commit is contained in:
parent
aa1b40a94d
commit
4d13e5d03b
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "fts-keystone",
|
"name": "fts-keystone",
|
||||||
"version": "0.0.3",
|
"version": "0.0.4",
|
||||||
"description": "Keystone authentication & multitenancy 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",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|
|
@ -27,13 +27,22 @@ describe('plugins/fts-keystone', ()=> {
|
||||||
let server;
|
let server;
|
||||||
|
|
||||||
beforeEach(()=> {
|
beforeEach(()=> {
|
||||||
|
let configGet = sinon.stub();
|
||||||
|
configGet.withArgs('fts-keystone.cookie.name').returns('keystone');
|
||||||
server = {
|
server = {
|
||||||
log: sinon.stub()
|
log: sinon.stub(),
|
||||||
|
config: function () {
|
||||||
|
return {
|
||||||
|
get: configGet
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return isBoom if session not available', ()=> {
|
it('should return isBoom if session not available', ()=> {
|
||||||
let request = {};
|
let request = {
|
||||||
|
state: {}
|
||||||
|
};
|
||||||
let errMsg = /Session support is missing/;
|
let errMsg = /Session support is missing/;
|
||||||
|
|
||||||
chai.expect(()=> {
|
chai.expect(()=> {
|
||||||
|
@ -41,14 +50,16 @@ describe('plugins/fts-keystone', ()=> {
|
||||||
}).to.throw(errMsg);
|
}).to.throw(errMsg);
|
||||||
|
|
||||||
request = {
|
request = {
|
||||||
yar: undefined
|
yar: undefined,
|
||||||
|
state: {}
|
||||||
};
|
};
|
||||||
chai.expect(()=> {
|
chai.expect(()=> {
|
||||||
retrieveToken(server, request);
|
retrieveToken(server, request);
|
||||||
}).to.throw(errMsg);
|
}).to.throw(errMsg);
|
||||||
|
|
||||||
request = {
|
request = {
|
||||||
session: null
|
session: null,
|
||||||
|
state: {}
|
||||||
};
|
};
|
||||||
chai.expect(()=> {
|
chai.expect(()=> {
|
||||||
retrieveToken(server, request);
|
retrieveToken(server, request);
|
||||||
|
@ -62,9 +73,11 @@ describe('plugins/fts-keystone', ()=> {
|
||||||
'get': sinon
|
'get': sinon
|
||||||
.stub()
|
.stub()
|
||||||
.withArgs(CONSTANTS.SESSION_TOKEN_KEY)
|
.withArgs(CONSTANTS.SESSION_TOKEN_KEY)
|
||||||
.returns(undefined)
|
.returns(undefined),
|
||||||
|
'reset': sinon.stub()
|
||||||
},
|
},
|
||||||
headers: {}
|
headers: {},
|
||||||
|
state: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = retrieveToken(server, request);
|
let result = retrieveToken(server, request);
|
||||||
|
@ -82,7 +95,8 @@ describe('plugins/fts-keystone', ()=> {
|
||||||
};
|
};
|
||||||
let request = {
|
let request = {
|
||||||
yar : yar,
|
yar : yar,
|
||||||
headers: {}
|
headers: {},
|
||||||
|
state: {}
|
||||||
};
|
};
|
||||||
let token;
|
let token;
|
||||||
|
|
||||||
|
@ -102,7 +116,7 @@ describe('plugins/fts-keystone', ()=> {
|
||||||
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 = {
|
||||||
'reset': sinon.spy(),
|
'reset': sinon.stub(),
|
||||||
'set' : sinon.spy(),
|
'set' : sinon.spy(),
|
||||||
'get' : sinon.stub()
|
'get' : sinon.stub()
|
||||||
};
|
};
|
||||||
|
@ -110,7 +124,8 @@ describe('plugins/fts-keystone', ()=> {
|
||||||
yar : yar,
|
yar : yar,
|
||||||
headers: {
|
headers: {
|
||||||
'x-auth-token': expectedToken
|
'x-auth-token': expectedToken
|
||||||
}
|
},
|
||||||
|
state: {}
|
||||||
};
|
};
|
||||||
let token;
|
let token;
|
||||||
|
|
||||||
|
@ -158,14 +173,15 @@ describe('plugins/fts-keystone', ()=> {
|
||||||
let token;
|
let token;
|
||||||
let request = {
|
let request = {
|
||||||
yar : yar,
|
yar : yar,
|
||||||
headers: headers
|
headers: headers,
|
||||||
|
state: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
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(RELOAD_SYMBOL);
|
chai.expect(token).to.be.eql(RELOAD_SYMBOL);
|
||||||
|
|
||||||
chai.expect(yar.reset.calledOnce).to.be.ok;
|
chai.expect(yar.reset.calledTwice).to.be.ok;
|
||||||
chai.expect(yar.get.calledOnce).to.be.ok;
|
chai.expect(yar.get.calledOnce).to.be.ok;
|
||||||
|
|
||||||
chai.expect(yar.set.callCount).to.be.eq(2);
|
chai.expect(yar.set.callCount).to.be.eq(2);
|
||||||
|
|
|
@ -43,6 +43,14 @@ module.exports = (server, request) => {
|
||||||
throw new Error('Session support is missing');
|
throw new Error('Session support is missing');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is a workaround for problem with 'default' session:
|
||||||
|
// when there is no session cookie present, then yar uses default session,
|
||||||
|
// as a result many clients use the same session - security risk!
|
||||||
|
const cookieName = server.config().get('fts-keystone.cookie.name');
|
||||||
|
if (!request.state[cookieName]) {
|
||||||
|
request.yar.reset();
|
||||||
|
}
|
||||||
|
|
||||||
// DEV PURPOSE ONLY
|
// DEV PURPOSE ONLY
|
||||||
// request.yar.set(SESSION_TOKEN_KEY, 'a60e832483c34526a0c2bc3c6f8fa320');
|
// request.yar.set(SESSION_TOKEN_KEY, 'a60e832483c34526a0c2bc3c6f8fa320');
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default () => {
|
||||||
|
|
||||||
if (diff >= 0) {
|
if (diff >= 0) {
|
||||||
session.reset();
|
session.reset();
|
||||||
return reply(Boom.unauthorized('User token has expired')).takeover();
|
return reply(Boom.unauthorized('User token has expired'));
|
||||||
} else {
|
} else {
|
||||||
return reply.continue({
|
return reply.continue({
|
||||||
credentials: userObj,
|
credentials: userObj,
|
||||||
|
|
|
@ -46,7 +46,7 @@ function bindAuthScheme(server) {
|
||||||
server.auth.strategy(
|
server.auth.strategy(
|
||||||
'session',
|
'session',
|
||||||
'keystone-token',
|
'keystone-token',
|
||||||
true,
|
false,
|
||||||
require('./auth/strategy')(server)
|
require('./auth/strategy')(server)
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -27,7 +27,7 @@ export default function (server, method, path) {
|
||||||
method : method,
|
method : method,
|
||||||
path : path,
|
path : path,
|
||||||
config : {
|
config : {
|
||||||
auth : false,
|
auth : 'session',
|
||||||
payload: {
|
payload: {
|
||||||
output: 'data',
|
output: 'data',
|
||||||
parse : false
|
parse : false
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default function (server, method, path) {
|
||||||
method : method,
|
method : method,
|
||||||
path : path,
|
path : path,
|
||||||
config : {
|
config : {
|
||||||
auth : false,
|
auth : 'session',
|
||||||
payload: {
|
payload: {
|
||||||
output: 'data',
|
output: 'data',
|
||||||
parse : false
|
parse : false
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Wreck from 'wreck';
|
import Wreck from 'wreck';
|
||||||
|
import Boom from 'boom';
|
||||||
|
|
||||||
import { SESSION_USER_KEY } from '../../../const';
|
import { SESSION_USER_KEY } from '../../../const';
|
||||||
import { getOpts } from '../_utils';
|
import { getOpts } from '../_utils';
|
||||||
|
@ -20,14 +21,15 @@ import kibanaIndex from '../../kibana/kibanaIndex';
|
||||||
import mapUri from '../_map_uri';
|
import mapUri from '../_map_uri';
|
||||||
|
|
||||||
export default function (server, method, path) {
|
export default function (server, method, path) {
|
||||||
const defaultKibanaIndex = defaultKibanaIndex;
|
const defaultKibanaIndex = server.config().get('kibana.index');
|
||||||
|
const logIndexPostionInUrl = 3;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
method : method,
|
method : method,
|
||||||
path : path,
|
path : path,
|
||||||
config : {
|
config : {
|
||||||
tags: ['elasticsearch', 'multitenancy'],
|
tags: ['elasticsearch', 'multitenancy'],
|
||||||
auth: false
|
auth: 'session'
|
||||||
},
|
},
|
||||||
handler: handler
|
handler: handler
|
||||||
};
|
};
|
||||||
|
@ -42,7 +44,11 @@ export default function (server, method, path) {
|
||||||
if (indexPos > -1) {
|
if (indexPos > -1) {
|
||||||
url[indexPos] = kibanaIndex(server, session[SESSION_USER_KEY]);
|
url[indexPos] = kibanaIndex(server, session[SESSION_USER_KEY]);
|
||||||
kibanaIndexRequest = true;
|
kibanaIndexRequest = true;
|
||||||
|
} else if (url.length > logIndexPostionInUrl
|
||||||
|
&& !url[logIndexPostionInUrl].startsWith(session[SESSION_USER_KEY].project.id)) {
|
||||||
|
return reply(Boom.unauthorized('User does not have access to this resource'));
|
||||||
}
|
}
|
||||||
|
|
||||||
url = url.join('/');
|
url = url.join('/');
|
||||||
|
|
||||||
const opts = getOpts(server, request, url);
|
const opts = getOpts(server, request, url);
|
||||||
|
|
Loading…
Reference in New Issue