Merge "Added a ls-members command to the ssh daemon."

This commit is contained in:
David Pursehouse
2013-05-09 07:43:14 +00:00
committed by Gerrit Code Review
8 changed files with 224 additions and 13 deletions

View File

@@ -60,6 +60,9 @@ link:cmd-ban-commit.html[gerrit ban-commit]::
link:cmd-ls-groups.html[gerrit ls-groups]::
List groups visible to the caller.
link:cmd-ls-members.html[gerrit ls-members]::
List the membership of a group visible to the caller.
link:cmd-ls-projects.html[gerrit ls-projects]::
List projects visible to the caller.

View File

@@ -0,0 +1,64 @@
gerrit ls-members
================
NAME
----
gerrit ls-members - Show members of a given group
SYNOPSIS
--------
[verse]
'ssh' -p <port> <host> 'gerrit ls-members GROUPNAME'
[--recursive]
DESCRIPTION
-----------
Displays the members of the given group, one per line, so long as the given
group is visible to the user. The users' id, username, full name and email are
shown tab-separated.
ACCESS
------
Any user who has configured an SSH key.
SCRIPTING
---------
This command is intended to be used in scripts. Output is either an error
message or a heading followed by zero or more lines, one for each member of the
group. If any field is not set, or if the field is the user's full name and the
name is empty, "n/a" is emitted as the field value.
All non-printable characters (ASCII value 31 or less) are escaped
according to the conventions used in languages like C, Python, and Perl,
employing standard sequences like `\n` and `\t`, and `\xNN` for all
others. In shell scripts, the `printf` command can be used to unescape
the output.
OPTIONS
-------
--recursive::
If a member of the group is itself a group, the sub-group's
members are included in the list. Otherwise members of any sub-group
are not shown and no indication is given that a sub-group is present
EXAMPLES
--------
List members of the Administrators group:
=====
$ ssh -p 29418 review.example.com gerrit ls-members Administrators
id username full name email
100000 jim Jim Bob somebody@example.com
100001 johnny John Smith n/a
100002 mrnoname n/a someoneelse@example.com
=====
List members of a non-existent group:
=====
$ ssh -p 29418 review.example.com gerrit ls-members BadlySpelledGroup
Group not found or not visible
=====
GERRIT
------
Part of link:index.html[Gerrit Code Review]

View File

@@ -31,7 +31,8 @@ Returns an account as an link:#account-info[AccountInfo] entity.
{
"_account_id": 1000096,
"name": "John Doe",
"email": "john.doe@example.com"
"email": "john.doe@example.com",
"username": "john"
}
----
@@ -459,6 +460,8 @@ Only set if detailed account information is requested.
|`email` |optional|
The email address the user prefers to be contacted through. +
Only set if detailed account information is requested.
|`username` |optional|The username of the user. +
Only set if detailed account information is requested.
|===========================
[[account-input]]

View File

@@ -293,12 +293,14 @@ describes the group.
{
"_account_id": 1000097,
"name": "Jane Roe",
"email": "jane.roe@example.com"
"email": "jane.roe@example.com",
"username": "jane"
},
{
"_account_id": 1000096,
"name": "John Doe",
"email": "john.doe@example.com"
"username": "john"
}
],
"includes": []
@@ -620,12 +622,14 @@ full name, preferred email and id.
{
"_account_id": 1000097,
"name": "Jane Roe",
"email": "jane.roe@example.com"
"email": "jane.roe@example.com",
"username": "jane"
},
{
"_account_id": 1000096,
"name": "John Doe",
"email": "john.doe@example.com"
"email": "john.doe@example.com",
"username": "john"
}
]
----
@@ -657,17 +661,20 @@ are not visible to the calling user are ignored.
{
"_account_id": 1000097,
"name": "Jane Roe",
"email": "jane.roe@example.com"
"email": "jane.roe@example.com",
"username": "jane"
},
{
"_account_id": 1000096,
"name": "John Doe",
"email": "john.doe@example.com"
"email": "john.doe@example.com",
"username": "john"
},
{
"_account_id": 1000098,
"name": "Richard Roe",
"email": "richard.roe@example.com"
"email": "richard.roe@example.com",
"username": "rroe"
}
]
----
@@ -698,7 +705,8 @@ AccountInfo] entity is returned that describes the group member.
{
"_account_id": 1000096,
"name": "John Doe",
"email": "john.doe@example.com"
"email": "john.doe@example.com",
"username": "john"
}
----
@@ -728,7 +736,8 @@ AccountInfo] entity is returned that describes the group member.
{
"_account_id": 1000037,
"name": "John Doe",
"email": "john.doe@example.com"
"email": "john.doe@example.com",
"username": "john"
}
----
@@ -782,12 +791,14 @@ already a member of the group.
{
"_account_id": 1000057,
"name": "Jane Roe",
"email": "jane.roe@example.com"
"email": "jane.roe@example.com",
"username": "jane"
},
{
"_account_id": 1000037,
"name": "John Doe",
"email": "john.doe@example.com"
"email": "john.doe@example.com",
"username": "john"
}
]
----

