ADD: output pinglog, test DNS and email ping.
authorFelix Dörre <felix@dogcraft.de>
Sun, 24 Aug 2014 16:41:25 +0000 (18:41 +0200)
committerFelix Dörre <felix@dogcraft.de>
Tue, 26 Aug 2014 18:58:06 +0000 (20:58 +0200)
13 files changed:
config/test.properties.template
doc/jenkinsJob/ci-tests-setup.txt [new file with mode: 0644]
doc/jenkinsJob/dyn-txt.php [new file with mode: 0644]
src/org/cacert/gigi/Domain.java
src/org/cacert/gigi/Gigi.java
src/org/cacert/gigi/pages/account/DomainAddForm.templ
src/org/cacert/gigi/pages/account/DomainDetails.templ [new file with mode: 0644]
src/org/cacert/gigi/pages/account/DomainManagementForm.java
src/org/cacert/gigi/pages/account/DomainManagementForm.templ
src/org/cacert/gigi/pages/account/DomainOverview.java
src/org/cacert/gigi/ping/DNSPinger.java
tests/org/cacert/gigi/ping/TestDNS.java [new file with mode: 0644]
tests/org/cacert/gigi/testUtils/ManagedTest.java

index a94f0b53218ba99a95cd286c60fa77b6d8875bdc..97ce26b49bf32ad04cbc0dc4b1441027bfda5f88 100644 (file)
@@ -20,3 +20,8 @@ sql.driver=com.mysql.jdbc.Driver
 sql.url=jdbc:mysql://localhost:3306/cacert
 sql.user=cacert
 sql.password=<password>
+
+
+domain.dnsmanage=http://you-installation-of-the/index.php
+domain.dnstest=the.dns.zone
+domain.dnsns=the.authorativ.ns.for.domain.dnstest
diff --git a/doc/jenkinsJob/ci-tests-setup.txt b/doc/jenkinsJob/ci-tests-setup.txt
new file mode 100644 (file)
index 0000000..2b75bd0
--- /dev/null
@@ -0,0 +1,14 @@
+-you need 4 domains resolving to the ci server (or localhost)
+preferably
+static.DOMAIN, secure.DOMAIN, www.DOMAIN and api.DOMAIN.
+enter them in the jenkins job to write them to "keys/config" and "config/test.properties"
+
+-you need credentials to an acessabible mysql database.
+make jenkins write them to "config/test.properties"
+
+-you need a dynamically managable dns zone.
+Write the zone name to "domain.dnstest" in "test.properties"
+and a manage script (see dyn-txt.php). Put the url with password in "domain.dnsmanage"
+
+Setup with bind9:
+dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST example.org.
diff --git a/doc/jenkinsJob/dyn-txt.php b/doc/jenkinsJob/dyn-txt.php
new file mode 100644 (file)
index 0000000..c7c73e4
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+header("Content-type: text/plain");
+
+if($_GET['token'] != "<here a secure password>"){
+       die ();
+}
+$t1 = $_GET['t1'];
+$t2 = $_GET['t2'];
+if(!preg_match("/[a-zA-Z0-9]+/", $t1) || !preg_match("/[a-zA-Z0-9]+/", $t2)){
+  die("Error");
+}
+
+$call = <<<EOF
+server localhost
+update delete cacert-{$t1}.<your fakezone here> TXT
+update add cacert-{$t1}.<your fakezone here>  60 TXT {$t2}
+send
+quit
+
+EOF;
+echo $call;
+
+$nsupdate = popen("/usr/bin/nsupdate -k <here your dnssec key>.key", 'w');
+fwrite($nsupdate, $call);
+$retval = pclose($nsupdate); // nsupdate doesn't return anything useful when called this way
+
+?>
index bcc66beb7ea826d31a7fd1ffb86437cba395b474..21fe668edda1d638beb90287e769758c8b1f860e 100644 (file)
@@ -3,7 +3,6 @@ package org.cacert.gigi;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-
 import org.cacert.gigi.database.DatabaseConnection;
 
 public class Domain {
@@ -138,4 +137,24 @@ public class Domain {
         }
         return false;
     }
+
+    public String[][] getPings() throws GigiApiException {
+        try {
+            PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT state, type, info, result FROM domainPinglog INNER JOIN pingconfig ON pingconfig.id=domainPinglog.configid WHERE pingconfig.domainid=? ORDER BY `when` DESC;");
+            ps.setInt(1, id);
+            ResultSet rs = ps.executeQuery();
+            rs.last();
+            String[][] contents = new String[rs.getRow()][];
+            rs.beforeFirst();
+            for (int i = 0; i < contents.length && rs.next(); i++) {
+                contents[i] = new String[] {
+                        rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4)
+                };
+            }
+            return contents;
+        } catch (SQLException e) {
+            throw new GigiApiException(e);
+        }
+
+    }
 }
