blob: d6f45f5cd8b1c25873000eeb10041f8cadda6a5d [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.client.AccountExternalId.SCHEME_USERNAME;
import com.google.gerrit.common.errors.InvalidUserNameException;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.gwtjsonrpc.common.VoidResult;
import com.google.gwtorm.server.OrmDuplicateKeyException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Callable;
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 newUsername;
@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.newUsername = newUsername;
}
public VoidResult call() throws OrmException, NameAlreadyUsedException,
InvalidUserNameException {
final Collection<AccountExternalId> old = old();
if (!old.isEmpty()) {
throw new IllegalStateException("Username cannot be changed.");
}
if (newUsername != null && !newUsername.isEmpty()) {
if (!USER_NAME_PATTERN.matcher(newUsername).matches()) {
throw new InvalidUserNameException();
}
final AccountExternalId.Key key =
new AccountExternalId.Key(SCHEME_USERNAME, newUsername);
try {
final AccountExternalId id =
new AccountExternalId(user.getAccountId(), key);
for (AccountExternalId i : old) {
if (i.getPassword() != null) {
id.setPassword(i.getPassword());
}
}
db.accountExternalIds().insert(Collections.singleton(id));
} catch (OrmDuplicateKeyException dupeErr) {
// If we are using this identity, don't report the exception.
//
AccountExternalId other = db.accountExternalIds().get(key);
if (other != null && other.getAccountId().equals(user.getAccountId())) {
return VoidResult.INSTANCE;
}
// Otherwise, someone else has this identity.
//
throw new NameAlreadyUsedException();
}
}
// If we have any older user names, remove them.
//
db.accountExternalIds().delete(old);
for (AccountExternalId i : old) {
sshKeyCache.evict(i.getSchemeRest());
accountCache.evictByUsername(i.getSchemeRest());
}
accountCache.evict(user.getAccountId());
accountCache.evictByUsername(newUsername);
sshKeyCache.evict(newUsername);
return VoidResult.INSTANCE;
}
private Collection<AccountExternalId> old() throws OrmException {
final Collection<AccountExternalId> r = new ArrayList<AccountExternalId>(1);
for (AccountExternalId i : db.accountExternalIds().byAccount(
user.getAccountId())) {
if (i.isScheme(SCHEME_USERNAME)) {
r.add(i);
}
}
return r;
}
}