From: Felix Dörre Date: Tue, 10 Sep 2019 21:45:40 +0000 (+0200) Subject: Merge "add: implement to define a strong authenticated login" X-Git-Url: https://code.wpia.club/?p=gigi.git;a=commitdiff_plain;h=87bf6551ebd440eeb363437f0a71345cd9d81192;hp=150cc0ac00db36ed7610683319a3177b0cb62da2 Merge "add: implement to define a strong authenticated login" --- diff --git a/src/club/wpia/gigi/api/CreateCertificate.java b/src/club/wpia/gigi/api/CreateCertificate.java index 6af284c0..1890e4a9 100644 --- a/src/club/wpia/gigi/api/CreateCertificate.java +++ b/src/club/wpia/gigi/api/CreateCertificate.java @@ -9,11 +9,11 @@ import javax.servlet.http.HttpServletResponse; import club.wpia.gigi.GigiApiException; import club.wpia.gigi.dbObjects.Certificate; +import club.wpia.gigi.dbObjects.Certificate.CertificateStatus; import club.wpia.gigi.dbObjects.CertificateProfile; import club.wpia.gigi.dbObjects.Job; import club.wpia.gigi.dbObjects.Organisation; import club.wpia.gigi.dbObjects.User; -import club.wpia.gigi.dbObjects.Certificate.CertificateStatus; import club.wpia.gigi.pages.account.certs.CertificateRequest; import club.wpia.gigi.util.AuthorizationContext; import club.wpia.gigi.util.CertExporter; @@ -38,7 +38,7 @@ public class CreateCertificate extends APIPoint { return; } } - AuthorizationContext ctx = new AuthorizationContext(u, u); + AuthorizationContext ctx = new AuthorizationContext(u, u, true); String asOrg = req.getParameter("asOrg"); if (asOrg != null) { try { @@ -54,7 +54,7 @@ public class CreateCertificate extends APIPoint { resp.sendError(500, "Error, Organisation with id " + i + " not found."); return; } else { - ctx = new AuthorizationContext(o0, u); + ctx = new AuthorizationContext(o0, u, true); } } catch (NumberFormatException e) { resp.sendError(500, "Error, as Org is not an integer"); diff --git a/src/club/wpia/gigi/pages/LoginPage.java b/src/club/wpia/gigi/pages/LoginPage.java index 66412a91..fccfea1d 100644 --- a/src/club/wpia/gigi/pages/LoginPage.java +++ b/src/club/wpia/gigi/pages/LoginPage.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.math.BigInteger; import java.security.cert.X509Certificate; +import java.util.Date; import java.util.Map; import javax.servlet.http.HttpServletRequest; @@ -15,6 +16,7 @@ import javax.servlet.http.HttpSession; import club.wpia.gigi.GigiApiException; import club.wpia.gigi.database.GigiPreparedStatement; import club.wpia.gigi.database.GigiResultSet; +import club.wpia.gigi.dbObjects.Certificate; import club.wpia.gigi.dbObjects.CertificateOwner; import club.wpia.gigi.dbObjects.Group; import club.wpia.gigi.dbObjects.User; @@ -140,7 +142,7 @@ public class LoginPage extends Page { } } - loginSession(req, user); + loginSession(req, user, false); req.getSession().setAttribute(LOGIN_METHOD, new TranslateCommand("Password")); return; } @@ -161,11 +163,15 @@ public class LoginPage extends Page { private void tryAuthWithCertificate(HttpServletRequest req, X509Certificate x509Certificate) { BigInteger serial = extractSerialFormCert(x509Certificate); + Certificate c = Certificate.getBySerial(serial); User user = fetchUserBySerial(serial); if (user == null) { return; } - loginSession(req, user); + if (c.getExpiryDate().before(new Date()) || c.getRevocationDate() != null || c.isLoginEnabled() == false) { + return; + } + loginSession(req, user, true); req.getSession().setAttribute(CERT_SERIAL, serial); req.getSession().setAttribute(CERT_ISSUER, x509Certificate.getIssuerDN()); req.getSession().setAttribute(LOGIN_METHOD, new TranslateCommand("Certificate")); @@ -194,7 +200,7 @@ public class LoginPage extends Page { private static final Group LOGIN_BLOCKED = Group.BLOCKED_LOGIN; - private void loginSession(HttpServletRequest req, User user) { + private void loginSession(HttpServletRequest req, User user, boolean isStronglyAuthenticated) { if (user.isInGroup(LOGIN_BLOCKED)) { return; } @@ -203,7 +209,7 @@ public class LoginPage extends Page { HttpSession hs = req.getSession(); hs.setAttribute(LOGGEDIN, true); hs.setAttribute(Language.SESSION_ATTRIB_NAME, user.getPreferredLocale()); - hs.setAttribute(AUTH_CONTEXT, new AuthorizationContext(user, user)); + hs.setAttribute(AUTH_CONTEXT, new AuthorizationContext(user, user, isStronglyAuthenticated)); } @Override diff --git a/src/club/wpia/gigi/pages/admin/support/SupportEnterTicketForm.java b/src/club/wpia/gigi/pages/admin/support/SupportEnterTicketForm.java index 58b2997e..918df15d 100644 --- a/src/club/wpia/gigi/pages/admin/support/SupportEnterTicketForm.java +++ b/src/club/wpia/gigi/pages/admin/support/SupportEnterTicketForm.java @@ -37,7 +37,7 @@ public class SupportEnterTicketForm extends Form { throw new GigiApiException("Ticket format malformed"); } else if (req.getParameter("deleteTicket") != null) { AuthorizationContext ac = LoginPage.getAuthorizationContext(req); - req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(ac.getActor(), ac.getActor())); + req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(ac.getActor(), ac.getActor(), ac.isStronglyAuthenticated())); return new RedirectResult(SupportEnterTicketPage.PATH); } throw new GigiApiException("No valid action given."); diff --git a/src/club/wpia/gigi/pages/admin/support/SupportUserDetailsForm.java b/src/club/wpia/gigi/pages/admin/support/SupportUserDetailsForm.java index 7445c52c..71909cf2 100644 --- a/src/club/wpia/gigi/pages/admin/support/SupportUserDetailsForm.java +++ b/src/club/wpia/gigi/pages/admin/support/SupportUserDetailsForm.java @@ -63,7 +63,7 @@ public class SupportUserDetailsForm extends Form { if (toMod == Group.SUPPORTER) { user.revoke(toMod); AuthorizationContext ac = LoginPage.getAuthorizationContext(req); - req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(ac.getActor(), ac.getActor())); + req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(ac.getActor(), ac.getActor(), ac.isStronglyAuthenticated())); return new RedirectResult(MyDetails.PATH); } } diff --git a/src/club/wpia/gigi/pages/orga/MyOrganisationsForm.java b/src/club/wpia/gigi/pages/orga/MyOrganisationsForm.java index 8858c5c0..c4120fc7 100644 --- a/src/club/wpia/gigi/pages/orga/MyOrganisationsForm.java +++ b/src/club/wpia/gigi/pages/orga/MyOrganisationsForm.java @@ -31,8 +31,9 @@ public class MyOrganisationsForm extends Form { @Override public SubmissionResult submit(HttpServletRequest req) throws GigiApiException { + AuthorizationContext sessionAc = (AuthorizationContext) req.getSession().getAttribute(Gigi.AUTH_CONTEXT); if (req.getParameter("org-leave") != null) { - req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(target.getActor(), target.getActor())); + req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(target.getActor(), target.getActor(), sessionAc.isStronglyAuthenticated())); return new RedirectResult(SwitchOrganisation.PATH); } Enumeration i = req.getParameterNames(); @@ -51,7 +52,7 @@ public class MyOrganisationsForm extends Form { for (Organisation org : target.getActor().getOrganisations()) { if (org.getId() == orgId) { - req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(org, target.getActor())); + req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(org, target.getActor(), sessionAc.isStronglyAuthenticated())); return new RedirectResult(SwitchOrganisation.PATH); } } diff --git a/src/club/wpia/gigi/util/AuthorizationContext.java b/src/club/wpia/gigi/util/AuthorizationContext.java index 13f70cc7..719ac923 100644 --- a/src/club/wpia/gigi/util/AuthorizationContext.java +++ b/src/club/wpia/gigi/util/AuthorizationContext.java @@ -24,7 +24,9 @@ public class AuthorizationContext implements Outputable, Serializable { private final String supporterTicketId; - public AuthorizationContext(CertificateOwner target, User actor) { + private final boolean isStronglyAuthenticated; + + public AuthorizationContext(CertificateOwner target, User actor, boolean isStronglyAuthenticated) { if (actor == null) { throw new Error("Internal Error: The actor of an AuthorizationContext must not be null!"); } @@ -34,6 +36,7 @@ public class AuthorizationContext implements Outputable, Serializable { this.target = target; this.actor = actor; this.supporterTicketId = null; + this.isStronglyAuthenticated = isStronglyAuthenticated; } public AuthorizationContext(User actor, String supporterTicket) throws GigiApiException { @@ -49,6 +52,7 @@ public class AuthorizationContext implements Outputable, Serializable { throw new GigiApiException("requires a supporter"); } this.supporterTicketId = supporterTicket; + this.isStronglyAuthenticated = true; } public CertificateOwner getTarget() { @@ -111,4 +115,8 @@ public class AuthorizationContext implements Outputable, Serializable { public boolean canVerify() { return target instanceof User && ((User) target).canVerify(); } + + public boolean isStronglyAuthenticated() { + return isStronglyAuthenticated; + } } diff --git a/tests/club/wpia/gigi/pages/account/TestCertificateRequest.java b/tests/club/wpia/gigi/pages/account/TestCertificateRequest.java index 03c9a8d1..023be5ae 100644 --- a/tests/club/wpia/gigi/pages/account/TestCertificateRequest.java +++ b/tests/club/wpia/gigi/pages/account/TestCertificateRequest.java @@ -16,7 +16,6 @@ import club.wpia.gigi.dbObjects.EmailAddress; import club.wpia.gigi.dbObjects.Group; import club.wpia.gigi.pages.account.certs.CertificateRequest; import club.wpia.gigi.testUtils.ClientTest; -import club.wpia.gigi.testUtils.TestEmailReceiver.TestMail; import club.wpia.gigi.util.AuthorizationContext; import club.wpia.gigi.util.TimeConditions; @@ -27,7 +26,7 @@ public class TestCertificateRequest extends ClientTest { AuthorizationContext ac; public TestCertificateRequest() throws GeneralSecurityException, IOException, GigiApiException { - ac = new AuthorizationContext(u, u); + ac = new AuthorizationContext(u, u, false); makeAgent(u.getId()); } diff --git a/tests/club/wpia/gigi/pages/main/CertStatusTest.java b/tests/club/wpia/gigi/pages/main/CertStatusTest.java index f16ebb36..ef6cf0c7 100644 --- a/tests/club/wpia/gigi/pages/main/CertStatusTest.java +++ b/tests/club/wpia/gigi/pages/main/CertStatusTest.java @@ -46,7 +46,7 @@ public class CertStatusTest extends ClientTest { KeyPair kp = generateKeypair(); String csr = generatePEMCSR(kp, "CN=test"); - CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u), csr); + CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u, false), csr); cr.update(CertificateRequest.DEFAULT_CN, Digest.SHA512.toString(), "client", null, null, "email:" + email + "\n"); cert = cr.draft(); Job j = cert.issue(null, "2y", u); diff --git a/tests/club/wpia/gigi/pages/main/KeyCompromiseTest.java b/tests/club/wpia/gigi/pages/main/KeyCompromiseTest.java index 406590d2..89180e86 100644 --- a/tests/club/wpia/gigi/pages/main/KeyCompromiseTest.java +++ b/tests/club/wpia/gigi/pages/main/KeyCompromiseTest.java @@ -79,7 +79,7 @@ public class KeyCompromiseTest extends ClientTest { KeyPair kp = generateKeypair(); priv = kp.getPrivate(); String csr = generatePEMCSR(kp, "CN=test"); - CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u), csr); + CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u, false), csr); cr.update(CertificateRequest.DEFAULT_CN, Digest.SHA512.toString(), "client", null, null, "email:" + email + "\n"); cert = cr.draft(); Job j = cert.issue(null, "2y", u); diff --git a/tests/club/wpia/gigi/pages/main/KeyCompromiseTestMessage.java b/tests/club/wpia/gigi/pages/main/KeyCompromiseTestMessage.java index 0cc355d4..f9fb6564 100644 --- a/tests/club/wpia/gigi/pages/main/KeyCompromiseTestMessage.java +++ b/tests/club/wpia/gigi/pages/main/KeyCompromiseTestMessage.java @@ -37,7 +37,7 @@ public class KeyCompromiseTestMessage extends ClientTest { KeyPair kp = generateKeypair(); priv = kp.getPrivate(); String csr = generatePEMCSR(kp, "CN=test"); - CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u), csr); + CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u, false), csr); cr.update(CertificateRequest.DEFAULT_CN, Digest.SHA512.toString(), "client", null, null, "email:" + email + "\n"); cert = cr.draft(); Job j = cert.issue(null, "2y", u); diff --git a/tests/club/wpia/gigi/util/TestAuthorizationContext.java b/tests/club/wpia/gigi/util/TestAuthorizationContext.java new file mode 100644 index 00000000..7f8e4e73 --- /dev/null +++ b/tests/club/wpia/gigi/util/TestAuthorizationContext.java @@ -0,0 +1,23 @@ +package club.wpia.gigi.util; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import club.wpia.gigi.testUtils.ClientBusinessTest; + +public class TestAuthorizationContext extends ClientBusinessTest { + + @Test + public void testStronglyAuthenticated() { + AuthorizationContext ac = new AuthorizationContext(u, u, true); + assertTrue(ac.isStronglyAuthenticated()); + } + + @Test + public void testNotStronglyAuthenticated() { + AuthorizationContext ac = new AuthorizationContext(u, u, false); + assertFalse(ac.isStronglyAuthenticated()); + } + +} diff --git a/tests/club/wpia/gigi/util/TestCAAValidation.java b/tests/club/wpia/gigi/util/TestCAAValidation.java index a7625700..f69c3ecb 100644 --- a/tests/club/wpia/gigi/util/TestCAAValidation.java +++ b/tests/club/wpia/gigi/util/TestCAAValidation.java @@ -63,7 +63,7 @@ public class TestCAAValidation extends ClientTest { Domain d = new Domain(u, u, PublicSuffixes.getInstance().getRegistrablePart(domain)); verify(d); String csr = generatePEMCSR(generateKeypair(), "CN=test"); - CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u), csr); + CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u, false), csr); try { cr.update("", Digest.SHA512.toString(), "server", null, null, "dns:" + domain + "\n"); } catch (GigiApiException e) { diff --git a/util-testing/club/wpia/gigi/DevelLauncher.java b/util-testing/club/wpia/gigi/DevelLauncher.java index 9789637a..e62ad681 100644 --- a/util-testing/club/wpia/gigi/DevelLauncher.java +++ b/util-testing/club/wpia/gigi/DevelLauncher.java @@ -212,7 +212,9 @@ public class DevelLauncher { } sess.setAttribute(LOGGEDIN, true); sess.setAttribute(Language.SESSION_ATTRIB_NAME, user.getPreferredLocale()); - sess.setAttribute(AUTH_CONTEXT, new AuthorizationContext(user, user)); + // ac.isStronglyAuthenticated() set to true to bypass + // certificate login for testing + sess.setAttribute(AUTH_CONTEXT, new AuthorizationContext(user, user, true)); req.getSession().setAttribute(LOGIN_METHOD, new TranslateCommand("Ticket")); resp.getWriter().println("ticket consumed"); ticketUsed = true; diff --git a/util-testing/club/wpia/gigi/pages/Manager.java b/util-testing/club/wpia/gigi/pages/Manager.java index f48c5bed..cdde36f2 100644 --- a/util-testing/club/wpia/gigi/pages/Manager.java +++ b/util-testing/club/wpia/gigi/pages/Manager.java @@ -302,6 +302,7 @@ public class Manager extends Page { @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { + AuthorizationContext sessionAc = (AuthorizationContext) req.getSession().getAttribute(Gigi.AUTH_CONTEXT); if (req.getParameter("create") != null) { String prefix = req.getParameter("prefix"); String domain = req.getParameter("suffix"); @@ -438,7 +439,7 @@ public class Manager extends Page { byte[] res = s.getEncoded(sign); - CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u), Base64.getEncoder().encodeToString(res), "challenge"); + CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u, sessionAc.isStronglyAuthenticated()), Base64.getEncoder().encodeToString(res), "challenge"); cr.update(CertificateRequest.DEFAULT_CN, Digest.SHA512.toString(), "client", null, "", "email:" + u.getEmail()); Certificate draft = cr.draft(); draft.issue(null, "2y", u).waitFor(10000);