Verify the user as a valid, accepted contributor agreement before upload

We cannot accept changes into most open source projects unless the
contributor has acknowledged they are permitted and willing to make
the code available under the necessary licenses.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2008-12-30 13:22:01 -08:00
parent 25dd683941
commit b0b3fc63e9
3 changed files with 95 additions and 3 deletions

View File

@@ -38,7 +38,7 @@ public class Link implements HistoryListener {
public static final String SETTINGS_SSHKEYS = "settings,ssh-keys"; public static final String SETTINGS_SSHKEYS = "settings,ssh-keys";
public static final String SETTINGS_WEBIDENT = "settings,web-identities"; public static final String SETTINGS_WEBIDENT = "settings,web-identities";
public static final String SETTINGS_AGREEMENTS = "settings,agreements"; public static final String SETTINGS_AGREEMENTS = "settings,agreements";
public static final String SETTINGS_CONTACT = "settings,contact-information"; public static final String SETTINGS_CONTACT = "settings,contact";
public static final String MINE = "mine"; public static final String MINE = "mine";
public static final String MINE_UNCLAIMED = "mine,unclaimed"; public static final String MINE_UNCLAIMED = "mine,unclaimed";

View File

@@ -106,6 +106,10 @@ public final class AccountAgreement {
status = Status.NEW.getCode(); status = Status.NEW.getCode();
} }
public ContributorAgreement.Id getAgreementId() {
return key.claId;
}
public Timestamp getAcceptedOn() { public Timestamp getAcceptedOn() {
return acceptedOn; return acceptedOn;
} }

View File

@@ -16,8 +16,11 @@ package com.google.gerrit.server.ssh;
import com.google.gerrit.client.Link; import com.google.gerrit.client.Link;
import com.google.gerrit.client.reviewdb.Account; import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountAgreement;
import com.google.gerrit.client.reviewdb.Branch; import com.google.gerrit.client.reviewdb.Branch;
import com.google.gerrit.client.reviewdb.Change; import com.google.gerrit.client.reviewdb.Change;
import com.google.gerrit.client.reviewdb.ContactInformation;
import com.google.gerrit.client.reviewdb.ContributorAgreement;
import com.google.gerrit.client.reviewdb.PatchSet; import com.google.gerrit.client.reviewdb.PatchSet;
import com.google.gerrit.git.PatchSetImporter; import com.google.gerrit.git.PatchSetImporter;
import com.google.gerrit.server.GerritServer; import com.google.gerrit.server.GerritServer;
@@ -78,11 +81,10 @@ class Receive extends AbstractGitCommand {
@Override @Override
protected void runImpl() throws IOException, Failure { protected void runImpl() throws IOException, Failure {
server = getGerritServer(); server = getGerritServer();
verifyActiveContributorAgreement();
lookup(reviewerId, "reviewer", reviewerEmail); lookup(reviewerId, "reviewer", reviewerEmail);
lookup(ccId, "cc", ccEmail); lookup(ccId, "cc", ccEmail);
// TODO verify user has signed a CLA for this project
rp = new ReceivePack(repo); rp = new ReceivePack(repo);
rp.setAllowCreates(true); rp.setAllowCreates(true);
rp.setAllowDeletes(true); rp.setAllowDeletes(true);
@@ -116,6 +118,92 @@ class Receive extends AbstractGitCommand {
} }
} }
private void verifyActiveContributorAgreement() throws Failure {
AccountAgreement bestAgreement = null;
ContributorAgreement bestCla = null;
try {
for (final AccountAgreement a : db.accountAgreements().byAccount(
userAccount.getId()).toList()) {
final ContributorAgreement cla =
db.contributorAgreements().get(a.getAgreementId());
if (cla == null || !cla.isActive()) {
continue;
}
if (bestAgreement == null
|| bestAgreement.getStatus() != AccountAgreement.Status.VERIFIED) {
bestAgreement = a;
bestCla = cla;
}
if (bestAgreement.getStatus() == AccountAgreement.Status.VERIFIED) {
break;
}
}
} catch (OrmException e) {
throw new Failure(1, "database error");
}
if (bestCla != null && bestCla.isRequireContactInformation()) {
final ContactInformation info = userAccount.getContactInformation();
boolean fail = false;
fail |= missing(userAccount.getFullName());
fail |= missing(userAccount.getPreferredEmail());
fail |= info == null || missing(info.getAddress());
if (fail) {
final StringBuilder msg = new StringBuilder();
msg.append("\nfatal: ");
msg.append(bestCla.getShortName());
msg.append(" contributor agreement requires");
msg.append(" current contact information.\n");
if (server.getCanonicalURL() != null) {
msg.append("\nPlease review your contact information");
msg.append(":\n\n ");
msg.append(server.getCanonicalURL());
msg.append("Gerrit#");
msg.append(Link.SETTINGS_CONTACT);
msg.append("\n");
}
msg.append("\n");
throw new Failure(1, msg.toString());
}
}
if (bestAgreement != null) {
switch (bestAgreement.getStatus()) {
case VERIFIED:
return;
case REJECTED:
throw new Failure(1, "\nfatal: " + bestCla.getShortName()
+ " contributor agreement was rejected."
+ "\n (rejected on " + bestAgreement.getReviewedOn()
+ ")\n");
case NEW:
throw new Failure(1, "\nfatal: " + bestCla.getShortName()
+ " contributor agreement is still pending review.\n");
}
}
final StringBuilder msg = new StringBuilder();
msg.append("\nfatal: A Contributor Agreement"
+ " must be completed before uploading");
if (server.getCanonicalURL() != null) {
msg.append(":\n\n ");
msg.append(server.getCanonicalURL());
msg.append("Gerrit#");
msg.append(Link.SETTINGS_AGREEMENTS);
msg.append("\n");
} else {
msg.append(".");
}
msg.append("\n");
throw new Failure(1, msg.toString());
}
private static boolean missing(final String value) {
return value == null || value.trim().equals("");
}
private void lookup(final Set<Account.Id> accountIds, private void lookup(final Set<Account.Id> accountIds,
final String addressType, final Set<String> emails) throws Failure { final String addressType, final Set<String> emails) throws Failure {
final StringBuilder errors = new StringBuilder(); final StringBuilder errors = new StringBuilder();