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.account; 16 : 17 : import static com.google.common.collect.Streams.stream; 18 : import static java.util.stream.Collectors.toList; 19 : import static java.util.stream.Collectors.toSet; 20 : import static java.util.stream.Stream.concat; 21 : 22 : import com.google.common.base.Strings; 23 : import com.google.common.collect.Sets; 24 : import com.google.gerrit.entities.Account; 25 : import com.google.gerrit.extensions.common.AccountInfo; 26 : import com.google.gerrit.extensions.common.AccountInfo.Tags; 27 : import com.google.gerrit.extensions.common.AvatarInfo; 28 : import com.google.gerrit.extensions.registration.DynamicItem; 29 : import com.google.gerrit.extensions.registration.DynamicMap; 30 : import com.google.gerrit.server.CurrentUser; 31 : import com.google.gerrit.server.IdentifiedUser; 32 : import com.google.gerrit.server.account.externalids.ExternalId; 33 : import com.google.gerrit.server.avatar.AvatarProvider; 34 : import com.google.gerrit.server.data.AccountAttribute; 35 : import com.google.gerrit.server.permissions.GlobalPermission; 36 : import com.google.gerrit.server.permissions.PermissionBackend; 37 : import com.google.gerrit.server.permissions.PermissionBackendException; 38 : import com.google.inject.AbstractModule; 39 : import com.google.inject.Inject; 40 : import com.google.inject.Provider; 41 : import com.google.inject.Singleton; 42 : import java.util.ArrayList; 43 : import java.util.Collection; 44 : import java.util.Collections; 45 : import java.util.EnumSet; 46 : import java.util.List; 47 : import java.util.Map; 48 : import java.util.Objects; 49 : import java.util.Set; 50 : import java.util.stream.Stream; 51 : 52 : @Singleton 53 : public class InternalAccountDirectory extends AccountDirectory { 54 150 : static final Set<FillOptions> ID_ONLY = Collections.unmodifiableSet(EnumSet.of(FillOptions.ID)); 55 150 : static final Set<FillOptions> ALL_ACCOUNT_ATTRIBUTES = 56 150 : Collections.unmodifiableSet( 57 150 : EnumSet.of(FillOptions.NAME, FillOptions.EMAIL, FillOptions.USERNAME)); 58 : 59 138 : public static class InternalAccountDirectoryModule extends AbstractModule { 60 : @Override 61 : protected void configure() { 62 138 : bind(AccountDirectory.class).to(InternalAccountDirectory.class); 63 138 : } 64 : } 65 : 66 : private final AccountCache accountCache; 67 : private final DynamicItem<AvatarProvider> avatar; 68 : private final IdentifiedUser.GenericFactory userFactory; 69 : private final Provider<CurrentUser> self; 70 : private final PermissionBackend permissionBackend; 71 : private final ServiceUserClassifier serviceUserClassifier; 72 : private final DynamicMap<AccountTagProvider> accountTagProviders; 73 : 74 : @Inject 75 : InternalAccountDirectory( 76 : AccountCache accountCache, 77 : DynamicItem<AvatarProvider> avatar, 78 : IdentifiedUser.GenericFactory userFactory, 79 : Provider<CurrentUser> self, 80 : PermissionBackend permissionBackend, 81 : ServiceUserClassifier serviceUserClassifier, 82 150 : DynamicMap<AccountTagProvider> accountTagProviders) { 83 150 : this.accountCache = accountCache; 84 150 : this.avatar = avatar; 85 150 : this.userFactory = userFactory; 86 150 : this.self = self; 87 150 : this.permissionBackend = permissionBackend; 88 150 : this.serviceUserClassifier = serviceUserClassifier; 89 150 : this.accountTagProviders = accountTagProviders; 90 150 : } 91 : 92 : @Override 93 : public void fillAccountInfo(Iterable<? extends AccountInfo> in, Set<FillOptions> options) 94 : throws PermissionBackendException { 95 109 : if (options.equals(ID_ONLY)) { 96 74 : return; 97 : } 98 : 99 109 : boolean canModifyAccount = false; 100 109 : Account.Id currentUserId = null; 101 109 : if (self.get().isIdentifiedUser()) { 102 109 : currentUserId = self.get().getAccountId(); 103 109 : if (permissionBackend.currentUser().test(GlobalPermission.MODIFY_ACCOUNT)) { 104 104 : canModifyAccount = true; 105 : } 106 : } 107 : 108 109 : Set<FillOptions> fillOptionsWithoutSecondaryEmails = 109 109 : Sets.difference(options, EnumSet.of(FillOptions.SECONDARY_EMAILS)); 110 109 : Set<Account.Id> ids = stream(in).map(a -> Account.id(a._accountId)).collect(toSet()); 111 109 : Map<Account.Id, AccountState> accountStates = accountCache.get(ids); 112 109 : for (AccountInfo info : in) { 113 109 : Account.Id id = Account.id(info._accountId); 114 109 : AccountState state = accountStates.get(id); 115 109 : if (state != null) { 116 109 : if (!options.contains(FillOptions.SECONDARY_EMAILS) 117 8 : || Objects.equals(currentUserId, state.account().id()) 118 : || canModifyAccount) { 119 109 : fill(info, accountStates.get(id), options); 120 : } else { 121 : // user is not allowed to see secondary emails 122 2 : fill(info, accountStates.get(id), fillOptionsWithoutSecondaryEmails); 123 : } 124 : 125 : } else { 126 0 : info._accountId = options.contains(FillOptions.ID) ? id.get() : null; 127 : } 128 109 : } 129 109 : } 130 : 131 : @Override 132 : public void fillAccountAttributeInfo(Iterable<? extends AccountAttribute> in) { 133 3 : Set<Account.Id> ids = stream(in).map(a -> Account.id(a.accountId)).collect(toSet()); 134 3 : Map<Account.Id, AccountState> accountStates = accountCache.get(ids); 135 3 : for (AccountAttribute accountAttribute : in) { 136 3 : Account.Id id = Account.id(accountAttribute.accountId); 137 3 : AccountState accountState = accountStates.get(id); 138 3 : if (accountState != null) { 139 3 : fill(accountAttribute, accountState, ALL_ACCOUNT_ATTRIBUTES); 140 : } else { 141 0 : accountAttribute.accountId = null; 142 : } 143 3 : } 144 3 : } 145 : 146 : private void fill( 147 : AccountAttribute accountAttribute, AccountState accountState, Set<FillOptions> options) { 148 3 : Account account = accountState.account(); 149 3 : if (options.contains(FillOptions.NAME)) { 150 3 : accountAttribute.name = Strings.emptyToNull(account.fullName()); 151 3 : if (accountAttribute.name == null) { 152 1 : accountAttribute.name = accountState.userName().orElse(null); 153 : } 154 : } 155 3 : if (options.contains(FillOptions.EMAIL)) { 156 3 : accountAttribute.email = account.preferredEmail(); 157 : } 158 3 : if (options.contains(FillOptions.USERNAME)) { 159 3 : accountAttribute.username = accountState.userName().orElse(null); 160 : } 161 3 : if (options.contains(FillOptions.ID)) { 162 0 : accountAttribute.accountId = account.id().get(); 163 : } else { 164 : // Was previously set to look up account for filling. 165 3 : accountAttribute.accountId = null; 166 : } 167 3 : } 168 : 169 : private void fill(AccountInfo info, AccountState accountState, Set<FillOptions> options) { 170 109 : Account account = accountState.account(); 171 109 : if (options.contains(FillOptions.ID)) { 172 109 : info._accountId = account.id().get(); 173 : } else { 174 : // Was previously set to look up account for filling. 175 0 : info._accountId = null; 176 : } 177 109 : if (options.contains(FillOptions.NAME)) { 178 109 : info.name = Strings.emptyToNull(account.fullName()); 179 109 : if (info.name == null) { 180 18 : info.name = accountState.userName().orElse(null); 181 : } 182 : } 183 109 : if (options.contains(FillOptions.EMAIL)) { 184 109 : info.email = account.preferredEmail(); 185 : } 186 109 : if (options.contains(FillOptions.SECONDARY_EMAILS)) { 187 8 : info.secondaryEmails = getSecondaryEmails(account, accountState.externalIds()); 188 : } 189 109 : if (options.contains(FillOptions.USERNAME)) { 190 109 : info.username = accountState.userName().orElse(null); 191 : } 192 : 193 109 : if (options.contains(FillOptions.DISPLAY_NAME)) { 194 109 : info.displayName = account.displayName(); 195 : } 196 : 197 109 : if (options.contains(FillOptions.STATUS)) { 198 109 : info.status = account.status(); 199 : } 200 : 201 109 : if (options.contains(FillOptions.STATE)) { 202 109 : info.inactive = account.inactive() ? true : null; 203 : } 204 : 205 109 : if (options.contains(FillOptions.TAGS)) { 206 109 : List<String> tags = getTags(account.id()); 207 109 : if (!tags.isEmpty()) { 208 2 : info.tags = tags; 209 : } 210 : } 211 : 212 109 : if (options.contains(FillOptions.AVATARS)) { 213 109 : AvatarProvider ap = avatar.get(); 214 109 : if (ap != null) { 215 0 : info.avatars = new ArrayList<>(); 216 0 : IdentifiedUser user = userFactory.create(account.id()); 217 : 218 : // PolyGerrit UI uses the following sizes for avatars: 219 : // - 32px for avatars next to names e.g. on the dashboard. This is also Gerrit's default. 220 : // - 56px for the user's own avatar in the menu 221 : // - 100ox for other user's avatars on dashboards 222 : // - 120px for the user's own profile settings page 223 0 : addAvatar(ap, info, user, AvatarInfo.DEFAULT_SIZE); 224 0 : if (!info.avatars.isEmpty()) { 225 0 : addAvatar(ap, info, user, 56); 226 0 : addAvatar(ap, info, user, 100); 227 0 : addAvatar(ap, info, user, 120); 228 : } 229 : } 230 : } 231 109 : } 232 : 233 : public List<String> getSecondaryEmails(Account account, Collection<ExternalId> externalIds) { 234 8 : return ExternalId.getEmails(externalIds) 235 8 : .filter(e -> !e.equals(account.preferredEmail())) 236 8 : .sorted() 237 8 : .collect(toList()); 238 : } 239 : 240 : private List<String> getTags(Account.Id id) { 241 109 : Stream<String> tagsFromProviders = 242 109 : stream(accountTagProviders.iterator()) 243 109 : .flatMap(accountTagProvider -> accountTagProvider.get().getTags(id).stream()); 244 : Stream<String> tagsFromServiceUserClassifier = 245 109 : serviceUserClassifier.isServiceUser(id) ? Stream.of(Tags.SERVICE_USER) : Stream.empty(); 246 109 : return concat(tagsFromProviders, tagsFromServiceUserClassifier).collect(toList()); 247 : } 248 : 249 : private static void addAvatar( 250 : AvatarProvider provider, AccountInfo account, IdentifiedUser user, int size) { 251 0 : String url = provider.getUrl(user, size); 252 0 : if (url != null) { 253 0 : AvatarInfo avatar = new AvatarInfo(); 254 0 : avatar.url = url; 255 0 : avatar.height = size; 256 0 : account.avatars.add(avatar); 257 : } 258 0 : } 259 : }