Line data Source code
1 : // Copyright (C) 2012 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.account; 16 : 17 : import com.google.common.collect.ImmutableList; 18 : import com.google.common.collect.ImmutableSet; 19 : import com.google.common.collect.Lists; 20 : import com.google.common.collect.Sets; 21 : import com.google.gerrit.entities.AccountGroup; 22 : import com.google.gerrit.entities.InternalGroup; 23 : import com.google.gerrit.server.CurrentUser; 24 : import com.google.inject.Inject; 25 : import com.google.inject.assistedinject.Assisted; 26 : import java.util.Collection; 27 : import java.util.HashSet; 28 : import java.util.List; 29 : import java.util.Map; 30 : import java.util.Set; 31 : import java.util.concurrent.ConcurrentHashMap; 32 : 33 : /** 34 : * Determines membership in the internal group system for a given user. 35 : * 36 : * <p>Groups the user is directly a member of are pulled from the in-memory AccountCache by way of 37 : * the IdentifiedUser. Transitive group memberhips are resolved on demand starting from the 38 : * requested group and looking for a path to a group the user is a member of. Other group backends 39 : * are supported by recursively invoking the universal GroupMembership. 40 : */ 41 : public class IncludingGroupMembership implements GroupMembership { 42 : public interface Factory { 43 : IncludingGroupMembership create(CurrentUser user); 44 : } 45 : 46 : private final GroupCache groupCache; 47 : private final GroupIncludeCache includeCache; 48 : private final CurrentUser user; 49 : private final Map<AccountGroup.UUID, Boolean> memberOf; 50 : private Set<AccountGroup.UUID> knownGroups; 51 : 52 : @Inject 53 : IncludingGroupMembership( 54 150 : GroupCache groupCache, GroupIncludeCache includeCache, @Assisted CurrentUser user) { 55 150 : this.groupCache = groupCache; 56 150 : this.includeCache = includeCache; 57 150 : this.user = user; 58 150 : memberOf = new ConcurrentHashMap<>(); 59 150 : } 60 : 61 : @Override 62 : public boolean contains(AccountGroup.UUID id) { 63 150 : if (id == null) { 64 0 : return false; 65 : } 66 : 67 150 : Boolean b = memberOf.get(id); 68 150 : return b != null ? b : containsAnyOf(ImmutableSet.of(id)); 69 : } 70 : 71 : @Override 72 : public boolean containsAnyOf(Iterable<AccountGroup.UUID> queryIds) { 73 : // Prefer lookup of a cached result over expanding includes. 74 150 : boolean tryExpanding = false; 75 150 : for (AccountGroup.UUID id : queryIds) { 76 150 : Boolean b = memberOf.get(id); 77 150 : if (b == null) { 78 150 : tryExpanding = true; 79 2 : } else if (b) { 80 0 : return true; 81 : } 82 150 : } 83 : 84 150 : if (tryExpanding) { 85 150 : Set<AccountGroup.UUID> queryIdsSet = new HashSet<>(); 86 150 : queryIds.forEach(i -> queryIdsSet.add(i)); 87 150 : Map<AccountGroup.UUID, InternalGroup> groups = groupCache.get(queryIdsSet); 88 150 : for (AccountGroup.UUID id : queryIds) { 89 150 : if (memberOf.containsKey(id)) { 90 : // Membership was earlier proven to be false. 91 0 : continue; 92 : } 93 : 94 150 : memberOf.put(id, false); 95 150 : InternalGroup group = groups.get(id); 96 150 : if (group == null) { 97 2 : continue; 98 : } 99 150 : if (user.isIdentifiedUser() && group.getMembers().contains(user.getAccountId())) { 100 150 : memberOf.put(id, true); 101 150 : return true; 102 : } 103 101 : if (search(group.getSubgroups())) { 104 3 : memberOf.put(id, true); 105 3 : return true; 106 : } 107 101 : } 108 : } 109 : 110 101 : return false; 111 : } 112 : 113 : @Override 114 : public Set<AccountGroup.UUID> intersection(Iterable<AccountGroup.UUID> groupIds) { 115 0 : Set<AccountGroup.UUID> r = new HashSet<>(); 116 0 : for (AccountGroup.UUID id : groupIds) { 117 0 : if (contains(id)) { 118 0 : r.add(id); 119 : } 120 0 : } 121 0 : return r; 122 : } 123 : 124 : private boolean search(Iterable<AccountGroup.UUID> ids) { 125 101 : return user.getEffectiveGroups().containsAnyOf(ids); 126 : } 127 : 128 : private ImmutableSet<AccountGroup.UUID> computeKnownGroups() { 129 20 : GroupMembership membership = user.getEffectiveGroups(); 130 : Collection<AccountGroup.UUID> direct = 131 20 : user.isIdentifiedUser() 132 20 : ? includeCache.getGroupsWithMember(user.getAccountId()) 133 20 : : ImmutableList.of(); 134 20 : direct.forEach(groupUuid -> memberOf.put(groupUuid, true)); 135 20 : Set<AccountGroup.UUID> r = Sets.newHashSet(direct); 136 20 : r.remove(null); 137 : 138 20 : List<AccountGroup.UUID> q = Lists.newArrayList(r); 139 20 : for (AccountGroup.UUID g : membership.intersection(includeCache.allExternalMembers())) { 140 1 : if (g != null && r.add(g)) { 141 1 : q.add(g); 142 : } 143 1 : } 144 : 145 20 : while (!q.isEmpty()) { 146 18 : AccountGroup.UUID id = q.remove(q.size() - 1); 147 18 : for (AccountGroup.UUID g : includeCache.parentGroupsOf(id)) { 148 1 : if (g != null && r.add(g)) { 149 1 : q.add(g); 150 1 : memberOf.put(g, true); 151 : } 152 1 : } 153 18 : } 154 20 : return ImmutableSet.copyOf(r); 155 : } 156 : 157 : @Override 158 : public Set<AccountGroup.UUID> getKnownGroups() { 159 20 : if (knownGroups == null) { 160 20 : knownGroups = computeKnownGroups(); 161 : } 162 20 : return knownGroups; 163 : } 164 : }