Fix expiration logic for ldap internal credential cache (#11395)
* Fix expiration logic for ldap internal credential cache
* Removed sleeps from tests
* Make method package scoped so it can be used in unit tests
* Removed unused thrown exceptions
diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/LdapUserPrincipal.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/LdapUserPrincipal.java
index cac753d..d185b02 100644
--- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/LdapUserPrincipal.java
+++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/LdapUserPrincipal.java
@@ -19,6 +19,7 @@
package org.apache.druid.security.basic.authentication;
+import com.google.common.annotations.VisibleForTesting;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.logger.Logger;
import org.apache.druid.security.basic.BasicAuthUtils;
@@ -50,7 +51,8 @@
this(name, credentials, searchResult, Instant.now());
}
- private LdapUserPrincipal(
+ @VisibleForTesting
+ public LdapUserPrincipal(
String name,
BasicAuthenticatorCredentials credentials,
SearchResult searchResult,
@@ -106,21 +108,26 @@
}
}
- public boolean isExpired(int duration, int maxDuration)
+ public boolean isExpired(int durationSeconds, int maxDurationSeconds)
{
- long now = System.currentTimeMillis();
+ return isExpired(durationSeconds, maxDurationSeconds, System.currentTimeMillis());
+ }
- long maxCutoff = now - (maxDuration * 1000L);
- if (this.createdAt.toEpochMilli() < maxCutoff) {
- long cutoff = now - (duration * 1000L);
- if (this.lastVerified.get().toEpochMilli() < cutoff) {
- return true;
- } else {
- return false;
- }
+ @VisibleForTesting
+ boolean isExpired(int durationSeconds, int maxDurationSeconds, long nowMillis)
+ {
+ long maxCutoffMillis = nowMillis - (maxDurationSeconds * 1000L);
+ if (this.createdAt.toEpochMilli() < maxCutoffMillis) {
+ // max cutoff is up...so expired
+ return true;
} else {
- return false;
+ long cutoffMillis = nowMillis - (durationSeconds * 1000L);
+ if (this.lastVerified.get().toEpochMilli() < cutoffMillis) {
+ // max cutoff not reached yet but cutoff since verified is up, so expired
+ return true;
+ }
}
+ return false;
}
@Override
diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/basic/authentication/LdapUserPrincipalTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/basic/authentication/LdapUserPrincipalTest.java
new file mode 100644
index 0000000..aaff67b
--- /dev/null
+++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/basic/authentication/LdapUserPrincipalTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.security.basic.authentication;
+
+import junit.framework.TestCase;
+import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentialUpdate;
+import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentials;
+import org.junit.Assert;
+
+import javax.naming.directory.SearchResult;
+import java.time.Instant;
+
+public class LdapUserPrincipalTest extends TestCase
+{
+
+ private static final BasicAuthenticatorCredentials USER_CREDENTIALS = new BasicAuthenticatorCredentials(
+ new BasicAuthenticatorCredentialUpdate("helloworld", 20)
+ );
+
+ private static final long CREATED_MILLIS = 10000;
+
+ // this will create a cache with createdAt now():
+ private static final LdapUserPrincipal PRINCIPAL = new LdapUserPrincipal(
+ "foo",
+ USER_CREDENTIALS,
+ new SearchResult("foo", null, null),
+ Instant.ofEpochMilli(CREATED_MILLIS)
+ );
+
+ public void testIsNotExpired()
+ {
+ Assert.assertFalse(PRINCIPAL.isExpired(1, 10, CREATED_MILLIS + 500));
+ }
+
+ public void testIsObviouslyExpired()
+ {
+ // real clock now should be so much bigger than CREATED_MILLIS....so it must have expired...
+ Assert.assertTrue(PRINCIPAL.isExpired(100, 1000));
+ }
+
+ public void testIsExpiredWhenMaxDurationIsSmall()
+ {
+ Assert.assertTrue(PRINCIPAL.isExpired(10, 1, CREATED_MILLIS + 1001));
+ }
+
+ public void testIsExpiredWhenDurationIsSmall()
+ {
+ Assert.assertTrue(PRINCIPAL.isExpired(1, 10, CREATED_MILLIS + 1001));
+ }
+
+ public void testIsExpiredWhenDurationsAreSmall()
+ {
+ Assert.assertTrue(PRINCIPAL.isExpired(1, 1, CREATED_MILLIS + 1001));
+ }
+}