]> WPIA git - gigi.git/commitdiff
upd: better display of (and collection of) error messages
authorFelix Dörre <felix@dogcraft.de>
Mon, 4 Jul 2016 08:47:02 +0000 (10:47 +0200)
committerFelix Dörre <felix@dogcraft.de>
Mon, 4 Jul 2016 10:24:56 +0000 (12:24 +0200)
Change-Id: I74654fab8f88f5e15bf4c1c424b8709b504affe2

14 files changed:
src/org/cacert/gigi/GigiApiException.java
src/org/cacert/gigi/output/template/Form.java
src/org/cacert/gigi/output/template/PlainOutputable.java [new file with mode: 0644]
src/org/cacert/gigi/pages/LoginPage.java
src/org/cacert/gigi/pages/account/mail/MailAddForm.java
src/org/cacert/gigi/pages/account/mail/MailOverview.java
src/org/cacert/gigi/pages/main/RegisterPage.java
src/org/cacert/gigi/pages/main/Signup.java
src/org/cacert/gigi/pages/wot/AssuranceForm.java
src/org/cacert/gigi/pages/wot/AssurePage.java
src/org/cacert/gigi/util/RateLimit.java
static/static/css/cacert.css
tests/org/cacert/gigi/pages/account/TestCertificateAdd.java
tests/org/cacert/gigi/testUtils/ManagedTest.java

