changeset 8839:2c8259176c46

Add configurable time tolerance to SAML ticket validation. This allows e.g. to account for time skew between the ISP and the server this servlet is run on.
author Tom Gottfried <tom@intevation.de>
date Wed, 28 Jun 2017 20:09:53 +0200
parents 1fa03f3c9d3d
children 98a3cf810916
files gwt-client/src/main/java/org/dive4elements/river/client/server/auth/saml/Assertion.java gwt-client/src/main/java/org/dive4elements/river/client/server/auth/saml/TicketValidator.java gwt-client/src/main/java/org/dive4elements/river/client/server/auth/was/Authenticator.java gwt-client/src/main/java/org/dive4elements/river/client/server/auth/was/Response.java gwt-client/src/main/webapp/WEB-INF/web.xml
diffstat 5 files changed, 34 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/auth/saml/Assertion.java	Tue May 30 12:51:42 2017 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/auth/saml/Assertion.java	Wed Jun 28 20:09:53 2017 +0200
@@ -48,7 +48,6 @@
     private static final String ATTR_CONT_ROLE =
         "urn:conterra:names:sdi-suite:policy:attribute:role";
 
-
     public Assertion(Element assertion) {
         this.assertion = assertion;
         this.roles = new LinkedList<String>();
@@ -174,14 +173,15 @@
      * Returns whether the ticket to which the assertion belongs is
      * valid at the time the method is called. The method returns true,
      * if both dates (notbefore and notonorafter) have been determined
-     * successfully and the current date/time is between both.
+     * successfully and the current date/time is between both (with given
+     * tolerance).
      * @return Whether the ticket is valid now.
      */
-    public boolean isValidNow() {
+    public boolean isValidNow(int timeEps) {
         Date now = new Date();
         return (this.notbefore != null && this.notonorafter != null
-                && now.after(this.notbefore)
-                && !this.notonorafter.before(now));
+            && now.after(new Date(this.notbefore.getTime() - timeEps))
+            && now.before(new Date(this.notonorafter.getTime() + timeEps)));
     }
 }
 // vim: set fileencoding=utf-8 ts=4 sw=4 et si tw=80:
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/auth/saml/TicketValidator.java	Tue May 30 12:51:42 2017 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/auth/saml/TicketValidator.java	Wed Jun 28 20:09:53 2017 +0200
@@ -48,11 +48,18 @@
     private Key trustedKey;
 
     /**
+     * Tolerance in milliseconds for validation based on NotBefore and
+     * NotOnOrAfter of the SAML ticket
+     */
+    private int timeEps;
+
+    /**
      * Creates a new TicketValidator from a trusted key.
      * @param trustedKey  The trusted key for the signature checks.
      */
-    public TicketValidator(Key trustedKey) {
+    public TicketValidator(Key trustedKey, int timeEps) {
         this.trustedKey = trustedKey;
+        this.timeEps = timeEps;
     }
 
     /**
@@ -61,9 +68,10 @@
      * @param filename The filename of the X509 certificate containing
      * the trusted public key.
      */
-    public TicketValidator(String filename) throws IOException,
-                                                   CertificateException {
+    public TicketValidator(String filename, int timeEps)
+        throws IOException, CertificateException {
         this.trustedKey = loadKey(filename);
+        this.timeEps = timeEps;
     }
 
     /**
@@ -107,10 +115,11 @@
         }
 
         Assertion assertion = new Assertion(assertionElement);
-        if (!assertion.isValidNow()) {
+        if (!assertion.isValidNow(this.timeEps)) {
             log.error("Ticket is not valid now"
                          + " (NotBefore: " + assertion.getFrom()
-                         + ", NotOnOrAfter: " + assertion.getUntil());
+                         + ", NotOnOrAfter: " + assertion.getUntil()
+                         + ", Tolerance (milliseconds): " + this.timeEps);
             return null;
         }
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/auth/was/Authenticator.java	Tue May 30 12:51:42 2017 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/auth/was/Authenticator.java	Wed Jun 28 20:09:53 2017 +0200
@@ -64,8 +64,10 @@
                 else {
                     String trustedKey =
                     (String)context.getInitParameter("saml-trusted-public-key");
+                    String timeEpsilon = context.getInitParameter(
+                        "saml-time-tolerance");
                     return new Response(entity, username, password, features,
-                                        context.getRealPath(trustedKey));
+                        context.getRealPath(trustedKey), timeEpsilon);
                 }
             }
             catch(GeneralSecurityException e) {
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/auth/was/Response.java	Tue May 30 12:51:42 2017 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/auth/was/Response.java	Wed Jun 28 20:09:53 2017 +0200
@@ -45,10 +45,11 @@
     private String password;
     private Features features;
     private String trustedKeyFile;
+    private String timeEpsilon;
 
 
     public Response(HttpEntity entity, String username, String password,
-                    Features features, String trustedKeyFile)
+            Features features, String trustedKeyFile, String timeEpsilon)
         throws AuthenticationException, IOException {
 
         if (entity == null) {
@@ -80,6 +81,7 @@
         this.password = password;
         this.features = features;
         this.trustedKeyFile = trustedKeyFile;
+        this.timeEpsilon = timeEpsilon;
     }
 
     @Override
@@ -97,8 +99,9 @@
     public Assertion getAssertion() {
         if (this.assertion == null && this.root != null) {
             try {
+                int timeEps = Integer.parseInt(this.timeEpsilon);
                 TicketValidator validator =
-                    new TicketValidator(this.trustedKeyFile);
+                    new TicketValidator(this.trustedKeyFile, timeEps);
                 this.assertion = validator.checkTicket(this.root);
             }
             catch (Exception e) {
--- a/gwt-client/src/main/webapp/WEB-INF/web.xml	Tue May 30 12:51:42 2017 +0200
+++ b/gwt-client/src/main/webapp/WEB-INF/web.xml	Wed Jun 28 20:09:53 2017 +0200
@@ -51,6 +51,13 @@
     </context-param>
 
     <context-param>
+        <!-- Tolerance in milliseconds for validation based on NotBefore and
+             NotOnOrAfter of the SAML ticket -->
+        <param-name>saml-time-tolerance</param-name>
+        <param-value>1000</param-value>
+    </context-param>
+
+    <context-param>
         <param-name>features-file</param-name>
         <param-value>/WEB-INF/features.xml</param-value>
     </context-param>

http://dive4elements.wald.intevation.org