From 8f4a157d8a052486d019936ec499f02f912e1ddf Mon Sep 17 00:00:00 2001 From: =?utf8?q?Felix=20D=C3=B6rre?= Date: Sat, 6 Aug 2016 19:50:52 +0200 Subject: [PATCH] add: store value for login-enabled for clientCertificates Change-Id: If857b9b97ad07923d53cc695d0dd6cf750d484ef --- .../gigi/database/DatabaseConnection.java | 2 +- .../cacert/gigi/database/tableStructure.sql | 8 ++--- .../cacert/gigi/database/upgrade/from_20.sql | 6 ++++ .../cacert/gigi/dbObjects/Certificate.java | 24 ++++++++++++++ .../gigi/dbObjects/CertificateOwner.java | 2 +- .../account/certs/CertificateIssueForm.java | 3 ++ tests/org/cacert/gigi/TestCertificate.java | 2 ++ .../cacert/gigi/TestCrossDomainAccess.java | 1 + .../cacert/gigi/TestSeparateSessionScope.java | 2 ++ .../org/cacert/gigi/api/ImportCATSResult.java | 1 + tests/org/cacert/gigi/api/IssueCert.java | 1 + .../gigi/dbObjects/TestCertificate.java | 32 +++++++++++++++++++ .../pages/account/TestCertificateAdd.java | 19 +++++++++-- .../gigi/testUtils/RestrictedApiTest.java | 1 + 14 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 src/org/cacert/gigi/database/upgrade/from_20.sql create mode 100644 tests/org/cacert/gigi/dbObjects/TestCertificate.java diff --git a/src/org/cacert/gigi/database/DatabaseConnection.java b/src/org/cacert/gigi/database/DatabaseConnection.java index e7c98488..80f96e8d 100644 --- a/src/org/cacert/gigi/database/DatabaseConnection.java +++ b/src/org/cacert/gigi/database/DatabaseConnection.java @@ -122,7 +122,7 @@ public class DatabaseConnection { } - public static final int CURRENT_SCHEMA_VERSION = 20; + public static final int CURRENT_SCHEMA_VERSION = 21; public static final int CONNECTION_TIMEOUT = 24 * 60 * 60; diff --git a/src/org/cacert/gigi/database/tableStructure.sql b/src/org/cacert/gigi/database/tableStructure.sql index 9bd231e8..f9010ccb 100644 --- a/src/org/cacert/gigi/database/tableStructure.sql +++ b/src/org/cacert/gigi/database/tableStructure.sql @@ -160,7 +160,6 @@ CREATE TABLE "certs" ( "revoked" timestamp NULL DEFAULT NULL, "expire" timestamp NULL DEFAULT NULL, "renewed" boolean NOT NULL DEFAULT 'false', - "disablelogin" boolean NOT NULL DEFAULT 'false', "pkhash" char(40) DEFAULT NULL, "certhash" char(40) DEFAULT NULL, "description" varchar(100) NOT NULL DEFAULT '', @@ -185,10 +184,9 @@ CREATE TABLE "certAvas" ( PRIMARY KEY ("certId", "name") ); -DROP TABLE IF EXISTS "clientcerts"; -CREATE TABLE "clientcerts" ( +DROP TABLE IF EXISTS "logincerts"; +CREATE TABLE "logincerts" ( "id" int NOT NULL, - "disablelogin" boolean NOT NULL DEFAULT 'false', PRIMARY KEY ("id") ); @@ -374,7 +372,7 @@ CREATE TABLE "schemeVersion" ( "version" smallint NOT NULL, PRIMARY KEY ("version") ); -INSERT INTO "schemeVersion" (version) VALUES(20); +INSERT INTO "schemeVersion" (version) VALUES(21); DROP TABLE IF EXISTS `passwordResetTickets`; CREATE TABLE `passwordResetTickets` ( diff --git a/src/org/cacert/gigi/database/upgrade/from_20.sql b/src/org/cacert/gigi/database/upgrade/from_20.sql new file mode 100644 index 00000000..ff2c839b --- /dev/null +++ b/src/org/cacert/gigi/database/upgrade/from_20.sql @@ -0,0 +1,6 @@ +ALTER TABLE "certs" DROP "disablelogin"; + + +ALTER TABLE "clientcerts" RENAME TO "logincerts"; +DELETE FROM "logincerts" WHERE "disablelogin" = 'true'; +ALTER TABLE "logincerts" DROP "disablelogin"; diff --git a/src/org/cacert/gigi/dbObjects/Certificate.java b/src/org/cacert/gigi/dbObjects/Certificate.java index 2e35c33e..9f3ca3bd 100644 --- a/src/org/cacert/gigi/dbObjects/Certificate.java +++ b/src/org/cacert/gigi/dbObjects/Certificate.java @@ -443,4 +443,28 @@ public class Certificate implements IdCachable { } return null; } + + public void setLoginEnabled(boolean activate) { + if (activate) { + if ( !isLoginEnabled()) { + try (GigiPreparedStatement prep = new GigiPreparedStatement("INSERT INTO `logincerts` SET `id`=?")) { + prep.setInt(1, id); + prep.execute(); + } + } + } else { + try (GigiPreparedStatement prep = new GigiPreparedStatement("DELETE FROM `logincerts` WHERE `id`=?")) { + prep.setInt(1, id); + prep.execute(); + } + } + } + + public boolean isLoginEnabled() { + try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT 1 FROM `logincerts` WHERE `id`=?")) { + prep.setInt(1, id); + GigiResultSet res = prep.executeQuery(); + return res.next(); + } + } } diff --git a/src/org/cacert/gigi/dbObjects/CertificateOwner.java b/src/org/cacert/gigi/dbObjects/CertificateOwner.java index 3ca821f3..e1a9cca0 100644 --- a/src/org/cacert/gigi/dbObjects/CertificateOwner.java +++ b/src/org/cacert/gigi/dbObjects/CertificateOwner.java @@ -116,7 +116,7 @@ public abstract class CertificateOwner implements IdCachable { } public static CertificateOwner getByEnabledSerial(String serial) { - try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT `memid` FROM `certs` WHERE serial=? AND `disablelogin`='f' AND `revoked` is NULL")) { + try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT `memid` FROM `certs` INNER JOIN `logincerts` ON `logincerts`.`id`=`certs`.`id` WHERE serial=? AND `revoked` is NULL")) { prep.setString(1, serial.toLowerCase()); GigiResultSet res = prep.executeQuery(); if (res.next()) { diff --git a/src/org/cacert/gigi/pages/account/certs/CertificateIssueForm.java b/src/org/cacert/gigi/pages/account/certs/CertificateIssueForm.java index eecb31fe..0a95497d 100644 --- a/src/org/cacert/gigi/pages/account/certs/CertificateIssueForm.java +++ b/src/org/cacert/gigi/pages/account/certs/CertificateIssueForm.java @@ -90,6 +90,9 @@ public class CertificateIssueForm extends Form { error.format(out, Page.getLanguage(req)); return false; } + if (login) { + result.setLoginEnabled(true); + } result.issue(issueDate.getFrom(), issueDate.getTo(), c.getActor()).waitFor(60000); this.result = result; return true; diff --git a/tests/org/cacert/gigi/TestCertificate.java b/tests/org/cacert/gigi/TestCertificate.java index b12d3a9e..b885881f 100644 --- a/tests/org/cacert/gigi/TestCertificate.java +++ b/tests/org/cacert/gigi/TestCertificate.java @@ -38,6 +38,7 @@ public class TestCertificate extends ManagedTest { final PrivateKey pk = kp.getPrivate(); await(c.issue(null, "2y", u)); final X509Certificate ce = c.cert(); + c.setLoginEnabled(true); assertNotNull(login(pk, ce)); } @@ -106,6 +107,7 @@ public class TestCertificate extends ManagedTest { String cookie = login(u.getEmail(), TEST_PASSWORD); testFails(CertificateStatus.ISSUED, c); X509Certificate cert = c.cert(); + c.setLoginEnabled(true); assertNotNull(login(pk, cert)); assertEquals(1, countRegex(IOUtils.readURL(get(cookie, Certificates.PATH)), "(?:REVOKED|ISSUED)")); assertEquals(1, countRegex(IOUtils.readURL(get(cookie, Certificates.PATH + "?withRevoked")), "(?:REVOKED|ISSUED)")); diff --git a/tests/org/cacert/gigi/TestCrossDomainAccess.java b/tests/org/cacert/gigi/TestCrossDomainAccess.java index e04235e5..0ddf10e1 100644 --- a/tests/org/cacert/gigi/TestCrossDomainAccess.java +++ b/tests/org/cacert/gigi/TestCrossDomainAccess.java @@ -51,6 +51,7 @@ public class TestCrossDomainAccess extends ManagedTest { String key = generatePEMCSR(kp, "CN=testmail@example.com"); Certificate c = new Certificate(u, u, Certificate.buildDN("CN", "testmail@example.com"), Digest.SHA256, key, CSRType.CSR, CertificateProfile.getById(1)); final PrivateKey pk = kp.getPrivate(); + c.setLoginEnabled(true); await(c.issue(null, "2y", u)); URLConnection con = new URL("https://" + ServerConstants.getSecureHostNamePort()).openConnection(); diff --git a/tests/org/cacert/gigi/TestSeparateSessionScope.java b/tests/org/cacert/gigi/TestSeparateSessionScope.java index 1bce9096..c5979acf 100644 --- a/tests/org/cacert/gigi/TestSeparateSessionScope.java +++ b/tests/org/cacert/gigi/TestSeparateSessionScope.java @@ -36,6 +36,7 @@ public class TestSeparateSessionScope extends ManagedTest { final PrivateKey pk = kp.getPrivate(); await(c.issue(null, "2y", u)); final X509Certificate ce = c.cert(); + c.setLoginEnabled(true); String scookie = login(pk, ce); assertTrue(isLoggedin(cookie)); @@ -59,6 +60,7 @@ public class TestSeparateSessionScope extends ManagedTest { await(c2.issue(null, "2y", u)); await(j1); final X509Certificate ce = c.cert(); + c.setLoginEnabled(true); String scookie = login(pk, ce); checkCertLogin(c, pk, scookie, 200); diff --git a/tests/org/cacert/gigi/api/ImportCATSResult.java b/tests/org/cacert/gigi/api/ImportCATSResult.java index e16657a0..6d0ad994 100644 --- a/tests/org/cacert/gigi/api/ImportCATSResult.java +++ b/tests/org/cacert/gigi/api/ImportCATSResult.java @@ -29,6 +29,7 @@ public class ImportCATSResult extends RestrictedApiTest { public void testLookupSerial() throws GigiApiException, IOException, GeneralSecurityException, InterruptedException { Certificate target2 = new Certificate(u, u, Certificate.buildDN("EMAIL", u.getEmail()), Digest.SHA256, generatePEMCSR(generateKeypair(), "EMAIL=" + u.getEmail()), CSRType.CSR, CertificateProfile.getByName("client"), new Certificate.SubjectAlternateName(SANType.EMAIL, "cats@cacert.org")); await(target2.issue(null, "2y", u)); + target2.setLoginEnabled(true); assertEquals(u.getId(), Integer.parseInt(apiLookup(target2))); } diff --git a/tests/org/cacert/gigi/api/IssueCert.java b/tests/org/cacert/gigi/api/IssueCert.java index f6040008..5211ec4c 100644 --- a/tests/org/cacert/gigi/api/IssueCert.java +++ b/tests/org/cacert/gigi/api/IssueCert.java @@ -44,6 +44,7 @@ public class IssueCert extends ClientTest { kp = generateKeypair(); String key1 = generatePEMCSR(kp, "EMAIL=testmail@example.com"); c = new Certificate(u, u, Certificate.buildDN("EMAIL", "testmail@example.com"), Digest.SHA256, key1, CSRType.CSR, CertificateProfile.getById(1)); + c.setLoginEnabled(true); pk = kp.getPrivate(); await(c.issue(null, "2y", u)); ce = c.cert(); diff --git a/tests/org/cacert/gigi/dbObjects/TestCertificate.java b/tests/org/cacert/gigi/dbObjects/TestCertificate.java new file mode 100644 index 00000000..8e7f8efe --- /dev/null +++ b/tests/org/cacert/gigi/dbObjects/TestCertificate.java @@ -0,0 +1,32 @@ +package org.cacert.gigi.dbObjects; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyPair; + +import org.cacert.gigi.GigiApiException; +import org.cacert.gigi.dbObjects.Certificate.CSRType; +import org.cacert.gigi.testUtils.ClientBusinessTest; +import org.junit.Test; + +public class TestCertificate extends ClientBusinessTest { + + @Test + public void testSetLoginEnabled() throws GeneralSecurityException, IOException, GigiApiException { + KeyPair kp = generateKeypair(); + String key = generatePEMCSR(kp, "CN=testmail@example.com"); + Certificate c = new Certificate(u, u, Certificate.buildDN("CN", "testmail@example.com"), Digest.SHA256, key, CSRType.CSR, CertificateProfile.getById(1)); + + assertFalse(c.isLoginEnabled()); + c.setLoginEnabled(true); + assertTrue(c.isLoginEnabled()); + c.setLoginEnabled(true); + assertTrue(c.isLoginEnabled()); + c.setLoginEnabled(false); + assertFalse(c.isLoginEnabled()); + c.setLoginEnabled(false); + assertFalse(c.isLoginEnabled()); + } +} diff --git a/tests/org/cacert/gigi/pages/account/TestCertificateAdd.java b/tests/org/cacert/gigi/pages/account/TestCertificateAdd.java index 5aebe2d8..6f7c8be4 100644 --- a/tests/org/cacert/gigi/pages/account/TestCertificateAdd.java +++ b/tests/org/cacert/gigi/pages/account/TestCertificateAdd.java @@ -31,6 +31,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.cacert.gigi.crypto.SPKAC; +import org.cacert.gigi.dbObjects.CertificateOwner; import org.cacert.gigi.dbObjects.Digest; import org.cacert.gigi.pages.account.certs.CertificateAdd; import org.cacert.gigi.pages.account.certs.CertificateRequest; @@ -222,14 +223,14 @@ public class TestCertificateAdd extends ClientTest { Date start = new Date(now); Date end = new Date(now + MS_PER_DAY * 10); String validity = "&validFrom=" + sdf.format(start) + "&validity=" + sdf.format(end); - X509Certificate res = createCertWithValidity(validity); + X509Certificate res = createCertWithValidity(validity, false); assertNotNull(validity, res); assertEquals(start, res.getNotBefore()); assertEquals(end, res.getNotAfter()); } private void testCertificateValidityRelative(int field, int amount, String length, boolean shouldsucceed) throws IOException, GeneralSecurityException, UnsupportedEncodingException, MalformedURLException, CertificateException { - X509Certificate parsed = createCertWithValidity("&validFrom=now&validity=" + length); + X509Certificate parsed = createCertWithValidity("&validFrom=now&validity=" + length, false); if (parsed == null) { assertTrue( !shouldsucceed); return; @@ -248,7 +249,7 @@ public class TestCertificateAdd extends ClientTest { assertEquals(c.getTime(), end); } - private X509Certificate createCertWithValidity(String validity) throws IOException, GeneralSecurityException, UnsupportedEncodingException, MalformedURLException, CertificateException { + private X509Certificate createCertWithValidity(String validity, boolean login) throws IOException, GeneralSecurityException, UnsupportedEncodingException, MalformedURLException, CertificateException { PKCS10Attributes atts = buildAtts(new ObjectIdentifier[] { CertificateRequest.OID_KEY_USAGE_SSL_CLIENT }, new RFC822Name(email)); @@ -263,6 +264,9 @@ public class TestCertificateAdd extends ClientTest { out.write(("csrf=" + URLEncoder.encode(csrf, "UTF-8")).getBytes("UTF-8")); out.write(("&profile=client&CN=" + CertificateRequest.DEFAULT_CN + "&SANs=" + URLEncoder.encode("email:" + email + "\n", "UTF-8")).getBytes("UTF-8")); out.write(("&hash_alg=SHA512&").getBytes("UTF-8")); + if (login) { + out.write(("login=1&").getBytes("UTF-8")); + } out.write(validity.getBytes("UTF-8")); String certurl = huc.getHeaderField("Location"); @@ -368,4 +372,13 @@ public class TestCertificateAdd extends ClientTest { String resultingCN = m.group(1); return resultingCN; } + + @Test + public void testSetLoginEnabled() throws IOException, GeneralSecurityException { + X509Certificate parsedLoginNotEnabled = createCertWithValidity("&validFrom=now&validity=1m", false); + assertNull(CertificateOwner.getByEnabledSerial(parsedLoginNotEnabled.getSerialNumber().toString(16))); + + X509Certificate parsedLoginEnabled = createCertWithValidity("&validFrom=now&validity=1m", true); + assertEquals(u, CertificateOwner.getByEnabledSerial(parsedLoginEnabled.getSerialNumber().toString(16))); + } } diff --git a/tests/org/cacert/gigi/testUtils/RestrictedApiTest.java b/tests/org/cacert/gigi/testUtils/RestrictedApiTest.java index e9fbcb67..e0f2947a 100644 --- a/tests/org/cacert/gigi/testUtils/RestrictedApiTest.java +++ b/tests/org/cacert/gigi/testUtils/RestrictedApiTest.java @@ -48,6 +48,7 @@ public class RestrictedApiTest extends ClientTest { pk = kp.getPrivate(); await(c.issue(null, "2y", u)); ce = c.cert(); + c.setLoginEnabled(true); } catch (IOException e) { throw new Error(e); } catch (GigiApiException e) { -- 2.39.2