Add fine-grained capabilities for administrative actions

The Global Capabilities section in All-Projects can now be used to
grant subcommands that are available over SSH and were previously
restricted to only Administrators.

Bug: issue 48
Bug: issue 742
Change-Id: I7d8a931b174915191817ff845f1f9a846181d709
This commit is contained in:
Shawn O. Pearce
2011-06-16 13:49:42 -07:00
parent eda6e36af7
commit 7f48514889
28 changed files with 323 additions and 72 deletions

View File

@@ -27,7 +27,8 @@ created in Gerrit that do not exist in the underlying LDAP directory.
ACCESS
------
Caller must be a member of the privileged 'Administrators' group.
Caller must be a member of the privileged 'Administrators' group,
or have been granted the 'Create Account' global capability.
SCRIPTING
---------

View File

@@ -26,7 +26,8 @@ becomes a member of the newly created group.
ACCESS
------
Caller must be a member of the privileged 'Administrators' group.
Caller must be a member of the privileged 'Administrators' group,
or have been granted the 'Create Group' global capability.
SCRIPTING
---------

View File

@@ -25,7 +25,8 @@ If no options are supplied, defaults to `--all`.
ACCESS
------
Caller must be a member of the privileged 'Administrators' group.
Caller must be a member of the privileged 'Administrators' group,
or have granted the 'Flush Caches' global capability.
SCRIPTING
---------

View File

@@ -18,7 +18,8 @@ its next cancellation point (which is usually blocking IO).
ACCESS
------
Caller must be a member of the privileged 'Administrators' group.
Caller must be a member of the privileged 'Administrators' group,
or have been granted the 'Kill Task' global capability.
SCRIPTING
---------

View File

@@ -52,7 +52,8 @@ just the affected project can update the mirrors.
ACCESS
------
Caller must be a member of the privileged 'Administrators' group.
Caller must be a member of the privileged 'Administrators' group,
or have been granted the 'Start Replication' global capability.
SCRIPTING
---------

View File

@@ -16,7 +16,8 @@ Display statistics about the size and hit ratio of in-memory caches.
ACCESS
------
Caller must be a member of the privileged 'Administrators' group.
Caller must be a member of the privileged 'Administrators' group,
or have been granted the 'View Caches' global capability.
SCRIPTING
---------

View File

@@ -18,7 +18,8 @@ an activity.
ACCESS
------
Caller must be a member of the privileged 'Administrators' group.
Caller must be a member of the privileged 'Administrators' group,
or have been granted the 'View Connections' global capability.
SCRIPTING
---------

View File

@@ -24,7 +24,12 @@ in these states.
ACCESS
------
Caller must be a member of the privileged 'Administrators' group.
End-users may see a task in the queue only if they can also see
the project the task is associated with. Tasks operating on other
projects, or that do not have a specific project are hidden.
Members of the group 'Administrators', or any group that has been
granted the 'View Queue' capability can see all queue entries.
SCRIPTING
---------

View File

