我完全理解并接受 Torkel 和 Grafana 团队决定在前端不使用只读模式,因为“邪恶”用户可以轻松地在其周围查询后端,因此从安全角度来看,他们是正确的。
但正如您所见,一些边缘案例/项目需要这样做,即使只是视觉上的怪癖。
警告:为了确保你明白,这是 grafana-web 的视觉样式,不提供任何安全性,“邪恶”用户仍然可以访问所有内容。
以下是我的实现方式:
- 所有用户在 Grafana 中都有编辑权限(按钮仅在前端隐藏)
- 将 grafana-web 加载到 iframe 中
- iframe 要么有一个遮罩(也可以移动 iframe 我们的视图或使其完全透明)
- 在 iframe DOMContentLoaded 处理程序上,我们在 iframe 文档上注册一个 MutationObserver 以在按钮添加到 DOM 时捕获和隐藏按钮,使用 Rafael Weinstein 的惊人的 Mutation Summary 库。
- 在这个阶段隐藏按钮是否已经渲染,以防 Observer 注册到较晚(这是对 Angular 渲染的竞争条件)
- 移除遮罩(将 iframe 移动到可见区域,制作 opac ...)
这是由 DOMContentLoaded 触发的定制器代码:
// Don't forget to load the mutation-summary.js lib
function iframeLoad (iframe) {
// Disable this if you want users to have access to playground buttons like:
// add Rows, edit Panels, dashboard settings ...
readOnlyMode = true;
// This is the iframe "window"
var iframe_window = iframe.contentWindow;
// This is the iframe "document" under which the MutationObserver will look for DOM changes
var iframe_document = iframe.contentDocument;
var queries = [{
// This is the main menu of grafana
element: '.navbar-brand-btn'
},{
// This is the dashboard selection right of the main menu
element: '.navbar-page-btn'
},{
// This is the share button appearing inside the .dashnav-action-icons, we don't want to allow
// this to anybody, as it's exposes the real url, thus bypassing this code
element: 'li[ng-show="::dashboardMeta.canShare"]'
},{
// This is the dashboard delete button, under dashboard setting button
element: 'a[ng-click="deleteDashboard();"]'
}];
if ( readOnlyMode ) {
queries.push({
// This is the three vertical dots button to open the row menu/edit
element: '.dash-row-menu-grip'
});
queries.push({
// This is the bottom "+ ADD ROW" button
element: '.add-row-panel-hint'
});
queries.push({
// This is the share button right of the dashboard menu
element: '.dashnav-action-icons'
});
queries.push({
// This is the "Panel" menu triggered by clicking the Panel name
element: '.panel-menu'
});
}
var observer;
observer = new MutationSummary({
callback: function (changes) {
changes.forEach(function (change) {
change.added.forEach(function (el) {
iframe_window.angular.element(el).addClass('ng-hide');
});
});
// Normally we disconnect here to free resources, but on new dashboards
// the buttons will be re-rendered by Angular, so we keep this to block that behaviour
//observer.disconnect();
},
queries: queries,
rootNode: iframe_document
});
// Hide the elements if they are already generated before we registred the Observer
// is a race condition afterall "Angular rendering" vs "registering the Observer"
queries.forEach( function (el) {
if ( iframe_window && iframe_window.angular ) {
iframe_window.angular.element(el.element).addClass('ng-hide');
}
});
// Remove the mask or move the iframe into view if needed
// [YOUR CODE HERE]
}
此方法已针对 grafana 版本 4.0.0-1476697633pre1(以及当前在 play.grafana.org 中运行的版本)进行了测试