index 0d600b973f3284fa17fe8861e51398148b9aa448..a61b4b5c68cc431445fa192f0f9d842d0e791ab4 100644 (file)
@@ -51,20 +51,20 @@ public class GigiApiException extends Exception {
     }
 
     public void format(PrintWriter out, Language language) {
-        out.println("<div class='formError'>");
+        out.println("<div class='bg-danger error-msgs'>");
         if (isInternalError()) {
             e.printStackTrace();
-            out.print("<div>");
+            out.print("<p>");
             out.println(language.getTranslation("An internal error occurred."));
-            out.println("</div>");
+            out.println("</p>");
         }
         HashMap<String, Object> map = new HashMap<>();
         for (Outputable message : messages) {
             map.clear();
 
-            out.print("<div>");
+            out.print("<p>");
             message.output(out, language, map);
-            out.println("</div>");
+            out.println("</p>");
         }
         out.println("</div>");
 
index 83a96f3813ad6bdb0c20bbc817f73f5eb2287b35..f2219581be3c09b2d77cbbd2dff8ae30ac32de5a 100644 (file)
@@ -4,13 +4,11 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Map;
 
-import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
 import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.localisation.Language;
-import org.cacert.gigi.pages.Page;
 import org.cacert.gigi.util.RandomToken;
 
 /**
@@ -73,7 +71,6 @@ public abstract class Form implements Outputable {
         } else {
             out.println("<form method='POST' action='" + action + "'>");
         }
-        failed = false;
         outputContent(out, l, vars);
         out.print("<input type='hidden' name='" + CSRF_FIELD + "' value='");
         out.print(getCSRFToken());
@@ -92,39 +89,6 @@ public abstract class Form implements Outputable {
      */
     protected abstract void outputContent(PrintWriter out, Language l, Map<String, Object> vars);
 
-    private boolean failed;
-
-    protected void outputError(PrintWriter out, ServletRequest req, String text, Object... contents) {
-        if ( !failed) {
-            failed = true;
-            out.println("<div class='formError'>");
-        }
-        out.print("<div>");
-        if (contents.length == 0) {
-            out.print(Page.translate(req, text));
-        } else {
-            out.print(String.format(Page.translate(req, text), contents));
-        }
-        out.println("</div>");
-    }
-
-    protected void outputErrorPlain(PrintWriter out, String text) {
-        if ( !failed) {
-            failed = true;
-            out.println("<div class='formError'>");
-        }
-        out.print("<div>");
-        out.print(text);
-        out.println("</div>");
-    }
-
-    public boolean isFailed(PrintWriter out) {
-        if (failed) {
-            out.println("</div>");
-        }
-        return failed;
-    }
-
     protected String getCSRFToken() {
         return csrf;
     }
diff --git a/src/org/cacert/gigi/output/template/PlainOutputable.java b/src/org/cacert/gigi/output/template/PlainOutputable.java
new file mode 100644 (file)
index 0000000..1ab3497
--- /dev/null
@@ -0,0 +1,22 @@
+package org.cacert.gigi.output.template;
+
+import java.io.PrintWriter;
+import java.util.Map;
+
+import org.cacert.gigi.localisation.Language;
+import org.cacert.gigi.util.HTMLEncoder;
+
+public class PlainOutputable implements Outputable {
+
+    String text;
+
+    public PlainOutputable(String text) {
+        this.text = HTMLEncoder.encodeHTML(text);
+    }
+
+    @Override
+    public void output(PrintWriter out, Language l, Map<String, Object> vars) {
+        out.print(text);
+    }
+
+}
index 141c6ca18cc6466ffae0248458347b10511228f7..c206ad444df43c5b0df0a08e4add6612fda6f5c4 100644 (file)
@@ -25,6 +25,7 @@ import org.cacert.gigi.pages.main.RegisterPage;
 import org.cacert.gigi.util.AuthorizationContext;
 import org.cacert.gigi.util.PasswordHash;
 import org.cacert.gigi.util.RateLimit;
+import org.cacert.gigi.util.RateLimit.RateLimitException;
 import org.cacert.gigi.util.ServerConstants;
 
 public class LoginPage extends Page {
@@ -40,8 +41,7 @@ public class LoginPage extends Page {
         @Override
         public boolean submit(PrintWriter out, HttpServletRequest req) throws GigiApiException {
             if (RegisterPage.RATE_LIMIT.isLimitExceeded(req.getRemoteAddr())) {
-                outputError(out, req, "Rate Limit Exceeded");
-                return false;
+                throw new RateLimitException();
             }
             tryAuthWithUnpw(req);
             return false;
@@ -56,12 +56,18 @@ public class LoginPage extends Page {
 
     public static final String LOGIN_RETURNPATH = "login-returnpath";
 
+    private static final String SUBMIT_EXCEPTION = "login-submit-exception";
+
     public LoginPage() {
         super("Password Login");
     }
 
     @Override
     public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        Object o = req.getAttribute(SUBMIT_EXCEPTION);
+        if (o != null) {
+            ((GigiApiException) o).format(resp.getWriter(), getLanguage(req));
+        }
         if (req.getHeader("Host").equals(ServerConstants.getSecureHostNamePort())) {
             resp.getWriter().println(getLanguage(req).getTranslation("Authentication with certificate failed. Try another certificate or use a password."));
         } else {
@@ -81,6 +87,8 @@ public class LoginPage extends Page {
                 try {
                     Form.getForm(req, LoginForm.class).submit(resp.getWriter(), req);
                 } catch (GigiApiException e) {
+                    req.setAttribute(SUBMIT_EXCEPTION, e);
+                    return false;
                 }
             }
         }
@@ -105,7 +113,7 @@ public class LoginPage extends Page {
         return false;
     }
 
-    private void tryAuthWithUnpw(HttpServletRequest req) {
+    private void tryAuthWithUnpw(HttpServletRequest req) throws GigiApiException {
         String un = req.getParameter("username");
         String pw = req.getParameter("password");
         try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `password`, `id` FROM `users` WHERE `email`=? AND verified='1'")) {
@@ -124,9 +132,11 @@ public class LoginPage extends Page {
                     }
                     loginSession(req, User.getById(rs.getInt(2)));
                     req.getSession().setAttribute(LOGIN_METHOD, new TranslateCommand("Password"));
+                    return;
                 }
             }
         }
+        throw new GigiApiException("Username and password didn't match.");
     }
 
     public static User getUser(HttpServletRequest req) {
index 6a2bb2c51d9b7550bd97ebdff654a6b0f7bee683..eaf35f4004975f6e840279f4ab53334209dbbac1 100644 (file)
@@ -10,6 +10,7 @@ import org.cacert.gigi.dbObjects.EmailAddress;
 import org.cacert.gigi.dbObjects.User;
 import org.cacert.gigi.localisation.Language;
 import org.cacert.gigi.output.template.Form;
+import org.cacert.gigi.output.template.PlainOutputable;
 import org.cacert.gigi.output.template.Template;
 import org.cacert.gigi.pages.Page;
 
@@ -30,17 +31,13 @@ public class MailAddForm extends Form {
     }
 
     @Override
-    public boolean submit(PrintWriter out, HttpServletRequest req) {
+    public boolean submit(PrintWriter out, HttpServletRequest req) throws GigiApiException {
         String formMail = req.getParameter("newemail");
         mail = formMail;
         try {
             new EmailAddress(target, mail, Page.getLanguage(req).getLocale());
         } catch (IllegalArgumentException e) {
-            out.println("<div class='formError'>Error: Invalid address!</div>");
-            return false;
-        } catch (GigiApiException e) {
-            e.format(out, Page.getLanguage(req));
-            return false;
+            throw new GigiApiException(new PlainOutputable("Invalid address."));
         }
         return true;
     }
index da3befd581b214c57a284431de6e9b2d7d11d522..b828b7189d7e6fcc8b10bd966cce9cbf7023003f 100644 (file)
@@ -7,6 +7,7 @@ import java.util.HashMap;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.dbObjects.User;
 import org.cacert.gigi.localisation.Language;
 import org.cacert.gigi.output.template.Form;
@@ -36,8 +37,12 @@ public class MailOverview extends Page {
         PrintWriter out = resp.getWriter();
         if (req.getParameter("addmail") != null) {
             MailAddForm f = Form.getForm(req, MailAddForm.class);
-            if (f.submit(out, req)) {
-                resp.sendRedirect(MailOverview.DEFAULT_PATH);
+            try {
+                if (f.submit(out, req)) {
+                    resp.sendRedirect(MailOverview.DEFAULT_PATH);
+                }
+            } catch (GigiApiException e) {
+                e.format(resp.getWriter(), getLanguage(req));
             }
         } else {
             MailManagementForm f = Form.getForm(req, MailManagementForm.class);
index 1e6b33783ee3b89c47c3e602cb90b0196bb949a8..30c428333702991664e451aa3dc6b9b9452d32d7 100644 (file)
@@ -8,6 +8,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.output.template.Form;
 import org.cacert.gigi.pages.Page;
 import org.cacert.gigi.util.AuthorizationContext;
@@ -42,13 +43,15 @@ public class RegisterPage extends Page {
     @Override
     public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         Signup s = Form.getForm(req, Signup.class);
-        if (s == null) {
-            resp.getWriter().println(translate(req, "CSRF token check failed."));
-        } else if (s.submit(resp.getWriter(), req)) {
-            HttpSession hs = req.getSession();
-            hs.setAttribute(SIGNUP_PROCESS, null);
-            resp.getWriter().println(translate(req, "Your information has been submitted" + " into our system. You will now be sent an email with a web link," + " you need to open that link in your web browser within 24 hours" + " or your information will be removed from our system!"));
-            return;
+        try {
+            if (s.submit(resp.getWriter(), req)) {
+                HttpSession hs = req.getSession();
+                hs.setAttribute(SIGNUP_PROCESS, null);
+                resp.getWriter().println(translate(req, "Your information has been submitted" + " into our system. You will now be sent an email with a web link," + " you need to open that link in your web browser within 24 hours" + " or your information will be removed from our system!"));
+                return;
+            }
+        } catch (GigiApiException e) {
+            e.format(resp.getWriter(), getLanguage(req));
         }
 
         outputGet(req, resp, s);
index d341df280f237ed2417d406c47f5570431211b20..4df3ba4903026ea32013dda55adcf83545fac9ec 100644 (file)
@@ -2,7 +2,6 @@ package org.cacert.gigi.pages.main;
 
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -17,12 +16,15 @@ import org.cacert.gigi.email.EmailProvider;
 import org.cacert.gigi.localisation.Language;
 import org.cacert.gigi.output.DateSelector;
 import org.cacert.gigi.output.template.Form;
+import org.cacert.gigi.output.template.PlainOutputable;
+import org.cacert.gigi.output.template.SprintfCommand;
 import org.cacert.gigi.output.template.Template;
 import org.cacert.gigi.pages.Page;
 import org.cacert.gigi.util.CalendarUtil;
 import org.cacert.gigi.util.HTMLEncoder;
 import org.cacert.gigi.util.Notary;
 import org.cacert.gigi.util.PasswordStrengthChecker;
+import org.cacert.gigi.util.RateLimit.RateLimitException;
 
 public class Signup extends Form {
 
@@ -92,51 +94,52 @@ public class Signup extends Form {
     }
 
     @Override
-    public synchronized boolean submit(PrintWriter out, HttpServletRequest req) {
+    public synchronized boolean submit(PrintWriter out, HttpServletRequest req) throws GigiApiException {
         if (RegisterPage.RATE_LIMIT.isLimitExceeded(req.getRemoteAddr())) {
-            outputError(out, req, "Rate Limit Exceeded");
-            return false;
+            throw new RateLimitException();
         }
 
         update(req);
+        GigiApiException ga = new GigiApiException();
         if (buildupName.getLname().trim().equals("")) {
-            outputError(out, req, "Last name were blank.");
+            ga.mergeInto(new GigiApiException("Last name were blank."));
         }
         if ( !myDoB.isValid()) {
-            outputError(out, req, "Invalid date of birth");
+            ga.mergeInto(new GigiApiException("Invalid date of birth"));
         }
 
         if ( !CalendarUtil.isOfAge(myDoB.getDate(), User.MINIMUM_AGE)) {
-            outputError(out, req, "Entered dated of birth is below the restricted age requirements.");
+            ga.mergeInto(new GigiApiException("Entered dated of birth is below the restricted age requirements."));
         }
 
         if ( !"1".equals(req.getParameter("tos_agree"))) {
-            outputError(out, req, "Acceptance of the ToS is required to continue.");
+            ga.mergeInto(new GigiApiException("Acceptance of the ToS is required to continue."));
         }
         if (email.equals("")) {
-            outputError(out, req, "Email Address was blank");
+            ga.mergeInto(new GigiApiException("Email Address was blank"));
         }
         String pw1 = req.getParameter("pword1");
         String pw2 = req.getParameter("pword2");
         if (pw1 == null || pw1.equals("")) {
-            outputError(out, req, "Pass Phrases were blank");
+            ga.mergeInto(new GigiApiException("Pass Phrases were blank"));
         } else if ( !pw1.equals(pw2)) {
-            outputError(out, req, "Pass Phrases don't match");
+            ga.mergeInto(new GigiApiException("Pass Phrases don't match"));
         }
         int pwpoints = PasswordStrengthChecker.checkpw(pw1, buildupName, email);
         if (pwpoints < 3) {
-            outputError(out, req, "The Pass Phrase you submitted failed to contain enough" + " differing characters and/or contained words from" + " your name and/or email address.");
+            ga.mergeInto(new GigiApiException("The Pass Phrase you submitted failed to contain enough" + " differing characters and/or contained words from" + " your name and/or email address."));
         }
-        if (isFailed(out)) {
-            return false;
+        if ( !ga.isEmpty()) {
+            throw ga;
         }
+        GigiApiException ga2 = new GigiApiException();
         try (GigiPreparedStatement q1 = new GigiPreparedStatement("SELECT * FROM `emails` WHERE `email`=? AND `deleted` IS NULL"); GigiPreparedStatement q2 = new GigiPreparedStatement("SELECT * FROM `certOwners` INNER JOIN `users` ON `users`.`id`=`certOwners`.`id` WHERE `email`=? AND `deleted` IS NULL")) {
             q1.setString(1, email);
             q2.setString(1, email);
             GigiResultSet r1 = q1.executeQuery();
             GigiResultSet r2 = q2.executeQuery();
             if (r1.next() || r2.next()) {
-                outputError(out, req, "This email address is currently valid in the system.");
+                ga2.mergeInto(new GigiApiException("This email address is currently valid in the system."));
             }
         }
         try (GigiPreparedStatement q3 = new GigiPreparedStatement("SELECT `domain` FROM `baddomains` WHERE `domain`=RIGHT(?, LENGTH(`domain`))")) {
@@ -145,7 +148,7 @@ public class Signup extends Form {
             GigiResultSet r3 = q3.executeQuery();
             if (r3.next()) {
                 String domain = r3.getString(1);
-                outputError(out, req, "We don't allow signups from people using email addresses from %s", domain);
+                ga2.mergeInto(new GigiApiException(SprintfCommand.createSimple("We don't allow signups from people using email addresses from {0}.", domain)));
             }
         }
         String mailResult = EmailProvider.FAIL;
@@ -155,32 +158,25 @@ public class Signup extends Form {
         }
         if ( !mailResult.equals(EmailProvider.OK)) {
             if (mailResult.startsWith("4")) {
-                outputError(out, req, "The mail server responsible for your domain indicated" + " a temporary failure. This may be due to anti-SPAM measures, such" + " as greylisting. Please try again in a few minutes.");
+                ga2.mergeInto(new GigiApiException("The mail server responsible for your domain indicated" + " a temporary failure. This may be due to anti-SPAM measures, such" + " as greylisting. Please try again in a few minutes."));
             } else {
-                outputError(out, req, "Email Address given was invalid, or a test connection" + " couldn't be made to your server, or the server" + " rejected the email address as invalid");
+                ga2.mergeInto(new GigiApiException("Email Address given was invalid, or a test connection" + " couldn't be made to your server, or the server" + " rejected the email address as invalid"));
             }
             if (mailResult.equals(EmailProvider.FAIL)) {
-                outputError(out, req, "Failed to make a connection to the mail server");
+                ga2.mergeInto(new GigiApiException("Failed to make a connection to the mail server"));
             } else {
-                outputErrorPlain(out, mailResult);
+                ga2.mergeInto(new GigiApiException(new PlainOutputable(mailResult)));
             }
         }
 
-        if (isFailed(out)) {
-            return false;
-        }
-        try {
-            run(req, pw1);
-        } catch (SQLException e) {
-            e.printStackTrace();
-        } catch (GigiApiException e) {
-            e.format(out, Page.getLanguage(req));
-            return false;
+        if ( !ga2.isEmpty()) {
+            throw ga2;
         }
+        run(req, pw1);
         return true;
     }
 
-    private void run(HttpServletRequest req, String password) throws SQLException, GigiApiException {
+    private void run(HttpServletRequest req, String password) throws GigiApiException {
         User u = new User(email, password, buildupName, myDoB.getDate(), Page.getLanguage(req).getLocale());
 
         try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `alerts` SET `memid`=?," + " `general`=?, `country`=?, `regional`=?, `radius`=?")) {
index 79f4d509ac263bad0775a04a91871ddf2aebede9..15642403851806c7f204284a41f20fa1a4f74e7f 100644 (file)
@@ -97,15 +97,16 @@ public class AssuranceForm extends Form {
     }
 
     @Override
-    public boolean submit(PrintWriter out, HttpServletRequest req) {
+    public boolean submit(PrintWriter out, HttpServletRequest req) throws GigiApiException {
         location = req.getParameter("location");
         date = req.getParameter("date");
+        GigiApiException gae = new GigiApiException();
         if (date == null || location == null) {
-            outputError(out, req, "You need to enter location and date!");
+            gae.mergeInto(new GigiApiException("You need to enter location and date!"));
         }
 
         if ( !"1".equals(req.getParameter("certify")) || !"1".equals(req.getParameter("rules")) || !"1".equals(req.getParameter("tos_agree")) || !"1".equals(req.getParameter("assertion"))) {
-            outputError(out, req, "You failed to check all boxes to validate" + " your adherence to the rules and policies of SomeCA");
+            gae.mergeInto(new GigiApiException("You failed to check all boxes to validate" + " your adherence to the rules and policies of SomeCA"));
         }
         if ("1".equals(req.getParameter("passwordReset"))) {
             aword = req.getParameter("passwordResetValue");
@@ -120,39 +121,33 @@ public class AssuranceForm extends Form {
             try {
                 type = AssuranceType.valueOf(val);
             } catch (IllegalArgumentException e) {
-                outputError(out, req, "Assurance Type wrong.");
+                gae.mergeInto(new GigiApiException("Assurance Type wrong."));
             }
         }
 
         int pointsI = 0;
         String points = req.getParameter("points");
         if (points == null || "".equals(points)) {
-            outputError(out, req, "For an assurance, you need to enter points.");
+            gae.mergeInto(new GigiApiException("For an assurance, you need to enter points."));
         } else {
             try {
                 pointsI = Integer.parseInt(points);
             } catch (NumberFormatException e) {
-                outputError(out, req, "The points entered were not a number.");
+                gae.mergeInto(new GigiApiException("The points entered were not a number."));
             }
         }
 
-        if (isFailed(out)) {
-            return false;
+        if ( !gae.isEmpty()) {
+            throw gae;
         }
-        try {
-            Notary.assure(assurer, assuree, assureeName, dob, pointsI, location, req.getParameter("date"), type);
-            if (aword != null && !aword.equals("")) {
-                Language l = Language.getInstance(assuree.getPreferredLocale());
-                String method = l.getTranslation("A password reset was triggered. If you did a password reset by assurance, please enter your secret password using this form:");
-                String subject = l.getTranslation("Password reset by assurance");
-                PasswordResetPage.initPasswordResetProcess(out, assuree, req, aword, l, method, subject);
-            }
-            return true;
-        } catch (GigiApiException e) {
-            e.format(out, Page.getLanguage(req));
+        Notary.assure(assurer, assuree, assureeName, dob, pointsI, location, req.getParameter("date"), type);
+        if (aword != null && !aword.equals("")) {
+            Language l = Language.getInstance(assuree.getPreferredLocale());
+            String method = l.getTranslation("A password reset was triggered. If you did a password reset by assurance, please enter your secret password using this form:");
+            String subject = l.getTranslation("Password reset by assurance");
+            PasswordResetPage.initPasswordResetProcess(out, assuree, req, aword, l, method, subject);
         }
-
-        return false;
+        return true;
     }
 
     public User getAssuree() {
index 94c582f220e5765f19c3d2d2d7d05d8182646d9c..e0cb4a211e57aed40125df653ee99a163283807e 100644 (file)
@@ -54,14 +54,18 @@ public class AssurePage extends Page {
         PrintWriter out = resp.getWriter();
         if (req.getParameter("search") == null) {
             AssuranceForm form = Form.getForm(req, AssuranceForm.class);
-            if (form.submit(out, req)) {
-                out.println(translate(req, "Assurance complete."));
-            } else {
+            try {
+                if (form.submit(out, req)) {
+                    out.println(translate(req, "Assurance complete."));
+                    return;
+                }
+            } catch (GigiApiException e) {
+                e.format(out, Page.getLanguage(req));
                 try {
                     Notary.checkAssuranceIsPossible(LoginPage.getUser(req), form.getAssuree());
                     form.output(out, getLanguage(req), new HashMap<String, Object>());
-                } catch (GigiApiException e) {
-                    e.format(out, Page.getLanguage(req));
+                } catch (GigiApiException e1) {
+                    e1.format(out, Page.getLanguage(req));
                 }
             }
 
@@ -97,10 +101,9 @@ public class AssurePage extends Page {
                     }
                 }
             } else {
-                out.print("<div class='formError'>");
-
-                out.println(translate(req, "I'm sorry, there was no email and date of birth matching" + " what you entered in the system. Please double check" + " your information."));
-                out.print("</div>");
+                GigiApiException e = new GigiApiException("I'm sorry, there was no email and date of birth matching" //
+                        + " what you entered in the system. Please double check your information.");
+                e.format(out, getLanguage(req));
             }
 
         }
index 65c1668494c05b03ecebd7d15ca5fbed1999031e..eb24563dce2e290d3c6a1ce9b4c0886b7906556f 100644 (file)
@@ -3,8 +3,17 @@ package org.cacert.gigi.util;
 import java.util.HashMap;
 import java.util.TreeSet;
 
+import org.cacert.gigi.GigiApiException;
+
 public class RateLimit {
 
+    public static final class RateLimitException extends GigiApiException {
+
+        public RateLimitException() {
+            super("Rate limit exceeded.");
+        }
+    }
+
     private class Entry implements Comparable<Entry> {
 
         long firstAccess;
index b9443976c787ad4c0dd4021db8edb1435922f629..34db4f1036bbec53e08d17a82c4502be09e5bae7 100644 (file)
@@ -17,3 +17,6 @@
 .experthidden{
        display: none;
 }
+div.error-msgs p{
+       padding: 6px;
+}
index 57a5e634c8d10585bba4312936bbfcb601441a7f..fba274d1bf1d46f3c8f463a980b730445bd1ad49 100644 (file)
@@ -56,6 +56,13 @@ import sun.security.x509.X509Key;
 
 public class TestCertificateAdd extends ClientTest {
 
+    private static class OnPageError extends Error {
+
+        public OnPageError(String page) {
+            super(page);
+        }
+    }
+
     KeyPair kp = generateKeypair();
 
     String csrf;
@@ -295,8 +302,9 @@ public class TestCertificateAdd extends ClientTest {
             assertArrayEquals(new String[] {
                     "client", CertificateRequest.DEFAULT_CN, "", Digest.SHA512.toString()
             }, res);
-        } catch (Error e) {
-            assertTrue(e.getMessage().startsWith("<div>Challenge mismatch"));
+        } catch (OnPageError e) {
+            String error = fetchStartErrorMessage(e.getMessage());
+            assertTrue(error, error.startsWith("<p>Challenge mismatch"));
         }
         return csrf;
     }
@@ -341,9 +349,8 @@ public class TestCertificateAdd extends ClientTest {
 
     private String[] extractFormData(HttpURLConnection uc) throws IOException, Error {
         String result = IOUtils.readURL(uc);
-        if (result.contains("<div class='formError'>")) {
-            String s = fetchStartErrorMessage(result);
-            throw new Error(s);
+        if (result.contains("<div class='bg-danger error-msgs'>")) {
+            throw new OnPageError(result);
         }
 
         String profileKey = extractPattern(result, Pattern.compile("<option value=\"([^\"]*)\" selected>"));
index aad0ef02c52e5d7ab653cdb0ff4783f8a3d3062f..d620ff3518c92fa8894bc512d64309819401f874 100644 (file)
@@ -259,7 +259,7 @@ public class ManagedTest extends ConfiguredTest {
     }
 
     public static String fetchStartErrorMessage(String d) throws IOException {
-        String formFail = "<div class='formError'>";
+        String formFail = "<div class='bg-danger error-msgs'>";
         int idx = d.indexOf(formFail);
         if (idx == -1) {
             return null;