Line data Source code
1 : // Copyright (C) 2017 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.git.validators; 16 : 17 : import static java.util.stream.Collectors.toSet; 18 : 19 : import com.google.common.collect.ImmutableList; 20 : import com.google.gerrit.common.Nullable; 21 : import com.google.gerrit.entities.Account; 22 : import com.google.gerrit.server.IdentifiedUser; 23 : import com.google.gerrit.server.account.AccountConfig; 24 : import com.google.gerrit.server.account.AccountProperties; 25 : import com.google.gerrit.server.config.AllUsersName; 26 : import com.google.gerrit.server.git.ValidationError; 27 : import com.google.gerrit.server.mail.send.OutgoingEmailValidator; 28 : import com.google.inject.Inject; 29 : import com.google.inject.Provider; 30 : import java.io.IOException; 31 : import java.util.List; 32 : import java.util.Optional; 33 : import org.eclipse.jgit.errors.ConfigInvalidException; 34 : import org.eclipse.jgit.lib.ObjectId; 35 : import org.eclipse.jgit.lib.Repository; 36 : import org.eclipse.jgit.revwalk.RevWalk; 37 : 38 : /** 39 : * Validator that is used to ensure that new commits on any ref in {@code refs/users} are conforming 40 : * to the NoteDb format for accounts. Used when a user pushes to one of the refs in {@code 41 : * refs/users} manually. 42 : */ 43 : public class AccountValidator { 44 : 45 : private final Provider<IdentifiedUser> self; 46 : private final AllUsersName allUsersName; 47 : private final OutgoingEmailValidator emailValidator; 48 : 49 : @Inject 50 : public AccountValidator( 51 : Provider<IdentifiedUser> self, 52 : AllUsersName allUsersName, 53 146 : OutgoingEmailValidator emailValidator) { 54 146 : this.self = self; 55 146 : this.allUsersName = allUsersName; 56 146 : this.emailValidator = emailValidator; 57 146 : } 58 : 59 : /** 60 : * Returns a list of validation messages. An empty list means that there were no issues found. If 61 : * the list is non-empty, the commit will be rejected. 62 : */ 63 : public List<String> validate( 64 : Account.Id accountId, 65 : Repository allUsersRepo, 66 : RevWalk rw, 67 : @Nullable ObjectId oldId, 68 : ObjectId newId) 69 : throws IOException { 70 2 : Optional<Account> oldAccount = Optional.empty(); 71 2 : if (oldId != null && !ObjectId.zeroId().equals(oldId)) { 72 : try { 73 2 : oldAccount = loadAccount(accountId, allUsersRepo, rw, oldId, null); 74 0 : } catch (ConfigInvalidException e) { 75 : // ignore, maybe the new commit is repairing it now 76 2 : } 77 : } 78 : 79 2 : ImmutableList.Builder<String> messages = ImmutableList.builder(); 80 : Optional<Account> newAccount; 81 : try { 82 2 : newAccount = loadAccount(accountId, allUsersRepo, rw, newId, messages); 83 1 : } catch (ConfigInvalidException e) { 84 1 : return ImmutableList.of( 85 1 : String.format( 86 : "commit '%s' has an invalid '%s' file for account '%s': %s", 87 1 : newId.name(), AccountProperties.ACCOUNT_CONFIG, accountId.get(), e.getMessage())); 88 2 : } 89 : 90 2 : if (!newAccount.isPresent()) { 91 0 : return ImmutableList.of(String.format("account '%s' does not exist", accountId.get())); 92 : } 93 : 94 2 : if (!newAccount.get().isActive() && accountId.equals(self.get().getAccountId())) { 95 1 : messages.add("cannot deactivate own account"); 96 : } 97 : 98 2 : String newPreferredEmail = newAccount.get().preferredEmail(); 99 2 : if (newPreferredEmail != null 100 2 : && (!oldAccount.isPresent() 101 2 : || !newPreferredEmail.equals(oldAccount.get().preferredEmail()))) { 102 1 : if (!emailValidator.isValid(newPreferredEmail)) { 103 1 : messages.add( 104 1 : String.format( 105 : "invalid preferred email '%s' for account '%s'", 106 1 : newPreferredEmail, accountId.get())); 107 : } 108 : } 109 : 110 2 : return messages.build(); 111 : } 112 : 113 : private Optional<Account> loadAccount( 114 : Account.Id accountId, 115 : Repository allUsersRepo, 116 : RevWalk rw, 117 : ObjectId commit, 118 : @Nullable ImmutableList.Builder<String> messages) 119 : throws IOException, ConfigInvalidException { 120 2 : rw.reset(); 121 2 : AccountConfig accountConfig = new AccountConfig(accountId, allUsersName, allUsersRepo); 122 2 : accountConfig.load(allUsersName, rw, commit); 123 2 : if (messages != null) { 124 2 : messages.addAll( 125 2 : accountConfig.getValidationErrors().stream() 126 2 : .map(ValidationError::getMessage) 127 2 : .collect(toSet())); 128 : } 129 2 : return accountConfig.getLoadedAccount(); 130 : } 131 : }