blob: ecdb4b93249f890c57e2a55b01095a4b45862ddd [file] [log] [blame]
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.account;
import static com.google.gerrit.reviewdb.AccountExternalId.SCHEME_USERNAME;
import com.google.common.collect.Lists;
import com.google.gerrit.common.errors.InvalidUserNameException;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountExternalId;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.gerrit.server.util.FutureUtil;
import com.google.gwtjsonrpc.client.VoidResult;
import com.google.gwtorm.client.OrmDuplicateKeyException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
/** Operation to change the username of an account. */
public class ChangeUserName implements Callable<VoidResult> {
private static final Pattern USER_NAME_PATTERN =
Pattern.compile(Account.USER_NAME_PATTERN);
/** Factory to change the username for the current user. */
public static class CurrentUser {
private final Factory factory;
private final Provider<ReviewDb> db;
private final Provider<IdentifiedUser> user;
@Inject
CurrentUser(Factory factory, Provider<ReviewDb> db,
Provider<IdentifiedUser> user) {
this.factory = factory;
this.db = db;
this.user = user;
}
public ChangeUserName create(String newUsername) {
return factory.create(db.get(), user.get(), newUsername);
}
}
/** Generic factory to change any user's username. */
public interface Factory {
ChangeUserName create(ReviewDb db, IdentifiedUser user, String newUsername);
}
private final AccountCache accountCache;
private final SshKeyCache sshKeyCache;
private final ReviewDb db;
private final IdentifiedUser user;
private final String newName;
@Inject
ChangeUserName(final AccountCache accountCache,
final SshKeyCache sshKeyCache,
@Assisted final ReviewDb db, @Assisted final IdentifiedUser user,
@Nullable @Assisted final String newUsername) {
this.accountCache = accountCache;
this.sshKeyCache = sshKeyCache;
this.db = db;
this.user = user;
this.newName = newUsername;
}
public VoidResult call() throws OrmException, NameAlreadyUsedException,
InvalidUserNameException {
if (hasUsername()) {
throw new IllegalStateException("Username cannot be changed.");
}
if (newName == null || !USER_NAME_PATTERN.matcher(newName).matches()) {
throw new InvalidUserNameException();
}
AccountExternalId.Key key = AccountExternalId.forUsername(newName);
List<Future<Void>> evictions = Lists.newArrayList();
try {
AccountExternalId id = new AccountExternalId(user.getAccountId(), key);
db.accountExternalIds().insert(Collections.singleton(id));
evictions.add(accountCache.evictAsync(key));
} catch (OrmDuplicateKeyException dupeErr) {
throw new NameAlreadyUsedException();
}
evictions.add(accountCache.evictAsync(user.getAccountId()));
evictions.add(accountCache.evictAsync(key));
evictions.add(sshKeyCache.evictAsync(newName));
FutureUtil.waitFor(evictions);
return VoidResult.INSTANCE;
}
private boolean hasUsername() {
for (AccountExternalId i : user.getAccountState().getExternalIds()) {
if (i.isScheme(SCHEME_USERNAME)) {
return true;
}
}
return false;
}
}