Repository: eclipse/leshan
Pull request #892 (2020-09-15 14:22:06)

Use Iterator instead of List in BootstrapSecurityStore.
BootstrapSecurityStore returns a List of securityInfo.
Each security infos are acceptable way to connect to bootstrap server.

Before this PR a list was returned and securityChecker will iterate over it and stop as soon as it find a matching credential.
Depending of implementation searching all security store could be costly, so using an iterator allow to search it 1 by 1 if necessary and to stop at first success.



Patch:
diff --git a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/BootstrapConfigSecurityStore.java b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/BootstrapConfigSecurityStore.java
index aeb9ec55..289d2427 100644
--- a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/BootstrapConfigSecurityStore.java
+++ b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/BootstrapConfigSecurityStore.java
@@ -19,7 +19,7 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
 import java.util.Arrays;
-import java.util.List;
+import java.util.Iterator;
 import java.util.Map;
 
 import org.eclipse.leshan.core.SecurityMode;
@@ -84,7 +84,7 @@ public class BootstrapConfigSecurityStore implements BootstrapSecurityStore {
     }
 
     @Override
-    public List<SecurityInfo> getAllByEndpoint(String endpoint) {
+    public Iterator<SecurityInfo> getAllByEndpoint(String endpoint) {
 
         BootstrapConfig bsConfig = bootstrapConfigStore.get(endpoint, null, null);
 
@@ -98,14 +98,14 @@ public class BootstrapConfigSecurityStore implements BootstrapSecurityStore {
             if (value.bootstrapServer && value.securityMode == SecurityMode.PSK) {
                 SecurityInfo securityInfo = SecurityInfo.newPreSharedKeyInfo(endpoint,
                         new String(value.publicKeyOrId, StandardCharsets.UTF_8), value.secretKey);
-                return Arrays.asList(securityInfo);
+                return Arrays.asList(securityInfo).iterator();
             }
             // Extract RPK security info
             else if (value.bootstrapServer && value.securityMode == SecurityMode.RPK) {
                 try {
                     SecurityInfo securityInfo = SecurityInfo.newRawPublicKeyInfo(endpoint,
                             SecurityUtil.publicKey.decode(value.publicKeyOrId));
-                    return Arrays.asList(securityInfo);
+                    return Arrays.asList(securityInfo).iterator();
                 } catch (IOException | GeneralSecurityException e) {
                     LOG.error("Unable to decode Client public key for {}", endpoint, e);
                     return null;
@@ -114,7 +114,7 @@ public class BootstrapConfigSecurityStore implements BootstrapSecurityStore {
             // Extract X509 security info
             else if (value.bootstrapServer && value.securityMode == SecurityMode.X509) {
                 SecurityInfo securityInfo = SecurityInfo.newX509CertInfo(endpoint);
-                return Arrays.asList(securityInfo);
+                return Arrays.asList(securityInfo).iterator();
             }
         }
         return null;
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSessionManager.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSessionManager.java
index c9dbb7de..dee92bb1 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSessionManager.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/bootstrap/DefaultBootstrapSessionManager.java
@@ -15,7 +15,7 @@
  *******************************************************************************/
 package org.eclipse.leshan.server.bootstrap;
 
-import java.util.List;
+import java.util.Iterator;
 
 import org.eclipse.leshan.core.request.BootstrapFinishRequest;
 import org.eclipse.leshan.core.request.BootstrapRequest;
@@ -67,7 +67,7 @@ public class DefaultBootstrapSessionManager implements BootstrapSessionManager {
     public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) {
         boolean authorized;
         if (bsSecurityStore != null) {
-            List<SecurityInfo> securityInfos = bsSecurityStore.getAllByEndpoint(request.getEndpointName());
+            Iterator<SecurityInfo> securityInfos = bsSecurityStore.getAllByEndpoint(request.getEndpointName());
             authorized = securityChecker.checkSecurityInfos(request.getEndpointName(), clientIdentity, securityInfos);
         } else {
             authorized = true;
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/BootstrapSecurityStore.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/BootstrapSecurityStore.java
index d6ce6bb2..fbcb7568 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/BootstrapSecurityStore.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/BootstrapSecurityStore.java
@@ -15,7 +15,7 @@
  *******************************************************************************/
 package org.eclipse.leshan.server.security;
 
-import java.util.List;
+import java.util.Iterator;
 
 /**
  * A store containing data needed to authenticate clients.
@@ -31,7 +31,7 @@ public interface BootstrapSecurityStore {
      * @param endpoint the client end-point
      * @return the security information of <code>null</code> if not found.
      */
-    List<SecurityInfo> getAllByEndpoint(String endpoint);
+    Iterator<SecurityInfo> getAllByEndpoint(String endpoint);
 
     /**
      * Returns the security information for a PSK identity.
diff --git a/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/SecurityChecker.java b/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/SecurityChecker.java
index 9ff3ae94..790bcc8e 100644
--- a/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/SecurityChecker.java
+++ b/leshan-server-core/src/main/java/org/eclipse/leshan/server/security/SecurityChecker.java
@@ -16,7 +16,7 @@
 package org.eclipse.leshan.server.security;
 
 import java.security.PublicKey;
-import java.util.List;
+import java.util.Iterator;
 
 import org.eclipse.leshan.core.request.Identity;
 import org.eclipse.leshan.core.util.Hex;
@@ -40,21 +40,23 @@ public class SecurityChecker {
      * @return true if client is correctly authenticated.
      * @see SecurityInfo
      */
-    public boolean checkSecurityInfos(String endpoint, Identity clientIdentity, List<SecurityInfo> securityInfos) {
+    public boolean checkSecurityInfos(String endpoint, Identity clientIdentity, Iterator<SecurityInfo> securityInfos) {
         // if this is a secure end-point, we must check that the registering client is using the right identity.
         if (clientIdentity.isSecure()) {
-            if (securityInfos == null || securityInfos.isEmpty()) {
+            if (securityInfos == null || !securityInfos.hasNext()) {
                 LOG.debug("Client '{}' without security info try to connect through the secure endpoint", endpoint);
                 return false;
             } else {
-                for (SecurityInfo securityInfo : securityInfos) {
+                // check of one expected security info matches client identity
+                do {
+                    SecurityInfo securityInfo = securityInfos.next();
                     if (checkSecurityInfo(endpoint, clientIdentity, securityInfo)) {
                         return true;
                     }
-                }
+                } while (securityInfos.hasNext());
                 return false;
             }
-        } else if (securityInfos != null && !securityInfos.isEmpty()) {
+        } else if (securityInfos != null && securityInfos.hasNext()) {
             LOG.debug("Client '{}' must connect using DTLS", endpoint);
             return false;
         }


Test patch:
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java
index 0a82e843..71b84ee4 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/BootstrapIntegrationTestHelper.java
@@ -34,7 +34,7 @@ import java.security.spec.KeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.List;
+import java.util.Iterator;
 import java.util.Map;
 
 import org.eclipse.leshan.client.californium.LeshanClientBuilder;
@@ -57,10 +57,10 @@ import org.eclipse.leshan.server.bootstrap.BootstrapFailureCause;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandler;
 import org.eclipse.leshan.server.bootstrap.BootstrapHandlerFactory;
 import org.eclipse.leshan.server.bootstrap.BootstrapSession;
-import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSession;
-import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSessionManager;
 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
 import org.eclipse.leshan.server.bootstrap.DefaultBootstrapHandler;
+import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSession;
+import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSessionManager;
 import org.eclipse.leshan.server.bootstrap.LwM2mBootstrapRequestSender;
 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
@@ -259,18 +259,18 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
             }
 
             @Override
-            public List<SecurityInfo> getAllByEndpoint(String endpoint) {
+            public Iterator<SecurityInfo> getAllByEndpoint(String endpoint) {
                 if (getCurrentEndpoint().equals(endpoint)) {
                     SecurityInfo info;
                     if (mode == SecurityMode.PSK) {
                         info = pskSecurityInfo();
-                        return Arrays.asList(info);
+                        return Arrays.asList(info).iterator();
                     } else if (mode == SecurityMode.RPK) {
                         info = rpkSecurityInfo();
-                        return Arrays.asList(info);
+                        return Arrays.asList(info).iterator();
                     }
                 }
-                return Arrays.asList();
+                return null;
             }
         };
     }
@@ -295,7 +295,7 @@ public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper
             }
 
             @Override
-            public List<SecurityInfo> getAllByEndpoint(String endpoint) {
+            public Iterator<SecurityInfo> getAllByEndpoint(String endpoint) {
                 return null;
             }
         };
diff --git a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilderTest.java b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilderTest.java
index 8c92d4e2..9ad1e1fc 100644
--- a/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilderTest.java
+++ b/leshan-server-cf/src/test/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilderTest.java
@@ -17,14 +17,12 @@ package org.eclipse.leshan.server.californium.bootstrap;
 
 import static org.junit.Assert.*;
 
-import java.util.List;
+import java.util.Iterator;
 
 import org.eclipse.leshan.core.request.Identity;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
 import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore;
 import org.eclipse.leshan.server.bootstrap.BootstrapSession;
-import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
-import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
 import org.eclipse.leshan.server.security.BootstrapSecurityStore;
 import org.eclipse.leshan.server.security.SecurityInfo;
 import org.junit.Before;
@@ -63,7 +61,7 @@ public class LeshanBootstrapServerBuilderTest {
             }
 
             @Override
-            public List<SecurityInfo> getAllByEndpoint(String endpoint) {
+            public Iterator<SecurityInfo> getAllByEndpoint(String endpoint) {
                 return null;
             }
         });
@@ -83,7 +81,7 @@ public class LeshanBootstrapServerBuilderTest {
             }
 
             @Override
-            public List<SecurityInfo> getAllByEndpoint(String endpoint) {
+            public Iterator<SecurityInfo> getAllByEndpoint(String endpoint) {
                 return null;
             }
         });
@@ -103,7 +101,7 @@ public class LeshanBootstrapServerBuilderTest {
             }
 
             @Override
-            public List<SecurityInfo> getAllByEndpoint(String endpoint) {
+            public Iterator<SecurityInfo> getAllByEndpoint(String endpoint) {
                 return null;
             }
         });