@@ -19,13 +19,46 @@ import java.util.List;
/** Server wide capabilities. Represented as {@link Permission} objects. */
public class GlobalCapability {
/** Can create any group on the server. */
public static final String CREATE_GROUP = "createGroup";
/** Can create any account on the server. */
public static final String CREATE_ACCOUNT = "createAccount";
/** Can flush any cache except the active web_sessions cache. */
public static final String FLUSH_CACHES = "flushCaches";
/** Can terminate any task using the kill command. */
public static final String KILL_TASK = "killTask";
/** Maximum result limit per executed query. */
public static final String QUERY_LIMIT = "queryLimit";
/** Forcefully restart replication to any configured destination. */
public static final String START_REPLICATION = "startReplication";
/** Can view the server's current cache states. */
public static final String VIEW_CACHES = "viewCaches";
/** Can view open connections to the server's SSH port. */
public static final String VIEW_CONNECTIONS = "viewConnections";
/** Can view all pending tasks in the queue (not just the filtered set). */
public static final String VIEW_QUEUE = "viewQueue";
private static final List<String> NAMES_LC;
static {
NAMES_LC = new ArrayList<String>();
NAMES_LC.add(CREATE_GROUP.toLowerCase());
NAMES_LC.add(CREATE_ACCOUNT.toLowerCase());
NAMES_LC.add(FLUSH_CACHES.toLowerCase());
NAMES_LC.add(KILL_TASK.toLowerCase());
NAMES_LC.add(QUERY_LIMIT.toLowerCase());
NAMES_LC.add(START_REPLICATION.toLowerCase());
NAMES_LC.add(VIEW_CACHES.toLowerCase());
NAMES_LC.add(VIEW_CONNECTIONS.toLowerCase());
NAMES_LC.add(VIEW_QUEUE.toLowerCase());
}
/** @return true if the name is recognized as a capability name. */

View File

@@ -21,8 +21,8 @@ import com.google.gerrit.reviewdb.AccountGroupMember;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.RemoteJsonService;
import com.google.gwtjsonrpc.client.RpcImpl;
import com.google.gwtjsonrpc.client.RpcImpl.Version;
import com.google.gwtjsonrpc.client.VoidResult;
import com.google.gwtjsonrpc.client.RpcImpl.Version;
import java.util.List;
import java.util.Set;
@@ -30,7 +30,7 @@ import java.util.Set;
@RpcImpl(version = Version.V2_0)
public interface GroupAdminService extends RemoteJsonService {
@SignInRequired
void visibleGroups(AsyncCallback<List<AccountGroup>> callback);
void visibleGroups(AsyncCallback<GroupList> callback);
@SignInRequired
void createGroup(String newName, AsyncCallback<AccountGroup.Id> callback);

View File

@@ -0,0 +1,40 @@
// Copyright (C) 2011 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.google.gerrit.common.data;
import com.google.gerrit.reviewdb.AccountGroup;
import java.util.List;
public class GroupList {
protected List<AccountGroup> groups;
protected boolean canCreateGroup;
public List<AccountGroup> getGroups() {
return groups;
}
public void setGroups(List<AccountGroup> groups) {
this.groups = groups;
}
public boolean isCanCreateGroup() {
return canCreateGroup;
}
public void setCanCreateGroup(boolean set) {
canCreateGroup = set;
}
}

View File

@@ -0,0 +1,24 @@
// Copyright (C) 2011 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.google.gerrit.common.errors;
/** Indicats the user cannot perform this task. */
public class PermissionDeniedException extends Exception {
private static final long serialVersionUID = 1L;
public PermissionDeniedException(String msg) {
super(msg);
}
}

View File

@@ -119,8 +119,24 @@ errorsMustBeFixed = Errors must be fixed before committing changes.
# Capability Names
capabilityNames = \
queryLimit
createAccount, \
createGroup, \
flushCaches, \
killTask, \
queryLimit, \
startReplication, \
viewCaches, \
viewConnections, \
viewQueue
createAccount = Create Account
createGroup = Create Group
flushCaches = Flush Caches
killTask = Kill Task
queryLimit = Query Limit
startReplication = Start Replication
viewCaches = View Caches
viewConnections = View Connections
viewQueue = View Queue
# Section Names
sectionTypeReference = Reference:

View File

@@ -22,6 +22,7 @@ import com.google.gerrit.client.ui.AccountScreen;
import com.google.gerrit.client.ui.OnEditEnabler;
import com.google.gerrit.client.ui.SmallHeading;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.GroupList;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
@@ -42,6 +43,7 @@ import java.util.List;
public class GroupListScreen extends AccountScreen {
private GroupTable groups;
private VerticalPanel addPanel;
private NpTextBox addTxt;
private Button addNew;
@@ -49,10 +51,11 @@ public class GroupListScreen extends AccountScreen {
protected void onLoad() {
super.onLoad();
Util.GROUP_SVC
.visibleGroups(new ScreenLoadCallback<List<AccountGroup>>(this) {
.visibleGroups(new ScreenLoadCallback<GroupList>(this) {
@Override
protected void preDisplay(final List<AccountGroup> result) {
groups.display(result);
protected void preDisplay(GroupList result) {
addPanel.setVisible(result.isCanCreateGroup());
groups.display(result.getGroups());
groups.finishDisplay();
}
});
@@ -66,9 +69,9 @@ public class GroupListScreen extends AccountScreen {
groups = new GroupTable(true /* hyperlink to admin */, PageLinks.ADMIN_GROUPS);
add(groups);
final VerticalPanel fp = new VerticalPanel();
fp.setStyleName(Gerrit.RESOURCES.css().addSshKeyPanel());
fp.add(new SmallHeading(Util.C.headingCreateGroup()));
addPanel = new VerticalPanel();
addPanel.setStyleName(Gerrit.RESOURCES.css().addSshKeyPanel());
addPanel.add(new SmallHeading(Util.C.headingCreateGroup()));
addTxt = new NpTextBox();
addTxt.setVisibleLength(60);
@@ -80,7 +83,7 @@ public class GroupListScreen extends AccountScreen {
}
}
});
fp.add(addTxt);
addPanel.add(addTxt);
addNew = new Button(Util.C.buttonCreateGroup());
addNew.setEnabled(false);
@@ -109,8 +112,8 @@ public class GroupListScreen extends AccountScreen {
groups.setRegisterKeys(true);
}
});
fp.add(addNew);
add(fp);
addPanel.add(addNew);
add(addPanel);
new OnEditEnabler(addNew, addTxt);
}

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.httpd.rpc.account;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.common.errors.PermissionDeniedException;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountGroup;
@@ -44,7 +45,14 @@ class CreateGroup extends Handler<AccountGroup.Id> {
}
@Override
public AccountGroup.Id call() throws OrmException, NameAlreadyUsedException {
public AccountGroup.Id call() throws OrmException, NameAlreadyUsedException,
PermissionDeniedException {
if (!user.getCapabilities().canCreateGroup()) {
throw new PermissionDeniedException(String.format(
"%s does not have \"Create Group\" capability.",
user.getUserName()));
}
final PerformCreateGroup performCreateGroup = performCreateGroupFactory.create();
final Account.Id me = user.getAccountId();
return performCreateGroup.createGroup(groupName, null, false, null, Collections.singleton(me), null);

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.httpd.rpc.account;
import com.google.gerrit.common.data.GroupAdminService;
import com.google.gerrit.common.data.GroupDetail;
import com.google.gerrit.common.data.GroupList;
import com.google.gerrit.common.data.GroupOptions;
import com.google.gerrit.common.errors.InactiveAccountException;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
@@ -88,28 +89,32 @@ class GroupAdminServiceImpl extends BaseServiceImplementation implements
this.groupDetailFactory = groupDetailFactory;
}
public void visibleGroups(final AsyncCallback<List<AccountGroup>> callback) {
run(callback, new Action<List<AccountGroup>>() {
public List<AccountGroup> run(ReviewDb db) throws OrmException {
public void visibleGroups(final AsyncCallback<GroupList> callback) {
run(callback, new Action<GroupList>() {
public GroupList run(ReviewDb db) throws OrmException {
final IdentifiedUser user = identifiedUser.get();
final List<AccountGroup> result;
final List<AccountGroup> list;
if (user.isAdministrator()) {
result = db.accountGroups().all().toList();
list = db.accountGroups().all().toList();
} else {
result = new ArrayList<AccountGroup>();
list = new ArrayList<AccountGroup>();
for(final AccountGroup group : db.accountGroups().all().toList()) {
final GroupControl c = groupControlFactory.controlFor(group);
if (c.isVisible()) {
result.add(c.getAccountGroup());
list.add(c.getAccountGroup());
}
}
}
Collections.sort(result, new Comparator<AccountGroup>() {
Collections.sort(list, new Comparator<AccountGroup>() {
public int compare(final AccountGroup a, final AccountGroup b) {
return a.getName().compareTo(b.getName());
}
});
return result;
GroupList res = new GroupList();
res.setGroups(list);
res.setCanCreateGroup(user.getCapabilities().canCreateGroup());
return res;
}
});
}

View File

@@ -64,6 +64,46 @@ public class CapabilityControl {
return user;
}
/** @return true if the user can create an account for another user. */
public boolean canCreateAccount() {
return canPerform(GlobalCapability.CREATE_ACCOUNT) || user.isAdministrator();
}
/** @return true if the user can create a group. */
public boolean canCreateGroup() {
return canPerform(GlobalCapability.CREATE_GROUP) || user.isAdministrator();
}
/** @return true if the user can kill any running task. */
public boolean canKillTask() {
return canPerform(GlobalCapability.KILL_TASK) || user.isAdministrator();
}
/** @return true if the user can view the server caches. */
public boolean canViewCaches() {
return canPerform(GlobalCapability.VIEW_CACHES) || user.isAdministrator();
}
/** @return true if the user can flush the server's caches. */
public boolean canFlushCaches() {
return canPerform(GlobalCapability.FLUSH_CACHES) || user.isAdministrator();
}
/** @return true if the user can view open connections. */
public boolean canViewConnections() {
return canPerform(GlobalCapability.VIEW_CONNECTIONS) || user.isAdministrator();
}
/** @return true if the user can view the entire queue. */
public boolean canViewQueue() {
return canPerform(GlobalCapability.VIEW_QUEUE) || user.isAdministrator();
}
/** @return true if the user can force replication to any configured destination. */
public boolean canStartReplication() {
return canPerform(GlobalCapability.START_REPLICATION) || user.isAdministrator();
}
/** True if the user has this permission. Works only for non labels. */
public boolean canPerform(String permissionName) {
return !access(permissionName).isEmpty();

View File

@@ -58,7 +58,7 @@ public abstract class BaseCommand implements Command {
private static final int PRIVATE_STATUS = 1 << 30;
static final int STATUS_CANCEL = PRIVATE_STATUS | 1;
static final int STATUS_NOT_FOUND = PRIVATE_STATUS | 2;
static final int STATUS_NOT_ADMIN = PRIVATE_STATUS | 3;
public static final int STATUS_NOT_ADMIN = PRIVATE_STATUS | 3;
@Option(name = "--help", usage = "display this help text", aliases = {"-h"})
private boolean help;

View File

@@ -26,7 +26,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.AdminCommand;
import com.google.gerrit.sshd.BaseCommand;
import com.google.gwtorm.client.OrmDuplicateKeyException;
import com.google.gwtorm.client.OrmException;
@@ -46,8 +45,7 @@ import java.util.HashSet;
import java.util.List;
/** Create a new user account. **/
@AdminCommand
final class AdminCreateAccount extends BaseCommand {
final class CreateAccountCommand extends BaseCommand {
@Option(name = "--group", aliases = {"-g"}, metaVar = "GROUP", usage = "groups to add account to")
private List<AccountGroup.Id> groups = new ArrayList<AccountGroup.Id>();
@@ -83,6 +81,13 @@ final class AdminCreateAccount extends BaseCommand {
startThread(new CommandRunnable() {
@Override
public void run() throws Exception {
if (!currentUser.getCapabilities().canCreateAccount()) {
String msg = String.format(
"fatal: %s does not have \"Create Account\" capability.",
currentUser.getUserName());
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
parseCommandLine();
createAccount();
}

View File

@@ -17,8 +17,8 @@ package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.PerformCreateGroup;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.sshd.BaseCommand;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
@@ -27,7 +27,6 @@ import org.apache.sshd.server.Environment;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
@@ -36,8 +35,7 @@ import java.util.Set;
* <p>
* Optionally, puts an initial set of user in the newly created group.
*/
@AdminCommand
public class AdminCreateGroup extends BaseCommand {
final class CreateGroupCommand extends BaseCommand {
@Option(name = "--owner", aliases = {"-o"}, metaVar = "GROUP", usage = "owning group, if not specified the group will be self-owning")
private AccountGroup.Id ownerGroupId;
@@ -64,14 +62,24 @@ public class AdminCreateGroup extends BaseCommand {
initialGroups.add(id);
}
@Inject
private IdentifiedUser currentUser;
@Inject
private PerformCreateGroup.Factory performCreateGroupFactory;
@Override
public void start(Environment env) throws IOException {
public void start(Environment env) {
startThread(new CommandRunnable() {
@Override
public void run() throws Exception {
if (!currentUser.getCapabilities().canCreateGroup()) {
String msg = String.format(
"fatal: %s does not have \"Create Group\" capability.",
currentUser.getUserName());
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
parseCommandLine();
createGroup();
}

View File

@@ -34,11 +34,11 @@ public class DefaultCommandModule extends CommandModule {
// SlaveCommandModule.
command(gerrit).toProvider(new DispatchCommandProvider(gerrit));
command(gerrit, "flush-caches").to(AdminFlushCaches.class);
command(gerrit, "flush-caches").to(FlushCaches.class);
command(gerrit, "ls-projects").to(ListProjects.class);
command(gerrit, "query").to(Query.class);
command(gerrit, "show-caches").to(AdminShowCaches.class);
command(gerrit, "show-connections").to(AdminShowConnections.class);
command(gerrit, "show-caches").to(ShowCaches.class);
command(gerrit, "show-connections").to(ShowConnections.class);
command(gerrit, "show-queue").to(ShowQueue.class);
command(gerrit, "stream-events").to(StreamEvents.class);
command(gerrit, "version").to(VersionCommand.class);
@@ -48,7 +48,7 @@ public class DefaultCommandModule extends CommandModule {
command(git, "upload-pack").to(Upload.class);
command("ps").to(ShowQueue.class);
command("kill").to(AdminKill.class);
command("kill").to(KillCommand.class);
command("scp").to(ScpCommand.class);
// Honor the legacy hyphenated forms as aliases for the non-hyphenated forms

View File

@@ -14,7 +14,9 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject;
import net.sf.ehcache.Ehcache;
@@ -27,8 +29,9 @@ import java.util.List;
import java.util.SortedSet;
/** Causes the caches to purge all entries and reload. */
@AdminCommand
final class AdminFlushCaches extends CacheCommand {
final class FlushCaches extends CacheCommand {
private static final String WEB_SESSIONS = "web_sessions";
@Option(name = "--cache", usage = "flush named cache", metaVar = "NAME")
private List<String> caches = new ArrayList<String>();
@@ -38,6 +41,9 @@ final class AdminFlushCaches extends CacheCommand {
@Option(name = "--list", usage = "list available caches")
private boolean list;
@Inject
IdentifiedUser currentUser;
private PrintWriter p;
@Override
@@ -45,6 +51,13 @@ final class AdminFlushCaches extends CacheCommand {
startThread(new CommandRunnable() {
@Override
public void run() throws Exception {
if (!currentUser.getCapabilities().canFlushCaches()) {
String msg = String.format(
"fatal: %s does not have \"Flush Caches\" capability.",
currentUser.getUserName());
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
parseCommandLine();
flush();
}
@@ -52,6 +65,13 @@ final class AdminFlushCaches extends CacheCommand {
}
private void flush() throws Failure {
if (caches.contains(WEB_SESSIONS) && !currentUser.isAdministrator()) {
String msg = String.format(
"fatal: only site administrators can flush %s",
WEB_SESSIONS);
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
p = toPrintWriter(err);
if (list) {
if (all || caches.size() > 0) {
@@ -113,7 +133,7 @@ final class AdminFlushCaches extends CacheCommand {
return true;
} else if (all) {
if ("web_sessions".equals(cacheName)) {
if (WEB_SESSIONS.equals(cacheName)) {
return false;
}
return true;

View File

@@ -14,10 +14,10 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.server.IdentifiedUser;
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.AdminCommand;
import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject;
@@ -29,8 +29,10 @@ import java.util.HashSet;
import java.util.Set;
/** Kill a task in the work queue. */
@AdminCommand
final class AdminKill extends BaseCommand {
final class KillCommand extends BaseCommand {
@Inject
private IdentifiedUser currentUser;
@Inject
private WorkQueue workQueue;
@@ -50,8 +52,15 @@ final class AdminKill extends BaseCommand {
startThread(new CommandRunnable() {
@Override
public void run() throws Exception {
if (!currentUser.getCapabilities().canKillTask()) {
String msg = String.format(
"fatal: %s does not have \"Kill Task\" capability.",
currentUser.getUserName());
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
parseCommandLine();
AdminKill.this.commitMurder();
KillCommand.this.commitMurder();
}
});
}

View File

@@ -26,13 +26,13 @@ public class MasterCommandModule extends CommandModule {
final CommandName gerrit = Commands.named("gerrit");
command(gerrit, "approve").to(ReviewCommand.class);
command(gerrit, "create-account").to(AdminCreateAccount.class);
command(gerrit, "create-group").to(AdminCreateGroup.class);
command(gerrit, "create-account").to(CreateAccountCommand.class);
command(gerrit, "create-group").to(CreateGroupCommand.class);
command(gerrit, "create-project").to(CreateProject.class);
command(gerrit, "gsql").to(AdminQueryShell.class);
command(gerrit, "modify-reviewers").to(ModifyReviewersCommand.class);
command(gerrit, "receive-pack").to(Receive.class);
command(gerrit, "replicate").to(AdminReplicate.class);
command(gerrit, "replicate").to(Replicate.class);
command(gerrit, "set-project-parent").to(AdminSetParent.class);
command(gerrit, "review").to(ReviewCommand.class);
}

View File

@@ -15,10 +15,10 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.PushAllProjectsOp;
import com.google.gerrit.server.git.ReplicationQueue;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject;
@@ -31,8 +31,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
/** Force a project to replicate, again. */
@AdminCommand
final class AdminReplicate extends BaseCommand {
final class Replicate extends BaseCommand {
@Option(name = "--all", usage = "push all known projects")
private boolean all;
@@ -42,6 +41,9 @@ final class AdminReplicate extends BaseCommand {
@Argument(index = 0, multiValued = true, metaVar = "PROJECT", usage = "project name")
private List<String> projectNames = new ArrayList<String>(2);
@Inject
IdentifiedUser currentUser;
@Inject
private PushAllProjectsOp.Factory pushAllOpFactory;
@@ -56,8 +58,15 @@ final class AdminReplicate extends BaseCommand {
startThread(new CommandRunnable() {
@Override
public void run() throws Exception {
if (!currentUser.getCapabilities().canStartReplication()) {
String msg = String.format(
"fatal: %s does not have \"Start Replication\" capability.",
currentUser.getUserName());
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
parseCommandLine();
AdminReplicate.this.schedule();
Replicate.this.schedule();
}
});
}

View File

@@ -14,7 +14,9 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Statistics;
@@ -26,8 +28,10 @@ import org.eclipse.jgit.storage.file.WindowCacheStatAccessor;
import java.io.PrintWriter;
/** Show the current cache states. */
@AdminCommand
final class AdminShowCaches extends CacheCommand {
final class ShowCaches extends CacheCommand {
@Inject
IdentifiedUser currentUser;
private PrintWriter p;
@Override
@@ -35,6 +39,13 @@ final class AdminShowCaches extends CacheCommand {
startThread(new CommandRunnable() {
@Override
public void run() throws Exception {
if (!currentUser.getCapabilities().canViewCaches()) {
String msg = String.format(
"fatal: %s does not have \"View Caches\" capability.",
currentUser.getUserName());
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
parseCommandLine();
display();
}

View File

@@ -17,7 +17,6 @@ package com.google.gerrit.sshd.commands;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.util.IdGenerator;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.sshd.BaseCommand;
import com.google.gerrit.sshd.SshDaemon;
import com.google.gerrit.sshd.SshSession;
@@ -41,13 +40,15 @@ import java.util.Date;
import java.util.List;
/** Show the current SSH connections. */
@AdminCommand
final class AdminShowConnections extends BaseCommand {
final class ShowConnections extends BaseCommand {
@Option(name = "--numeric", aliases = {"-n"}, usage = "don't resolve names")
private boolean numeric;
private PrintWriter p;
@Inject
IdentifiedUser currentUser;
@Inject
private SshDaemon daemon;
@@ -56,8 +57,15 @@ final class AdminShowConnections extends BaseCommand {
startThread(new CommandRunnable() {
@Override
public void run() throws Exception {
if (!currentUser.getCapabilities().canViewConnections()) {
String msg = String.format(
"fatal: %s does not have \"View Connections\" capability.",
currentUser.getUserName());
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
parseCommandLine();
AdminShowConnections.this.display();
ShowConnections.this.display();
}
});
}

View File

@@ -15,7 +15,7 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.WorkQueue.ProjectTask;
import com.google.gerrit.server.git.WorkQueue.Task;
@@ -50,7 +50,7 @@ final class ShowQueue extends BaseCommand {
private ProjectCache projectCache;
@Inject
private CurrentUser userProvider;
private IdentifiedUser currentUser;
private PrintWriter p;
private int columns = 80;
@@ -110,7 +110,7 @@ final class ShowQueue extends BaseCommand {
int numberOfPendingTasks = 0;
final long now = System.currentTimeMillis();
final boolean isAdministrator = userProvider.isAdministrator();
final boolean viewAll = currentUser.getCapabilities().canViewQueue();
for (final Task<?> task : pending) {
final long delay = task.getDelay(TimeUnit.MILLISECONDS);
@@ -137,7 +137,7 @@ final class ShowQueue extends BaseCommand {
Project.NameKey projectName = null;
String remoteName = null;
if (!isAdministrator) {
if (!viewAll) {
if (task instanceof ProjectTask<?>) {
projectName = ((ProjectTask<?>)task).getProjectNameKey();
remoteName = ((ProjectTask<?>)task).getRemoteName();
@@ -149,7 +149,7 @@ final class ShowQueue extends BaseCommand {
e = projectCache.get(projectName);
}
regularUserCanSee = e != null && e.controlFor(userProvider).isVisible();
regularUserCanSee = e != null && e.controlFor(currentUser).isVisible();
if (regularUserCanSee) {
numberOfPendingTasks++;
@@ -157,7 +157,7 @@ final class ShowQueue extends BaseCommand {
}
// Shows information about tasks depending on the user rights
if (isAdministrator || (!hasCustomizedPrint && regularUserCanSee)) {
if (viewAll || (!hasCustomizedPrint && regularUserCanSee)) {
p.print(String.format("%8s %-12s %-8s %s\n", //
id(task.getTaskId()), start, "", format(task)));
} else if (regularUserCanSee) {
@@ -174,7 +174,7 @@ final class ShowQueue extends BaseCommand {
p.print("----------------------------------------------"
+ "--------------------------------\n");
if (isAdministrator) {
if (viewAll) {
numberOfPendingTasks = pending.size();
}