Enforce Date-of-births to be day-only.
authorFelix Dörre <felix@dogcraft.de>
Sat, 28 May 2016 18:26:55 +0000 (20:26 +0200)
committerFelix Dörre <felix@dogcraft.de>
Sat, 28 May 2016 18:45:27 +0000 (20:45 +0200)
Change-Id: I23fc14dc6fa83c338297b686b2c8d4ed4fa9baba

12 files changed:
src/org/cacert/gigi/dbObjects/SupportedUser.java
src/org/cacert/gigi/dbObjects/User.java
src/org/cacert/gigi/output/DateSelector.java
src/org/cacert/gigi/output/template/Template.java
src/org/cacert/gigi/pages/wot/AssuranceForm.java
src/org/cacert/gigi/util/DayDate.java [new file with mode: 0644]
src/org/cacert/gigi/util/Notary.java
tests/org/cacert/gigi/TestObjectCache.java
tests/org/cacert/gigi/TestUser.java
tests/org/cacert/gigi/pages/account/TestMyDetailsEdit.java
tests/org/cacert/gigi/pages/wot/TestAssurance.java
util-testing/org/cacert/gigi/pages/Manager.java

index 044f712b45f0a0ea8988a7f019d24097688824fb..df5b54e88d0c5d5753c7bcfa4c4acf2ae3390dad 100644 (file)
@@ -1,10 +1,9 @@
 package org.cacert.gigi.dbObjects;
 
-import java.sql.Date;
-
 import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.database.GigiPreparedStatement;
 import org.cacert.gigi.dbObjects.Certificate.CertificateStatus;
