I'm trying to write a Kibana plugin that implements role based access control, in which Kibana apps represent the resources users will have permissions to use (or not).
How can I programmatically show/hide apps from the left tabs according to the presence of some HTTP headers?
/cc @Stacey-Gammon and @spalger since you were both talking about this recently.
mark
WARNING: this just hides the links from the side bar like you asked, it does not prevent users from accessing them with a direct url or prevent them from being linked to in other ways.
In your plugin, try exporting a replaceInjectedVars
function to inject vars based on the request headers:
// my_plugin/index.js
export default (kibana) => new kibana.Plugin({
uiExports: {
replaceInjectedVars(injectedVars, request) {
if (/*some condition based on request.headers */) {
injectedVars.hiddenAppIds = ['kibana:discover'];
}
return injectedVars
},
hacks: [
'my_plugin/hacks/hide_nav'
]
}
})
Then you could consume that injected vars and hide the app links with the ui/chrome
module:
// my_plugin/public/hacks/hide_nav.js
import chrome from 'ui/chrome'
const hiddenAppIds = chrome.getInjected('hiddenAppIds') || []
hiddenAppIds.forEach(id => {
chrome.getNavLinkById(id).hidden = true;
})
Cool! Thanks @spalger, this helps a lot.
So do you mean that if I wanted to prevent navigation to direct app URLs, that would be not possible from a plugin? Is there no server-side way to override the handlers of an arbitrary path a from plugin?
Is there no server-side way to override the handlers of an arbitrary path a from plugin?
You would need both a server-side and client-side hack that filtered all requests/route resolution. Client side you could potentially use a uiRoutes.addSetupWork()
or uiRoutes.defaults(/.*/)
call to add some default resolver to every route that checked that it was allowed. You would also have to do something similar server-side with an onRequest
server extension (see hapi docs).
could you show some demo code?
@sscarduzio if you get something working would you mind sharing?
@spalger sure, I'm having a taste of what's possible for now. Full explorative phase, will hack something up soon! Once again, thanks for the suggestions.
@sscarduzio great!!, your demo code is expected
I made almost everything work, quite cool, it took just a day!
Client side: I can successfully hide any app icon via the suggested 'hack'. I understood the hack files are the go-to place where to apply anything that has a global scope in the UI functionality. Very handy.
Server side: I can prevent forced navigation to independent apps (i.e. accessing timelion from a bookmark) intercepting certain paths from the suggested onRequest
hapi API, where I put this kind of logic:
hiddenApps.forEach((ha) => {
if (request.path.match('(.*/app/' + ha + '.*)|(.*/api/' + ha + '.*)')) {
forbid = true;
}
})
Unfortunately I didn't find a way to prevent forced navigation to kibana sub-apps like kibana:discover
. The navigation between kibana:discover, kibana:visualize, kibana:dev_tools, and kibana:management actually happens entirely on the client side (makes sense).
So I guess this can be handled via uiRoutes.<something>
, but I'm stuck on how to use it, I'd like to prevent navigation if the destination is an unwanted app or sub-app.
I found this example, but there's no "path" to make a conditional statement: https://github.com/elastic/kibana/blob/8b4c05288950638a7f56538c13960e6627bbcd7a/src/core_plugins/kibana/public/dashboard/index.js#L14
Suggestions on this last step?
uiRoutes
is a wrapper around the default angular router that add methods specifically for this sort of thing.
uiRoutes.addSetupWork()
will add a function that runs before each route starts working on it's resolve
functions, so this would be an appropriate place to verify that the route is allowed and call notify.error()
(to display an error notification about access controls) and kbnUrl.redirect()
(to properly abort route resolution and redirect)
yeah, cool. that was really great if anyone can give some client side code
@xycloud this is my current client side snippet (the "hack" file)
import chrome from "ui/chrome";
uiRoutes.addSetupWork(function ($http) {
$http.get('../elasticsearch/').then((response) => {
const hiddenAppIds = response.headers()['x-kibana-hide-apps']
hiddenAppIds.split(",").forEach(id => {
chrome.getNavLinkById(id.trim()).hidden = true;
})
})
})
@sscarduzio then the plugin origin url cannot be access if they are hidden from client side code?
@xycloud the above is obtaining the list of apps to hide from a header and removing the icons from the UI. I'm still doing the part where I block the navigation on the client side (I'm having some issues with the build system ATM, I lost an entire day on it because I'm not so expert with this tools).
@sscarduzio I am sorry, could you share your code if there is any movement
Hey guys,
I found a simple way in kibana 5.4.0 to hidden plugins from left panel and the plugin origin url can be accessed,
`export default function (kibana) {
return new kibana.Plugin({
require: ['elasticsearch'],
uiExports: {
app: {
title: 'Perf Step',
description: 'An awesome Kibana plugin to show step',
main: 'plugins/big_table_step/app',
icon: 'plugins/kibana/assets/icon.svg',
hidden: false,
listed: false,
disabled: true
},
translations: [
resolve(__dirname, './translations/es.json')
],
hacks: [
'plugins/sf_big_table_step/hack'
]
},
config(Joi) {
return Joi.object({
enabled: Joi.boolean().default(true),
}).default();
},
init(server, options) {
// Add server routes and initalize the plugin here
routes(server);
}
});
};
`
which will hide apps from left panel and also can be accessed.
set listed: false, disabled: true, I am using Kibana 5.4.0, hope helps!!!
@sscarduzio , where I can get your work, I am trying to get knowledge on this, thanks
finnaly, I found another way:
uiRoutes.addSetupWork(function ($http, $location, kbnUrl) {
if($location.$$path === "/dashboards---") {
let baseUrl = $location.$$absUrl;
baseUrl = baseUrl.substr(0, baseUrl.indexOf("/app"));
window.location.href=(baseUrl + "/app/myplugin#");
}
...
Linking to the work being done for Granular Application Privileges in https://github.com/elastic/kibana/issues/20277
@kobelb would feature controls (https://github.com/elastic/kibana/issues/20277) resolve this?
@alexfrancoeur Feature Controls are doing this exact behavior. The interface for doing so isn't properly documented for external consumers as the "Spaces" and "Security" plugins are the only ones doing so. However, I'd consider this request satisfied.
Hi guys, I don't know why but the Machine Learning App it's the only app that i can't hide. Does everybody have this issue? I think something on source code overrides my changes...
Most helpful comment
Hey guys,
I found a simple way in kibana 5.4.0 to hidden plugins from left panel and the plugin origin url can be accessed,
`export default function (kibana) {
return new kibana.Plugin({
require: ['elasticsearch'],
uiExports: {
});
};
`
which will hide apps from left panel and also can be accessed.
set listed: false, disabled: true, I am using Kibana 5.4.0, hope helps!!!