diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/RequiresCapability.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/RequiresCapability.java similarity index 85% rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/RequiresCapability.java rename to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/RequiresCapability.java index cc41a79a57..382f4eafb9 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/RequiresCapability.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/RequiresCapability.java @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.sshd; +package com.google.gerrit.extensions.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -21,7 +21,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Annotation on {@link SshCommand} declaring a capability must be granted. + * Annotation on {@link SshCommand} or {@link RestApiServlet} declaring a + * capability must be granted. */ @Target({ElementType.TYPE}) @Retention(RUNTIME) diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RestApiServlet.java index 8105e25580..ff41ad8160 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RestApiServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RestApiServlet.java @@ -15,10 +15,14 @@ package com.google.gerrit.httpd; import com.google.common.base.Strings; +import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.account.CapabilityControl; import com.google.gerrit.util.cli.CmdLineParser; -import com.google.gwtjsonrpc.server.RPCServletUtils; import com.google.gwtjsonrpc.common.JsonConstants; +import com.google.gwtjsonrpc.server.RPCServletUtils; import com.google.inject.Inject; +import com.google.inject.Provider; import org.kohsuke.args4j.CmdLineException; import org.slf4j.Logger; @@ -62,12 +66,24 @@ public abstract class RestApiServlet extends HttpServlet { } } + private final Provider currentUser; + + @Inject + protected RestApiServlet(final Provider currentUser) { + this.currentUser = currentUser; + } + @Override protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { noCache(res); try { + checkRequiresCapability(); super.service(req, res); + } catch (RequireCapabilityException err) { + res.setStatus(HttpServletResponse.SC_FORBIDDEN); + noCache(res); + sendText(req, res, err.getMessage()); } catch (Error err) { handleError(err, req, res); } catch (RuntimeException err) { @@ -75,6 +91,20 @@ public abstract class RestApiServlet extends HttpServlet { } } + private void checkRequiresCapability() throws RequireCapabilityException { + RequiresCapability rc = getClass().getAnnotation(RequiresCapability.class); + if (rc != null) { + CurrentUser user = currentUser.get(); + CapabilityControl ctl = user.getCapabilities(); + if (!ctl.canPerform(rc.value()) && !ctl.canAdministrateServer()) { + String msg = String.format( + "fatal: %s does not have \"%s\" capability.", + user.getUserName(), rc.value()); + throw new RequireCapabilityException(msg); + } + } + } + private static void noCache(HttpServletResponse res) { res.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT"); res.setHeader("Pragma", "no-cache"); @@ -175,4 +205,11 @@ public abstract class RestApiServlet extends HttpServlet { return true; } } + + @SuppressWarnings("serial") // Never serialized or thrown out of this class. + private static class RequireCapabilityException extends Exception { + public RequireCapabilityException(String msg) { + super(msg); + } + } } diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountCapabilitiesServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountCapabilitiesServlet.java index 0d0ffe7a95..a33c2093af 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountCapabilitiesServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountCapabilitiesServlet.java @@ -58,8 +58,9 @@ public class AccountCapabilitiesServlet extends RestApiServlet { private final Provider factory; @Inject - AccountCapabilitiesServlet( + AccountCapabilitiesServlet(final Provider currentUser, ParameterParser paramParser, Provider factory) { + super(currentUser); this.paramParser = paramParser; this.factory = factory; } diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/change/ListChangesServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/change/ListChangesServlet.java index 91fc5b0880..b501d43895 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/change/ListChangesServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/change/ListChangesServlet.java @@ -15,6 +15,7 @@ package com.google.gerrit.httpd.rpc.change; import com.google.gerrit.httpd.RestApiServlet; +import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.OutputFormat; import com.google.gerrit.server.query.QueryParseException; import com.google.gerrit.server.query.change.ListChanges; @@ -43,7 +44,9 @@ public class ListChangesServlet extends RestApiServlet { private final Provider factory; @Inject - ListChangesServlet(ParameterParser paramParser, Provider ls) { + ListChangesServlet(final Provider currentUser, + ParameterParser paramParser, Provider ls) { + super(currentUser); this.paramParser = paramParser; this.factory = ls; } diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListProjectsServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListProjectsServlet.java index 27576408d3..d327d35975 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListProjectsServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListProjectsServlet.java @@ -16,6 +16,7 @@ package com.google.gerrit.httpd.rpc.project; import com.google.common.base.Strings; import com.google.gerrit.httpd.RestApiServlet; +import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.OutputFormat; import com.google.gerrit.server.project.ListProjects; import com.google.inject.Inject; @@ -36,7 +37,9 @@ public class ListProjectsServlet extends RestApiServlet { private final Provider factory; @Inject - ListProjectsServlet(ParameterParser paramParser, Provider ls) { + ListProjectsServlet(final Provider currentUser, + ParameterParser paramParser, Provider ls) { + super(currentUser); this.paramParser = paramParser; this.factory = ls; } diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AliasCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AliasCommand.java index d28d1029b1..9582c936a9 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AliasCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AliasCommand.java @@ -16,6 +16,7 @@ package com.google.gerrit.sshd; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Atomics; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.account.CapabilityControl; import com.google.inject.Provider; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java index f0810baaa0..37f141124f 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java @@ -16,6 +16,7 @@ package com.google.gerrit.sshd; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Atomics; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.account.CapabilityControl; import com.google.gerrit.sshd.args4j.SubcommandHandler; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java index 5f1992ccc2..08c650cde9 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java @@ -15,8 +15,8 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.sshd.AdminHighPriorityCommand; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.inject.Inject; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java index 047cdd4bec..cfd917c611 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java @@ -15,6 +15,7 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.config.AllProjectsName; import com.google.gerrit.server.git.MetaDataUpdate; @@ -22,7 +23,6 @@ import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectState; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.inject.Inject; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java index 29f22949d9..ac7ee082a2 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java @@ -16,6 +16,7 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.errors.InvalidSshKeyException; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountExternalId; import com.google.gerrit.reviewdb.client.AccountGroup; @@ -27,7 +28,6 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountByEmailCache; import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.ssh.SshKeyCache; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.gwtorm.server.OrmDuplicateKeyException; import com.google.gwtorm.server.OrmException; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java index 28b6f4880b..728c20cf0f 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java @@ -17,10 +17,10 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.errors.NameAlreadyUsedException; import com.google.gerrit.common.errors.PermissionDeniedException; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.server.account.PerformCreateGroup; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java index 1f5bc6f442..eafe54f358 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java @@ -16,6 +16,7 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.errors.ProjectCreationFailedException; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project.SubmitType; @@ -23,7 +24,6 @@ import com.google.gerrit.server.project.CreateProject; import com.google.gerrit.server.project.CreateProjectArgs; import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.SuggestParentCandidates; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.inject.Inject; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java index 9ba20ed2a0..1d4cf005c2 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java @@ -15,9 +15,9 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.sshd.BaseCommand; -import com.google.gerrit.sshd.RequiresCapability; import com.google.inject.Inject; import net.sf.ehcache.Ehcache; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/KillCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/KillCommand.java index 12ab225ad2..83e88e55b0 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/KillCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/KillCommand.java @@ -15,11 +15,11 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.git.WorkQueue.Task; import com.google.gerrit.server.util.IdGenerator; import com.google.gerrit.sshd.AdminHighPriorityCommand; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.inject.Inject; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java index 2328847661..12722ec133 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java @@ -16,9 +16,9 @@ package com.google.gerrit.sshd.commands; import com.google.common.base.Strings; import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.server.plugins.PluginInstallException; import com.google.gerrit.server.plugins.PluginLoader; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.inject.Inject; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java index ff34269d63..6d7490fb03 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java @@ -15,9 +15,9 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.server.plugins.ListPlugins; import com.google.gerrit.sshd.BaseCommand; -import com.google.gerrit.sshd.RequiresCapability; import com.google.inject.Inject; import org.apache.sshd.server.Environment; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java index d60465cca2..d2429a9320 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java @@ -17,8 +17,8 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.server.plugins.InvalidPluginException; import com.google.gerrit.server.plugins.PluginInstallException; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.server.plugins.PluginLoader; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.inject.Inject; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java index 6444e71ee7..8baab77661 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java @@ -16,8 +16,8 @@ package com.google.gerrit.sshd.commands; import com.google.common.collect.Sets; import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.server.plugins.PluginLoader; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.inject.Inject; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java index 97a0d8606d..66013e6c06 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java @@ -16,11 +16,11 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.Version; import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.git.WorkQueue.Task; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshDaemon; import com.google.inject.Inject; diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java index 4085dcb09c..a1a5b8f369 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java @@ -15,10 +15,10 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.util.IdGenerator; -import com.google.gerrit.sshd.RequiresCapability; import com.google.gerrit.sshd.SshCommand; import com.google.gerrit.sshd.SshDaemon; import com.google.gerrit.sshd.SshSession;