index 1e7ee0f99c72a8be115da691be0348f008e8685a..f013468e06d61d74a6bc718b833585b9ccfa678c 100644 (file)
@@ -88,7 +88,7 @@ public class Gigi extends HttpServlet {
             putPage(RegisterPage.PATH, new RegisterPage(), "Join CAcert.org");
             putPage(CertificateAdd.PATH, new CertificateAdd(), "Certificates");
             putPage(MailOverview.DEFAULT_PATH, new MailOverview("My email addresses"), "Certificates");
-            putPage(DomainOverview.PATH, new DomainOverview("Domains"), "Certificates");
+            putPage(DomainOverview.PATH + "*", new DomainOverview("Domains"), "Certificates");
             putPage(MyPoints.PATH, new MyPoints("My Points"), "CAcert Web of Trust");
             putPage("/wot/rules", new StaticPage("CAcert Web of Trust Rules", AssurePage.class.getResourceAsStream("Rules.templ")), "CAcert Web of Trust");
             baseTemplate = new Template(Gigi.class.getResource("Gigi.templ"));
index 1f83ea1302b37e22dfc755f63a33000c627b3742..2da1b0e323b4378c9e17a91cb7a345d4cc331cd1 100644 (file)
@@ -35,7 +35,7 @@
     <td>
         Please insert the following DNS TXT entry into the SOA-file of your domain:<br/>
         <pre>
-        cacert-<?=$tokenName?> IN      TXT     <?=$tokenValue?>
+        cacert-<?=$tokenName?> IN TXT <?=$tokenValue?>
         </pre>
     </td>
   </tr>
diff --git a/src/org/cacert/gigi/pages/account/DomainDetails.templ b/src/org/cacert/gigi/pages/account/DomainDetails.templ
new file mode 100644 (file)
index 0000000..650f87a
--- /dev/null
@@ -0,0 +1,15 @@
+<h2><?=_Ping log for?> '<?=$domainname?>'</h2>
+<table class="wrapper dataTable">
+<tr><th><?=_Type?></th>
+<th><?=_State?></th>
+<th><?=_Config?></th>
+<th><?=_Result?></th></tr>
+<? foreach($pings) { ?>
+<tr>
+<td><?=$type?></td>
+<td><?=$state?></td>
+<td><?=$config?></td>
+<td><?=$result?></td>
+</tr>
+<?}?>
+</table>
\ No newline at end of file
index 5df5711456b413de405280dec5caba493634a70c..b6d957b0e55320e9deae85922614796093898d53 100644 (file)
@@ -13,6 +13,7 @@ import org.cacert.gigi.output.Form;
 import org.cacert.gigi.output.template.IterableDataset;
 import org.cacert.gigi.output.template.Template;
 import org.cacert.gigi.pages.Page;
+import org.cacert.gigi.util.ServerConstants;
 
 public class DomainManagementForm extends Form {
 
@@ -60,6 +61,7 @@ public class DomainManagementForm extends Form {
                 }
                 Domain domain = doms[point];
                 vars.put("id", domain.getId());
+                vars.put("domainhref", "https://" + ServerConstants.getWwwHostNamePort() + DomainOverview.PATH + domain.getId());
                 vars.put("domain", domain.getSuffix());
                 vars.put("status", l.getTranslation(domain.isVerified() ? "verified" : "not verified"));
                 point++;
@@ -69,5 +71,4 @@ public class DomainManagementForm extends Form {
         vars.put("domains", dts);
         t.output(out, l, vars);
     }
-
 }
index 58b8fe5f91685443e371f066c51c25658e50be33..0bdc9d37fe0a85f3dcb5d02e2bfaf5b093ac97ad 100644 (file)
@@ -12,7 +12,7 @@
   <tr>
        <td><input type="checkbox" name="delid[]" value="<?=$id?>" /></td>
        <td><?=$status?></td>
-       <td><?=$domain?></td>
+       <td><a href='<?=$domainhref?>'><?=$domain?></a></td>
   </tr>
   <? } ?>
   <tr>
index 4b298f87e5db08dbc2497277d3f9808291664f76..d8868f93817178023661515cec0879012785a39b 100644 (file)
@@ -2,17 +2,25 @@ package org.cacert.gigi.pages.account;
 
 import java.io.IOException;
 import java.util.HashMap;
