Introduce StreamEvents capability

This change introduces new global capability for streaming
Gerrit events.

Only admins and users with this capability are able to run
gerrit stream-events.

Change-Id: I0a8a110758b0ac43969e5b8ae799b9b89db318e2
Signed-off-by: Ed Bartosh <bartosh@gmail.com>
Signed-off-by: Alexander Kanevskiy <kad@blackcatlinux.com>
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 9ef6e13..306dee7 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -1166,6 +1166,15 @@
 replication plugin is installed on the server.
 
 
+[[capability_streamEvents]]
+Stream Events
+~~~~~~~~~~~~~
+
+Allow performing streaming of Gerrit events. This capability
+allows the granted group to
+link:cmd-stream-events.html[stream Gerrit events via ssh].
+
+
 [[capability_viewCaches]]
 View Caches
 ~~~~~~~~~~~
diff --git a/Documentation/cmd-stream-events.txt b/Documentation/cmd-stream-events.txt
index ce23da6..6da0ef0 100644
--- a/Documentation/cmd-stream-events.txt
+++ b/Documentation/cmd-stream-events.txt
@@ -23,7 +23,9 @@
 
 ACCESS
 ------
-Any user who has configured an SSH key.
+Caller must be a member of the privileged 'Administrators' group,
+or have been granted
+link:access-control.html#capability_streamEvents[the 'Stream Events' global capability].
 
 SCRIPTING
 ---------
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
index 7db691d..8c08feb 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
@@ -73,6 +73,9 @@
   /** Forcefully restart replication to any configured destination. */
   public static final String START_REPLICATION = "startReplication";
 
+  /** Can perform streaming of Gerrit events. */
+  public static final String STREAM_EVENTS = "streamEvents";
+
   /** Can view the server's current cache states. */
   public static final String VIEW_CACHES = "viewCaches";
 
@@ -99,6 +102,7 @@
     NAMES_ALL.add(QUERY_LIMIT);
     NAMES_ALL.add(RUN_GC);
     NAMES_ALL.add(START_REPLICATION);
+    NAMES_ALL.add(STREAM_EVENTS);
     NAMES_ALL.add(VIEW_CACHES);
     NAMES_ALL.add(VIEW_CONNECTIONS);
     NAMES_ALL.add(VIEW_QUEUE);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index 1637919..ce27780 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -158,6 +158,7 @@
   queryLimit, \
   runGC, \
   startReplication, \
+  streamEvents, \
   viewCaches, \
   viewConnections, \
   viewQueue
@@ -173,6 +174,7 @@
 queryLimit = Query Limit
 runGC = Run Garbage Collection
 startReplication = Start Replication
+streamEvents = Stream Events
 viewCaches = View Caches
 viewConnections = View Connections
 viewQueue = View Queue
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
index 942b0d7..d2014ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
@@ -142,6 +142,12 @@
         || canAdministrateServer();
   }
 
+  /** @return true if the user can stream Gerrit events. */
+  public boolean canStreamEvents() {
+    return canPerform(GlobalCapability.STREAM_EVENTS)
+        || canAdministrateServer();
+  }
+
   /** @return true if the user can run the Git garbage collection. */
   public boolean canRunGC() {
     return canPerform(GlobalCapability.RUN_GC)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
index 196ddee..54f1980 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
@@ -24,6 +24,7 @@
 import static com.google.gerrit.common.data.GlobalCapability.PRIORITY;
 import static com.google.gerrit.common.data.GlobalCapability.RUN_GC;
 import static com.google.gerrit.common.data.GlobalCapability.START_REPLICATION;
+import static com.google.gerrit.common.data.GlobalCapability.STREAM_EVENTS;
 import static com.google.gerrit.common.data.GlobalCapability.VIEW_CACHES;
 import static com.google.gerrit.common.data.GlobalCapability.VIEW_CONNECTIONS;
 import static com.google.gerrit.common.data.GlobalCapability.VIEW_QUEUE;
@@ -104,6 +105,7 @@
     have.put(VIEW_QUEUE, cc.canViewQueue());
     have.put(RUN_GC, cc.canRunGC());
     have.put(START_REPLICATION, cc.canStartReplication());
+    have.put(STREAM_EVENTS, cc.canStreamEvents());
     have.put(ACCESS_DATABASE, cc.canAccessDatabase());
 
     QueueProvider.QueueType queue = cc.getQueueType();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
index 1b81a47..99d4baa 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
@@ -16,6 +16,8 @@
 
 import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.common.ChangeListener;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.events.ChangeEvent;
 import com.google.gerrit.server.git.WorkQueue;
@@ -33,6 +35,7 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.LinkedBlockingQueue;
 
+@RequiresCapability(GlobalCapability.STREAM_EVENTS)
 @CommandMetaData(name = "stream-events", descr = "Monitor events occurring in real time")
 final class StreamEvents extends BaseCommand {
   /** Maximum number of events that may be queued up for each connection. */