Added a ls-members command to the ssh daemon.
Change-Id: Ie6272d48dfab90290ba6164acb01e1507f8d98b5
This commit is contained in:
@@ -60,6 +60,9 @@ link:cmd-ban-commit.html[gerrit ban-commit]::
|
|||||||
link:cmd-ls-groups.html[gerrit ls-groups]::
|
link:cmd-ls-groups.html[gerrit ls-groups]::
|
||||||
List groups visible to the caller.
|
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]::
|
link:cmd-ls-projects.html[gerrit ls-projects]::
|
||||||
List projects visible to the caller.
|
List projects visible to the caller.
|
||||||
|
|
||||||
|
|||||||
64
Documentation/cmd-ls-members.txt
Normal file
64
Documentation/cmd-ls-members.txt
Normal 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]
|
||||||
@@ -31,7 +31,8 @@ Returns an account as an link:#account-info[AccountInfo] entity.
|
|||||||
{
|
{
|
||||||
"_account_id": 1000096,
|
"_account_id": 1000096,
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
"email": "john.doe@example.com"
|
"email": "john.doe@example.com",
|
||||||
|
"username": "john"
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -410,6 +411,8 @@ Only set if detailed account information is requested.
|
|||||||
|`email` |optional|
|
|`email` |optional|
|
||||||
The email address the user prefers to be contacted through. +
|
The email address the user prefers to be contacted through. +
|
||||||
Only set if detailed account information is requested.
|
Only set if detailed account information is requested.
|
||||||
|
|`username` |optional|The username of the user. +
|
||||||
|
Only set if detailed account information is requested.
|
||||||
|===========================
|
|===========================
|
||||||
|
|
||||||
[[capability-info]]
|
[[capability-info]]
|
||||||
|
|||||||
@@ -293,12 +293,14 @@ describes the group.
|
|||||||
{
|
{
|
||||||
"_account_id": 1000097,
|
"_account_id": 1000097,
|
||||||
"name": "Jane Roe",
|
"name": "Jane Roe",
|
||||||
"email": "jane.roe@example.com"
|
"email": "jane.roe@example.com",
|
||||||
|
"username": "jane"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_account_id": 1000096,
|
"_account_id": 1000096,
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
"email": "john.doe@example.com"
|
"email": "john.doe@example.com"
|
||||||
|
"username": "john"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"includes": []
|
"includes": []
|
||||||
@@ -620,12 +622,14 @@ full name, preferred email and id.
|
|||||||
{
|
{
|
||||||
"_account_id": 1000097,
|
"_account_id": 1000097,
|
||||||
"name": "Jane Roe",
|
"name": "Jane Roe",
|
||||||
"email": "jane.roe@example.com"
|
"email": "jane.roe@example.com",
|
||||||
|
"username": "jane"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_account_id": 1000096,
|
"_account_id": 1000096,
|
||||||
"name": "John Doe",
|
"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,
|
"_account_id": 1000097,
|
||||||
"name": "Jane Roe",
|
"name": "Jane Roe",
|
||||||
"email": "jane.roe@example.com"
|
"email": "jane.roe@example.com",
|
||||||
|
"username": "jane"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_account_id": 1000096,
|
"_account_id": 1000096,
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
"email": "john.doe@example.com"
|
"email": "john.doe@example.com",
|
||||||
|
"username": "john"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_account_id": 1000098,
|
"_account_id": 1000098,
|
||||||
"name": "Richard Roe",
|
"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,
|
"_account_id": 1000096,
|
||||||
"name": "John Doe",
|
"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,
|
"_account_id": 1000037,
|
||||||
"name": "John Doe",
|
"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,
|
"_account_id": 1000057,
|
||||||
"name": "Jane Roe",
|
"name": "Jane Roe",
|
||||||
"email": "jane.roe@example.com"
|
"email": "jane.roe@example.com",
|
||||||
|
"username": "jane"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_account_id": 1000037,
|
"_account_id": 1000037,
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
"email": "john.doe@example.com"
|
"email": "john.doe@example.com",
|
||||||
|
"username": "john"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -112,12 +112,14 @@ public class AccountInfo {
|
|||||||
public Integer _account_id;
|
public Integer _account_id;
|
||||||
public String name;
|
public String name;
|
||||||
public String email;
|
public String email;
|
||||||
|
public String username;
|
||||||
|
|
||||||
private void fill(Account account, boolean detailed) {
|
private void fill(Account account, boolean detailed) {
|
||||||
name = account.getFullName();
|
name = account.getFullName();
|
||||||
if (detailed) {
|
if (detailed) {
|
||||||
_account_id = account.getId().get();
|
_account_id = account.getId().get();
|
||||||
email = account.getPreferredEmail();
|
email = account.getPreferredEmail();
|
||||||
|
username = account.getUserName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class ListMembers implements RestReadView<GroupResource> {
|
|||||||
private boolean recursive;
|
private boolean recursive;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ListMembers(GroupCache groupCache,
|
protected ListMembers(GroupCache groupCache,
|
||||||
GroupDetailFactory.Factory groupDetailFactory,
|
GroupDetailFactory.Factory groupDetailFactory,
|
||||||
AccountInfo.Loader.Factory accountLoaderFactory) {
|
AccountInfo.Loader.Factory accountLoaderFactory) {
|
||||||
this.groupCache = groupCache;
|
this.groupCache = groupCache;
|
||||||
@@ -63,8 +63,19 @@ public class ListMembers implements RestReadView<GroupResource> {
|
|||||||
if (resource.toAccountGroup() == null) {
|
if (resource.toAccountGroup() == null) {
|
||||||
throw new MethodNotAllowedException();
|
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 =
|
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());
|
final List<AccountInfo> memberInfos = Lists.newArrayList(members.values());
|
||||||
Collections.sort(memberInfos, new Comparator<AccountInfo>() {
|
Collections.sort(memberInfos, new Comparator<AccountInfo>() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public class DefaultCommandModule extends CommandModule {
|
|||||||
command(gerrit, BanCommitCommand.class);
|
command(gerrit, BanCommitCommand.class);
|
||||||
command(gerrit, FlushCaches.class);
|
command(gerrit, FlushCaches.class);
|
||||||
command(gerrit, ListProjectsCommand.class);
|
command(gerrit, ListProjectsCommand.class);
|
||||||
|
command(gerrit, ListMembersCommand.class);
|
||||||
command(gerrit, ListGroupsCommand.class);
|
command(gerrit, ListGroupsCommand.class);
|
||||||
command(gerrit, LsUserRefs.class);
|
command(gerrit, LsUserRefs.class);
|
||||||
command(gerrit, Query.class);
|
command(gerrit, Query.class);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user