+import org.cacert.gigi.util.DayDate;
 
 public class SupportedUser {
 
@@ -29,8 +28,8 @@ public class SupportedUser {
         return true;
     }
 
-    public boolean setDob(Date dob) throws GigiApiException {
-        if (dob.toString().equals(target.getDoB().toString())) {
+    public boolean setDob(DayDate dob) throws GigiApiException {
+        if (dob.equals(target.getDoB())) {
             return false;
         }
         writeSELog("SE dob change");
index 70fd821442ccbce19454cd91f888b1a07dc15d80..d68b183fa330ead188befeecd67eb73bd1e74c31 100644 (file)
@@ -1,6 +1,5 @@
 package org.cacert.gigi.dbObjects;
 
-import java.sql.Date;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
@@ -15,6 +14,7 @@ import org.cacert.gigi.database.GigiPreparedStatement;
 import org.cacert.gigi.database.GigiResultSet;
 import org.cacert.gigi.localisation.Language;
 import org.cacert.gigi.output.DateSelector;
+import org.cacert.gigi.util.DayDate;
 import org.cacert.gigi.util.Notary;
 import org.cacert.gigi.util.PasswordHash;
 import org.cacert.gigi.util.PasswordStrengthChecker;
@@ -27,7 +27,7 @@ public class User extends CertificateOwner {
 
     private Name name = new Name(null, null, null, null);
 
-    private Date dob;
+    private DayDate dob;
 
     private String email;
 
@@ -46,7 +46,7 @@ public class User extends CertificateOwner {
 
     private void updateName(GigiResultSet rs) {
         name = new Name(rs.getString("fname"), rs.getString("lname"), rs.getString("mname"), rs.getString("suffix"));
-        dob = rs.getDate("dob");
+        dob = new DayDate(rs.getDate("dob"));
         email = rs.getString("email");
 
         String localeStr = rs.getString("language");
@@ -67,7 +67,7 @@ public class User extends CertificateOwner {
         }
     }
 
-    public User(String email, String password, Name name, Date dob, Locale locale) throws GigiApiException {
+    public User(String email, String password, Name name, DayDate dob, Locale locale) throws GigiApiException {
         this.email = email;
         this.dob = dob;
         this.name = name;
@@ -79,7 +79,7 @@ public class User extends CertificateOwner {
             query.setString(4, name.getMname());
             query.setString(5, name.getLname());
             query.setString(6, name.getSuffix());
-            query.setDate(7, dob);
+            query.setDate(7, dob.toSQLDate());
             query.setString(8, locale.toString());
             query.setInt(9, getId());
             query.execute();
@@ -91,11 +91,11 @@ public class User extends CertificateOwner {
         return name;
     }
 
-    public Date getDoB() {
+    public DayDate getDoB() {
         return dob;
     }
 
-    public void setDoB(Date dob) {
+    public void setDoB(DayDate dob) {
         this.dob = dob;
     }
 
@@ -222,7 +222,7 @@ public class User extends CertificateOwner {
 
     public boolean isOfAge(int desiredAge) {
         Calendar c = Calendar.getInstance();
-        c.setTime(dob);
+        c.setTimeInMillis(dob.getTime());
         int year = c.get(Calendar.YEAR);
         int month = c.get(Calendar.MONTH);
         int day = c.get(Calendar.DAY_OF_MONTH);
@@ -335,7 +335,7 @@ public class User extends CertificateOwner {
             update.setString(2, name.getLname());
             update.setString(3, name.getMname());
             update.setString(4, name.getSuffix());
-            update.setDate(5, getDoB());
+            update.setDate(5, getDoB().toSQLDate());
             update.setInt(6, getId());
             update.executeUpdate();
         }
index ad1bdf33e3b543da3b64a56fb28ad09627446571..22a0f620854eafb45a2f1826f62b4a8a572f1d2e 100644 (file)
@@ -4,7 +4,6 @@ import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Calendar;
-import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.Map;
 import java.util.TimeZone;
@@ -14,16 +13,17 @@ import javax.servlet.http.HttpServletRequest;
 import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.localisation.Language;
 import org.cacert.gigi.output.template.Outputable;
+import org.cacert.gigi.util.DayDate;
 import org.cacert.gigi.util.HTMLEncoder;
 
 public class DateSelector implements Outputable {
 
     private String[] names;
 
-    public DateSelector(String day, String month, String year, Date date) {
+    public DateSelector(String day, String month, String year, DayDate date) {
         this(day, month, year);
         Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTF"));
-        cal.setTime(date);
+        cal.setTimeInMillis(date.getTime());
         this.day = cal.get(Calendar.DAY_OF_MONTH);
         this.month = cal.get(Calendar.MONTH) + 1;
         this.year = cal.get(Calendar.YEAR);
@@ -126,10 +126,11 @@ public class DateSelector implements Outputable {
         return "DateSelector [names=" + Arrays.toString(names) + ", day=" + day + ", month=" + month + ", year=" + year + "]";
     }
 
-    public java.sql.Date getDate() {
+    public DayDate getDate() {
         Calendar gc = GregorianCalendar.getInstance();
-        gc.set(year, month - 1, day);
-        return new java.sql.Date(gc.getTime().getTime());
+        gc.set(year, month - 1, day, 0, 0, 0);
+        gc.set(Calendar.MILLISECOND, 0);
+        return new DayDate(gc.getTime().getTime());
     }
 
     public static SimpleDateFormat getDateFormat() {
index 858b04806bcbe9e2591682492f1512936f6472f3..84727f25758dfd6b6f795c40c17d5064284c9331 100644 (file)
@@ -18,6 +18,7 @@ import java.util.regex.Pattern;
 
 import org.cacert.gigi.localisation.Language;
 import org.cacert.gigi.output.DateSelector;
+import org.cacert.gigi.util.DayDate;
 import org.cacert.gigi.util.HTMLEncoder;
 
 public class Template implements Outputable {
@@ -190,8 +191,8 @@ public class Template implements Outputable {
         }
         if (s instanceof Outputable) {
             ((Outputable) s).output(out, l, vars);
-        } else if (s instanceof java.sql.Date) {
-            out.print(DateSelector.getDateFormat().format(s));
+        } else if (s instanceof DayDate) {
+            out.print(DateSelector.getDateFormat().format(((DayDate) s).toDate()));
         } else if (s instanceof Date) {
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             out.print(sdf.format(s));
index a840aa757cafc25176d03cf8f7567eafbcb224fa..05aed3d2a465a2cd8e5d2d072a3f6e316d678342 100644 (file)
@@ -4,7 +4,6 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URLEncoder;
 import java.text.SimpleDateFormat;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -23,6 +22,7 @@ import org.cacert.gigi.output.template.IterableDataset;
 import org.cacert.gigi.output.template.Template;
 import org.cacert.gigi.pages.Page;
 import org.cacert.gigi.pages.PasswordResetPage;
+import org.cacert.gigi.util.DayDate;
 import org.cacert.gigi.util.Notary;
 import org.cacert.gigi.util.RandomToken;
 import org.cacert.gigi.util.ServerConstants;
@@ -33,7 +33,7 @@ public class AssuranceForm extends Form {
 
     private Name assureeName;
 
-    private Date dob;
+    private DayDate dob;
 
     private String location = "";
 
@@ -69,8 +69,8 @@ public class AssuranceForm extends Form {
         res.put("nameExplicit", assuree.getName());
         res.put("name", assuree.getName().toString());
         res.put("maxpoints", assurer.getMaxAssurePoints());
-        res.put("dob", sdf.format(assuree.getDoB()));
-        res.put("dobFmt2", sdf2.format(assuree.getDoB()));
+        res.put("dob", sdf.format(assuree.getDoB().toDate()));
+        res.put("dobFmt2", sdf2.format(assuree.getDoB().toDate()));
         res.put("location", location);
         res.put("date", date);
         res.put("aword", aword);
diff --git a/src/org/cacert/gigi/util/DayDate.java b/src/org/cacert/gigi/util/DayDate.java
new file mode 100644 (file)
index 0000000..0b7ba38
--- /dev/null
@@ -0,0 +1,70 @@
+package org.cacert.gigi.util;
+
+import java.sql.Date;
+
+/**
+ * This Class consists of a millisecond timestamp that is only interesting up to
+ * day-precision.
+ */
+public class DayDate {
+
+    public static final long MILLI_DAY = 24 * 60 * 60 * 1000;
+
+    private long time;
+
+    /**
+     * Creates a new {@link DayDate} from the SQL Day-exact variant {@link Date}
+     * .
+     * 
+     * @see #toSQLDate()
+     */
+    public DayDate(Date date) {
+        this(date.getTime());
+    }
+
+    /**
+     * Creates a new {@link DayDate} based on the given millisecond timestamp.
+     * 
+     * @param millis
+     *            the timestamp to create the Date from.
+     * @throws IllegalArgumentException
+     *             if the parameter contains more precision than needed.
+     */
+    public DayDate(long millis) {
+        this.time = millis;
+        if (millis % MILLI_DAY != 0) {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    /**
+     * Gets the enclosed timestamp.
+     * 
+     * @return the enclosed timestamp.
+     */
+    public long getTime() {
+        return time;
+    }
+
+    /**
+     * Converts this DayDate to an {@link Date}.
+     * 
+     * @return the corresponding {@link Date}
+     * @see #DayDate(Date)
+     */
+    public Date toSQLDate() {
+        return new Date(time);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if ( !(obj instanceof DayDate)) {
+            throw new Error("You may not compare this date somthing other than a DayDate");
+        }
+        return ((DayDate) obj).time == time;
+    }
+
+    public java.util.Date toDate() {
+        return new java.util.Date(time);
+    }
+}
index 7cb15aad203dc14619143dfd442b238221c28b62..24118fc3678f4f738818b0fbd7b95cdc738b62f3 100644 (file)
@@ -72,7 +72,7 @@ public class Notary {
      * @throws GigiApiException
      *             if the assurance fails (for various reasons)
      */
-    public synchronized static void assure(User assurer, User assuree, Name assureeName, Date dob, int awarded, String location, String date, AssuranceType type) throws GigiApiException {
+    public synchronized static void assure(User assurer, User assuree, Name assureeName, DayDate dob, int awarded, String location, String date, AssuranceType type) throws GigiApiException {
         may(assurer, assuree, AssuranceType.FACE_TO_FACE);
         GigiApiException gae = new GigiApiException();
         if ( !gae.isEmpty()) {
index e4dec8a3b541224bd90d11b11d0cd7ad1bf2cf51..d2a04595b0c6014d7ad75fd5a3a4ab280b3c2f4c 100644 (file)
@@ -3,7 +3,6 @@ package org.cacert.gigi;
 import static org.hamcrest.CoreMatchers.*;
 import static org.junit.Assert.*;
 
-import java.sql.Date;
 import java.sql.SQLException;
 import java.util.Calendar;
 import java.util.Locale;
@@ -13,6 +12,7 @@ import org.cacert.gigi.dbObjects.EmailAddress;
 import org.cacert.gigi.dbObjects.Name;
 import org.cacert.gigi.dbObjects.User;
 import org.cacert.gigi.testUtils.ManagedTest;
+import org.cacert.gigi.util.DayDate;
 import org.junit.Test;
 
 public class TestObjectCache extends ManagedTest {
@@ -24,8 +24,9 @@ public class TestObjectCache extends ManagedTest {
         assertThat(User.getById(uid), is(sameInstance(User.getById(uid))));
 
         Calendar c = Calendar.getInstance();
-        c.set(1950, 1, 1);
-        User u = new User(createUniqueName() + "@example.org", TEST_PASSWORD, new Name("fname", "lname", "mname", "suffix"), new Date(c.getTime().getTime()), Locale.ENGLISH);
+        c.set(1950, 1, 1, 0, 0, 0);
+        c.set(Calendar.MILLISECOND, 0);
+        User u = new User(createUniqueName() + "@example.org", TEST_PASSWORD, new Name("fname", "lname", "mname", "suffix"), new DayDate(c.getTime().getTime()), Locale.ENGLISH);
 
         assertThat(u, is(sameInstance(User.getById(u.getId()))));
         assertThat(User.getById(u.getId()), is(sameInstance(User.getById(u.getId()))));
index 1124179ac4bc3998a997d0e7d14a7e2fa982beff..a3dfb777b3c5b2c336b41f20ac754579355ee072 100644 (file)
@@ -3,7 +3,6 @@ package org.cacert.gigi;
 import static org.junit.Assert.*;
 
 import java.io.IOException;
-import java.sql.Date;
 import java.sql.SQLException;
 import java.util.Locale;
 
@@ -13,6 +12,7 @@ import org.cacert.gigi.dbObjects.EmailAddress;
 import org.cacert.gigi.dbObjects.Name;
 import org.cacert.gigi.dbObjects.User;
 import org.cacert.gigi.testUtils.ManagedTest;
+import org.cacert.gigi.util.DayDate;
 import org.junit.Test;
 
 public class TestUser extends ManagedTest {
@@ -21,7 +21,7 @@ public class TestUser extends ManagedTest {
     public void testStoreAndLoad() throws SQLException, GigiApiException {
         long dob = System.currentTimeMillis();
         dob -= dob % (1000 * 60 * 60 * 24);
-        User u = new User(createUniqueName() + "a@email.org", "password", new Name("user", "last", "", ""), new java.sql.Date(dob), Locale.ENGLISH);
+        User u = new User(createUniqueName() + "a@email.org", "password", new Name("user", "last", "", ""), new DayDate(dob), Locale.ENGLISH);
         int id = u.getId();
         User u2 = User.getById(id);
         assertEquals(u.getName(), u2.getName());
@@ -95,7 +95,9 @@ public class TestUser extends ManagedTest {
 
     @Test
     public void testDoubleInsert() throws GigiApiException {
-        User u = new User(createUniqueName() + "@example.org", TEST_PASSWORD, new Name("f", "k", "m", "s"), new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 365), Locale.ENGLISH);
+        long d = System.currentTimeMillis();
+        d -= d % DayDate.MILLI_DAY;
+        User u = new User(createUniqueName() + "@example.org", TEST_PASSWORD, new Name("f", "k", "m", "s"), new DayDate(d + 1000L * 60 * 60 * 24 * 365), Locale.ENGLISH);
         Assurance[] ma = u.getMadeAssurances();
         Assurance[] ma2 = u.getMadeAssurances();
         Assurance[] ra = u.getReceivedAssurances();
index 536634be1e978e2c14a5ef3e595b8e3b0be19353..d40bb98be351d0e778cf070d3f9ea80031247733 100644 (file)
@@ -102,7 +102,7 @@ public class TestMyDetailsEdit extends ManagedTest {
         cal.set(Calendar.DAY_OF_MONTH, Calendar.FEBRUARY);
         cal.set(Calendar.MONTH, 1);
         Date d = new Date(cal.getTimeInMillis());
-        assertEquals(d.toString(), u.getDoB().toString());
+        assertEquals(d.toString(), u.getDoB().toSQLDate().toString());
     }
 
     @Test
index ffc9ea5788fc1f83f04b229572fdf88aad79a6ba..3468f1f4c669b014b1847f65ec28173ad11bb837 100644 (file)
@@ -107,27 +107,42 @@ public class TestAssurance extends ManagedTest {
 
     @Test
     public void testAssureFormRaceName() throws IOException, SQLException {
-        testAssureFormRace(true);
+        testAssureFormRace(true, false);
     }
 
     @Test
     public void testAssureFormRaceDoB() throws IOException, SQLException {
-        testAssureFormRace(false);
+        testAssureFormRace(false, false);
     }
 
-    public void testAssureFormRace(boolean name) throws IOException, SQLException {
+    @Test
+    public void testAssureFormRaceNameBlind() throws IOException, SQLException {
+        testAssureFormRace(true, true);
+    }
+
+    @Test
+    public void testAssureFormRaceDoBBlind() throws IOException, SQLException {
+        testAssureFormRace(false, true);
+    }
+
+    public void testAssureFormRace(boolean name, boolean succeed) throws IOException, SQLException {
         URLConnection uc = buildupAssureFormConnection(true);
 
         String assureeCookie = login(assureeM, TEST_PASSWORD);
-        String newName = "lname=" + (name ? "c" : "a") + "&fname=a&mname=&suffix=";
-        String newDob = "day=1&month=1&year=" + (name ? 1910 : 1911);
+        String newName = "lname=" + (name && !succeed ? "a" : "c") + "&fname=a&mname=&suffix=";
+        String newDob = "day=1&month=1&year=" + ( !name && !succeed ? 1911 : 1910);
 
         assertNull(executeBasicWebInteraction(assureeCookie, MyDetails.PATH, newName + "&" + newDob + "&processDetails", 0));
 
         uc.getOutputStream().write(("date=2000-01-01&location=testcase&certify=1&rules=1&CCAAgreed=1&assertion=1&points=10").getBytes("UTF-8"));
         uc.getOutputStream().flush();
         String error = fetchStartErrorMessage(IOUtils.readURL(uc));
-        assertTrue(error, !error.startsWith("</div>"));
+        if (succeed) {
+            assertNull(error);
+        } else {
+            assertTrue(error, !error.startsWith("</div>"));
+            assertThat(error, containsString("changed his personal details"));
+        }
     }
 
     @Test
index 3ac191ac030bf4d4fc886cb685237d08e4c5977f..460fc4d22ff13203831ba313e48b8e7a53b82059 100644 (file)
@@ -7,7 +7,6 @@ import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.Signature;
-import java.sql.Date;
 import java.util.Base64;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
@@ -45,6 +44,7 @@ import org.cacert.gigi.pages.account.certs.CertificateRequest;
 import org.cacert.gigi.ping.DomainPinger;
 import org.cacert.gigi.ping.PingerDaemon;
 import org.cacert.gigi.util.AuthorizationContext;
+import org.cacert.gigi.util.DayDate;
 import org.cacert.gigi.util.Notary;
 
 import sun.security.x509.X509Key;
@@ -210,7 +210,7 @@ public class Manager extends Page {
     private void createUser(String email) throws GigiApiException, IllegalAccessException {
         Calendar gc = GregorianCalendar.getInstance();
         gc.set(1990, 0, 1);
-        User u = new User(email, "xvXV12°§", new Name("Först", "Läst", "Müddle", "Süffix"), new Date(gc.getTime().getTime()), Locale.ENGLISH);
+        User u = new User(email, "xvXV12°§", new Name("Först", "Läst", "Müddle", "Süffix"), new DayDate(gc.getTime().getTime()), Locale.ENGLISH);
         EmailAddress ea = u.getEmails()[0];
         if (f == null) {
             System.out.println("verification failed");