+import java.util.Map;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.cacert.gigi.Domain;
+import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.User;
+import org.cacert.gigi.localisation.Language;
 import org.cacert.gigi.output.Form;
+import org.cacert.gigi.output.template.IterableDataset;
+import org.cacert.gigi.output.template.Template;
 import org.cacert.gigi.pages.Page;
 
 public class DomainOverview extends Page {
 
-    public static final String PATH = "/account/domains";
+    public static final String PATH = "/account/domains/";
+
+    private Template domainDetails = new Template(DomainOverview.class.getResource("DomainDetails.templ"));
 
     public DomainOverview(String title) {
         super(title);
@@ -21,6 +29,48 @@ public class DomainOverview extends Page {
     @Override
     public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         User u = getUser(req);
+        String pi = req.getPathInfo();
+        if (pi.length() - PATH.length() > 0) {
+            int i = Integer.parseInt(pi.substring(PATH.length()));
+            Domain d = Domain.getById(i);
+            if (u.getId() != d.getOwner().getId()) {
+                System.out.println(u.getId());
+                System.out.println(d.getOwner().getId());
+                return;
+            }
+            try {
+                final String[][] pings = d.getPings();
+                HashMap<String, Object> vars = new HashMap<>();
+                vars.put("domainname", d.getSuffix());
+                vars.put("pings", new IterableDataset() {
+
+                    int counter = 0;
+
+                    @Override
+                    public boolean next(Language l, Map<String, Object> vars) {
+                        if (counter >= pings.length) {
+                            return false;
+                        }
+                        vars.put("state", pings[counter][0]);
+                        vars.put("type", pings[counter][1]);
+                        vars.put("config", pings[counter][2]);
+                        String ping3 = pings[counter][3];
+                        if (ping3 == null) {
+                            vars.put("result", "");
+                        } else {
+                            vars.put("result", ping3);
+                        }
+                        counter++;
+                        return true;
+                    }
+                });
+                domainDetails.output(resp.getWriter(), getLanguage(req), vars);
+                return;
+            } catch (GigiApiException e) {
+                e.format(resp.getWriter(), getLanguage(req));
+            }
+
+        }
         DomainManagementForm domMan = new DomainManagementForm(req, u);
         DomainAddForm domAdd = new DomainAddForm(req, u);
         HashMap<String, Object> vars = new HashMap<>();
index 35e17148908988dcd342a41c3cfb6b6675ad3128..a611a239bc361d07380473a8105a00010a6ca1ec 100644 (file)
@@ -3,6 +3,7 @@ package org.cacert.gigi.ping;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.util.Arrays;
 import java.util.LinkedList;
 
 import org.cacert.gigi.Domain;
@@ -31,8 +32,9 @@ public class DNSPinger extends DomainPinger {
             nameservers:
             for (String NS : nameservers) {
                 String[] call = new String[] {
-                        "dig", "@" + NS, "+short", "TXT", "cacert-" + tokenParts[0] + "." + domain
+                        "dig", "@" + NS, "+short", "TXT", "cacert-" + tokenParts[0] + "." + domain.getSuffix()
                 };
+                System.out.println(Arrays.toString(call));
                 p = Runtime.getRuntime().exec(call);
                 br = new BufferedReader(new InputStreamReader(p.getInputStream()));
                 String token = null;
diff --git a/tests/org/cacert/gigi/ping/TestDNS.java b/tests/org/cacert/gigi/ping/TestDNS.java
new file mode 100644 (file)
index 0000000..0a28491
--- /dev/null
@@ -0,0 +1,112 @@
+package org.cacert.gigi.ping;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.cacert.gigi.database.DatabaseConnection;
+import org.cacert.gigi.pages.account.DomainOverview;
+import org.cacert.gigi.testUtils.IOUtils;
+import org.cacert.gigi.testUtils.ManagedTest;
+import org.cacert.gigi.testUtils.TestEmailReciever.TestMail;
+import org.cacert.gigi.util.RandomToken;
+import org.junit.Test;
+
+public class TestDNS extends ManagedTest {
+
+    @Test
+    public void testDNSSanity() throws IOException {
+
+        String token = RandomToken.generateToken(16);
+        String value = RandomToken.generateToken(16);
+
+        Process p = updateDNS(token, value);
+        String reRead = new String(IOUtils.readURL(p.getInputStream()));
+        reRead = reRead.trim();
+        reRead = reRead.substring(1, reRead.length() - 1);
+        assertEquals(value, reRead);
+
+    }
+
+    @Test
+    public void testEmailAndDNS() throws IOException, InterruptedException, SQLException {
+        String email = createUniqueName() + "@example.org";
+        int uid = createVerifiedUser("a", "b", email, TEST_PASSWORD);
+        String cookie = login(email, TEST_PASSWORD);
+
+        String test = getTestProps().getProperty("domain.dnstest");
+        URL u = new URL("https://" + getServerName() + DomainOverview.PATH);
+        URLConnection openConnection = u.openConnection();
+        openConnection.setRequestProperty("Cookie", cookie);
+        String content1 = IOUtils.readURL(openConnection);
+        String csrf = getCSRF(1, content1);
+
+        Pattern p = Pattern.compile("cacert-([A-Za-z0-9]+) IN TXT ([A-Za-z0-9]+)");
+        Matcher m = p.matcher(content1);
+        m.find();
+        updateDNS(m.group(1), m.group(2));
+
+        String content = "newdomain=" + URLEncoder.encode(test, "UTF-8") + //
+                "&emailType=y&email=2&DNSType=y" + //
+                "&ssl-type-0=direct&ssl-port-0=" + //
+                "&ssl-type-1=direct&ssl-port-1=" + //
+                "&ssl-type-2=direct&ssl-port-2=" + //
+                "&ssl-type-3=direct&ssl-port-3=" + //
+                "&adddomain&csrf=" + csrf;
+        openConnection = u.openConnection();
+        openConnection.setRequestProperty("Cookie", cookie);
+        openConnection.setDoOutput(true);
+        openConnection.getOutputStream().write(content.getBytes());
+        openConnection.getHeaderField("Location");
+
+        String newcontent = IOUtils.readURL(cookie(u.openConnection(), cookie));
+        Pattern dlink = Pattern.compile(DomainOverview.PATH + "([0-9]+)'>");
+        Matcher m1 = dlink.matcher(newcontent);
+        m1.find();
+        URL u2 = new URL(u.toString() + m1.group(1));
+
+        TestMail mail = getMailReciever().recieve();
+        String link = mail.extractLink();
+        new URL(link).openConnection().getHeaderField("");
+
+        PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT COUNT(*) FROM domainPinglog");
+        while (true) {
+            ResultSet rs = ps.executeQuery();
+            rs.next();
+            if (rs.getInt(1) >= 2) {
+                break;
+            }
+            Thread.sleep(200);
+        }
+
+        newcontent = IOUtils.readURL(cookie(u2.openConnection(), cookie));
+        Pattern pat = Pattern.compile("<td>dns</td>\\s*<td>success</td>");
+        assertTrue(newcontent, pat.matcher(newcontent).find());
+        pat = Pattern.compile("<td>email</td>\\s*<td>success</td>");
+        assertTrue(newcontent, pat.matcher(newcontent).find());
+    }
+
+    private Process updateDNS(String token, String value) throws IOException, MalformedURLException {
+        String test = getTestProps().getProperty("domain.dnstest");
+        String targetDomain = "cacert-" + token + "." + test;
+        String manage = getTestProps().getProperty("domain.dnsmanage");
+        String url = manage + "t1=" + token + "&t2=" + value;
+        assertEquals(200, ((HttpURLConnection) new URL(url).openConnection()).getResponseCode());
+
+        Process p = Runtime.getRuntime().exec(new String[] {
+                "dig", "@" + getTestProps().getProperty("domain.testns"), "+short", "TXT", targetDomain
+        });
+        return p;
+    }
+
+}
index a9dd42015c8bdb4930d9fb1e6225b69c2ef0ef5f..b0f3670a68f1be76e9077f337d392c6e3b9756a4 100644 (file)
@@ -78,6 +78,11 @@ public class ManagedTest {
     }
 
     static Properties testProps = new Properties();
+
+    public static Properties getTestProps() {
+        return testProps;
+    }
+
     static {
         InitTruststore.run();
         HttpURLConnection.setFollowRedirects(false);
@@ -395,6 +400,10 @@ public class ManagedTest {
 
     public static String getCSRF(URLConnection u, int formIndex) throws IOException {
         String content = IOUtils.readURL(u);
+        return getCSRF(formIndex, content);
+    }
+
+    public static String getCSRF(int formIndex, String content) throws Error {
         Pattern p = Pattern.compile("<input type='hidden' name='csrf' value='([^']+)'>");
         Matcher m = p.matcher(content);
         for (int i = 0; i < formIndex + 1; i++) {
@@ -459,4 +468,9 @@ public class ManagedTest {
         return adrr;
     }
 
+    public static URLConnection cookie(URLConnection openConnection, String cookie) {
+        openConnection.setRequestProperty("Cookie", cookie);
+        return openConnection;
+    }
+
 }