blob: fa56966f4a15f2e9e69580b1d006e563e3590929 [file] [log] [blame]
// Copyright (C) 2012 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.schema;
import static com.google.common.base.Strings.isNullOrEmpty;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.naming.NamingException;
import javax.naming.ldap.LdapName;
public class Schema_69 extends SchemaVersion {
private final GitRepositoryManager mgr;
private final PersonIdent serverUser;
@Inject
Schema_69(Provider<Schema_68> prior,
GitRepositoryManager mgr,
@GerritPersonIdent PersonIdent serverUser) {
super(prior);
this.mgr = mgr;
this.serverUser = serverUser;
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui)
throws OrmException, SQLException {
// Find all groups that have an LDAP type.
Map<AccountGroup.UUID, GroupReference> ldapUUIDMap = Maps.newHashMap();
Set<AccountGroup.UUID> toResolve = Sets.newHashSet();
List<AccountGroup.Id> toDelete = Lists.newArrayList();
List<AccountGroup.NameKey> namesToDelete = Lists.newArrayList();
Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
try {
ResultSet rs = stmt.executeQuery(
"SELECT group_id, group_uuid, external_name, name FROM account_groups"
+ " WHERE group_type ='LDAP'");
try {
while (rs.next()) {
AccountGroup.Id groupId = new AccountGroup.Id(rs.getInt(1));
AccountGroup.UUID groupUUID = new AccountGroup.UUID(rs.getString(2));
AccountGroup.NameKey name = new AccountGroup.NameKey(rs.getString(4));
String dn = rs.getString(3);
if (isNullOrEmpty(dn)) {
// The LDAP group does not have a DN. Determine if the UUID is used.
toResolve.add(groupUUID);
} else {
toDelete.add(groupId);
namesToDelete.add(name);
GroupReference ref = groupReference(dn);
ldapUUIDMap.put(groupUUID, ref);
}
}
} catch (NamingException e) {
throw new RuntimeException(e);
} finally {
rs.close();
}
} finally {
stmt.close();
}
if (toDelete.isEmpty() && toResolve.isEmpty()) {
return; // No ldap groups. Nothing to do.
}
ui.message("Update LDAP groups to be GroupReferences.");
// Update the groupOwnerUUID for LDAP groups to point to the new UUID.
List<AccountGroup> toUpdate = Lists.newArrayList();
Set<AccountGroup.UUID> resolveToUpdate = Sets.newHashSet();
Map<AccountGroup.UUID, AccountGroup> resolveGroups = Maps.newHashMap();
for (AccountGroup g : db.accountGroups().all()) {
if (ldapUUIDMap.containsKey(g.getGroupUUID())) {
continue; // Ignore the LDAP groups with a valid DN.
} else if (toResolve.contains(g.getGroupUUID())) {
resolveGroups.put(g.getGroupUUID(), g); // Keep the ones to resolve.
continue;
}
GroupReference ref = ldapUUIDMap.get(g.getOwnerGroupUUID());
if (ref != null) {
// Update the owner group UUID to the new ldap UUID scheme.
g.setOwnerGroupUUID(ref.getUUID());
toUpdate.add(g);
} else if (toResolve.contains(g.getOwnerGroupUUID())) {
// The unresolved group is used as an owner.
// Add to the list of LDAP groups to be made INTERNAL.
resolveToUpdate.add(g.getOwnerGroupUUID());
}
}
toResolve.removeAll(resolveToUpdate);
// Update project.config group references to use the new LDAP GroupReference
for (Project.NameKey name : mgr.list()) {
Repository git;
try {
git = mgr.openRepository(name);
} catch (RepositoryNotFoundException e) {
throw new OrmException(e);
} catch (IOException e) {
throw new OrmException(e);
}
try {
MetaDataUpdate md =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, name, git);
md.getCommitBuilder().setAuthor(serverUser);
md.getCommitBuilder().setCommitter(serverUser);
ProjectConfig config = ProjectConfig.read(md);
// Update the existing refences to the new reference.
boolean updated = false;
for (Map.Entry<AccountGroup.UUID, GroupReference> entry: ldapUUIDMap.entrySet()) {
GroupReference ref = config.getGroup(entry.getKey());
if (ref != null) {
updated = true;
ref.setName(entry.getValue().getName());
ref.setUUID(entry.getValue().getUUID());
config.resolve(ref);
}
}
// Determine if a toResolve group is used and should be made INTERNAL.
Iterator<AccountGroup.UUID> iter = toResolve.iterator();
while (iter.hasNext()) {
AccountGroup.UUID uuid = iter.next();
if (config.getGroup(uuid) != null) {
resolveToUpdate.add(uuid);
iter.remove();
}
}
if (!updated) {
continue;
}
md.setMessage("Switch LDAP group UUIDs to DNs\n");
config.commit(md);
} catch (IOException e) {
throw new OrmException(e);
} catch (ConfigInvalidException e) {
throw new OrmException(e);
} finally {
git.close();
}
}
for (AccountGroup.UUID uuid : resolveToUpdate) {
AccountGroup group = resolveGroups.get(uuid);
group.setType(AccountGroup.Type.INTERNAL);
toUpdate.add(group);
ui.message(String.format(
"*** Group has no DN and is inuse. Updated to be INTERNAL: %s",
group.getName()));
}
for (AccountGroup.UUID uuid : toResolve) {
AccountGroup group = resolveGroups.get(uuid);
toDelete.add(group.getId());
namesToDelete.add(group.getNameKey());
}
// Update group owners
db.accountGroups().update(toUpdate);
// Delete existing LDAP groups
db.accountGroupNames().deleteKeys(namesToDelete);
db.accountGroups().deleteKeys(toDelete);
}
private static GroupReference groupReference(String dn)
throws NamingException {
LdapName name = new LdapName(dn);
Preconditions.checkState(!name.isEmpty(), "Invalid LDAP dn: %s", dn);
String cn = name.get(name.size() - 1);
int index = cn.indexOf('=');
if (index >= 0) {
cn = cn.substring(index + 1);
}
return new GroupReference(new AccountGroup.UUID("ldap:" + dn), "ldap/" + cn);
}
}