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.server.group; 16 : 17 : import static java.util.Objects.requireNonNull; 18 : import static java.util.stream.Collectors.toSet; 19 : 20 : import com.google.common.annotations.VisibleForTesting; 21 : import com.google.common.base.MoreObjects; 22 : import com.google.common.collect.ImmutableMap; 23 : import com.google.common.collect.ImmutableSet; 24 : import com.google.gerrit.common.Nullable; 25 : import com.google.gerrit.entities.AccountGroup; 26 : import com.google.gerrit.entities.GroupDescription; 27 : import com.google.gerrit.entities.GroupReference; 28 : import com.google.gerrit.server.CurrentUser; 29 : import com.google.gerrit.server.ExternalUser; 30 : import com.google.gerrit.server.IdentifiedUser; 31 : import com.google.gerrit.server.StartupCheck; 32 : import com.google.gerrit.server.StartupException; 33 : import com.google.gerrit.server.account.AbstractGroupBackend; 34 : import com.google.gerrit.server.account.GroupMembership; 35 : import com.google.gerrit.server.account.ListGroupMembership; 36 : import com.google.gerrit.server.config.GerritServerConfig; 37 : import com.google.gerrit.server.group.db.Groups; 38 : import com.google.gerrit.server.project.ProjectState; 39 : import com.google.inject.Inject; 40 : import com.google.inject.Singleton; 41 : import java.io.IOException; 42 : import java.util.ArrayList; 43 : import java.util.Collection; 44 : import java.util.Collections; 45 : import java.util.HashMap; 46 : import java.util.List; 47 : import java.util.Locale; 48 : import java.util.Map; 49 : import java.util.NavigableMap; 50 : import java.util.Optional; 51 : import java.util.Set; 52 : import java.util.TreeMap; 53 : import org.eclipse.jgit.errors.ConfigInvalidException; 54 : import org.eclipse.jgit.lib.Config; 55 : 56 : @Singleton 57 : public class SystemGroupBackend extends AbstractGroupBackend { 58 : public static final String SYSTEM_GROUP_SCHEME = "global:"; 59 : 60 : /** Common UUID assigned to the "Anonymous Users" group. */ 61 152 : public static final AccountGroup.UUID ANONYMOUS_USERS = 62 152 : AccountGroup.uuid(SYSTEM_GROUP_SCHEME + "Anonymous-Users"); 63 : 64 : /** Common UUID assigned to the "Registered Users" group. */ 65 152 : public static final AccountGroup.UUID REGISTERED_USERS = 66 152 : AccountGroup.uuid(SYSTEM_GROUP_SCHEME + "Registered-Users"); 67 : 68 : /** Common UUID assigned to the "Project Owners" placeholder group. */ 69 152 : public static final AccountGroup.UUID PROJECT_OWNERS = 70 152 : AccountGroup.uuid(SYSTEM_GROUP_SCHEME + "Project-Owners"); 71 : 72 : /** Common UUID assigned to the "Change Owner" placeholder group. */ 73 152 : public static final AccountGroup.UUID CHANGE_OWNER = 74 152 : AccountGroup.uuid(SYSTEM_GROUP_SCHEME + "Change-Owner"); 75 : 76 152 : private static final AccountGroup.UUID[] all = { 77 : ANONYMOUS_USERS, REGISTERED_USERS, PROJECT_OWNERS, CHANGE_OWNER, 78 : }; 79 : 80 : public static boolean isSystemGroup(AccountGroup.UUID uuid) { 81 150 : return uuid.get().startsWith(SYSTEM_GROUP_SCHEME); 82 : } 83 : 84 : public static boolean isAnonymousOrRegistered(GroupReference ref) { 85 0 : return isAnonymousOrRegistered(ref.getUUID()); 86 : } 87 : 88 : public static boolean isAnonymousOrRegistered(AccountGroup.UUID uuid) { 89 0 : return ANONYMOUS_USERS.equals(uuid) || REGISTERED_USERS.equals(uuid); 90 : } 91 : 92 : private final ImmutableSet<String> reservedNames; 93 : private final NavigableMap<String, GroupReference> namesToGroups; 94 : private final ImmutableSet<String> names; 95 : private final ImmutableMap<AccountGroup.UUID, GroupReference> uuids; 96 : private final ImmutableSet<AccountGroup.UUID> externalUserMemberships; 97 : 98 : @Inject 99 : @VisibleForTesting 100 151 : public SystemGroupBackend(@GerritServerConfig Config cfg) { 101 151 : NavigableMap<String, GroupReference> n = new TreeMap<>(); 102 151 : ImmutableMap.Builder<AccountGroup.UUID, GroupReference> u = ImmutableMap.builder(); 103 : 104 151 : ImmutableSet.Builder<String> reservedNamesBuilder = ImmutableSet.builder(); 105 151 : for (AccountGroup.UUID uuid : all) { 106 151 : int c = uuid.get().indexOf(':'); 107 151 : String defaultName = uuid.get().substring(c + 1).replace('-', ' '); 108 151 : reservedNamesBuilder.add(defaultName); 109 151 : String configuredName = cfg.getString("groups", uuid.get(), "name"); 110 151 : GroupReference ref = 111 151 : GroupReference.create(uuid, MoreObjects.firstNonNull(configuredName, defaultName)); 112 151 : n.put(ref.getName().toLowerCase(Locale.US), ref); 113 151 : u.put(ref.getUUID(), ref); 114 : } 115 151 : reservedNames = reservedNamesBuilder.build(); 116 151 : namesToGroups = Collections.unmodifiableNavigableMap(n); 117 151 : names = 118 151 : ImmutableSet.copyOf( 119 151 : namesToGroups.values().stream().map(GroupReference::getName).collect(toSet())); 120 151 : uuids = u.build(); 121 151 : externalUserMemberships = 122 151 : cfg.getBoolean("groups", null, "includeExternalUsersInRegisteredUsersGroup", true) 123 151 : ? ImmutableSet.of(ANONYMOUS_USERS, REGISTERED_USERS) 124 151 : : ImmutableSet.of(ANONYMOUS_USERS); 125 151 : } 126 : 127 : public GroupReference getGroup(AccountGroup.UUID uuid) { 128 151 : return requireNonNull(uuids.get(uuid), () -> String.format("group %s not found", uuid.get())); 129 : } 130 : 131 : public Set<String> getNames() { 132 29 : return names; 133 : } 134 : 135 : public Set<String> getReservedNames() { 136 29 : return reservedNames; 137 : } 138 : 139 : @Override 140 : public boolean handles(AccountGroup.UUID uuid) { 141 150 : return isSystemGroup(uuid); 142 : } 143 : 144 : @Nullable 145 : @Override 146 : public GroupDescription.Basic get(AccountGroup.UUID uuid) { 147 13 : final GroupReference ref = uuids.get(uuid); 148 13 : if (ref == null) { 149 0 : return null; 150 : } 151 13 : return new GroupDescription.Basic() { 152 : @Override 153 : public String getName() { 154 9 : return ref.getName(); 155 : } 156 : 157 : @Override 158 : public AccountGroup.UUID getGroupUUID() { 159 8 : return ref.getUUID(); 160 : } 161 : 162 : @Nullable 163 : @Override 164 : public String getUrl() { 165 7 : return null; 166 : } 167 : 168 : @Nullable 169 : @Override 170 : public String getEmailAddress() { 171 0 : return null; 172 : } 173 : }; 174 : } 175 : 176 : @Override 177 : public Collection<GroupReference> suggest(String name, ProjectState project) { 178 43 : String nameLC = name.toLowerCase(Locale.US); 179 43 : NavigableMap<String, GroupReference> matches = 180 43 : namesToGroups.tailMap(nameLC, /* inclusive= */ true); 181 43 : if (matches.isEmpty()) { 182 28 : return new ArrayList<>(); 183 : } 184 : 185 31 : List<GroupReference> r = new ArrayList<>(matches.size()); 186 31 : for (Map.Entry<String, GroupReference> e : matches.entrySet()) { 187 31 : if (e.getKey().startsWith(nameLC)) { 188 8 : r.add(e.getValue()); 189 : } else { 190 : break; 191 : } 192 8 : } 193 31 : return r; 194 : } 195 : 196 : @Override 197 : public GroupMembership membershipsOf(CurrentUser user) { 198 150 : if (user instanceof ExternalUser) { 199 1 : return new ListGroupMembership(externalUserMemberships); 200 : } 201 150 : if (user instanceof IdentifiedUser) { 202 150 : return new ListGroupMembership(ImmutableSet.of(ANONYMOUS_USERS, REGISTERED_USERS)); 203 : } 204 0 : return new ListGroupMembership(ImmutableSet.of(ANONYMOUS_USERS)); 205 : } 206 : 207 : public static class NameCheck implements StartupCheck { 208 : private final Config cfg; 209 : private final Groups groups; 210 : 211 : @Inject 212 138 : NameCheck(@GerritServerConfig Config cfg, Groups groups) { 213 138 : this.cfg = cfg; 214 138 : this.groups = groups; 215 138 : } 216 : 217 : @Override 218 : public void check() throws StartupException { 219 138 : Map<AccountGroup.UUID, String> configuredNames = new HashMap<>(); 220 138 : Map<String, AccountGroup.UUID> byLowerCaseConfiguredName = new HashMap<>(); 221 138 : for (AccountGroup.UUID uuid : all) { 222 138 : String configuredName = cfg.getString("groups", uuid.get(), "name"); 223 138 : if (configuredName != null) { 224 1 : configuredNames.put(uuid, configuredName); 225 1 : byLowerCaseConfiguredName.put(configuredName.toLowerCase(Locale.US), uuid); 226 : } 227 : } 228 138 : if (configuredNames.isEmpty()) { 229 138 : return; 230 : } 231 : 232 : Optional<GroupReference> conflictingGroup; 233 : try { 234 1 : conflictingGroup = 235 : groups 236 1 : .getAllGroupReferences() 237 1 : .filter(group -> hasConfiguredName(byLowerCaseConfiguredName, group)) 238 1 : .findAny(); 239 : 240 0 : } catch (IOException | ConfigInvalidException ignored) { 241 0 : return; 242 1 : } 243 : 244 1 : if (conflictingGroup.isPresent()) { 245 0 : GroupReference group = conflictingGroup.get(); 246 0 : String groupName = group.getName(); 247 0 : AccountGroup.UUID systemGroupUuid = byLowerCaseConfiguredName.get(groupName); 248 0 : throw new StartupException( 249 0 : getAmbiguousNameMessage(groupName, group.getUUID(), systemGroupUuid)); 250 : } 251 1 : } 252 : 253 : private static boolean hasConfiguredName( 254 : Map<String, AccountGroup.UUID> byLowerCaseConfiguredName, GroupReference group) { 255 1 : String name = group.getName().toLowerCase(Locale.US); 256 1 : return byLowerCaseConfiguredName.keySet().contains(name); 257 : } 258 : 259 : private static String getAmbiguousNameMessage( 260 : String groupName, AccountGroup.UUID groupUuid, AccountGroup.UUID systemGroupUuid) { 261 0 : return String.format( 262 : "The configured name '%s' for system group '%s' is ambiguous" 263 : + " with the name '%s' of existing group '%s'." 264 : + " Please remove/change the value for groups.%s.name in" 265 : + " gerrit.config.", 266 0 : groupName, systemGroupUuid.get(), groupName, groupUuid.get(), systemGroupUuid.get()); 267 : } 268 : } 269 : }