Package com.google.gerrit.server.schema

Source Code of com.google.gerrit.server.schema.Schema_65

// 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 com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.Longs;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ContributorAgreement;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroupMember;
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
import com.google.gerrit.reviewdb.client.AccountGroupName;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.GroupUUID;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AnonymousCowardName;
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.gerrit.server.git.VersionedMetaData.BatchMetaDataUpdate;
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.lib.CommitBuilder;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.SystemReader;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

public class Schema_65 extends SchemaVersion {
  private final AllProjectsName allProjects;
  private final GitRepositoryManager mgr;
  private final PersonIdent serverUser;
  private final @AnonymousCowardName String anonymousCowardName;

  @Inject
  Schema_65(Provider<Schema_64> prior,
      AllProjectsName allProjects,
      GitRepositoryManager mgr,
      @GerritPersonIdent PersonIdent serverUser,
      @AnonymousCowardName String anonymousCowardName) {
    super(prior);
    this.allProjects = allProjects;
    this.mgr = mgr;
    this.serverUser = serverUser;
    this.anonymousCowardName = anonymousCowardName;
  }

  @Override
  protected void migrateData(ReviewDb db, UpdateUI ui)
      throws OrmException, SQLException {
    Repository git;
    try {
      git = mgr.openRepository(allProjects);
    } catch (IOException e) {
      throw new OrmException(e);
    }
    try {
      MetaDataUpdate md =
          new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjects, git);
      ProjectConfig config = ProjectConfig.read(md);
      Map<Integer, ContributorAgreement> agreements = getAgreementToAdd(db, config);
      if (agreements.isEmpty()) {
        return;
      }
      ui.message("Moved contributor agreements to project.config");

      // Create the auto verify groups.
      List<AccountGroup.UUID> adminGroupUUIDs = getAdministrateServerGroups(db, config);
      for (ContributorAgreement agreement : agreements.values()) {
        if (agreement.getAutoVerify() != null) {
          getOrCreateGroupForIndividuals(db, config, adminGroupUUIDs, agreement);
        }
      }

      // Scan AccountAgreement
      long minTime = addAccountAgreements(db, config, adminGroupUUIDs, agreements);

      ProjectConfig base = ProjectConfig.read(md, null);
      for (ContributorAgreement agreement : agreements.values()) {
        base.replace(agreement);
      }
      base.getAccountsSection().setSameGroupVisibility(
          config.getAccountsSection().getSameGroupVisibility());

      BatchMetaDataUpdate batch = base.openUpdate(md);
      try {
        // Scan AccountGroupAgreement
        List<AccountGroupAgreement> groupAgreements =
            getAccountGroupAgreements(db, agreements);

        // Find the earliest change
        for (AccountGroupAgreement aga : groupAgreements) {
          minTime = Math.min(minTime, aga.getTime());
        }
        minTime -= 60 * 1000; // 1 Minute

        CommitBuilder commit = new CommitBuilder();
        commit.setAuthor(new PersonIdent(serverUser, new Date(minTime)));
        commit.setCommitter(new PersonIdent(serverUser, new Date(minTime)));
        commit.setMessage("Add the ContributorAgreements for upgrade to Gerrit Code Review schema 65\n");
        batch.write(commit);

        for (AccountGroupAgreement aga : groupAgreements) {
          AccountGroup group = db.accountGroups().get(aga.groupId);
          if (group == null) {
            continue;
          }

          ContributorAgreement agreement = agreements.get(aga.claId);
          agreement.getAccepted().add(new PermissionRule(config.resolve(group)));
          base.replace(agreement);

          PersonIdent ident = null;
          if (aga.reviewedBy != null) {
            Account ua = db.accounts().get(aga.reviewedBy);
            if (ua != null) {
              String name = ua.getFullName();
              String email = ua.getPreferredEmail();

              if (email == null || email.isEmpty()) {
                // No preferred email is configured. Use a generic identity so we
                // don't leak an address the user may have given us, but doesn't
                // necessarily want to publish through Git records.
                //
                String user = ua.getUserName();
                if (user == null || user.isEmpty()) {
                  user = "account-" + ua.getId().toString();
                }

                String host = SystemReader.getInstance().getHostname();
                email = user + "@" + host;
              }

              if (name == null || name.isEmpty()) {
                final int at = email.indexOf('@');
                if (0 < at) {
                  name = email.substring(0, at);
                } else {
                  name = anonymousCowardName;
                }
              }

              ident = new PersonIdent(name, email, new Date(aga.getTime()), TimeZone.getDefault());
            }
          }
          if (ident == null) {
            ident = new PersonIdent(serverUser, new Date(aga.getTime()));
          }

          // Build the commits such that it keeps track of the date added and
          // who added it.
          commit = new CommitBuilder();
          commit.setAuthor(ident);
          commit.setCommitter(new PersonIdent(serverUser, new Date(aga.getTime())));

          String msg = String.format("Accept %s contributor agreement for %s\n",
              agreement.getName(), group.getName());
          if (!Strings.isNullOrEmpty(aga.reviewComments)) {
            msg += "\n" + aga.reviewComments + "\n";
          }
          commit.setMessage(msg);
          batch.write(commit);
        }

        // Merge the agreements with the other data in project.config.
        commit = new CommitBuilder();
        commit.setAuthor(serverUser);
        commit.setCommitter(serverUser);
        commit.setMessage("Upgrade to Gerrit Code Review schema 65\n");
        commit.addParentId(config.getRevision());
        batch.write(config, commit);

        // Save the the final metadata.
        batch.commitAt(config.getRevision());
      } finally {
        batch.close();
      }
    } catch (IOException e) {
      throw new OrmException(e);
    } catch (ConfigInvalidException e) {
      throw new OrmException(e);
    } finally {
      git.close();
    }
  }

  private Map<Integer, ContributorAgreement> getAgreementToAdd(
      ReviewDb db, ProjectConfig config) throws SQLException {
    Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
    try {
      ResultSet rs = stmt.executeQuery(
          "SELECT short_name, id, require_contact_information," +
          "       short_description, agreement_url, auto_verify " +
          "FROM contributor_agreements WHERE active = 'Y'");
      try {
        Map<Integer, ContributorAgreement> agreements = Maps.newHashMap();
        while (rs.next()) {
          String name = rs.getString(1);
          if (config.getContributorAgreement(name) != null) {
            continue; // already exists
          }
          ContributorAgreement a = config.getContributorAgreement(name, true);
          agreements.put(rs.getInt(2), a);

          a.setRequireContactInformation("Y".equals(rs.getString(3)));
          a.setDescription(rs.getString(4));
          a.setAgreementUrl(rs.getString(5));
          if ("Y".equals(rs.getString(6))) {
            a.setAutoVerify(new GroupReference(null, null));
          }
        }
        return agreements;
      } finally {
        rs.close();
      }
    } finally {
      stmt.close();
    }
  }

  private AccountGroup createGroup(ReviewDb db, String groupName,
      AccountGroup.UUID adminGroupUUID, String description)
          throws OrmException {
    final AccountGroup.Id groupId =
        new AccountGroup.Id(db.nextAccountGroupId());
    final AccountGroup.NameKey nameKey = new AccountGroup.NameKey(groupName);
    final AccountGroup.UUID uuid = GroupUUID.make(groupName, serverUser);
    final AccountGroup group = new AccountGroup(nameKey, groupId, uuid);
    group.setOwnerGroupUUID(adminGroupUUID);
    group.setDescription(description);
    final AccountGroupName gn = new AccountGroupName(group);
    // first insert the group name to validate that the group name hasn't
    // already been used to create another group
    db.accountGroupNames().insert(Collections.singleton(gn));
    db.accountGroups().insert(Collections.singleton(group));
    return group;
  }

  private List<AccountGroup.UUID> getAdministrateServerGroups(
      ReviewDb db, ProjectConfig cfg) {
    List<PermissionRule> rules = cfg.getAccessSection(AccessSection.GLOBAL_CAPABILITIES)
       .getPermission(GlobalCapability.ADMINISTRATE_SERVER)
       .getRules();

    List<AccountGroup.UUID> groups =
        Lists.newArrayListWithExpectedSize(rules.size());
    for (PermissionRule rule : rules) {
      if (rule.getAction() == Action.ALLOW) {
        groups.add(rule.getGroup().getUUID());
      }
    }
    if (groups.isEmpty()) {
      throw new IllegalStateException("no administrator group found");
    }

    return groups;
  }

  private GroupReference getOrCreateGroupForIndividuals(ReviewDb db,
      ProjectConfig config, List<AccountGroup.UUID> adminGroupUUIDs,
      ContributorAgreement agreement)
          throws OrmException {
    if (!agreement.getAccepted().isEmpty()) {
      return agreement.getAccepted().get(0).getGroup();
    }

    String name = "CLA Accepted - " + agreement.getName();
    AccountGroupName agn =
        db.accountGroupNames().get(new AccountGroup.NameKey(name));
    AccountGroup ag;
    if (agn != null) {
      ag = db.accountGroups().get(agn.getId());
      if (ag == null) {
        throw new IllegalStateException(
            "account group name exists but account group does not: " + name);
      }

      if (!adminGroupUUIDs.contains(ag.getOwnerGroupUUID())) {
        throw new IllegalStateException(
            "individual group exists with non admin owner group: " + name);
      }
    } else {
      ag = createGroup(db, name, adminGroupUUIDs.get(0),
          String.format("Users who have accepted the %s CLA", agreement.getName()));
    }
    GroupReference group = config.resolve(ag);
    agreement.setAccepted(Lists.newArrayList(new PermissionRule(group)));
    if (agreement.getAutoVerify() != null) {
      agreement.setAutoVerify(group);
    }

    // Don't allow accounts in the same individual CLA group to see each
    // other in same group visibility mode.
    List<PermissionRule> sameGroupVisibility =
        config.getAccountsSection().getSameGroupVisibility();
    PermissionRule rule = new PermissionRule(group);
    rule.setDeny();
    if (!sameGroupVisibility.contains(rule)) {
      sameGroupVisibility.add(rule);
    }
    return group;
  }

  private long addAccountAgreements(ReviewDb db, ProjectConfig config,
      List<AccountGroup.UUID> adminGroupUUIDs,
      Map<Integer, ContributorAgreement> agreements)
          throws SQLException, OrmException {
    Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
    try {
      ResultSet rs = stmt.executeQuery(
          "SELECT account_id, cla_id, accepted_on, reviewed_by," +
          "       reviewed_on, review_comments " +
          "FROM account_agreements WHERE status = 'V'");
      try {
        long minTime = System.currentTimeMillis();
        while (rs.next()) {
          Account.Id accountId = new Account.Id(rs.getInt(1));
          Account.Id reviewerId = new Account.Id(rs.getInt(4));
          if (rs.wasNull()) {
            reviewerId = accountId;
          }

          int claId = rs.getInt(2);
          ContributorAgreement agreement = agreements.get(claId);
          if (agreement == null) {
            continue// Agreement is invalid
          }

          Timestamp acceptedOn = rs.getTimestamp(3);
          minTime = Math.min(minTime, acceptedOn.getTime());

          // Enter Agreement
          GroupReference individualGroup =
              getOrCreateGroupForIndividuals(db, config, adminGroupUUIDs, agreement);
          AccountGroup.Id groupId = db.accountGroups()
              .byUUID(individualGroup.getUUID())
              .toList()
              .get(0)
              .getId();

          final AccountGroupMember.Key key =
              new AccountGroupMember.Key(accountId, groupId);
          AccountGroupMember m = db.accountGroupMembers().get(key);
          if (m == null) {
            m = new AccountGroupMember(key);
            db.accountGroupMembersAudit().insert(
                Collections.singleton(
                    new AccountGroupMemberAudit(m, reviewerId, acceptedOn)));
            db.accountGroupMembers().insert(Collections.singleton(m));
          }
        }
        return minTime;
      } finally {
        rs.close();
      }
    } finally {
      stmt.close();
    }
  }

  private static class AccountGroupAgreement {
    private AccountGroup.Id groupId;
    private int claId;
    private Timestamp acceptedOn;
    private Account.Id reviewedBy;
    private Timestamp reviewedOn;
    private String reviewComments;

    private long getTime() {
      return (reviewedOn == null) ? acceptedOn.getTime() : reviewedOn.getTime();
    }
  }

  private List<AccountGroupAgreement> getAccountGroupAgreements(
      ReviewDb db, Map<Integer, ContributorAgreement> agreements)
          throws SQLException {

    Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
    try {
      ResultSet rs = stmt.executeQuery(
          "SELECT group_id, cla_id, accepted_on, reviewed_by, reviewed_on, " +
          "       review_comments " +
          "FROM account_group_agreements");
      try {
        List<AccountGroupAgreement> groupAgreements = Lists.newArrayList();
        while (rs.next()) {
          AccountGroupAgreement a = new AccountGroupAgreement();
          a.groupId = new AccountGroup.Id(rs.getInt(1));
          a.claId = rs.getInt(2);
          if (!agreements.containsKey(a.claId)) {
            continue; // Agreement is invalid
          }
          a.acceptedOn = rs.getTimestamp(3);
          a.reviewedBy = new Account.Id(rs.getInt(4));
          if (rs.wasNull()) {
            a.reviewedBy = null;
          }

          a.reviewedOn = rs.getTimestamp(5);
          if (rs.wasNull()) {
            a.reviewedOn = null;
          }

          a.reviewComments = rs.getString(6);
          if (rs.wasNull()) {
            a.reviewComments = null;
          }
          groupAgreements.add(a);
        }
        Collections.sort(groupAgreements, new Comparator<AccountGroupAgreement>() {
          @Override
          public int compare(
              AccountGroupAgreement a1, AccountGroupAgreement a2) {
            return Longs.compare(a1.getTime(), a2.getTime());
          }
        });
        return groupAgreements;
      } finally {
        rs.close();
      }
    } finally {
      stmt.close();
    }
  }
}
TOP

Related Classes of com.google.gerrit.server.schema.Schema_65

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.