]> WPIA git - gigi.git/commitdiff
Merge "Fix debian build"
authorLucas Werkmeister <mail@lucaswerkmeister.de>
Fri, 19 Aug 2016 18:35:16 +0000 (20:35 +0200)
committerGerrit Code Review <gigi-system@dogcraft.de>
Fri, 19 Aug 2016 18:35:16 +0000 (20:35 +0200)
25 files changed:
src/org/cacert/gigi/Gigi.java
src/org/cacert/gigi/dbObjects/Certificate.java
src/org/cacert/gigi/dbObjects/Domain.java
src/org/cacert/gigi/dbObjects/ObjectCache.java
src/org/cacert/gigi/dbObjects/Organisation.java
src/org/cacert/gigi/dbObjects/SupportedUser.java
src/org/cacert/gigi/output/CountrySelector.java
src/org/cacert/gigi/output/TrustchainIterable.java [new file with mode: 0644]
src/org/cacert/gigi/pages/account/NamesForm.templ
src/org/cacert/gigi/pages/account/certs/CertificateDisplay.templ
src/org/cacert/gigi/pages/account/certs/CertificateModificationForm.templ
src/org/cacert/gigi/pages/account/certs/Certificates.java
src/org/cacert/gigi/pages/account/certs/RevokeSingleCertForm.java [new file with mode: 0644]
src/org/cacert/gigi/pages/account/certs/RevokeSingleCertForm.templ [new file with mode: 0644]
src/org/cacert/gigi/pages/account/domain/DomainManagementForm.templ
src/org/cacert/gigi/pages/account/domain/DomainOverview.java
src/org/cacert/gigi/pages/account/mail/MailManagementForm.templ
src/org/cacert/gigi/pages/admin/support/FindCertForm.java [new file with mode: 0644]
src/org/cacert/gigi/pages/admin/support/FindCertForm.templ [new file with mode: 0644]
src/org/cacert/gigi/pages/admin/support/FindCertPage.java [new file with mode: 0644]
src/org/cacert/gigi/pages/admin/support/FindCertPage.templ [new file with mode: 0644]
src/org/cacert/gigi/pages/admin/support/FindUserByDomainForm.java
src/org/cacert/gigi/pages/admin/support/SupportRevokeCertificatesForm.templ
static/static/js/expert.js
tests/org/cacert/gigi/pages/account/TestDomain.java

index 1108aa04bf26ff2fd40b30b70fc05caba37b2cd8..7740f803fc947cf6bd0c8f6c75d91122d158e4cf 100644 (file)
@@ -58,6 +58,7 @@ import org.cacert.gigi.pages.account.certs.Certificates;
 import org.cacert.gigi.pages.account.domain.DomainOverview;
 import org.cacert.gigi.pages.account.mail.MailOverview;
 import org.cacert.gigi.pages.admin.TTPAdminPage;
+import org.cacert.gigi.pages.admin.support.FindCertPage;
 import org.cacert.gigi.pages.admin.support.FindUserByDomainPage;
 import org.cacert.gigi.pages.admin.support.FindUserByEmailPage;
 import org.cacert.gigi.pages.admin.support.SupportEnterTicketPage;
@@ -138,7 +139,7 @@ public final class Gigi extends HttpServlet {
 
             putPage("/secure", new TestSecure(), null);
             putPage(Verify.PATH, new Verify(), null);
-            putPage(Certificates.PATH + "/*", new Certificates(), "Certificates");
+            putPage(Certificates.PATH + "/*", new Certificates(false), "Certificates");
             putPage(RegisterPage.PATH, new RegisterPage(), "SomeCA.org");
             putPage(CertificateAdd.PATH, new CertificateAdd(), "Certificates");
             putPage(MailOverview.DEFAULT_PATH, new MailOverview(), "Certificates");
@@ -155,6 +156,7 @@ public final class Gigi extends HttpServlet {
             putPage(SupportEnterTicketPage.PATH, new SupportEnterTicketPage(), "Support Console");
             putPage(FindUserByEmailPage.PATH, new FindUserByEmailPage(), "Support Console");
             putPage(FindUserByDomainPage.PATH, new FindUserByDomainPage(), "Support Console");
+            putPage(FindCertPage.PATH, new FindCertPage(), "Support Console");
 
             putPage(SupportUserDetailsPage.PATH + "*", new SupportUserDetailsPage(), null);
             putPage(ChangePasswordPage.PATH, new ChangePasswordPage(), "My Account");
@@ -171,6 +173,7 @@ public final class Gigi extends HttpServlet {
             putPage(MyDetails.PATH, new MyDetails(), "My Account");
             putPage(UserTrainings.SUPPORT_PATH, new UserTrainings(true), null);
             putPage(Points.SUPPORT_PATH, new Points(true), null);
+            putPage(Certificates.SUPPORT_PATH + "/*", new Certificates(true), null);
 
             putPage(PasswordResetPage.PATH, new PasswordResetPage(), null);
             putPage(LogoutPage.PATH, new LogoutPage(), null);
index 37ba66b743b5737829b7923b375afc65a7a2d375..b0c85e96b9f691b13b6bdd68e5b678b5b95264da 100644 (file)
@@ -467,4 +467,30 @@ public class Certificate implements IdCachable {
             return res.next();
         }
     }
+
+    public static Certificate[] findBySerialPattern(String serial) {
+        try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT `id` FROM `certs` WHERE `serial` LIKE ? GROUP BY `id`  LIMIT 100", true)) {
+            prep.setString(1, serial);
+            return fetchCertsToArray(prep);
+        }
+    }
+
+    public static Certificate[] findBySANPattern(String request, SANType type) {
+        try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT `certId` FROM `subjectAlternativeNames` WHERE `contents` LIKE ? and `type`=?::`SANType` GROUP BY `certId` LIMIT 100", true)) {
+            prep.setString(1, request);
+            prep.setString(2, type.getOpensslName());
+            return fetchCertsToArray(prep);
+        }
+    }
+
+    private static Certificate[] fetchCertsToArray(GigiPreparedStatement prep) {
+        GigiResultSet res = prep.executeQuery();
+        res.last();
+        Certificate[] certs = new Certificate[res.getRow()];
+        res.beforeFirst();
+        for (int i = 0; res.next(); i++) {
+            certs[i] = Certificate.getById(res.getInt(1));
+        }
+        return certs;
+    }
 }
