]> WPIA git - gigi.git/commitdiff
add: ensure to revoke certificates if email address is deleted
authorINOPIAE <m.maengel@inopiae.de>
Mon, 13 May 2019 13:28:26 +0000 (15:28 +0200)
committerINOPIAE <m.maengel@inopiae.de>
Thu, 19 Sep 2019 13:39:28 +0000 (15:39 +0200)
Make sure all certificates that are not expired containing an email
address are revoked if email address is deleted from user account.
Function deleteEmailCerts is introduced to enable support to delete an
email address or account in a later patch.

Related to issue 60

Change-Id: I88c94e398b0e22465ac0cbf9623b101bcea072a6

src/club/wpia/gigi/dbObjects/User.java
src/club/wpia/gigi/pages/account/mail/MailManagementForm.templ
tests/club/wpia/gigi/dbObjects/TestUserManaged.java [new file with mode: 0644]
tests/club/wpia/gigi/testUtils/BusinessTest.java

index e3beaf86cf65ba2bd6a38eb883996d48859a455b..3213fd8ec31aa2c6b12b013b407c6f1abaa5866e 100644 (file)
@@ -17,6 +17,7 @@ import club.wpia.gigi.GigiApiException;
 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;
@@ -360,16 +361,51 @@ public class User extends CertificateOwner {
             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())) {
+                    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
+                    }
+                }
                 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() {
index 2449bca10f0f2ee6b6a0acf60cda9a7b62986a33..c71306f0a27b89c476b095c6b8af6b3ef6651e4e 100644 (file)
@@ -19,7 +19,7 @@
                <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>
  <? } ?>
diff --git a/tests/club/wpia/gigi/dbObjects/TestUserManaged.java b/tests/club/wpia/gigi/dbObjects/TestUserManaged.java
new file mode 100644 (file)
index 0000000..50ac27d
--- /dev/null
@@ -0,0 +1,39 @@
+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(60000);
+
+        u.deleteEmail(testAddress);
+
+        assertFalse(c.getRevocationDate().toString().isEmpty());
+        certs = u.getCertificates(false);
+        assertEquals(certCount, certs.length);
+
+    }
+}
index 28da80f828679c78113bcedec7408a928da49647..4566d0e1f34391f5e6b46533e041bd49381e46ca 100644 (file)
@@ -183,4 +183,13 @@ public abstract class BusinessTest extends ConfiguredTest {
         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;
+    }
 }