LCOV - code coverage report
Current view: top level - server/group - SystemGroupBackend.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 81 95 85.3 %
Date: 2022-11-19 15:00:39 Functions: 18 23 78.3 %

          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             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750