Javamelody: Protect monitoring url based on pugin own capability

Change-Id: I12c7efd287a9a6c73ad38d9ec31e527b9201977b
diff --git a/src/main/java/com/googlesource/gerrit/plugins/javamelody/GerritMonitoringFilter.java b/src/main/java/com/googlesource/gerrit/plugins/javamelody/GerritMonitoringFilter.java
index 8139caf..4bbdedb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/javamelody/GerritMonitoringFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/javamelody/GerritMonitoringFilter.java
@@ -21,26 +21,52 @@
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import net.bull.javamelody.MonitoringFilter;
 
+import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.httpd.AllRequestFilter;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.CapabilityControl;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
 @Singleton
 class GerritMonitoringFilter extends AllRequestFilter {
-  private final MonitoringFilter monitoring;
+  private final JavamelodyFilter monitoring;
+  private final Provider<CurrentUser> userProvider;
+  private final String pluginName;
 
   @Inject
-  GerritMonitoringFilter(MonitoringFilter monitoring) {
+  GerritMonitoringFilter(JavamelodyFilter monitoring,
+      Provider<CurrentUser> userProvider,
+      @PluginName String pluginName) {
     this.monitoring = monitoring;
+    this.userProvider = userProvider;
+    this.pluginName = pluginName;
   }
 
   @Override
   public void doFilter(ServletRequest request, ServletResponse response,
       FilterChain chain) throws IOException, ServletException {
-    monitoring.doFilter(request, response, chain);
+    if (!(request instanceof HttpServletRequest)
+        || !(response instanceof HttpServletResponse)) {
+      chain.doFilter(request, response);
+      return;
+    }
+
+    HttpServletResponse httpResponse = (HttpServletResponse) response;
+    HttpServletRequest httpRequest = (HttpServletRequest) request;
+
+    if (canMonitor(httpRequest)) {
+      monitoring.doFilter(request, response, chain);
+    } else {
+      httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN,
+          "Forbidden access");
+    }
   }
 
   @Override
@@ -52,4 +78,24 @@
   public void destroy() {
     monitoring.destroy();
   }
+
+  private boolean canMonitor(HttpServletRequest httpRequest) {
+    if (httpRequest.getRequestURI().equals(monitoring
+        .getJavamelodyUrl(httpRequest))) {
+      if (userProvider.get().isIdentifiedUser()) {
+        CapabilityControl ctl = userProvider.get().getCapabilities();
+        return ctl.canAdministrateServer()
+            || ctl.canPerform(String.format("%s-%s",
+               pluginName, MonitoringCapability.ID));
+      }
+      return false;
+    }
+    return true;
+  }
+
+  static class JavamelodyFilter extends MonitoringFilter {
+    public String getJavamelodyUrl(HttpServletRequest httpRequest) {
+      return getMonitoringUrl(httpRequest);
+    }
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/javamelody/Module.java b/src/main/java/com/googlesource/gerrit/plugins/javamelody/Module.java
index f6b6026..e300143 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/javamelody/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/javamelody/Module.java
@@ -14,6 +14,8 @@
 
 package com.googlesource.gerrit.plugins.javamelody;
 
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.config.CapabilityDefinition;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.webui.TopMenu;
 import com.google.inject.AbstractModule;
@@ -22,7 +24,10 @@
 
   @Override
   protected void configure() {
+    bind(CapabilityDefinition.class)
+      .annotatedWith(Exports.named(MonitoringCapability.ID))
+      .to(MonitoringCapability.class);
     DynamicSet.bind(binder(), TopMenu.class)
-        .to(MonitoringTopMenu.class);
+      .to(MonitoringTopMenu.class);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/javamelody/MonitoringCapability.java b/src/main/java/com/googlesource/gerrit/plugins/javamelody/MonitoringCapability.java
new file mode 100644
index 0000000..98c32f0
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/javamelody/MonitoringCapability.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.javamelody;
+
+import com.google.gerrit.extensions.config.CapabilityDefinition;
+
+public class MonitoringCapability extends CapabilityDefinition {
+  static final String ID = "monitoring";
+
+  @Override
+  public String getDescription() {
+    return "Javamelody Monitoring";
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/javamelody/MonitoringTopMenu.java b/src/main/java/com/googlesource/gerrit/plugins/javamelody/MonitoringTopMenu.java
index abad806..59510d7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/javamelody/MonitoringTopMenu.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/javamelody/MonitoringTopMenu.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2014 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -18,18 +18,25 @@
 import java.util.List;
 
 import com.google.common.collect.Lists;
+import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.webui.TopMenu;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.CapabilityControl;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
 public class MonitoringTopMenu implements TopMenu {
   private final List<MenuEntry> menuEntries;
+  private final Provider<CurrentUser> userProvider;
+  private final String pluginName;
 
   @Inject
-  public MonitoringTopMenu(Provider<CurrentUser> u) {
+  public MonitoringTopMenu(Provider<CurrentUser> userProvider,
+      @PluginName String pluginName) {
+    this.userProvider = userProvider;
+    this.pluginName = pluginName;
     menuEntries = Lists.newArrayList();
-    if (u.get().getCapabilities().canAdministrateServer()) {
+    if (canMonitor()) {
       menuEntries.add(new MenuEntry("Monitoring", Collections
           .singletonList(new MenuItem("JavaMelody", "monitoring"))));
     }
@@ -39,4 +46,14 @@
   public List<MenuEntry> getEntries() {
     return menuEntries;
   }
+
+  private boolean canMonitor() {
+    if (userProvider.get().isIdentifiedUser()) {
+      CapabilityControl ctl = userProvider.get().getCapabilities();
+      return ctl.canAdministrateServer()
+          || ctl.canPerform(String.format("%s-%s",
+             pluginName, MonitoringCapability.ID));
+    }
+    return false;
+  }
 }
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index d242c6a..f7326e9 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -3,5 +3,8 @@
 This plugin integrates [JavaMelody](https://code.google.com/p/javamelody) in
 Gerrit in order to retrieve live instrumentation data from Gerrit.
 
-It adds top menu item "Monitoring" that is only accessible to administratos
-to access java melody page.
+To access the monitoring URL a user must be a member of a group that is
+granted the 'Javamelody Monitoring' capability (provided by this plugin)
+or the 'Administrate Server' capability.
+
+It adds top menu item "Monitoring" to access java melody page.