From 10e752c710c50035de036a79bc26fbedc7c8a460 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Felix=20D=C3=B6rre?= Date: Sat, 22 Jul 2017 23:12:25 +0200 Subject: [PATCH] fix: correct validation of minimum and maximum ages MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Instead of mapping a DayDate to a single point in time, introduce the concept of an “earliest start” and “latest end” of a date, and use the appropriate one in various places. Change-Id: Idbc2aa2daadf15fbdbc99e5079eb98dc7aaca92d --- src/club/wpia/gigi/dbObjects/User.java | 2 +- src/club/wpia/gigi/pages/main/Signup.java | 2 +- src/club/wpia/gigi/util/CalendarUtil.java | 10 ++++---- src/club/wpia/gigi/util/DayDate.java | 24 ++++++++++++++++++- src/club/wpia/gigi/util/Notary.java | 9 +++---- tests/club/wpia/gigi/TestCalendarUtil.java | 7 ++++-- .../gigi/pages/account/TestMyDetailsEdit.java | 15 ++++++++---- 7 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/club/wpia/gigi/dbObjects/User.java b/src/club/wpia/gigi/dbObjects/User.java index b65dbc5a..cdd00d6f 100644 --- a/src/club/wpia/gigi/dbObjects/User.java +++ b/src/club/wpia/gigi/dbObjects/User.java @@ -167,7 +167,7 @@ public class User extends CertificateOwner { throw new GigiApiException("Entered date of birth is below the restricted age requirements."); } - if (CalendarUtil.isOfAge(dob, User.MAXIMUM_PLAUSIBLE_AGE)) { + if (CalendarUtil.isYearsInFuture(dob.end(), User.MAXIMUM_PLAUSIBLE_AGE)) { throw new GigiApiException("Entered date of birth exceeds the maximum age set in our policies. Please check your DoB is correct and contact support if the issue persists."); } this.dob = dob; diff --git a/src/club/wpia/gigi/pages/main/Signup.java b/src/club/wpia/gigi/pages/main/Signup.java index 8c070782..746fd803 100644 --- a/src/club/wpia/gigi/pages/main/Signup.java +++ b/src/club/wpia/gigi/pages/main/Signup.java @@ -120,7 +120,7 @@ public class Signup extends Form { ga.mergeInto(new GigiApiException("Entered date of birth is below the restricted age requirements.")); } - if (CalendarUtil.isOfAge(myDoB.getDate(), User.MAXIMUM_PLAUSIBLE_AGE)) { + if (CalendarUtil.isYearsInFuture(myDoB.getDate().end(), User.MAXIMUM_PLAUSIBLE_AGE)) { ga.mergeInto(new GigiApiException("Entered date of birth exceeds the maximum age set in our policies. Please check your DoB is correct and contact support if the issue persists.")); } diff --git a/src/club/wpia/gigi/util/CalendarUtil.java b/src/club/wpia/gigi/util/CalendarUtil.java index 6ff7bb29..c7b2bb0f 100644 --- a/src/club/wpia/gigi/util/CalendarUtil.java +++ b/src/club/wpia/gigi/util/CalendarUtil.java @@ -16,12 +16,12 @@ public class CalendarUtil { } public static boolean isOfAge(DayDate dob, int age) { + return isYearsInFuture(dob.start(), age); + } + + public static boolean isYearsInFuture(Date dt, int age) { Calendar c = Calendar.getInstance(); - c.setTimeInMillis(dob.getTime()); - int year = c.get(Calendar.YEAR); - int month = c.get(Calendar.MONTH); - int day = c.get(Calendar.DAY_OF_MONTH); - c.set(year, month, day); + c.setTime(dt); c.add(Calendar.YEAR, age); return System.currentTimeMillis() >= c.getTime().getTime(); diff --git a/src/club/wpia/gigi/util/DayDate.java b/src/club/wpia/gigi/util/DayDate.java index 8ade3427..85473ecc 100644 --- a/src/club/wpia/gigi/util/DayDate.java +++ b/src/club/wpia/gigi/util/DayDate.java @@ -8,7 +8,9 @@ import java.sql.Date; */ public class DayDate { - public static final long MILLI_DAY = 24 * 60 * 60 * 1000; + private static final int MILLI_HOUR = 60 * 60 * 1000; + + public static final long MILLI_DAY = 24 * MILLI_HOUR; private long time; @@ -75,4 +77,24 @@ public class DayDate { public java.util.Date toDate() { return new java.util.Date(time); } + + // Timezones currently reach from UTC-12 to UTC+14 that allows us to define + // the "earliest start" and "latest end" of a Date. + /** + * Gets this date's starting point. + * + * @return The earliest point in time where this date was in any timezone + */ + public java.util.Date start() { + return new java.util.Date(time - 12 * MILLI_HOUR); + } + + /** + * Gets this date's ending point. + * + * @return The latest point in time where this Date was any timezone + */ + public java.util.Date end() { + return new java.util.Date(time + 14 * MILLI_HOUR + 24 * MILLI_HOUR); + } } diff --git a/src/club/wpia/gigi/util/Notary.java b/src/club/wpia/gigi/util/Notary.java index 36f1fa48..c349a0f9 100644 --- a/src/club/wpia/gigi/util/Notary.java +++ b/src/club/wpia/gigi/util/Notary.java @@ -3,7 +3,6 @@ package club.wpia.gigi.util; import java.io.IOException; import java.text.ParseException; import java.util.Calendar; -import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; @@ -93,13 +92,11 @@ public class Notary { gae.mergeInto(new GigiApiException("You must enter the date when you met the applicant.")); } else { try { - Date d = DateSelector.getDateFormat().parse(date); - Calendar gc = GregorianCalendar.getInstance(); - gc.setTimeInMillis(System.currentTimeMillis()); - gc.add(Calendar.HOUR_OF_DAY, 12); - if (d.getTime() > gc.getTimeInMillis()) { + DayDate d = new DayDate(DateSelector.getDateFormat().parse(date).getTime()); + if (d.start().getTime() > System.currentTimeMillis()) { gae.mergeInto(new GigiApiException("You must not enter a date in the future.")); } + Calendar gc = GregorianCalendar.getInstance(); gc.setTimeInMillis(System.currentTimeMillis()); gc.add(Calendar.MONTH, -LIMIT_MAX_MONTHS_VERIFICATION); if (d.getTime() < gc.getTimeInMillis()) { diff --git a/tests/club/wpia/gigi/TestCalendarUtil.java b/tests/club/wpia/gigi/TestCalendarUtil.java index 5bed6e56..25685e7a 100644 --- a/tests/club/wpia/gigi/TestCalendarUtil.java +++ b/tests/club/wpia/gigi/TestCalendarUtil.java @@ -47,8 +47,11 @@ public class TestCalendarUtil { assertTrue(CalendarUtil.isOfAge(dob, 13)); assertTrue(CalendarUtil.isOfAge(dob, 14)); - - dob = CalendarUtil.getDateFromComponents(year - 14, month, days + 1); + // We need one day as safety margin. Between 10:00 and 23:59 UTC there + // is a place on earth (UTC+1 to UTC+14) where a person having + // birthday "tomorrow" is already of that age. So we need the day after + // tomorrow for doing this check the easy way. + dob = CalendarUtil.getDateFromComponents(year - 14, month, days + 2); assertFalse(CalendarUtil.isOfAge(dob, 14)); } diff --git a/tests/club/wpia/gigi/pages/account/TestMyDetailsEdit.java b/tests/club/wpia/gigi/pages/account/TestMyDetailsEdit.java index 7f2a811f..d5b1cf33 100644 --- a/tests/club/wpia/gigi/pages/account/TestMyDetailsEdit.java +++ b/tests/club/wpia/gigi/pages/account/TestMyDetailsEdit.java @@ -17,9 +17,8 @@ import club.wpia.gigi.GigiApiException; import club.wpia.gigi.dbObjects.Group; import club.wpia.gigi.dbObjects.Name; import club.wpia.gigi.dbObjects.NamePart; -import club.wpia.gigi.dbObjects.User; import club.wpia.gigi.dbObjects.NamePart.NamePartType; -import club.wpia.gigi.pages.account.MyDetails; +import club.wpia.gigi.dbObjects.User; import club.wpia.gigi.testUtils.ManagedTest; public class TestMyDetailsEdit extends ManagedTest { @@ -83,11 +82,19 @@ public class TestMyDetailsEdit extends ManagedTest { assertNotNull(executeBasicWebInteraction(cookie, MyDetails.PATH, "day=1&month=1&year=test&action=updateDoB", 0)); } + /** + * Tests that changing the date of birth to a too recent one results in an + * error. + * + * @throws IOException + * when web interactions fail. + * @see club.wpia.gigi.TestCalendarUtil#testIsOfAge() + */ @Test public void testChangeTooYoung() throws IOException { Calendar c = GregorianCalendar.getInstance(); c.add(Calendar.YEAR, -User.MINIMUM_AGE); - c.add(Calendar.DAY_OF_MONTH, +1); + c.add(Calendar.DAY_OF_MONTH, +2); assertNotNull(executeBasicWebInteraction(cookie, MyDetails.PATH, "day=" + c.get(Calendar.DAY_OF_MONTH) + "&month=" + (c.get(Calendar.MONTH) + 1) + "&year=" + c.get(Calendar.YEAR) + "&action=updateDoB", 0)); } @@ -95,7 +102,7 @@ public class TestMyDetailsEdit extends ManagedTest { public void testChangeTooOld() throws IOException { Calendar c = GregorianCalendar.getInstance(); c.add(Calendar.YEAR, -User.MAXIMUM_PLAUSIBLE_AGE); - c.add(Calendar.DAY_OF_MONTH, -1); + c.add(Calendar.DAY_OF_MONTH, -2); assertNotNull(executeBasicWebInteraction(cookie, MyDetails.PATH, "day=" + c.get(Calendar.DAY_OF_MONTH) + "&month=" + (c.get(Calendar.MONTH) + 1) + "&year=" + c.get(Calendar.YEAR) + "&action=updateDoB", 0)); } -- 2.39.2