Line data Source code
1 : // Copyright (C) 2013 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : package com.google.gerrit.sshd.commands;
16 :
17 : import static java.util.stream.Collectors.joining;
18 : import static java.util.stream.Collectors.toList;
19 :
20 : import com.google.common.base.MoreObjects;
21 : import com.google.common.collect.Streams;
22 : import com.google.gerrit.entities.Account;
23 : import com.google.gerrit.entities.AccountGroup;
24 : import com.google.gerrit.entities.InternalGroup;
25 : import com.google.gerrit.extensions.restapi.IdString;
26 : import com.google.gerrit.extensions.restapi.RestApiException;
27 : import com.google.gerrit.extensions.restapi.TopLevelResource;
28 : import com.google.gerrit.server.account.AccountCache;
29 : import com.google.gerrit.server.account.AccountState;
30 : import com.google.gerrit.server.account.GroupCache;
31 : import com.google.gerrit.server.group.GroupResource;
32 : import com.google.gerrit.server.restapi.group.AddMembers;
33 : import com.google.gerrit.server.restapi.group.AddSubgroups;
34 : import com.google.gerrit.server.restapi.group.DeleteMembers;
35 : import com.google.gerrit.server.restapi.group.DeleteSubgroups;
36 : import com.google.gerrit.server.restapi.group.GroupsCollection;
37 : import com.google.gerrit.sshd.CommandMetaData;
38 : import com.google.gerrit.sshd.SshCommand;
39 : import com.google.inject.Inject;
40 : import java.io.IOException;
41 : import java.io.UnsupportedEncodingException;
42 : import java.util.ArrayList;
43 : import java.util.List;
44 : import java.util.Optional;
45 : import org.kohsuke.args4j.Argument;
46 : import org.kohsuke.args4j.Option;
47 :
48 : @CommandMetaData(
49 : name = "set-members",
50 : description = "Modify members of specific group or number of groups")
51 1 : public class SetMembersCommand extends SshCommand {
52 :
53 1 : @Option(
54 : name = "--add",
55 : aliases = {"-a"},
56 : metaVar = "USER",
57 : usage = "users that should be added as group member")
58 : private List<Account.Id> accountsToAdd = new ArrayList<>();
59 :
60 1 : @Option(
61 : name = "--remove",
62 : aliases = {"-r"},
63 : metaVar = "USER",
64 : usage = "users that should be removed from the group")
65 : private List<Account.Id> accountsToRemove = new ArrayList<>();
66 :
67 1 : @Option(
68 : name = "--include",
69 : aliases = {"-i"},
70 : metaVar = "GROUP",
71 : usage = "group that should be included as group member")
72 : private List<AccountGroup.UUID> groupsToInclude = new ArrayList<>();
73 :
74 1 : @Option(
75 : name = "--exclude",
76 : aliases = {"-e"},
77 : metaVar = "GROUP",
78 : usage = "group that should be excluded from the group")
79 : private List<AccountGroup.UUID> groupsToRemove = new ArrayList<>();
80 :
81 1 : @Argument(
82 : index = 0,
83 : required = true,
84 : multiValued = true,
85 : metaVar = "GROUP",
86 : usage = "groups to modify")
87 : private List<AccountGroup.UUID> groups = new ArrayList<>();
88 :
89 : @Inject private AddMembers addMembers;
90 :
91 : @Inject private DeleteMembers deleteMembers;
92 :
93 : @Inject private AddSubgroups addSubgroups;
94 :
95 : @Inject private DeleteSubgroups deleteSubgroups;
96 :
97 : @Inject private GroupsCollection groupsCollection;
98 :
99 : @Inject private GroupCache groupCache;
100 :
101 : @Inject private AccountCache accountCache;
102 :
103 : @Override
104 : protected void run() throws UnloggedFailure, Failure, Exception {
105 0 : enableGracefulStop();
106 : try {
107 0 : for (AccountGroup.UUID groupUuid : groups) {
108 0 : GroupResource resource =
109 0 : groupsCollection.parse(TopLevelResource.INSTANCE, IdString.fromUrl(groupUuid.get()));
110 0 : if (!accountsToRemove.isEmpty()) {
111 0 : deleteMembers.apply(resource, fromMembers(accountsToRemove));
112 0 : reportMembersAction("removed from", resource, accountsToRemove);
113 : }
114 0 : if (!groupsToRemove.isEmpty()) {
115 0 : deleteSubgroups.apply(resource, fromGroups(groupsToRemove));
116 0 : reportGroupsAction("excluded from", resource, groupsToRemove);
117 : }
118 0 : if (!accountsToAdd.isEmpty()) {
119 0 : addMembers.apply(resource, fromMembers(accountsToAdd));
120 0 : reportMembersAction("added to", resource, accountsToAdd);
121 : }
122 0 : if (!groupsToInclude.isEmpty()) {
123 0 : addSubgroups.apply(resource, fromGroups(groupsToInclude));
124 0 : reportGroupsAction("included to", resource, groupsToInclude);
125 : }
126 0 : }
127 0 : } catch (RestApiException e) {
128 0 : throw die(e.getMessage());
129 0 : }
130 0 : }
131 :
132 : private void reportMembersAction(
133 : String action, GroupResource group, List<Account.Id> accountIdList)
134 : throws UnsupportedEncodingException, IOException {
135 0 : String names =
136 0 : accountIdList.stream()
137 0 : .map(
138 : accountId -> {
139 0 : Optional<AccountState> accountState = accountCache.get(accountId);
140 0 : if (!accountState.isPresent()) {
141 0 : return "n/a";
142 : }
143 0 : return MoreObjects.firstNonNull(
144 0 : accountState.get().account().preferredEmail(), "n/a");
145 : })
146 0 : .collect(joining(", "));
147 0 : out.write(
148 0 : String.format("Members %s group %s: %s\n", action, group.getName(), names).getBytes(ENC));
149 0 : }
150 :
151 : private void reportGroupsAction(
152 : String action, GroupResource group, List<AccountGroup.UUID> groupUuidList)
153 : throws UnsupportedEncodingException, IOException {
154 0 : String names =
155 0 : groupUuidList.stream()
156 0 : .map(uuid -> groupCache.get(uuid).map(InternalGroup::getName))
157 0 : .flatMap(Streams::stream)
158 0 : .collect(joining(", "));
159 0 : out.write(
160 0 : String.format("Groups %s group %s: %s\n", action, group.getName(), names).getBytes(ENC));
161 0 : }
162 :
163 : private AddSubgroups.Input fromGroups(List<AccountGroup.UUID> accounts) {
164 0 : return AddSubgroups.Input.fromGroups(accounts.stream().map(Object::toString).collect(toList()));
165 : }
166 :
167 : private AddMembers.Input fromMembers(List<Account.Id> accounts) {
168 0 : return AddMembers.Input.fromMembers(accounts.stream().map(Object::toString).collect(toList()));
169 : }
170 : }
|