View File

@@ -112,12 +112,14 @@ public class AccountInfo {
public Integer _account_id;
public String name;
public String email;
public String username;
private void fill(Account account, boolean detailed) {
name = account.getFullName();
if (detailed) {
_account_id = account.getId().get();
email = account.getPreferredEmail();
username = account.getUserName();
}
}
}

View File

@@ -49,7 +49,7 @@ public class ListMembers implements RestReadView<GroupResource> {
private boolean recursive;
@Inject
ListMembers(GroupCache groupCache,
protected ListMembers(GroupCache groupCache,
GroupDetailFactory.Factory groupDetailFactory,
AccountInfo.Loader.Factory accountLoaderFactory) {
this.groupCache = groupCache;
@@ -63,8 +63,19 @@ public class ListMembers implements RestReadView<GroupResource> {
if (resource.toAccountGroup() == null) {
throw new MethodNotAllowedException();
}
return apply(resource.getGroupUUID());
}
public List<AccountInfo> apply(AccountGroup group)
throws MethodNotAllowedException, OrmException {
return apply(group.getGroupUUID());
}
public List<AccountInfo> apply(AccountGroup.UUID groupId)
throws MethodNotAllowedException, OrmException {
final Map<Account.Id, AccountInfo> members =
getMembers(resource.getGroupUUID(), new HashSet<AccountGroup.UUID>());
getMembers(groupId, new HashSet<AccountGroup.UUID>());
final List<AccountInfo> memberInfos = Lists.newArrayList(members.values());
Collections.sort(memberInfos, new Comparator<AccountInfo>() {
@Override

View File

@@ -39,6 +39,7 @@ public class DefaultCommandModule extends CommandModule {
command(gerrit, BanCommitCommand.class);
command(gerrit, FlushCaches.class);
command(gerrit, ListProjectsCommand.class);
command(gerrit, ListMembersCommand.class);
command(gerrit, ListGroupsCommand.class);
command(gerrit, LsUserRefs.class);
command(gerrit, Query.class);

View File

@@ -0,0 +1,116 @@
// Copyright (C) 2013 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.sshd.commands;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountInfo;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupDetailFactory.Factory;
import com.google.gerrit.server.group.ListMembers;
import com.google.gerrit.server.ioutil.ColumnFormatter;
import com.google.gerrit.sshd.BaseCommand;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gwtorm.server.OrmException;
import org.apache.sshd.server.Environment;
import org.kohsuke.args4j.Argument;
import java.io.PrintWriter;
import java.util.List;
import javax.inject.Inject;
/**
* Implements a command that allows the user to see the members of a group.
*/
@CommandMetaData(name = "ls-members", descr = "Lists the members of a given group")
public class ListMembersCommand extends BaseCommand {
@Inject
ListMembersCommandImpl impl;
@Override
public void start(Environment env) {
startThread(new CommandRunnable() {
@Override
public void run() throws Exception {
parseCommandLine(impl);
final PrintWriter stdout = toPrintWriter(out);
try {
impl.display(stdout);
} finally {
stdout.flush();
}
}
});
}
private static class ListMembersCommandImpl extends ListMembers {
@Argument(required = true, usage = "the name of the group", metaVar = "GROUPNAME")
private String name;
private final GroupCache groupCache;
@Inject
protected ListMembersCommandImpl(GroupCache groupCache,
Factory groupDetailFactory,
AccountInfo.Loader.Factory accountLoaderFactory,
AccountCache accountCache) {
super(groupCache, groupDetailFactory, accountLoaderFactory);
this.groupCache = groupCache;
}
void display(PrintWriter writer) throws UnloggedFailure, OrmException {
AccountGroup group = groupCache.get(new AccountGroup.NameKey(name));
String errorText = "Group not found or not visible\n";
if (group == null) {
writer.write(errorText);
writer.flush();
return;
}
try {
List<AccountInfo> members = apply(group.getGroupUUID());
ColumnFormatter formatter = new ColumnFormatter(writer, '\t');
formatter.addColumn("id");
formatter.addColumn("username");
formatter.addColumn("full name");
formatter.addColumn("email");
formatter.nextLine();
for (AccountInfo member : members) {
if (member == null) {
continue;
}
formatter.addColumn(member._id.toString());
formatter.addColumn(Objects.firstNonNull(member.username, "n/a"));
formatter.addColumn(Objects.firstNonNull(
Strings.emptyToNull(member.name), "n/a"));
formatter.addColumn(Objects.firstNonNull(member.email, "n/a"));
formatter.nextLine();
}
formatter.finish();
} catch (MethodNotAllowedException e) {
writer.write(errorText);
writer.flush();
}
}
}
}