Initial commit of kibana security plugin

Original commit: elastic/x-pack-elasticsearch@bb6a86866c
This commit is contained in:
Lukas Olson 2015-11-06 16:53:11 -07:00
parent 28326a2c4c
commit b4aa90b104
10 changed files with 268 additions and 0 deletions

20
shield/kibana/.gitignore vendored Normal file

@ -0,0 +1,20 @@
work
.idea
agent/logs
agent/data
agent/target
agent/.project
agent/.classpath
agent/.settings
agent/config
agent/lib
agent/.local-execution-hints.log
.DS_Store
*.iml
*.log
node_modules
esvm
build
.aws-config.json
html_docs
target

63
shield/kibana/index.js Normal file

@ -0,0 +1,63 @@
const _ = require('lodash');
const hapiAuthCookie = require('hapi-auth-cookie');
const getAuthHeader = require('./server/lib/get_auth_header');
module.exports = (kibana) => new kibana.Plugin({
name: 'security',
require: ['elasticsearch'],
config(Joi) {
return Joi.object({
enabled: Joi.boolean().default(true),
encryptionKey: Joi.string().default('secret'),
sessionTimeout: Joi.number().default(30 * 60 * 1000)
}).default()
},
uiExports: {
apps: [{
id: 'login',
title: 'Login',
main: 'plugins/security/login',
hidden: true,
autoload: kibana.autoload.styles
}, {
id: 'logout',
title: 'Logout',
main: 'plugins/security/login/logout',
hidden: false,
autoload: kibana.autoload.styles
}]
},
init(server, options) {
const isValidUser = require('./server/lib/is_valid_user')(server.plugins.elasticsearch.client);
const config = server.config();
server.register(hapiAuthCookie, (error) => {
if (error != null) throw error;
server.auth.strategy('session', 'cookie', 'required', {
cookie: 'sid',
password: config.get('security.encryptionKey'),
ttl: config.get('security.sessionTimeout'),
clearInvalid: true,
keepAlive: true,
isSecure: false, // TODO: Remove this
redirectTo: '/login',
validateFunc(request, session, callback) {
const {username, password} = session;
return isValidUser(username, password).then(() => {
_.assign(request.headers, getAuthHeader(username, password));
return callback(null, true);
}, (error) => {
return callback(error, false);
});
}
});
});
require('./server/routes/authentication')(server, this);
}
});

@ -0,0 +1,25 @@
{
"author": {
"name": "Elasticsearch",
"company": "Elasticsearch BV"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"precommit": "gulp lint"
},
"name": "security",
"version": "0.0.0",
"repository": {
"type": "git",
"url": "http://github.com/elastic/x-plugins"
},
"devDependencies": {},
"dependencies": {
"bluebird": "^3.0.0",
"boom": "^2.10.0",
"hapi": "^11.0.2",
"hapi-auth-cookie": "^3.1.0",
"joi": "^6.9.1",
"lodash": "^3.10.1"
}
}

@ -0,0 +1,17 @@
<div class="container">
<h1><img src="{{login.kibanaLogoUrl}}" /></h1>
<form id="login-form" ng-submit="login.submit(username, password)" class="animated infinite bounce">
<div class="form-group inner-addon left-addon">
<i class="fa fa-user fa-lg fa-fw"></i>
<input type="text" ng-model="username" class="form-control" id="username" name="username" placeholder="Username" />
</div>
<div class="form-group inner-addon left-addon">
<i class="fa fa-lock fa-lg fa-fw"></i>
<input type="password" ng-model="password" class="form-control" id="password" name="password" placeholder="Password" />
</div>
<div ng-show="login.error" class="form-group has-error">Oops! That is an invalid username/password combination.</div>
<button type="submit" ng-disabled="!username || !password" class="btn btn-default login">LOG IN</button>
<!--<span ng-show="login.loading" class="fa fa-spinner fa-spin"></span>-->
</form>
</div>

@ -0,0 +1,27 @@
require('plugins/security/login/login.less');
const kibanaLogoUrl = require('ui/images/kibana-transparent-white.svg');
require('ui/chrome')
.setVisible(false)
.setRootTemplate(require('plugins/security/login/login.html'))
.setRootController('login', ($http) => {
var login = {
loading: false,
kibanaLogoUrl
};
login.submit = (username, password) => {
login.loading = true;
$http.post('/login', {
username: username,
password: password
}).then(
(response) => window.location.href = '/', // TODO: Redirect more intelligently
(error) => login.error = true
).finally(() => login.loading = false);
};
return login;
});

@ -0,0 +1,54 @@
body {
background: #222222;
}
.inner-addon {
position: relative;
}
.inner-addon .fa {
position: absolute;
padding: 10px;
pointer-events: none;
color: #A2A4AC;
}
.left-addon .fa { left: 0px;}
.right-addon .fa { right: 0px;}
.left-addon input { padding-left: 30px !important; }
.right-addon input { padding-right: 30px !important; }
.container {
width: 350px;
margin: auto;
text-align: center;
h1, button {
margin: 1em;
}
}
.has-error {
color: red;
}
.login {
background-color: #94C63D;
color: white;
width: 200px;
font-size: 1.5em;
border: none;
&:disabled {
background-color: #44532A;
color: #636464;
}
}
input.form-control {
border: none;
font-size: 1.25em;
height: auto;
padding: 0.5em;
}

@ -0,0 +1,4 @@
module.exports = (username, password) => {
const auth = new Buffer(`${username}:${password}`, 'utf8').toString('base64');
return {'Authorization': `Basic ${auth}`};
};

@ -0,0 +1,5 @@
const getAuthHeader = require('./get_auth_header');
module.exports = (client) => (username, password) => client.info({
headers: getAuthHeader(username, password)
});

@ -0,0 +1,53 @@
const Boom = require('boom');
const Joi = require('joi');
module.exports = (server, uiExports) => {
const login = uiExports.apps.byId.login;
const isValidUser = require('../lib/is_valid_user')(server.plugins.elasticsearch.client);
server.route({
method: 'GET',
path: '/login',
handler(request, reply) {
return reply.renderApp(login);
},
config: {
auth: false
}
});
server.route({
method: 'POST',
path: '/login',
handler(request, reply) {
return isValidUser(request.payload.username, request.payload.password).then(() => {
request.auth.session.set({username: request.payload.username, password: request.payload.password});
return reply({
statusCode: 200,
payload: 'success'
});
}, (error) => {
request.auth.session.clear();
return reply(Boom.unauthorized(error));
})
},
config: {
auth: false,
validate: {
payload: {
username: Joi.string().required(),
password: Joi.string().required()
}
}
}
});
server.route({
method: 'GET',
path: '/app/logout', // TODO: Change to /logout
handler(request, reply) {
request.auth.session.clear();
return reply.redirect('/');
}
});
};