Commit dc8624e8 authored by Jochen Kressin's avatar Jochen Kressin
Browse files

disable default and global tenant, closes #1

parent c648acc3
......@@ -66,7 +66,9 @@ export default function (kibana) {
title: 'Tenants',
main: 'plugins/searchguard/apps/multitenancy',
hidden: false,
auth: true
auth: true,
order: 9010,
icon: 'plugins/searchguard/assets/networking.svg',
}
],
chromeNavControls: [
......@@ -139,15 +141,30 @@ export default function (kibana) {
require('./lib/multitenancy/routes')(pluginRoot, server, this, APP_ROOT, API_ROOT);
require('./lib/multitenancy/headers')(pluginRoot, server, this, APP_ROOT, API_ROOT);
server.state('searchguard_preferences', {
ttl: 2217100485000,
path: '/',
isSecure: false,
isHttpOnly: false,
clearInvalid: true, // remove invalid cookies
strictHeader: true, // don't allow violations of RFC 6265
encoding: 'iron',
password: config.get("searchguard.cookie.password")
});
server.state('searchguard_tenant', {
ttl: null,
path: '/',
isSecure: false,
isHttpOnly: false,
clearInvalid: true, // remove invalid cookies
strictHeader: true // don't allow violations of RFC 6265
strictHeader: true, // don't allow violations of RFC 6265
encoding: 'iron',
password: config.get("searchguard.cookie.password")
});
this.status.yellow("Search Guard multitenancy enabled");
} else {
this.status.yellow("Search Guard multitenancy disabled");
......
......@@ -57,14 +57,31 @@ module.exports = function (pluginRoot, server, kbnServer, APP_ROOT, API_ROOT) {
session.expiryTime = Date.now() + sessionTTL;
}
request.auth.session.set(session);
return reply({
username: user.username,
tenants: user.tenants
});
// handle tenants if MT is enabled
if(server.config().get("searchguard.multitenancy.enabled")) {
// get the preferred tenant of the user
let globalTenantEnabled = server.config().get("searchguard.multitenancy.tenants.enable_global");
let privateTenantEnabled = server.config().get("searchguard.multitenancy.tenants.enable_private");
let preferredTenant = server.plugins.searchguard.getSearchGuardBackend().getTenantByPreference(request, user.username, user.tenants, globalTenantEnabled, privateTenantEnabled);
return reply({
username: user.username,
tenants: user.tenants
}).state('searchguard_tenant', preferredTenant);
} else {
// no MT, nothing more to do
return reply({
username: user.username,
tenants: user.tenants
});
}
} catch (error) {
if (error instanceof AuthenticationError) {
return reply(Boom.unauthorized(error.message));
} else {
console.log(error);
return reply(Boom.badImplementation());
}
}
......
......@@ -74,4 +74,58 @@ export default class SearchGuardBackend {
};
}
updateAndGetTenantPreferences(request, user, tenant) {
var prefs = request.state.searchguard_preferences;
// no prefs cookie present
if (!prefs) {
var newPrefs = {};
newPrefs[user] = tenant;
return newPrefs;
}
prefs[user] = tenant;
return prefs;
}
getTenantByPreference(request, username, tenants, globalEnabled, privateEnabled) {
// delete user from tenants first to check if we have a tenant to choose from at all
// keep original preferences untouched, we need the original values again
// http://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object
var tenantsCopy = JSON.parse(JSON.stringify(tenants));
delete tenantsCopy[username];
// sanity check
if (!globalEnabled && !privateEnabled && _.isEmpty(tenantsCopy)) {
throw new AuthenticationError("No tenant available.");
}
// get users preferred tenant
var prefs = request.state.searchguard_preferences;
var preferredTenant = prefs[username];
console.log("Tenenat for user "+ username + " is " + preferredTenant);
// user has a preferred tenant, check if it is accessible
if (preferredTenant && tenants[preferredTenant]) {
return preferredTenant;
}
// special case: in tenants returned from SG, the private tenant is
// the username of the logged in user, but the header value is __user__
if (preferredTenant == "__user__" && tenants[username] && privateEnabled) {
return "__user__";
}
// no preference, or tenant no accessible anymore, choose either global or private
if (globalEnabled) {
return "";
}
if (privateEnabled) {
return "__user__";
}
// this point can be reached if global and private are disabled,
// and the preferred tenant is not accessible anymore.
throw new AuthenticationError("No tenant available, preferred tenant has probably been removed.");
}
}
......@@ -23,12 +23,16 @@ module.exports = function (pluginRoot, server, kbnServer, APP_ROOT, API_ROOT) {
const config = server.config();
const sessionTTL = config.get('searchguard.session.ttl');
const loginApp = kbnServer.apps.byId['searchguard-login'];
const backend = server.plugins.searchguard.getSearchGuardBackend();
server.route({
method: 'POST',
path: `${API_ROOT}/v1/tenant`,
handler: (request, reply) => {
return reply(request.payload.tenant).state('searchguard_tenant', request.payload.tenant);
var username = request.payload.username;
var selectedTenant = request.payload.tenant;
var prefs = backend.updateAndGetTenantPreferences(request, username, selectedTenant);
return reply(request.payload.tenant).state('searchguard_tenant', selectedTenant).state('searchguard_preferences', prefs);
}
});
......
......@@ -64,7 +64,6 @@ export default function LoginController($scope, $http, $window) {
if (allTenants == null || allTenants.length == 0 || _.isEmpty(allTenants)) {
this.errorMessage = 'No tenant available for this user, please contact your system administrator.';
} else {
// todo: choose default tenant based on user preferences and available tenants
$window.location.href = `${ROOT}/`;
}
}
......
<div class="container">
<div class="row">
<div class="col-xs-12">
<h3 class="text-center">Available tenants</h3>
<p class="error-message" ng-if="ctrl.errorMessage">{{ ctrl.errorMessage }}</p>
<h4 class="text-center" ng-bind="ctrl.tenantLabel" style="margin-bottom:30px;"></h4>
<div class="searchguard app-container ng-scope">
<kbn-top-nav name="searchguard" config="topNavMenu">
<div class="kuiLocalNav">
<div class="kuiLocalNavRow"></div>
<div class="kuiLocalNavRow kuiLocalNavRow--secondary">
<div data-transclude-slot="bottomRow" class="kuiLocalTabs">
<span class="kuiLocalTab">
Tenants
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Permissions</th>
<th style="text-align: right">Action</th>
</tr>
</thead>
<tbody>
<tr ng-class="{'selected': ctrl.currentTenant == ''}" ng-show="ctrl.globalEnabled">
<td>
{{ctrl.GLOBAL_USER_LABEL}}
</td>
<td>
<div>read/write</div>
</td>
<td class="actions" style="text-align: right">
<button class="btn-primary" ng-disabled="ctrl.currentTenant == ''"
ng-show="ctrl.currentTenant != ctrl.GLOBAL_USER_VALUE"
ng-click="ctrl.selectTenant(ctrl.GLOBAL_USER_LABEL, ctrl.GLOBAL_USER_VALUE)">Select
</button>
</td>
</tr>
<tr ng-class="{'selected': ctrl.currentTenant == ctrl.PRIVATE_USER_VALUE}" ng-show="ctrl.privateEnabled">
<td>
{{ctrl.PRIVATE_USER_LABEL}}
</td>
<td>
<div>read/write</div>
</td>
<td class="actions" style="text-align: right">
<button class="btn-primary" ng-disabled="ctrl.currentTenant == ctrl.PRIVATE_USER_VALUE"
ng-click="ctrl.selectTenant(ctrl.PRIVATE_USER_LABEL, ctrl.PRIVATE_USER_VALUE)">Select
</button>
</td>
</tr>
</kbn-top-nav>
<div class="container">
<div class="row">
<div class="col-xs-12">
<h3 class="text-center">Available tenants</h3>
<p class="error-message" ng-if="ctrl.errorMessage">{{ ctrl.errorMessage }}</p>
<h4 class="text-center" ng-bind="ctrl.tenantLabel" style="margin-bottom:30px;"></h4>
</div>
</div>
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Permissions</th>
<th style="text-align: right">Action</th>
</tr>
</thead>
<tbody>
<tr ng-class="{'selected': ctrl.currentTenant == ''}" ng-show="ctrl.globalEnabled">
<td>
{{ctrl.GLOBAL_USER_LABEL}}
</td>
<td>
<div>read/write</div>
</td>
<td class="actions" style="text-align: right">
<button class="btn-primary" ng-disabled="ctrl.currentTenant == ''"
ng-show="ctrl.currentTenant != ctrl.GLOBAL_USER_VALUE"
ng-click="ctrl.selectTenant(ctrl.GLOBAL_USER_LABEL, ctrl.GLOBAL_USER_VALUE)">Select
</button>
</td>
</tr>
<tr ng-class="{'selected': ctrl.currentTenant == ctrl.PRIVATE_USER_VALUE}" ng-show="ctrl.privateEnabled">
<td>
{{ctrl.PRIVATE_USER_LABEL}}
</td>
<td>
<div>read/write</div>
</td>
<td class="actions" style="text-align: right">
<button class="btn-primary" ng-disabled="ctrl.currentTenant == ctrl.PRIVATE_USER_VALUE"
ng-click="ctrl.selectTenant(ctrl.PRIVATE_USER_LABEL, ctrl.PRIVATE_USER_VALUE)">Select
</button>
</td>
</tr>
<tr ng-repeat="tenantkey in ctrl.tenantkeys" ng-class="{'selected': tenantkey == ctrl.currentTenant}">
<td>
{{tenantkey}}
</td>
<td>
<div ng-if="ctrl.allTenants[tenantkey] == 'rw'">read/write</div>
<div ng-if="ctrl.allTenants[tenantkey] == 'ro'">read only</div>
</td>
<td class="actions" style="text-align: right">
<button class="btn-primary" ng-disabled="ctrl.currentTenant == tenantkey"
ng-click="ctrl.selectTenant(tenantkey, tenantkey)">Select
</button>
</td>
</tr>
</tbody>
</table>
<tr ng-repeat="tenantkey in ctrl.tenantkeys" ng-class="{'selected': tenantkey == ctrl.currentTenant}">
<td>
{{tenantkey}}
</td>
<td>
<div ng-if="ctrl.allTenants[tenantkey] == 'rw'">read/write</div>
<div ng-if="ctrl.allTenants[tenantkey] == 'ro'">read only</div>
</td>
<td class="actions" style="text-align: right">
<button class="btn-primary" ng-disabled="ctrl.currentTenant == tenantkey"
ng-click="ctrl.selectTenant(tenantkey, tenantkey)">Select
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
......@@ -17,6 +17,7 @@
import chrome from 'ui/chrome';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import Notifier from 'ui/notify/notifier';
import 'ui/autoload/styles';
import 'plugins/searchguard/apps/multitenancy/multitenancy.less';
......@@ -37,6 +38,7 @@ uiModules
var APP_ROOT = `${chrome.getBasePath()}/searchguard`;
var API_ROOT = `${APP_ROOT}/api/v1`;
let notify = new Notifier({});
this.privateEnabled = chrome.getInjected("multitenancy.tenants.enable_private");
this.globalEnabled = chrome.getInjected("multitenancy.tenants.enable_global");
......@@ -95,6 +97,7 @@ uiModules
chrome.getNavLinkById("kibana:discover").lastSubUrl = chrome.getNavLinkById("kibana:discover").url;
chrome.getNavLinkById("timelion").lastSubUrl = chrome.getNavLinkById("timelion").url;
sessionStorage.clear();
notify.info("Tenant changed");
},
(error) => notify.error(error)
);
......
......@@ -23,12 +23,3 @@
border-top-color: #5e9b24;
color: #ffffff;
}
//.error-message {
// color: @state-danger-bg;
// text-align: center;
//
// margin-top: @form-group-margin-bottom;
// margin-bottom: 0;
//}
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.1" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="40" height="40"
viewBox="177.609 177.609 40 40" overflow="visible" enable-background="new 177.609 177.609 40 40" xml:space="preserve">
<g>
<circle fill="#FFFFFF" cx="198.692" cy="185.625" r="7.016"/>
<path fill="#FFFFFF" d="M203.655,195.555L203.655,195.555c-1.713,0.783-3.589,1.093-5.612,1.093s-3.899-0.473-5.612-1.093l0,0l0,0
c-5.466,0-9.822,4.373-9.822,9.821v11.699c0,0.311,0.31,0.621,0.62,0.621h29.646c0.312,0,0.621-0.311,0.621-0.621v-11.699
C213.478,199.912,209.123,195.555,203.655,195.555z"/>
</g>
</svg>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment