Merge "Split off raw SQL access into its own permission"
This commit is contained in:
		@@ -1366,6 +1366,13 @@ This limit applies not only to the link:cmd-query.html[`gerrit query`]
 | 
			
		||||
command, but also to the web UI results pagination size.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[capability_accessDatabase]]
 | 
			
		||||
Access Database
 | 
			
		||||
~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
Allow users to access the database using the `gsql` command.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[capability_startReplication]]
 | 
			
		||||
Start Replication
 | 
			
		||||
~~~~~~~~~~~~~~~~~
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,9 @@ import java.util.List;
 | 
			
		||||
 | 
			
		||||
/** Server wide capabilities. Represented as {@link Permission} objects. */
 | 
			
		||||
public class GlobalCapability {
 | 
			
		||||
  /** Ability to access the database (with gsql). */
 | 
			
		||||
  public static final String ACCESS_DATABASE = "accessDatabase";
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Denotes the server's administrators.
 | 
			
		||||
   * <p>
 | 
			
		||||
@@ -81,6 +84,7 @@ public class GlobalCapability {
 | 
			
		||||
 | 
			
		||||
  static {
 | 
			
		||||
    NAMES_ALL = new ArrayList<String>();
 | 
			
		||||
    NAMES_ALL.add(ACCESS_DATABASE);
 | 
			
		||||
    NAMES_ALL.add(ADMINISTRATE_SERVER);
 | 
			
		||||
    NAMES_ALL.add(CREATE_ACCOUNT);
 | 
			
		||||
    NAMES_ALL.add(CREATE_GROUP);
 | 
			
		||||
 
 | 
			
		||||
@@ -146,6 +146,7 @@ errorsMustBeFixed = Errors must be fixed before committing changes.
 | 
			
		||||
 | 
			
		||||
# Capability Names
 | 
			
		||||
capabilityNames = \
 | 
			
		||||
  accessDatabase, \
 | 
			
		||||
  administrateServer, \
 | 
			
		||||
  createAccount, \
 | 
			
		||||
  createGroup, \
 | 
			
		||||
@@ -159,6 +160,7 @@ capabilityNames = \
 | 
			
		||||
  viewCaches, \
 | 
			
		||||
  viewConnections, \
 | 
			
		||||
  viewQueue
 | 
			
		||||
accessDatabase = Access Database
 | 
			
		||||
administrateServer = Administrate Server
 | 
			
		||||
createAccount = Create Account
 | 
			
		||||
createGroup = Create Group
 | 
			
		||||
 
 | 
			
		||||
@@ -130,6 +130,12 @@ public class CapabilityControl {
 | 
			
		||||
      || canAdministrateServer();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /** @return true if the user can access the database (with gsql). */
 | 
			
		||||
  public boolean canAccessDatabase() {
 | 
			
		||||
    return canPerform(GlobalCapability.ACCESS_DATABASE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @return true if the user can force replication to any configured destination. */
 | 
			
		||||
  public boolean canStartReplication() {
 | 
			
		||||
    return canPerform(GlobalCapability.START_REPLICATION)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@
 | 
			
		||||
 | 
			
		||||
package com.google.gerrit.server.account;
 | 
			
		||||
 | 
			
		||||
import static com.google.gerrit.common.data.GlobalCapability.ACCESS_DATABASE;
 | 
			
		||||
import static com.google.gerrit.common.data.GlobalCapability.CREATE_ACCOUNT;
 | 
			
		||||
import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP;
 | 
			
		||||
import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
 | 
			
		||||
@@ -109,6 +110,7 @@ class GetCapabilities implements RestReadView<AccountResource> {
 | 
			
		||||
    have.put(VIEW_CONNECTIONS, cc.canViewConnections());
 | 
			
		||||
    have.put(VIEW_QUEUE, cc.canViewQueue());
 | 
			
		||||
    have.put(START_REPLICATION, cc.canStartReplication());
 | 
			
		||||
    have.put(ACCESS_DATABASE, cc.canAccessDatabase());
 | 
			
		||||
 | 
			
		||||
    QueueProvider.QueueType queue = cc.getQueueType();
 | 
			
		||||
    if (queue != QueueProvider.QueueType.INTERACTIVE
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ import org.kohsuke.args4j.Option;
 | 
			
		||||
 | 
			
		||||
/** Opens a query processor. */
 | 
			
		||||
@AdminHighPriorityCommand
 | 
			
		||||
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
 | 
			
		||||
@RequiresCapability(GlobalCapability.ACCESS_DATABASE)
 | 
			
		||||
@CommandMetaData(name = "gsql", descr = "Administrative interface to active database")
 | 
			
		||||
final class AdminQueryShell extends SshCommand {
 | 
			
		||||
  @Inject
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,9 @@
 | 
			
		||||
package com.google.gerrit.sshd.commands;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.common.Version;
 | 
			
		||||
import com.google.gerrit.common.errors.PermissionDeniedException;
 | 
			
		||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
			
		||||
import com.google.gerrit.server.IdentifiedUser;
 | 
			
		||||
import com.google.gson.JsonObject;
 | 
			
		||||
import com.google.gwtorm.jdbc.JdbcSchema;
 | 
			
		||||
import com.google.gwtorm.server.OrmException;
 | 
			
		||||
@@ -55,6 +57,7 @@ public class QueryShell {
 | 
			
		||||
  private final BufferedReader in;
 | 
			
		||||
  private final PrintWriter out;
 | 
			
		||||
  private final SchemaFactory<ReviewDb> dbFactory;
 | 
			
		||||
  private final IdentifiedUser currentUser;
 | 
			
		||||
  private OutputFormat outputFormat = OutputFormat.PRETTY;
 | 
			
		||||
 | 
			
		||||
  private ReviewDb db;
 | 
			
		||||
@@ -63,12 +66,14 @@ public class QueryShell {
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  QueryShell(final SchemaFactory<ReviewDb> dbFactory,
 | 
			
		||||
      final IdentifiedUser currentUser,
 | 
			
		||||
 | 
			
		||||
  @Assisted final InputStream in, @Assisted final OutputStream out)
 | 
			
		||||
      throws UnsupportedEncodingException {
 | 
			
		||||
    this.dbFactory = dbFactory;
 | 
			
		||||
    this.in = new BufferedReader(new InputStreamReader(in, "UTF-8"));
 | 
			
		||||
    this.out = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
 | 
			
		||||
    this.currentUser = currentUser;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void setOutputFormat(OutputFormat fmt) {
 | 
			
		||||
@@ -77,6 +82,7 @@ public class QueryShell {
 | 
			
		||||
 | 
			
		||||
  public void run() {
 | 
			
		||||
    try {
 | 
			
		||||
      checkPermission();
 | 
			
		||||
      db = dbFactory.open();
 | 
			
		||||
      try {
 | 
			
		||||
        connection = ((JdbcSchema) db).getConnection();
 | 
			
		||||
@@ -99,6 +105,8 @@ public class QueryShell {
 | 
			
		||||
 | 
			
		||||
    } catch (SQLException err) {
 | 
			
		||||
      out.println("fatal: Cannot open connection: " + err.getMessage());
 | 
			
		||||
    } catch (PermissionDeniedException err) {
 | 
			
		||||
      out.println("fatal: " + err.getMessage());
 | 
			
		||||
    } finally {
 | 
			
		||||
      out.flush();
 | 
			
		||||
    }
 | 
			
		||||
@@ -106,6 +114,7 @@ public class QueryShell {
 | 
			
		||||
 | 
			
		||||
  public void execute(String query) {
 | 
			
		||||
    try {
 | 
			
		||||
      checkPermission();
 | 
			
		||||
      db = dbFactory.open();
 | 
			
		||||
      try {
 | 
			
		||||
        connection = ((JdbcSchema) db).getConnection();
 | 
			
		||||
@@ -127,11 +136,31 @@ public class QueryShell {
 | 
			
		||||
 | 
			
		||||
    } catch (SQLException err) {
 | 
			
		||||
      out.println("fatal: Cannot open connection: " + err.getMessage());
 | 
			
		||||
    } catch (PermissionDeniedException err) {
 | 
			
		||||
      out.println("fatal: " + err.getMessage());
 | 
			
		||||
    } finally {
 | 
			
		||||
      out.flush();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Assert that the current user is permitted to perform raw queries.
 | 
			
		||||
   * <p>
 | 
			
		||||
   * As the @RequireCapability guards at various entry points of internal
 | 
			
		||||
   * commands implicitly add administrators (which we want to avoid), we also
 | 
			
		||||
   * check permissions within QueryShell and grant access only to those who
 | 
			
		||||
   * canPerformRawQuery, regardless of whether they are administrators or not.
 | 
			
		||||
   *
 | 
			
		||||
   * @throws PermissionDeniedException
 | 
			
		||||
   */
 | 
			
		||||
  private void checkPermission() throws PermissionDeniedException {
 | 
			
		||||
    if (!currentUser.getCapabilities().canAccessDatabase()) {
 | 
			
		||||
      throw new PermissionDeniedException(String.format(
 | 
			
		||||
          "%s does not have \"Perform Raw Query\" capability.",
 | 
			
		||||
          currentUser.getUserName()));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void readEvalPrintLoop() {
 | 
			
		||||
    final StringBuilder buffer = new StringBuilder();
 | 
			
		||||
    boolean executed = false;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user