From 34daecdb56cd063819e3e2913b9b17a6dc0229d2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Felix=20D=C3=B6rre?= Date: Mon, 6 Feb 2017 23:45:13 +0100 Subject: [PATCH] fix: display verify information only when verification token is known. Change-Id: I12ea06f13fddc3ad931751e9751f7d87fefd6c60 --- src/org/cacert/gigi/dbObjects/Domain.java | 8 ++ .../cacert/gigi/dbObjects/EmailAddress.java | 9 +++ src/org/cacert/gigi/dbObjects/Verifyable.java | 2 + src/org/cacert/gigi/pages/Verify.java | 7 ++ tests/org/cacert/gigi/pages/TestVerify.java | 74 +++++++++++++++++++ 5 files changed, 100 insertions(+) create mode 100644 tests/org/cacert/gigi/pages/TestVerify.java diff --git a/src/org/cacert/gigi/dbObjects/Domain.java b/src/org/cacert/gigi/dbObjects/Domain.java index 3ecf7285..f7c963c9 100644 --- a/src/org/cacert/gigi/dbObjects/Domain.java +++ b/src/org/cacert/gigi/dbObjects/Domain.java @@ -125,6 +125,14 @@ public class Domain implements IdCachable, Verifyable { configs = null; } + public synchronized boolean isVerifyable(String hash) throws GigiApiException { + try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT 1 FROM `domainPinglog` WHERE `challenge`=? AND `state`='open' AND `configId` IN (SELECT `id` FROM `pingconfig` WHERE `domainid`=? AND `type`='email')")) { + ps.setString(1, hash); + ps.setInt(2, id); + return ps.executeQuery().next(); + } + } + public synchronized void verify(String hash) throws GigiApiException { try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `domainPinglog` SET `state`='success' WHERE `challenge`=? AND `state`='open' AND `configId` IN (SELECT `id` FROM `pingconfig` WHERE `domainid`=? AND `type`='email')")) { ps.setString(1, hash); diff --git a/src/org/cacert/gigi/dbObjects/EmailAddress.java b/src/org/cacert/gigi/dbObjects/EmailAddress.java index b2106d8b..78470359 100644 --- a/src/org/cacert/gigi/dbObjects/EmailAddress.java +++ b/src/org/cacert/gigi/dbObjects/EmailAddress.java @@ -95,6 +95,15 @@ public class EmailAddress implements IdCachable, Verifyable { return address; } + public synchronized boolean isVerifyable(String hash) throws GigiApiException { + try (GigiPreparedStatement stmt = new GigiPreparedStatement("SELECT 1 FROM `emailPinglog` WHERE `email`=? AND `uid`=? AND `type`='active' AND `challenge`=? AND `status`='open'::`pingState`")) { + stmt.setString(1, address); + stmt.setInt(2, owner.getId()); + stmt.setString(3, hash); + return stmt.executeQuery().next(); + } + } + public synchronized void verify(String hash) throws GigiApiException { try (GigiPreparedStatement stmt = new GigiPreparedStatement("UPDATE `emailPinglog` SET `status`='success'::`pingState` WHERE `email`=? AND `uid`=? AND `type`='active' AND `challenge`=? AND `status`='open'::`pingState`")) { stmt.setString(1, address); diff --git a/src/org/cacert/gigi/dbObjects/Verifyable.java b/src/org/cacert/gigi/dbObjects/Verifyable.java index e1c997e4..ba4dd0c5 100644 --- a/src/org/cacert/gigi/dbObjects/Verifyable.java +++ b/src/org/cacert/gigi/dbObjects/Verifyable.java @@ -6,4 +6,6 @@ public interface Verifyable { public void verify(String hash) throws GigiApiException; + public boolean isVerifyable(String hash) throws GigiApiException; + } diff --git a/src/org/cacert/gigi/pages/Verify.java b/src/org/cacert/gigi/pages/Verify.java index 2b4cd826..bc463981 100644 --- a/src/org/cacert/gigi/pages/Verify.java +++ b/src/org/cacert/gigi/pages/Verify.java @@ -52,6 +52,13 @@ public class Verify extends Page { } else { throw new IllegalArgumentException(); } + try { + if ( !target.isVerifyable(hash)) { + throw new IllegalArgumentException(); + } + } catch (GigiApiException e) { + throw new IllegalArgumentException(e); + } } @Override diff --git a/tests/org/cacert/gigi/pages/TestVerify.java b/tests/org/cacert/gigi/pages/TestVerify.java new file mode 100644 index 00000000..28572eb3 --- /dev/null +++ b/tests/org/cacert/gigi/pages/TestVerify.java @@ -0,0 +1,74 @@ +package org.cacert.gigi.pages; + +import static org.junit.Assert.*; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.cacert.gigi.GigiApiException; +import org.cacert.gigi.dbObjects.EmailAddress; +import org.cacert.gigi.testUtils.ClientBusinessTest; +import org.cacert.gigi.testUtils.TestEmailReceiver.TestMail; +import org.hamcrest.CustomMatcher; +import org.junit.Test; + +public class TestVerify extends ClientBusinessTest { + + @Test + public void testVerify() throws GigiApiException { + EmailAddress ea = new EmailAddress(u, "test@example.com", Locale.ENGLISH); + TestMail tm = getMailReceiver().receive(); + Pattern p = Pattern.compile(".*hash=(.*)"); + Matcher m = p.matcher(tm.extractLink()); + assertTrue(m.matches()); + String correctToken = m.group(1); + + // Assert initial state + assertFalse(ea.isVerified()); + + // wrong hashes + assertFalse(ea.isVerifyable(correctToken + "w")); + assertFalse(ea.isVerifyable("")); + // correct token + assertTrue(ea.isVerifyable(correctToken)); + // state transition not possible with wrong token + org.hamcrest.Matcher isInvalidToken = verificationDoesNotWork(ea); + assertThat(correctToken + "a", isInvalidToken); + assertThat("", isInvalidToken); + + // state transition + assertFalse(ea.isVerified()); + ea.verify(correctToken); + assertTrue(ea.isVerified()); + + // no token is correct anymore + assertFalse(ea.isVerifyable(correctToken)); + assertFalse(ea.isVerifyable("")); + // no verification is possible anymore + assertThat(correctToken + "a", isInvalidToken); + assertThat(correctToken, isInvalidToken); + assertThat("", isInvalidToken); + } + + private org.hamcrest.Matcher verificationDoesNotWork(final EmailAddress ea) { + return new CustomMatcher("Invalid token for validation.") { + + @Override + public boolean matches(Object item) { + if ( !(item instanceof String)) { + return false; + } + String s = (String) item; + try { + ea.verify(s); + return false; + } catch (IllegalArgumentException e) { + return true; + } catch (GigiApiException e) { + throw new Error(e); + } + } + }; + } +} -- 2.39.2