CertificateRequest cr = new CertificateRequest(ctx, csr, cp);
Certificate result = cr.draft();
Job job = result.issue(null, "2y", u);
- job.waitFor(60000);
+ job.waitFor(Job.WAIT_MIN);
if (result.getStatus() != CertificateStatus.ISSUED) {
resp.sendError(510, "Error, issuing timed out");
return;
}
Job job = c.revoke(RevocationType.USER);
- job.waitFor(60000);
+ job.waitFor(Job.WAIT_MIN);
if (c.getStatus() != CertificateStatus.REVOKED) {
resp.sendError(510, "Error, issuing timed out");
return;
import club.wpia.gigi.GigiApiException;
import club.wpia.gigi.database.GigiPreparedStatement;
import club.wpia.gigi.database.GigiResultSet;
+import club.wpia.gigi.dbObjects.Certificate.RevocationType;
import club.wpia.gigi.util.DomainAssessment;
public class Domain implements IdCachable, Verifyable {
ps.setInt(1, id);
ps.execute();
}
+ LinkedList<Job> revokes = new LinkedList<Job>();
+ for (Certificate cert : fetchActiveCertificates()) {
+ revokes.add(cert.revoke(RevocationType.USER));
+ }
+ long start = System.currentTimeMillis();
+ for (Job job : revokes) {
+ int toWait = (int) (60000 + start - System.currentTimeMillis());
+ if (toWait > 0) {
+ job.waitFor(toWait);
+ } else {
+ break; // canceled... waited too log
+ }
+ }
}
}
public class Job implements IdCachable {
+ public static int WAIT_MIN = 60000;
+
private int id;
private Job(int id) {
package club.wpia.gigi.dbObjects;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
import java.util.Map;
import club.wpia.gigi.GigiApiException;
import club.wpia.gigi.localisation.Language;
import club.wpia.gigi.output.template.Outputable;
import club.wpia.gigi.util.HTMLEncoder;
+import club.wpia.gigi.util.TimeConditions;
public class Name implements Outputable, IdCachable {
}
return initals.toString();
}
+
+ public boolean isValidVerification() {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis());
+ c.add(Calendar.MONTH, -TimeConditions.getInstance().getVerificationMonths());
+ String date = sdf.format(new Date(c.getTimeInMillis()));
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT COUNT(id) FROM `notary` WHERE `to` = ? AND `deleted` IS NULL AND (`expire` IS NULL OR `expire` > CURRENT_TIMESTAMP) AND `date` > ?")) {
+ query.setInt(1, getId());
+ query.setString(2, date);
+ GigiResultSet rs = query.executeQuery();
+
+ if (rs.next()) {
+ if (rs.getInt(1) > 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
}
// TODO Check for open jobs!
if (cert.getStatus() == CertificateStatus.ISSUED) {
writeSELog("SE Revoke certificate");
- cert.revoke(RevocationType.SUPPORT).waitFor(60000);
+ cert.revoke(RevocationType.SUPPORT).waitFor(Job.WAIT_MIN);
// send notification to support
String subject = "Revoke certificate";
Outputable message = SprintfCommand.createSimple("Certificate with serial number {0} for {1} <{2}> has been revoked.", cert.getSerial(), target.getPreferredName().toString(), target.getEmail());
import club.wpia.gigi.database.GigiPreparedStatement;
import club.wpia.gigi.database.GigiResultSet;
import club.wpia.gigi.dbObjects.CATS.CATSType;
+import club.wpia.gigi.dbObjects.Certificate.RevocationType;
import club.wpia.gigi.dbObjects.Country.CountryCodeType;
import club.wpia.gigi.dbObjects.Verification.VerificationType;
import club.wpia.gigi.email.EmailProvider;
return false;
}
+ public boolean isValidNameVerification(String name) {
+ for (Name n : getNames()) {
+ if (n.matches(name) && n.isValidVerification()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void updateDefaultEmail(EmailAddress newMail) throws GigiApiException {
for (EmailAddress email : getEmails()) {
if (email.getAddress().equals(newMail.getAddress())) {
throw new GigiApiException("Can't delete user's default e-mail.");
}
+ deleteEmailCerts(delMail, RevocationType.USER);
+ }
+
+ private void deleteEmailCerts(EmailAddress delMail, RevocationType rt) throws GigiApiException {
for (EmailAddress email : getEmails()) {
if (email.getId() == delMail.getId()) {
try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `emails` SET `deleted`=CURRENT_TIMESTAMP WHERE `id`=?")) {
ps.setInt(1, delMail.getId());
ps.execute();
}
+ LinkedList<Job> revokes = new LinkedList<Job>();
+ for (Certificate cert : fetchActiveEmailCertificates(delMail.getAddress())) {
+ cert.revoke(RevocationType.USER).waitFor(Job.WAIT_MIN);
+ }
+ long start = System.currentTimeMillis();
+ for (Job job : revokes) {
+ int toWait = (int) (60000 + start - System.currentTimeMillis());
+ if (toWait > 0) {
+ job.waitFor(toWait);
+ } else {
+ break; // canceled... waited too log
+ }
+ }
return;
}
+
}
throw new GigiApiException("Email not one of user's email addresses.");
+
+ }
+
+ public Certificate[] fetchActiveEmailCertificates(String email) {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT DISTINCT `certs`.`id` FROM `certs` INNER JOIN `subjectAlternativeNames` ON `subjectAlternativeNames`.`certId` = `certs`.`id` WHERE `contents`=? AND `type`='email' AND `revoked` IS NULL AND `expire` > CURRENT_TIMESTAMP AND `memid`=?", true)) {
+ ps.setString(1, email);
+ ps.setInt(2, getId());
+ GigiResultSet rs = ps.executeQuery();
+ rs.last();
+ Certificate[] res = new Certificate[rs.getRow()];
+ rs.beforeFirst();
+ int i = 0;
+ while (rs.next()) {
+ res[i++] = Certificate.getById(rs.getInt(1));
+ }
+ return res;
+ }
}
public synchronized Verification[] getReceivedVerifications() {
import club.wpia.gigi.dbObjects.Certificate.SubjectAlternateName;
import club.wpia.gigi.dbObjects.CertificateProfile;
import club.wpia.gigi.dbObjects.Domain;
+import club.wpia.gigi.dbObjects.Job;
import club.wpia.gigi.dbObjects.Organisation;
import club.wpia.gigi.dbObjects.User;
import club.wpia.gigi.localisation.Language;
}
result.setDescription(description);
}
- result.issue(issueDate.getFrom(), issueDate.getTo(), c.getActor()).waitFor(60000);
+ result.issue(issueDate.getFrom(), issueDate.getTo(), c.getActor()).waitFor(Job.WAIT_MIN);
this.result = result;
Certificate c = result;
if (c.getStatus() != CertificateStatus.ISSUED) {
}
long start = System.currentTimeMillis();
for (Job job : revokes) {
- int toWait = (int) (60000 + start - System.currentTimeMillis());
+ int toWait = (int) (Job.WAIT_MIN + start - System.currentTimeMillis());
if (toWait > 0) {
job.waitFor(toWait);
} else {
subject.put("OU", ou);
}
}
- System.out.println(subject);
+
if ( !error.isEmpty()) {
throw error;
}
User u = (User) ctx.getTarget();
if (name != null && u.isValidName(name)) {
if (realIsOK) {
- verifiedCN = name;
+ if (u.isValidNameVerification(name)) {
+ verifiedCN = name;
+ } else {
+ error.mergeInto(new GigiApiException(SprintfCommand.createSimple("The entered name needs a valid verification within the last {0} months.", TimeConditions.getInstance().getVerificationMonths())));
+ }
} else {
error.mergeInto(new GigiApiException("Your real name is not allowed in this certificate."));
if (defaultIsOK) {
import club.wpia.gigi.GigiApiException;
import club.wpia.gigi.dbObjects.Certificate;
import club.wpia.gigi.dbObjects.Certificate.RevocationType;
+import club.wpia.gigi.dbObjects.Job;
import club.wpia.gigi.dbObjects.SupportedUser;
import club.wpia.gigi.localisation.Language;
import club.wpia.gigi.output.template.Form;
if (target != null) {
target.revokeCertificate(c);
} else {
- c.revoke(RevocationType.USER).waitFor(60000);
+ c.revoke(RevocationType.USER).waitFor(Job.WAIT_MIN);
}
return new RedirectResult(req.getPathInfo());
}
</tr>
<? foreach($domains) { ?>
<tr>
- <td><? if($buttonvisible) { ?><button class="btn btn-danger btn-confirm" data-confirm="<?=_Do you really want to delete this domain??>" data-reply="<?=_Cancel?>,<?=_Confirm?>" type="submit" name="delete" value="<?=$id?>">Delete</button><? } ?></td>
+ <td><? if($buttonvisible) { ?><button class="btn btn-danger btn-confirm" data-confirm="<?=_All certificates that reference the affected domain (including those where several different domains appear) will be revoked.?>" data-reply="<?=_Cancel?>,<?=_Confirm?>" type="submit" name="delete" value="<?=$id?>">Delete</button><? } ?></td>
<td><?=$status?></td>
<td><? if($domainhref) { ?><a href='<?=$domainhref?>'><?=$domain?><? } else { ?><?=$domain?><? } ?></a></td>
</tr>
<td><?=$verification?></td>
<td><? if($last_verification) { ?><?=$last_verification?><? } else { ?><?=_N/A?><? } ?></td>
<td><?=$address?></td>
- <td><button class="btn btn-danger btn-confirm" data-confirm="<?=_Do you really want to delete this email address??>" data-reply="<?=_Cancel?>,<?=_Confirm?>" type="submit" name="delete" value="<?=$id?>"<?=$deletable?>><?=_Delete?></button></td>
+ <td><button class="btn btn-danger btn-confirm" data-confirm="<?=_Do you really want to delete this email address? ALL active certificates that reference the affected email address (including multiple emails, code signing, TLS and S/MIME) will be revoked.?>" data-reply="<?=_Cancel?>,<?=_Confirm?>" type="submit" name="delete" value="<?=$id?>"<?=$deletable?>><?=_Delete?></button></td>
<td><button class="btn btn-primary" type="submit" name="reping" value="<?=$id?>"><?=_Request reping?></button></td>
</tr>
<? } ?>
throw new GigiApiException("Sending the notification mail failed.");
}
Job j = c.revoke(challenge, Base64.getEncoder().encodeToString(signature), message);
- if ( !j.waitFor(60000)) {
+ if ( !j.waitFor(Job.WAIT_MIN)) {
throw new PermamentFormException(new GigiApiException("Revocation timed out."));
}
if (c.getStatus() != CertificateStatus.REVOKED) {
--- /dev/null
+package club.wpia.gigi.dbObjects;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+
+import org.junit.Test;
+
+import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.dbObjects.Certificate.CSRType;
+import club.wpia.gigi.dbObjects.Certificate.SANType;
+import club.wpia.gigi.testUtils.ManagedTest;
+
+public class TestDomain extends ManagedTest {
+
+ @Test
+ public void testDeleteDomainWithCertificate() throws GigiApiException, GeneralSecurityException, IOException, InterruptedException {
+ User u = User.getById(createVerificationUser("Kurti", "Hansel", createUniqueName() + "@email.com", TEST_PASSWORD));
+ String domain = createUniqueName() + ".org";
+ Domain d = new Domain(u, u, domain);
+ KeyPair kp = generateKeypair();
+ String key = generatePEMCSR(kp, "CN=" + domain);
+ Certificate c = new Certificate(u, u, Certificate.buildDN("CN", domain), Digest.SHA256, key, CSRType.CSR, getClientProfile(), new Certificate.SubjectAlternateName(SANType.DNS, domain));
+ c.issue(null, "2y", u).waitFor(60000);
+
+ c = new Certificate(u, u, Certificate.buildDN("CN", domain), Digest.SHA256, key, CSRType.CSR, getClientProfile(), new Certificate.SubjectAlternateName(SANType.DNS, "www." + domain));
+ c.issue(null, "2y", u).waitFor(60000);
+
+ Certificate[] certs = d.fetchActiveCertificates();
+ assertEquals(2, certs.length);
+
+ d.delete();
+
+ certs = u.getCertificates(false);
+ assertEquals(0, certs.length);
+ certs = d.fetchActiveCertificates();
+ assertEquals(0, certs.length);
+
+ }
+
+}
assertThat(result, ArrayContains.contains(CoreMatchers.equalTo(type)));
}
+ @Test
+ public void testValidVerification() throws GigiApiException {
+ User u0 = User.getById(createVerifiedUser("f", "l", createUniqueName() + "@email.com", TEST_PASSWORD));
+ assertFalse(u0.isValidNameVerification(u0.getPreferredName().toString()));
+
+ add100Points(u0.getId());
+ assertTrue(u0.isValidNameVerification(u0.getPreferredName().toString()));
+
+ setVerificationDateToPast(u0.getPreferredName());
+ assertFalse(u0.isValidNameVerification(u0.getPreferredName().toString()));
+
+ add100Points(u0.getId());
+ assertTrue(u0.isValidNameVerification(u0.getPreferredName().toString()));
+ }
+
}
--- /dev/null
+package club.wpia.gigi.dbObjects;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+
+import org.junit.Test;
+
+import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.dbObjects.Certificate.CSRType;
+import club.wpia.gigi.dbObjects.Certificate.SANType;
+import club.wpia.gigi.testUtils.ManagedTest;
+
+public class TestUserManaged extends ManagedTest {
+
+ @Test
+ public void testDeleteEmailWithCertificate() throws GigiApiException, GeneralSecurityException, IOException, InterruptedException {
+
+ int id = createVerifiedUser("Test", "User", createUniqueName() + "test@test.tld", TEST_PASSWORD);
+ String email = createUniqueName() + "test@test.tld";
+ User u = User.getById(id);
+ Certificate[] certs = u.getCertificates(false);
+ int certCount = certs.length;
+ EmailAddress testAddress = createVerifiedEmail(u, email);
+ KeyPair kp = generateKeypair();
+ String key = generatePEMCSR(kp, "CN=" + email);
+ Certificate c = new Certificate(u, u, Certificate.buildDN("CN", email), Digest.SHA256, key, CSRType.CSR, getClientProfile(), new Certificate.SubjectAlternateName(SANType.EMAIL, email));
+ c.issue(null, "2y", u).waitFor(Job.WAIT_MIN);
+
+ u.deleteEmail(testAddress);
+
+ assertFalse(c.getRevocationDate().toString().isEmpty());
+ certs = u.getCertificates(false);
+ assertEquals(certCount, certs.length);
+
+ }
+}
import org.junit.Test;
import club.wpia.gigi.GigiApiException;
-import club.wpia.gigi.dbObjects.Country;
-import club.wpia.gigi.dbObjects.Name;
-import club.wpia.gigi.dbObjects.NamePart;
-import club.wpia.gigi.dbObjects.User;
-import club.wpia.gigi.dbObjects.Verification.VerificationType;
import club.wpia.gigi.dbObjects.Country.CountryCodeType;
import club.wpia.gigi.dbObjects.NamePart.NamePartType;
+import club.wpia.gigi.dbObjects.Verification.VerificationType;
import club.wpia.gigi.testUtils.ClientBusinessTest;
import club.wpia.gigi.util.Notary;
assertEquals(10, n4.getVerificationPoints());
assertEquals(10, u.getMaxVerifyPoints());
}
+
+ @Test
+ public void testValidVerification() throws GigiApiException {
+ User u0 = User.getById(createVerifiedUser("f", "l", createUniqueName() + "@email.com", TEST_PASSWORD));
+ assertFalse(u0.getPreferredName().isValidVerification());
+
+ add100Points(u0.getId());
+ assertTrue(u0.getPreferredName().isValidVerification());
+
+ setVerificationDateToPast(u0.getPreferredName());
+ assertFalse(u0.getPreferredName().isValidVerification());
+
+ add100Points(u0.getId());
+ assertTrue(u0.getPreferredName().isValidVerification());
+ }
+
}
import club.wpia.gigi.dbObjects.EmailAddress;
import club.wpia.gigi.dbObjects.Group;
import club.wpia.gigi.pages.account.certs.CertificateRequest;
+import club.wpia.gigi.testUtils.ClientBusinessTest;
import club.wpia.gigi.testUtils.ClientTest;
import club.wpia.gigi.util.AuthorizationContext;
import club.wpia.gigi.util.TimeConditions;
}
}
+
+ @Test
+ public void testVerificationInPast() throws IOException, GeneralSecurityException, GigiApiException {
+
+ ClientBusinessTest.setVerificationDateToPast(u.getPreferredName());
+ try {
+ CertificateRequest cr = new CertificateRequest(ac, generatePEMCSR(kp, "CN=a ab"));
+ cr.update(u.getPreferredName().toString(), "SHA512", "client-a", null, null, "email:" + email);
+ cr.draft();
+ fail();
+ } catch (GigiApiException e) {
+ assertThat(e.getMessage(), containsString("The entered name needs a valid verification within the last"));
+ }
+
+ }
}
supporter.refreshGroups();
return supporter;
}
+
+ public EmailAddress createVerifiedEmail(User u, String email) throws InterruptedException, GigiApiException {
+ EmailAddress addr = new EmailAddress(u, email, Locale.ENGLISH);
+ TestMail testMail = getMailReceiver().receive(addr.getAddress());
+ String hash = testMail.extractLink().substring(testMail.extractLink().lastIndexOf('=') + 1);
+ addr.verify(hash);
+ getMailReceiver().assertEmpty();
+ return addr;
+ }
}
package club.wpia.gigi.testUtils;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.database.GigiPreparedStatement;
import club.wpia.gigi.dbObjects.Name;
import club.wpia.gigi.dbObjects.User;
+import club.wpia.gigi.util.TimeConditions;
public class ClientBusinessTest extends BusinessTest {
throw new Error(e);
}
}
+
+ public static void setVerificationDateToPast(Name name) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis());
+ c.add(Calendar.MONTH, -TimeConditions.getInstance().getVerificationMonths());
+ String date = sdf.format(new Date(c.getTimeInMillis()));
+ GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `notary` SET `date`=? WHERE `to`=? AND `date`>?");
+ ps.setString(1, date);
+ ps.setInt(2, name.getId());
+ ps.setString(3, date);
+ ps.execute();
+ ps.close();
+ }
}
}
public static void add100Points(int uid) {
- try (GigiPreparedStatement ps2 = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, points='100'")) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ try (GigiPreparedStatement ps2 = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, points='100', `date`=?")) {
ps2.setInt(1, uid);
ps2.setInt(2, User.getById(uid).getPreferredName().getId());
+ ps2.setString(3, sdf.format(new Date(System.currentTimeMillis())));
ps2.execute();
}
}
import club.wpia.gigi.dbObjects.DomainPingType;
import club.wpia.gigi.dbObjects.EmailAddress;
import club.wpia.gigi.dbObjects.Group;
+import club.wpia.gigi.dbObjects.Name;
import club.wpia.gigi.dbObjects.NamePart;
import club.wpia.gigi.dbObjects.NamePart.NamePartType;
import club.wpia.gigi.dbObjects.User;
resp.getWriter().println("User has been verified " + verifications + " times." + info);
+ } else if (req.getParameter("verifyexpire") != null) {
+ String mail = req.getParameter("verifyEmail");
+ User byEmail = User.getByEmail(mail);
+ if (byEmail == null) {
+ resp.getWriter().println("User not found.");
+ return;
+ } else {
+ setVerificationDateToPast(byEmail.getPreferredName());
+ }
+
+ resp.getWriter().println("Verification set to time past the limit.");
} else if (req.getParameter("letverify") != null) {
String mail = req.getParameter("letverifyEmail");
User byEmail = User.getByEmail(mail);
form.output(resp.getWriter(), getLanguage(req), vars);
}
+
+ private static void setVerificationDateToPast(Name name) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis());
+ c.add(Calendar.MONTH, -TimeConditions.getInstance().getVerificationMonths());
+ String date = sdf.format(new Date(c.getTimeInMillis()));
+ GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `notary` SET `date`=? WHERE `to`=? AND `date`>?");
+ ps.setString(1, date);
+ ps.setInt(2, name.getId());
+ ps.setString(3, date);
+ ps.execute();
+ ps.close();
+ }
}
</td><td>
Verification Points to issue to preferred name: </br>
<input type="text" name="verificationPoints" value="100"/> <input type="submit" value="Add Points" name="verify"/>
+<input type="submit" value="Set Verification date past limit" name="verifyexpire"/>
</td></tr>
<tr><td>