index eecf37669a3e91438e40ba0c8bf103c7de87caca..fcd6709f2d50c7721d4cebab2f621ac1215a469e 100644 (file)
@@ -17,18 +17,10 @@ public class Domain implements IdCachable, Verifyable {
 
     private int id;
 
-    private Domain(int id) {
-        try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `memid`, `domain` FROM `domains` WHERE `id`=? AND `deleted` IS NULL")) {
-            ps.setInt(1, id);
-
-            GigiResultSet rs = ps.executeQuery();
-            if ( !rs.next()) {
-                throw new IllegalArgumentException("Invalid domain id " + id);
-            }
-            this.id = id;
-            owner = CertificateOwner.getById(rs.getInt(1));
-            suffix = rs.getString(2);
-        }
+    private Domain(GigiResultSet rs, int id) {
+        this.id = id;
+        owner = CertificateOwner.getById(rs.getInt(1));
+        suffix = rs.getString(2);
     }
 
     public Domain(User actor, CertificateOwner owner, String suffix) throws GigiApiException {
@@ -74,9 +66,12 @@ public class Domain implements IdCachable, Verifyable {
         if (id == 0) {
             throw new GigiApiException("not inserted.");
         }
-        try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `domains` SET `deleted`=CURRENT_TIMESTAMP WHERE `id`=?")) {
-            ps.setInt(1, id);
-            ps.execute();
+        synchronized (Domain.class) {
+            myCache.remove(this);
+            try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `domains` SET `deleted`=CURRENT_TIMESTAMP WHERE `id`=?")) {
+                ps.setInt(1, id);
+                ps.execute();
+            }
         }
     }
 
@@ -163,16 +158,23 @@ public class Domain implements IdCachable, Verifyable {
 
     private static final ObjectCache<Domain> myCache = new ObjectCache<>();
 
-    public static synchronized Domain getById(int id) throws IllegalArgumentException {
+    public static synchronized Domain getById(int id) {
         Domain em = myCache.get(id);
         if (em == null) {
-            myCache.put(em = new Domain(id));
+            try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `memid`, `domain` FROM `domains` WHERE `id`=? AND `deleted` IS NULL")) {
+                ps.setInt(1, id);
+                GigiResultSet rs = ps.executeQuery();
+                if ( !rs.next()) {
+                    return null;
+                }
+                myCache.put(em = new Domain(rs, id));
+            }
         }
         return em;
     }
 
-    public static Domain searchUserIdByDomain(String domain) {
-        try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `id` FROM `domains` WHERE `domain` = ?")) {
+    public static Domain searchDomain(String domain) {
+        try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `id` FROM `domains` WHERE `domain` = ? AND `deleted` IS NULL")) {
             ps.setString(1, domain);
             GigiResultSet res = ps.executeQuery();
             if (res.next()) {
index 0d9fc14ba51b4f2d0e4005a3977367bdd3c66ff5..125477defc5fd26e33bff9d51153d9617c06a15b 100644 (file)
@@ -34,6 +34,6 @@ public class ObjectCache<T extends IdCachable> {
     }
 
     public void remove(T toRm) {
-        hashmap.remove(toRm);
+        hashmap.remove(toRm.getId());
     }
 }
index 7f6df7520717cd80c33b48f12ab53a1916bb4309..9c57049a969ed4a2e18045183bc92cefaeb1b75d 100644 (file)
@@ -54,7 +54,7 @@ public class Organisation extends CertificateOwner {
 
     private String name;
 
-    private Country state;
+    private Country country;
 
     private String province;
 
@@ -66,15 +66,15 @@ public class Organisation extends CertificateOwner {
 
     private String postalAddress;
 
-    public Organisation(String name, Country state, String province, String city, String email, String optionalName, String postalAddress, User creator) throws GigiApiException {
+    public Organisation(String name, Country country, String province, String city, String email, String optionalName, String postalAddress, User creator) throws GigiApiException {
         if ( !creator.isInGroup(Group.ORGASSURER)) {
             throw new GigiApiException("Only Organisation RA Agents may create organisations.");
         }
-        if (state == null) {
+        if (country == null) {
             throw new GigiApiException("Got country code of illegal type.");
         }
         this.name = name;
-        this.state = state;
+        this.country = country;
         this.province = province;
         this.city = city;
         this.email = email;
@@ -84,7 +84,7 @@ public class Organisation extends CertificateOwner {
         try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO organisations SET id=?, name=?, state=?, province=?, city=?, contactEmail=?, optional_name=?, postal_address=?, creator=?")) {
             ps.setInt(1, id);
             ps.setString(2, name);
-            ps.setString(3, state.getCode());
+            ps.setString(3, country.getCode());
             ps.setString(4, province);
             ps.setString(5, city);
             ps.setString(6, email);
@@ -100,7 +100,7 @@ public class Organisation extends CertificateOwner {
     protected Organisation(GigiResultSet rs) throws GigiApiException {
         super(rs.getInt("id"));
         name = rs.getString("name");
-        state = Country.getCountryByCode(rs.getString("state"), CountryCodeType.CODE_2_CHARS);
+        country = Country.getCountryByCode(rs.getString("state"), CountryCodeType.CODE_2_CHARS);
         province = rs.getString("province");
         city = rs.getString("city");
         email = rs.getString("contactEmail");
@@ -113,7 +113,7 @@ public class Organisation extends CertificateOwner {
     }
 
     public Country getState() {
-        return state;
+        return country;
     }
 
     public String getProvince() {
@@ -228,7 +228,7 @@ public class Organisation extends CertificateOwner {
             ps.executeUpdate();
         }
         name = o;
-        state = c;
+        country = c;
         province = st;
         city = l;
     }
index 47c17e83460662e70b15cd1828bcdb81946a4249..a663215a8c658913c0d4991b552de9cc68941c6d 100644 (file)
@@ -47,6 +47,15 @@ public class SupportedUser {
         }
     }
 
+    public void revokeCertificate(Certificate cert) throws GigiApiException {
+
+        // TODO Check for open jobs!
+        if (cert.getStatus() == CertificateStatus.ISSUED) {
+            writeSELog("SE Revoke certificate");
+            cert.revoke().waitFor(60000);
+        }
+    }
+
     private void writeSELog(String type) throws GigiApiException {
         if (ticket == null) {
             throw new GigiApiException("No ticket set!");
index c58600a74bd961a0f60310d494b717dafcc31a3c..bb1fab920e661dca205f49173c59fe54413161da 100644 (file)
@@ -30,9 +30,9 @@ public class CountrySelector implements Outputable {
         this.optional = optional;
     }
 
-    public CountrySelector(String name, boolean optional, Country state) {
+    public CountrySelector(String name, boolean optional, Country country) {
         this(name, optional);
-        selected = state;
+        selected = country;
     }
 
     public void update(HttpServletRequest r) throws GigiApiException {
diff --git a/src/org/cacert/gigi/output/TrustchainIterable.java b/src/org/cacert/gigi/output/TrustchainIterable.java
new file mode 100644 (file)
index 0000000..2cee5fd
--- /dev/null
@@ -0,0 +1,31 @@
+package org.cacert.gigi.output;
+
+import java.util.Map;
+
+import org.cacert.gigi.dbObjects.CACertificate;
+import org.cacert.gigi.localisation.Language;
+import org.cacert.gigi.output.template.IterableDataset;
+
+public class TrustchainIterable implements IterableDataset {
+
+    CACertificate cert;
+
+    public TrustchainIterable(CACertificate cert) {
+        this.cert = cert;
+    }
+
+    @Override
+    public boolean next(Language l, Map<String, Object> vars) {
+        if (cert == null) {
+            return false;
+        }
+        vars.put("name", cert.getKeyname());
+        vars.put("link", cert.getLink());
+        if (cert.isSelfsigned()) {
+            cert = null;
+            return true;
+        }
+        cert = cert.getParent();
+        return true;
+    }
+}
index b38b71790b37697d4d2fefb5e7b73ca626f2f3d7..b9da748b3640b1781cc69761144d564147173786 100644 (file)
@@ -11,8 +11,8 @@
   <tr>
     <td><?=$name?></td>
     <td><?=$npoints?></td>
-    <td><button class="btn btn-warning" name="deprecateName" value="<?=$id?>" type="submit"<?=$deprecated?>><?=_Deprecate Name?></button></td>
-    <td><button class="btn btn-danger" name="removeName" value="<?=$id?>" type="submit"<?=$preferred?>><?=_Remove Name?></button></td>
+    <td><button class="btn btn-warning btn-confirm" data-confirm="<?=_Do you really want to deprecate this name??>" data-reply="<?=_Cancel?>,<?=_Confirm?>" name="deprecateName" value="<?=$id?>" type="submit"<?=$deprecated?>><?=_Deprecate Name?></button></td>
+    <td><button class="btn btn-danger btn-confirm" data-confirm="<?=_Do you really want to delete this name??>" data-reply="<?=_Cancel?>,<?=_Confirm?>" name="removeName" value="<?=$id?>" type="submit"<?=$preferred?>><?=_Remove Name?></button></td>
     <td><button class="btn btn-primary" name="preferred" value="<?=$id?>" type="submit"<?=$preferred?>><?=_Set as Preferred?></button></td>
   </tr>
   <? } ?>
index 0d559cdf5ca0bf0eedfd611bd0b119d8f1e5f3c1..c77de8f3cbd18990cb5a66e903984af8a241b07f 100644 (file)
-<a href='<?=$serial?>.crt'><?=_PEM encoded Certificate?></a>
-<? foreach($trustchain) { ?>
- <?=_issued by?> <a href='<?=$link?>'><?=$name?></a>
-<? } ?><br/>
-<a href='<?=$serial?>.crt?chain'><?=_PEM encoded Certificate Cain?></a><br/>
-<a href='<?=$serial?>.crt?chain&noAnchor'><?=_PEM encoded Certificate Cain (Excluding Anchor)?></a><br/>
-<a href='<?=$serial?>.cer'><?=_DER encoded Certificate?></a><br/>
-<a href='<?=$serial?>.cer?install&chain'><?=_Install into browser.?></a><br/>
-<a href='<?=$serial?>.cer?install'><?=_Install into browser. (Chrome)?></a>. <?=_Please ensure that the intermediate certificates listed above are installed prior to installing the certificate.?><br/>
-<pre>
-<?=$cert?>
-</pre>
+<table>
+<? if($support) {?>
+
+  <tr>
+    <th colspan="2"><?=_Support Info?>:</th>
+  </tr>
+
+  <tr>
+    <td><?=$type?>:</td>
+    <td><a href="<?=$link?>"><?=$name?></a></td>
+  </tr>
+<? } ?>
+
+
+  <tr>
+    <th colspan="2"><?=_X.509 Info?>:</th>
+  </tr>
+
+  <tr>
+    <td><?=_Profile?>:</td>
+    <td><?=$profile?></td>
+  </tr
+
+  <tr>
+    <td><?=_Serial Number?>:</td>
+    <td><?=$serial?></td>
+  </tr
+
+<? if($support) {?>
+  <tr>
+    <td><?=_Trustchain?>:</td>
+    <td>
+    <? foreach($trustchain) { ?>
+      <?=_issued by?> <code><?=$name?></code>
+    <? } ?>
+    </td>
+  </tr>
+<? } else { ?>
+  <tr>
+    <td valign="top"><?=_Certificate and Chain?>:</td>
+    <td>
+        <a href='<?=$serial?>.crt'><?=_PEM encoded Certificate?></a>
+        <? foreach($trustchain) { ?>
+            <?=_issued by?> <a href='<?=$link?>'><?=$name?></a>
+        <? } ?><br/>
+        <a href='<?=$serial?>.crt?chain'><?=_PEM encoded Certificate Chain?></a><br/>
+        <a href='<?=$serial?>.crt?chain&noAnchor'><?=_PEM encoded Certificate Chain (Excluding Anchor)?></a><br/>
+        <a href='<?=$serial?>.cer'><?=_DER encoded Certificate?></a><br/>
+        <a href='<?=$serial?>.cer?install&chain'><?=_Install into browser.?></a><br/>
+        <a href='<?=$serial?>.cer?install'><?=_Install into browser. (Chrome)?></a>. <?=_Please ensure that the intermediate certificates listed above are installed prior to installing the certificate.?><br/>
+    </td>
+  </tr>
+<? } ?>
+
+  <tr>
+    <th colspan="2"><?=_Validity?>:</th>
+  </tr>
+
+  <tr>
+    <td><?=_Status?>:</td>
+    <td><?=$status?></td>
+  </tr>
+
+  <tr>
+    <td><?=_Issue Date?>:</td>
+    <td><?=$issued?></td>
+  </tr>
+
+  <tr>
+    <td><?=_Expire Date?>:</td>
+    <td><?=$expire?></td>
+  </tr>
+
+  <tr>
+    <td><?=_Revocation Date?>:</td>
+    <td><?=$revoked?></td>
+  </tr>
+
+  <tr>
+    <th colspan="2"><?=_Certificate Info?>:</th>
+  </tr>
+
+  <tr>
+    <td><?=_Fingerprint?>:</td>
+    <td><?=$fingerprint?></td>
+  </tr>
+
+  <tr>
+    <td valign="top"><?=_Certificate (PEM)?>:</td>
+    <td><pre><?=$cert?></pre></td>
+  </tr>
+
+  <tr>
+    <th colspan="2"><?=_Certificate Details?>:</th>
+  </tr>
+
+  <tr>
+    <td><?=_Login enabled?>:</td>
+    <td><?=$login?></td>
+  </tr>
+
+  <tr>
+    <td><?=_Digest?>:</td>
+    <td><?=$digest?></td>
+  </tr>
+
+  <tr>
+    <td><?=_Distinguished Name (DN)?>:</td>
+    <td><?=$DN?></td>
+  </tr>
+
+  <tr>
+    <td><?=_Subject Alternative Names (SAN)?>:</td>
+    <td>
+    <? foreach($san) { ?>
+      <?=$entry?>
+    <? } ?>
+    </td>
+  </tr>
+
+</table>
+<? if($revokeForm) {?>
+<?=$revokeForm?>
+<? } ?>
index d4e2f74e90ea3a0a1b29328ba4645506fe805f27..4a5554faa621e327c1bfbd92bd35e2171dbc657e 100644 (file)
@@ -1,3 +1,3 @@
 <a href="?" class="btn <?=$current?>"><?=_Show Current Certificates?></a> <a href="?withRevoked" class="btn <?=$all?>"><?=_Show All Certificates?></a>
 <?=$certTable?>
-<button class="btn btn-danger" name="action" value="revoke"><?=_Revoke Selected Certificates?></button>
+<button class="btn btn-danger btn-confirm" data-confirm="<?=_Do you really want to revoke ALL selected certificates??>" data-reply="<?=_Cancel?>,<?=_Confirm?>" name="action" value="revoke"><?=_Revoke Selected Certificates?></button>
index 9d05db4fcb3bba6011f17d629af7d25d8750d7a1..45ca5637ceb0f018d582663fced91abd6a2077a6 100644 (file)
@@ -4,22 +4,31 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URLEncoder;
 import java.security.GeneralSecurityException;
+import java.security.cert.X509Certificate;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.cacert.gigi.dbObjects.CACertificate;
 import org.cacert.gigi.dbObjects.Certificate;
+import org.cacert.gigi.dbObjects.Certificate.CertificateStatus;
+import org.cacert.gigi.dbObjects.Certificate.SubjectAlternateName;
+import org.cacert.gigi.dbObjects.CertificateOwner;
+import org.cacert.gigi.dbObjects.Organisation;
+import org.cacert.gigi.dbObjects.SupportedUser;
+import org.cacert.gigi.dbObjects.User;
 import org.cacert.gigi.localisation.Language;
+import org.cacert.gigi.output.TrustchainIterable;
 import org.cacert.gigi.output.template.Form;
 import org.cacert.gigi.output.template.IterableDataset;
 import org.cacert.gigi.output.template.Template;
 import org.cacert.gigi.pages.HandlesMixedRequest;
 import org.cacert.gigi.pages.LoginPage;
 import org.cacert.gigi.pages.Page;
+import org.cacert.gigi.util.AuthorizationContext;
 import org.cacert.gigi.util.CertExporter;
 import org.cacert.gigi.util.PEM;
 
@@ -29,33 +38,13 @@ public class Certificates extends Page implements HandlesMixedRequest {
 
     public static final String PATH = "/account/certs";
 
-    static class TrustchainIterable implements IterableDataset {
+    public static final String SUPPORT_PATH = "/support/certs";
 
-        CACertificate cert;
+    private final boolean support;
 
-        public TrustchainIterable(CACertificate cert) {
-            this.cert = cert;
-        }
-
-        @Override
-        public boolean next(Language l, Map<String, Object> vars) {
-            if (cert == null) {
-                return false;
-            }
-            vars.put("name", cert.getKeyname());
-            vars.put("link", cert.getLink());
-            if (cert.isSelfsigned()) {
-                cert = null;
-                return true;
-            }
-            cert = cert.getParent();
-            return true;
-        }
-
-    }
-
-    public Certificates() {
-        super("Certificates");
+    public Certificates(boolean support) {
+        super(support ? "Support Certificates" : "Certificates");
+        this.support = support;
     }
 
     @Override
@@ -113,11 +102,18 @@ public class Certificates extends Page implements HandlesMixedRequest {
         if (req.getQueryString() != null && !req.getQueryString().equals("") && !req.getQueryString().equals("withRevoked")) {
             return;// Block actions by get parameters.
         }
+        if (support && "revoke".equals(req.getParameter("action"))) {
+            if (Form.getForm(req, RevokeSingleCertForm.class).submitProtected(resp.getWriter(), req)) {
+                resp.sendRedirect(req.getPathInfo());
+                return;
+            }
+        }
         if ( !req.getPathInfo().equals(PATH)) {
             resp.sendError(500);
             return;
         }
         Form.getForm(req, CertificateModificationForm.class).submit(resp.getWriter(), req);
+
         doGet(req, resp);
     }
 
@@ -130,15 +126,93 @@ public class Certificates extends Page implements HandlesMixedRequest {
 
             String serial = pi;
             Certificate c = Certificate.getBySerial(serial);
-            if (c == null || LoginPage.getAuthorizationContext(req).getTarget().getId() != c.getOwner().getId()) {
+            Language l = LoginPage.getLanguage(req);
+
+            if ( !support && (c == null || LoginPage.getAuthorizationContext(req).getTarget().getId() != c.getOwner().getId())) {
                 resp.sendError(404);
                 return;
             }
             HashMap<String, Object> vars = new HashMap<>();
             vars.put("serial", URLEncoder.encode(serial, "UTF-8"));
-            vars.put("trustchain", new TrustchainIterable(c.getParent()));
+
+            CertificateStatus st = c.getStatus();
+
+            if (support) {
+                vars.put("support", "support");
+                CertificateOwner user = c.getOwner();
+                if (st == CertificateStatus.ISSUED) {
+                    if (user instanceof User) {
+                        vars.put("revokeForm", new RevokeSingleCertForm(req, c, new SupportedUser((User) user, getUser(req), LoginPage.getAuthorizationContext(req).getSupporterTicketId())));
+                    }
+                }
+            }
+
+            CertificateOwner co = c.getOwner();
+            int ownerId = co.getId();
+            vars.put("certid", c.getStatus());
+            if (co instanceof Organisation) {
+                vars.put("type", l.getTranslation("Organisation Acount"));
+                vars.put("name", Organisation.getById(ownerId).getName());
+                vars.put("link", ""); // TODO
+            } else {
+                vars.put("type", l.getTranslation("Personal Account"));
+                vars.put("name", User.getById(ownerId).getPreferredName());
+                vars.put("link", "/support/user/" + ownerId + "/");
+            }
+            vars.put("status", c.getStatus());
+            vars.put("DN", c.getDistinguishedName());
+            vars.put("digest", c.getMessageDigest());
+            vars.put("profile", c.getProfile().getVisibleName());
+            vars.put("fingerprint", "TBD"); // TODO function needs to be
+                                            // implemented in Certificate.java
             try {
-                vars.put("cert", PEM.encode("CERTIFICATE", c.cert().getEncoded()));
+
+                if (st == CertificateStatus.ISSUED || st == CertificateStatus.REVOKED) {
+                    X509Certificate certx = c.cert();
+                    vars.put("issued", certx.getNotBefore());
+                    vars.put("expire", certx.getNotAfter());
+                    vars.put("cert", PEM.encode("CERTIFICATE", c.cert().getEncoded()));
+                } else {
+                    vars.put("issued", l.getTranslation("N/A"));
+                    vars.put("expire", l.getTranslation("N/A"));
+                    vars.put("cert", l.getTranslation("N/A"));
+                }
+                if (st == CertificateStatus.REVOKED) {
+                    vars.put("revoked", c.getRevocationDate());
+                } else {
+                    vars.put("revoked", l.getTranslation("N/A"));
+                }
+                if (st == CertificateStatus.ISSUED || st == CertificateStatus.REVOKED) {
+                    vars.put("trustchain", new TrustchainIterable(c.getParent()));
+                    try {
+                        vars.put("cert", PEM.encode("CERTIFICATE", c.cert().getEncoded()));
+                    } catch (GeneralSecurityException e) {
+                        e.printStackTrace();
+                    }
+                } else {
+                    vars.put("trustchain", l.getTranslation("N/A"));
+                    vars.put("cert", l.getTranslation("N/A"));
+                }
+                final List<SubjectAlternateName> san = c.getSANs();
+                vars.put("san", new IterableDataset() {
+
+                    int j = 0;
+
+                    @Override
+                    public boolean next(Language l, Map<String, Object> vars) {
+                        if (j == san.size()) {
+                            return false;
+                        }
+                        vars.put("entry", san.get(j).getName() + (j < san.size() - 1 ? ", " : ""));
+                        j++;
+                        return true;
+                    }
+                });
+                if (c.isLoginEnabled()) {
+                    vars.put("login", l.getTranslation("Yes"));
+                } else {
+                    vars.put("login", l.getTranslation("No"));
+                }
             } catch (GeneralSecurityException e) {
                 e.printStackTrace();
             }
@@ -151,4 +225,15 @@ public class Certificates extends Page implements HandlesMixedRequest {
         new CertificateModificationForm(req, req.getParameter("withRevoked") != null).output(out, getLanguage(req), vars);
     }
 
+    @Override
+    public boolean isPermitted(AuthorizationContext ac) {
+        if (ac == null) {
+            return false;
+        }
+        if (support) {
+            return ac.canSupport();
+        } else {
+            return true;
+        }
+    }
 }
diff --git a/src/org/cacert/gigi/pages/account/certs/RevokeSingleCertForm.java b/src/org/cacert/gigi/pages/account/certs/RevokeSingleCertForm.java
new file mode 100644 (file)
index 0000000..5219081
--- /dev/null
@@ -0,0 +1,44 @@
+package org.cacert.gigi.pages.account.certs;
+
+import java.io.PrintWriter;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.cacert.gigi.GigiApiException;
+import org.cacert.gigi.dbObjects.Certificate;
+import org.cacert.gigi.dbObjects.SupportedUser;
+import org.cacert.gigi.localisation.Language;
+import org.cacert.gigi.output.template.Form;
+import org.cacert.gigi.output.template.Template;
+
+public class RevokeSingleCertForm extends Form {
+
+    private static final Template t = new Template(RevokeSingleCertForm.class.getResource("RevokeSingleCertForm.templ"));
+
+    private Certificate c;
+
+    private SupportedUser target;
+
+    public RevokeSingleCertForm(HttpServletRequest hsr, Certificate c, SupportedUser target) {
+        super(hsr);
+        this.c = c;
+        this.target = target;
+    }
+
+    @Override
+    public boolean submit(PrintWriter out, HttpServletRequest req) throws GigiApiException {
+        if (target != null) {
+            target.revokeCertificate(c);
+        } else {
+            c.revoke().waitFor(60000);
+        }
+        return true;
+    }
+
+    @Override
+    protected void outputContent(PrintWriter out, Language l, Map<String, Object> vars) {
+        t.output(out, l, vars);
+    }
+
+}
diff --git a/src/org/cacert/gigi/pages/account/certs/RevokeSingleCertForm.templ b/src/org/cacert/gigi/pages/account/certs/RevokeSingleCertForm.templ
new file mode 100644 (file)
index 0000000..ac521d2
--- /dev/null
@@ -0,0 +1 @@
+<button class="btn btn-danger btn-confirm" data-confirm="<?=_Do you really want to revoke this certificate??>" data-reply="<?=_Cancel?>,<?=_Confirm?>" type="submit" name="action" value="revoke"><?=_Revoke Certificate?></button>
index 6dbbdb40cb83cade215a484f970155b5189bee89..c003a48a07b4527cb10016fbb0d5d17aa782acd5 100644 (file)
@@ -10,7 +10,7 @@
   </tr>
   <? foreach($domains) { ?>
   <tr>
-       <td><button class="btn btn-danger" type="submit" name="delete" value="<?=$id?>">Delete</button></td>
+       <td><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><?=$status?></td>
        <td><? if($domainhref) { ?><a href='<?=$domainhref?>'><?=$domain?><? } else { ?><?=$domain?><? } ?></a></td>
   </tr>
index c4b91ad0a16dc31015fb3754dbc6ec44ad0c9dd1..12f3916a5d2689df2c09c42d55bbb7ab967d3ee4 100644 (file)
@@ -89,7 +89,7 @@ public class DomainOverview extends Page {
             if (f.submit(resp.getWriter(), req)) {
                 resp.sendRedirect(PATH);
             }
-        } else if (req.getParameter("domdel") != null) {
+        } else if (req.getParameter("delete") != null) {
             DomainManagementForm f = Form.getForm(req, DomainManagementForm.class);
             if (f.submitProtected(resp.getWriter(), req)) {
                 resp.sendRedirect(PATH);
index d80703084c1a74b434d89723125c93df073d39f7..10d44328e786ad54d3209c31d6752ed00805f340 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" 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??>" 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/src/org/cacert/gigi/pages/admin/support/FindCertForm.java b/src/org/cacert/gigi/pages/admin/support/FindCertForm.java
new file mode 100644 (file)
index 0000000..07d9b92
--- /dev/null
@@ -0,0 +1,69 @@
+package org.cacert.gigi.pages.admin.support;
+
+import java.io.PrintWriter;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.cacert.gigi.GigiApiException;
+import org.cacert.gigi.dbObjects.Certificate;
+import org.cacert.gigi.dbObjects.Certificate.SANType;
+import org.cacert.gigi.localisation.Language;
+import org.cacert.gigi.output.template.Form;
+import org.cacert.gigi.output.template.SprintfCommand;
+import org.cacert.gigi.output.template.Template;
+
+public class FindCertForm extends Form {
+
+    private static final Template t = new Template(FindCertForm.class.getResource("FindCertForm.templ"));
+
+    private final String SERIAL = "serial";
+
+    private String certType = SERIAL;
+
+    public Certificate certs[];
+
+    public FindCertForm(HttpServletRequest hsr) {
+        super(hsr);
+    }
+
+    @Override
+    public boolean submit(PrintWriter out, HttpServletRequest req) throws GigiApiException {
+        this.certType = req.getParameter("certType");
+        String request = req.getParameter("cert").trim();
+
+        if ( !SERIAL.equals(certType) && !SANType.EMAIL.getOpensslName().equals(certType) && !SANType.DNS.getOpensslName().equals(certType)) {
+            throw new GigiApiException("Invalid search type.");
+        }
+
+        if (SERIAL.equals(certType)) {
+            certs = Certificate.findBySerialPattern(request);
+            if (certs.length <= 0) {
+                throw new GigiApiException(SprintfCommand.createSimple("No certificate found matching serial number {0}", request));
+            }
+        }
+
+        if (SANType.EMAIL.getOpensslName().equals(certType) || SANType.DNS.getOpensslName().equals(certType)) {
+            SANType stype = SANType.valueOf(certType.toUpperCase());
+            certs = Certificate.findBySANPattern(request, stype);
+            if (certs.length <= 0) {
+                throw new GigiApiException(SprintfCommand.createSimple("No certificate found matching {0}", request));
+            }
+        }
+        return true;
+    }
+
+    @Override
+    protected void outputContent(PrintWriter out, Language l, Map<String, Object> vars) {
+        vars.put("serial", !SERIAL.equals(certType) ? "" : "checked");
+        vars.put("email", !SANType.EMAIL.getOpensslName().equals(certType) ? "" : "checked");
+        vars.put("dns", !SANType.DNS.getOpensslName().equals(certType) ? "" : "checked");
+
+        t.output(out, l, vars);
+    }
+
+    public Certificate[] getCerts() {
+        return certs;
+    }
+
+}
diff --git a/src/org/cacert/gigi/pages/admin/support/FindCertForm.templ b/src/org/cacert/gigi/pages/admin/support/FindCertForm.templ
new file mode 100644 (file)
index 0000000..678bb63
--- /dev/null
@@ -0,0 +1,23 @@
+<table class="table">
+  <tbody><tr>
+    <th colspan="2"><?=_Find Certificate?></th>
+  </tr>
+
+  <tr>
+    <td><?=_Search for?>:</td>
+    <td>
+       <input type="radio" name="certType" value="serial" <?=$serial?>> <?=_Serial Number?></input>
+       <input type="radio" name="certType" value="email" <?=$email?>> <?=_Email Address?></input>
+       <input type="radio" name="certType" value="DNS" <?=$dns?>> <?=_Domain?></input>
+    </td>
+  </tr>
+
+  <tr>
+    <td><?=_Data?>:</td>
+    <td><input class="form-control" name="cert" value="" size="30" title="<?=_use % as wildcard?>" placeholder="<?=_use % as wildcard?>" type="text" required/></td>
+  </tr>
+
+  <tr>
+    <td colspan="2"><input class="btn btn-primary" name="process" value="<?=_Next?>" type="submit"/></td>
+  </tr>
+</tbody></table>
\ No newline at end of file
diff --git a/src/org/cacert/gigi/pages/admin/support/FindCertPage.java b/src/org/cacert/gigi/pages/admin/support/FindCertPage.java
new file mode 100644 (file)
index 0000000..fb9d14f
--- /dev/null
@@ -0,0 +1,85 @@
+package org.cacert.gigi.pages.admin.support;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.cacert.gigi.dbObjects.Certificate;
+import org.cacert.gigi.dbObjects.Certificate.SubjectAlternateName;
+import org.cacert.gigi.localisation.Language;
+import org.cacert.gigi.output.ArrayIterable;
+import org.cacert.gigi.output.template.Form;
+import org.cacert.gigi.output.template.IterableDataset;
+import org.cacert.gigi.output.template.SprintfCommand;
+import org.cacert.gigi.pages.LoginPage;
+import org.cacert.gigi.pages.Page;
+import org.cacert.gigi.pages.account.certs.Certificates;
+import org.cacert.gigi.util.AuthorizationContext;
+
+public class FindCertPage extends Page {
+
+    public static final String PATH = "/support/find/certs";
+
+    public FindCertPage() {
+        super("Find Certificate");
+    }
+
+    @Override
+    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        new FindCertForm(req).output(resp.getWriter(), Page.getLanguage(req), new HashMap<String, Object>());
+    }
+
+    @Override
+    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        FindCertForm form = Form.getForm(req, FindCertForm.class);
+        if (form.submitProtected(resp.getWriter(), req)) {
+            final Certificate[] certs = form.getCerts();
+            if (certs.length == 1) {
+                resp.sendRedirect(Certificates.SUPPORT_PATH + certs[0].getSerial() + "/");
+            } else {
+                HashMap<String, Object> vars = new HashMap<String, Object>();
+                Language l = LoginPage.getLanguage(req);
+                if (certs.length >= 100) {
+                    vars.put("limit", l.getTranslation("100 or more entries available, only the first 100 are displayed."));
+                } else {
+                    vars.put("limit", SprintfCommand.createSimple("{0} entries found", certs.length));
+                }
+                vars.put("certtable", new ArrayIterable<Certificate>(certs) {
+
+                    @Override
+                    public void apply(Certificate t, Language l, Map<String, Object> vars) {
+                        vars.put("id", t.getId());
+                        vars.put("serial", t.getSerial());
+
+                        final List<SubjectAlternateName> san = t.getSANs();
+                        vars.put("san", new IterableDataset() {
+
+                            int j = 0;
+
+                            @Override
+                            public boolean next(Language l, Map<String, Object> vars) {
+                                if (j == san.size()) {
+                                    return false;
+                                }
+                                vars.put("entry", san.get(j).getName() + (j < san.size() - 1 ? ", " : ""));
+                                j++;
+                                return true;
+                            }
+
+                        });
+                    }
+                });
+                getDefaultTemplate().output(resp.getWriter(), getLanguage(req), vars);
+            }
+        }
+    }
+
+    @Override
+    public boolean isPermitted(AuthorizationContext ac) {
+        return ac != null && ac.canSupport();
+    }
+}
diff --git a/src/org/cacert/gigi/pages/admin/support/FindCertPage.templ b/src/org/cacert/gigi/pages/admin/support/FindCertPage.templ
new file mode 100644 (file)
index 0000000..d8f61c1
--- /dev/null
@@ -0,0 +1,16 @@
+<p><?=_Multiple certificates?>: <?=$limit?></p>
+<table class="table">
+<tr>
+<th>Id</th><th><?=_Serial number?></th><th><?=_SAN?></th></tr>
+<? foreach($certtable) {?>
+  <tr>
+    <td><a href="/support/certs/<?=$serial?>"><?=$id?></a></td>
+    <td><a href="/support/certs/<?=$serial?>"><?=$serial?></a></td>
+    <td>
+    <? foreach($san) {?>
+       <?=$entry?>
+    <? } ?>
+    </td>
+  </tr>
+<? } ?>
+</table>
index 779b0026bb83aeefbdbc98c4ff80defc5559d363..cce4aa0d84929f1a041a51d76451ceec1485f3ae 100644 (file)
@@ -34,7 +34,7 @@ public class FindUserByDomainForm extends Form {
                 throw new GigiApiException(SprintfCommand.createSimple("No personal domains found matching the id {0}", request.substring(1)));
             }
         } else {
-            d = Domain.searchUserIdByDomain(request);
+            d = Domain.searchDomain(request);
         }
         if (d == null) {
             throw new GigiApiException(SprintfCommand.createSimple("No personal domains found matching {0}", request));
index 1cc4de345f124627261165b7988d7966a144aff7..757ce3ca014a064b85148818572b5ce990734ca1 100644 (file)
@@ -22,7 +22,7 @@
             <? } ?>
         <tr>
             <th colspan="6">
-                    <input class="btn btn-danger" name="revokeall" value="<?=_revoke certificates?>" type="submit">
+                    <input class="btn btn-danger btn-confirm" data-confirm="<?=_Do you really want to revoke ALL certificates of this user??>" data-reply="<?=_Cancel?>,<?=_Confirm?>" name="revokeall" value="<?=_revoke certificates?>" type="submit">
             </th>
         </tr>
     </tbody></table>
\ No newline at end of file
index 07d1da4976d809d1a6b4474583efd528fdb6a003..731dddde58f991e28868b2ec48e016fe4ab21384 100644 (file)
@@ -1,4 +1,53 @@
 (function() {
+       var modal = undefined;
+       
+       function showModal(content){
+               var HTML = `<div class="modal fade" id="confirmation-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&#x1F5D9;</span></button>
+        <h4 class="modal-title" id="myModalLabel">&nbsp;</h4>
+      </div>
+      <div class="modal-body">
+        Body
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default btn-cancel" data-dismiss="modal">&#x1F5D9;</button>
+        <button type="button" class="btn btn-danger btn-confirm">&checkmark;</button>
+      </div>
+    </div>
+  </div>
+</div>`;
+               if(modal === undefined) {
+                       modal = $(HTML);
+                       modal.action = function(){};
+                       modal.appendTo("body");
+                       $(modal.get(0)).find(".modal-footer .btn-confirm").click(function(){
+                               modal.action();
+                               modal.modal("hide");
+                       });
+               }
+               var m = $(modal.get(0));
+               m.find(".modal-body").text($(content).attr("data-confirm"));
+               var reply = $(content).attr("data-reply").split(",");
+               m.find(".modal-footer .btn-cancel").text(reply[0]);
+               m.find(".modal-footer .btn-confirm").text(reply[1]);
+               modal.action=function(){
+                       content.confirmed=true;
+                       $(content).click();
+               };
+               modal.modal("show");
+       }
+       function initConfirm() {
+               $(".btn-confirm").click(function(){
+                       if(this.confirmed === true){
+                               return true;
+                       }
+                       showModal(this);
+                       return false;
+               });
+       }
        function showExpert(isExpert)
        {
          var elements = document.getElementsByClassName("expert");
@@ -43,6 +92,7 @@
                        panel.find(".panel-heading [type=\"radio\"]").change(refresh);
                        return this.id;
                });
+               initConfirm();
        }
        (function(oldLoad) {
                if (oldLoad == undefined) {
index 770c90e864298cd7b60bcc8e2d5dfa475a2dad65..49f1b85cb03940b8be38602ed198a3b5b0413d74 100644 (file)
@@ -5,6 +5,7 @@ import static org.junit.Assert.*;
 import java.io.IOException;
 import java.net.URLEncoder;
 
+import org.cacert.gigi.dbObjects.Domain;
 import org.cacert.gigi.pages.account.domain.DomainOverview;
 import org.cacert.gigi.testUtils.ClientTest;
 import org.junit.Test;
@@ -29,6 +30,21 @@ public class TestDomain extends ClientTest {
         assertNotNull(addDomain(cookie, "google.com"));
     }
 
+    @Test
+    public void testDelete() throws IOException {
+        String domain = uniq + ".de";
+        assertNull(addDomain(cookie, domain));
+        Domain d0 = Domain.searchDomain(domain);
+        assertNull(executeBasicWebInteraction(cookie, DomainOverview.PATH, "delete=" + d0.getId(), 0));
+        // double delete
+        assertNotNull(executeBasicWebInteraction(cookie, DomainOverview.PATH, "delete=" + d0.getId(), 0));
+        // re-add
+        assertNull(addDomain(cookie, domain));
+        Domain d1 = Domain.searchDomain(domain);
+        assertNotEquals(d0.getId(), d1.getId());
+        assertNull(executeBasicWebInteraction(cookie, DomainOverview.PATH, "delete=" + d1.getId(), 0));
+    }
+
     public static String addDomain(String session, String domain) throws IOException {
         return executeBasicWebInteraction(session, DomainOverview.PATH, "adddomain&newdomain=" + URLEncoder.encode(domain, "UTF-8"), 1);
     }