From d4154597d46f825930d488e5f6c12ea308228556 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?L=C3=A9o=20Banno-Cloutier?=
 <leo.banno-cloutier@savoirfairelinux.com>
Date: Tue, 18 Jul 2023 10:54:25 -0400
Subject: [PATCH] jams-server: refactoring

Change-Id: I9be481bf2bce262ff655f3e075edb3f8e7bf384c
---
 .../objects/requests/RevocationRequest.java   |   4 +
 .../net/jami/jams/common/utils/Validator.java |  28 ++-
 .../CustomUiPreview/CustomUiPreview.js        |   2 +-
 .../src/views/Blueprint/parsePolicyData.js    |   2 +-
 .../core/workflows/RegisterDeviceFlow.java    |  30 ++-
 .../core/workflows/RevokeDeviceFlow.java      |  12 +-
 .../api/auth/device/DeviceServlet.java        | 237 ++++++++----------
 .../directory/SearchDirectoryServlet.java     |  39 ++-
 .../api/image/FileHandlerServlet.java         |  27 +-
 9 files changed, 194 insertions(+), 187 deletions(-)

diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java
index e82f1980..671e52ae 100644
--- a/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java
@@ -22,13 +22,17 @@
 */
 package net.jami.jams.common.objects.requests;
 
+import lombok.AllArgsConstructor;
 import lombok.Getter;
+import lombok.NoArgsConstructor;
 import lombok.Setter;
 
 import java.math.BigInteger;
 
 @Getter
 @Setter
+@AllArgsConstructor
+@NoArgsConstructor
 public class RevocationRequest {
     private BigInteger identifier;
     private RevocationType revocationType;
diff --git a/jams-common/src/main/java/net/jami/jams/common/utils/Validator.java b/jams-common/src/main/java/net/jami/jams/common/utils/Validator.java
index c939cd76..a506c8ec 100644
--- a/jams-common/src/main/java/net/jami/jams/common/utils/Validator.java
+++ b/jams-common/src/main/java/net/jami/jams/common/utils/Validator.java
@@ -30,19 +30,23 @@ import java.security.interfaces.RSAPublicKey;
 
 public class Validator {
 
-    public static boolean validateCARequests(CreateCARequest request){
-        if(request.getCertificate() != null && request.getPrivateKey() != null){
-            if(request.getCertificate().getBasicConstraints() != -1){
-                RSAPublicKey rsaPublicKey = (RSAPublicKey) request.getCertificate().getPublicKey();
-                RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) request.getPrivateKey();
-                return rsaPublicKey.getModulus().equals( rsaPrivateKey.getModulus() )
-                        && BigInteger.valueOf( 2 ).modPow( rsaPublicKey.getPublicExponent()
-                                .multiply( rsaPrivateKey.getPrivateExponent() ).subtract( BigInteger.ONE ),
-                        rsaPublicKey.getModulus() ).equals( BigInteger.ONE );
-            }
+    public static boolean validateCARequests(CreateCARequest request) {
+        if (request.getCertificate() == null || request.getPrivateKey() == null) {
+            return true;
+        }
+
+        if (request.getCertificate().getBasicConstraints() == -1) {
             return false;
         }
-        return true;
-    }
 
+        RSAPublicKey rsaPublicKey = (RSAPublicKey) request.getCertificate().getPublicKey();
+        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) request.getPrivateKey();
+
+        return rsaPublicKey.getModulus().equals(rsaPrivateKey.getModulus())
+                // https://en.wikipedia.org/wiki/RSA_(cryptosystem)
+                // 2 ** (e * d - 1) mod n == 1
+                && BigInteger.valueOf(2).modPow(rsaPublicKey.getPublicExponent()
+                        .multiply(rsaPrivateKey.getPrivateExponent()).subtract(BigInteger.ONE),
+                        rsaPublicKey.getModulus()).equals(BigInteger.ONE);
+    }
 }
diff --git a/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.js b/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.js
index f03f04ab..3c78a42b 100644
--- a/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.js
+++ b/jams-react-client/src/components/CustomUiPreview/CustomUiPreview.js
@@ -141,7 +141,7 @@ export default function CustomUiPreview({
     backgroundColor = "";
     backgroundUrl = backgroundImage;
   } else if (backgroundUrl) {
-    backgroundUrl = encodeURI(`${url_path}:${url_port}/${backgroundUrl}`);
+    backgroundUrl = encodeURI(`${url_path}:${url_port}${backgroundUrl}`);
   }
 
   if (!hasMainBoxColor) {
diff --git a/jams-react-client/src/views/Blueprint/parsePolicyData.js b/jams-react-client/src/views/Blueprint/parsePolicyData.js
index 48f7afa8..25b5e1a7 100644
--- a/jams-react-client/src/views/Blueprint/parsePolicyData.js
+++ b/jams-react-client/src/views/Blueprint/parsePolicyData.js
@@ -108,7 +108,7 @@ const setCustomizationSettings = (policyData) => {
   // Get the name of the file from the url
   ui.hasLogo = result.logoUrl !== "";
   ui.logoUrl = result.logoUrl;
-  ui.logoSize = result.logoSize;
+  ui.logoSize = result.logoSize || DEFAULT_UI_CUSTOMIZATION.logoSize;
 
   policyData.uiCustomization = ui;
   return policyData;
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java
index df115eb4..a23bf745 100644
--- a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java
+++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java
@@ -97,17 +97,11 @@ public class RegisterDeviceFlow {
             }
 
             DeviceRegistrationResponse response = new DeviceRegistrationResponse();
-            if (!group.isEmpty() && group.hasBlueprint()) {
-                StatementElement st2 = new StatementElement("name", "=", group.getBlueprint(), "");
-                StatementList statementList2 = new StatementList();
-                statementList2.addStatement((st2));
-                try {
-                    Policy policy = dataStore.getPolicyDao().getObjects(statementList2).get(0);
-                    response.setPolicyData(policy.getPolicyData());
-                } catch (Exception e1) {
-                    log.warn("No policy available for user - not adding a policy component to response");
-                }
+            String policyData = getPolicyData(group);
+            if (policyData != null) {
+                response.setPolicyData(policyData);
             }
+
             // Device receipt
             String[] devReceipt = DeviceReceiptGenerator.generateReceipt(user.getPrivateKey(),
                     user.getCertificate().getPublicKey(), device.getCertificate().getPublicKey(), user.getEthAddress());
@@ -127,4 +121,20 @@ public class RegisterDeviceFlow {
             return null;
         }
     }
+
+    private static String getPolicyData(Group group) {
+        if (!group.isEmpty() && group.hasBlueprint()) {
+            StatementElement st2 = new StatementElement("name", "=", group.getBlueprint(), "");
+            StatementList statementList2 = new StatementList();
+            statementList2.addStatement((st2));
+            try {
+                Policy policy = dataStore.getPolicyDao().getObjects(statementList2).get(0);
+                return policy.getPolicyData();
+            } catch (Exception e1) {
+                log.warn("No policy available for user - not adding a policy component to response");
+            }
+        }
+
+        return null;
+    }
 }
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java
index 02590f91..986629fc 100644
--- a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java
+++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java
@@ -33,6 +33,8 @@ import net.jami.jams.common.objects.responses.DeviceRevocationResponse;
 import static net.jami.jams.server.Server.certificateAuthority;
 import static net.jami.jams.server.Server.dataStore;
 
+import java.math.BigInteger;
+
 @Slf4j
 public class RevokeDeviceFlow {
 
@@ -49,13 +51,15 @@ public class RevokeDeviceFlow {
                 log.error("Could not find device!");
                 return null;
             }
-            RevocationRequest request = new RevocationRequest();
-            request.setRevocationType(RevocationType.DEVICE);
-            request.setIdentifier(device.getCertificate().getSerialNumber());
+
+            BigInteger serialNumber = device.getCertificate().getSerialNumber();
+
+            RevocationRequest request = new RevocationRequest(serialNumber, RevocationType.DEVICE);
             certificateAuthority.revokeCertificate(request);
+
             long statTime = System.currentTimeMillis();
             while(certificateAuthority.getLatestCRL().get()
-                    .getRevokedCertificate(device.getCertificate().getSerialNumber()) == null){
+                    .getRevokedCertificate(serialNumber) == null){
                 log.warn("Certificate has not yet appeared in CRL!");
                 if(System.currentTimeMillis() - statTime > 1000){
                     log.error("The certificate has not appeared within 1 second, we are considering the operation has failed");
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java
index da9d2105..5d222747 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java
@@ -22,10 +22,16 @@
  */
 package net.jami.jams.server.servlets.api.auth.device;
 
+import static net.jami.jams.server.Server.certificateAuthority;
+import static net.jami.jams.server.Server.dataStore;
+
+import java.io.IOException;
+
+import com.google.gson.Gson;
 import com.google.gson.JsonObject;
-import com.google.gson.JsonPrimitive;
 import com.jsoniter.JsonIterator;
 import com.jsoniter.output.JsonStream;
+
 import jakarta.servlet.ServletException;
 import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
@@ -43,13 +49,6 @@ import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler;
 import net.jami.jams.server.core.workflows.RegisterDeviceFlow;
 import net.jami.jams.server.core.workflows.RevokeDeviceFlow;
 
-import com.google.gson.Gson;
-
-import java.io.IOException;
-
-import static net.jami.jams.server.Server.certificateAuthority;
-import static net.jami.jams.server.Server.dataStore;
-
 @WebServlet("/api/auth/device/*")
 public class DeviceServlet extends HttpServlet {
 
@@ -63,26 +62,27 @@ public class DeviceServlet extends HttpServlet {
      *
      * @apiSuccess (200) {body} Device device information
      * @apiSuccessExample {json} Success-Response:
-     * {
-     *     "certificate":"pem_encoded_certificate",
-     *     "displayName":"My Galaxy S8",
-     *     "deviceId":"6aec6252ad",
-     *     "revoked":true
-     * }
+     *                    {
+     *                    "certificate":"pem_encoded_certificate",
+     *                    "displayName":"My Galaxy S8",
+     *                    "deviceId":"6aec6252ad",
+     *                    "revoked":true
+     *                    }
      * @apiError (500) {null} null Device could not be retrieved
      */
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String username = req.getAttribute("username").toString();
-        String deviceId = req.getPathInfo().replace("/","");
+        String deviceId = req.getPathInfo().replace("/", "");
         StatementList statementList = new StatementList();
-        StatementElement st1 = new StatementElement("owner","=",username,"AND");
-        StatementElement st2 = new StatementElement("deviceId","=",deviceId,"");
+        StatementElement st1 = new StatementElement("owner", "=", username, "AND");
+        StatementElement st2 = new StatementElement("deviceId", "=", deviceId, "");
         statementList.addStatement(st1);
         statementList.addStatement(st2);
         Device device = dataStore.getDeviceDao().getObjects(statementList).get(0);
-        if(certificateAuthority.getLatestCRL().get() !=  null)
-            device.setRevoked(certificateAuthority.getLatestCRL().get().getRevokedCertificate(device.getCertificate().getSerialNumber()) != null);
+        if (certificateAuthority.getLatestCRL().get() != null)
+            device.setRevoked(certificateAuthority.getLatestCRL().get()
+                    .getRevokedCertificate(device.getCertificate().getSerialNumber()) != null);
         else
             device.setRevoked(false);
 
@@ -97,113 +97,85 @@ public class DeviceServlet extends HttpServlet {
      *
      * @apiParam {body} DeviceRegistrationRequest device registration request
      * @apiParamExample {json} Request-Example:
-     * {
-     *     "csr":"pem_encoded_csr",
-     *     "deviceName":"My Galaxy S8"
-     * }
+     *                  {
+     *                  "csr":"pem_encoded_csr",
+     *                  "deviceName":"My Galaxy S8"
+     *                  }
      *
      * @apiSuccess (200) {body} DeviceRegistrationResponse registration response
      * @apiError (500) {null} null Device could not be enrolled
      * @apiSuccessExample {json} Success-Response:
-     * {
-     *     "certificateChain":"pem_encoded_certificate_chain"
-     *     "displayName":"John Doe"
-     *     "nameServer":"https://mydomain.com"
-     *     "deviceReceipt": "device_receipt_object"
-     *     "receiptSignature":"receipt_signature_object"
-     *     "userPhoto":"base64_encoded_photo"
-     * }
+     *                    {
+     *                    "certificateChain":"pem_encoded_certificate_chain"
+     *                    "displayName":"John Doe"
+     *                    "nameServer":"https://mydomain.com"
+     *                    "deviceReceipt": "device_receipt_object"
+     *                    "receiptSignature":"receipt_signature_object"
+     *                    "userPhoto":"base64_encoded_photo"
+     *                    }
      */
     @Override
-    @ScopedServletMethod(securityGroups = {AccessLevel.USER})
+    @ScopedServletMethod(securityGroups = { AccessLevel.USER })
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
-        DeviceRegistrationRequest request = JsonIterator.deserialize(req.getInputStream().readAllBytes(), DeviceRegistrationRequest.class);
-        DeviceRegistrationResponse devResponse = RegisterDeviceFlow.registerDevice(req.getAttribute("username").toString(),request);
-        if(devResponse != null) {
-            Gson gson = new Gson();
-            String filteredJson = gson.toJson(devResponse);
-            JsonObject obj = gson.fromJson(filteredJson, JsonObject.class);
+        DeviceRegistrationRequest request = JsonIterator.deserialize(req.getInputStream().readAllBytes(),
+                DeviceRegistrationRequest.class);
+        DeviceRegistrationResponse devResponse = RegisterDeviceFlow
+                .registerDevice(req.getAttribute("username").toString(), request);
 
-            if (obj.get("videoEnabled") != null) {
-                obj.add("Account.videoEnabled", obj.get("videoEnabled"));
-                obj.remove("videoEnabled");
-            }
-
-            if (obj.get("publicInCalls") != null) {
-                obj.add("DHT.PublicInCalls", obj.get("publicInCalls"));
-                obj.remove("publicInCalls");
-            }
+        if (devResponse == null) {
+            TomcatCustomErrorHandler.sendCustomError(resp, 500, "could not enroll a device due to server-side error");
+            return;
+        }
 
-            if (obj.get("autoAnswer") != null) {
-                obj.add("Account.autoAnswer", obj.get("autoAnswer"));
-                obj.remove("autoAnswer");
-            }
-            if (obj.get("peerDiscovery") != null) {
-                obj.add("Account.peerDiscovery", obj.get("peerDiscovery"));
-                obj.remove("peerDiscovery");
-            }
-            if (obj.get("accountDiscovery") != null) {
-                obj.add("Account.accountDiscovery", obj.get("accountDiscovery"));
-                obj.remove("accountDiscovery");
-            }
-            if (obj.get("accountPublish") != null) {
-                obj.add("Account.accountPublish", obj.get("accountPublish"));
-                obj.remove("accountPublish");
-            }
+        Gson gson = new Gson();
+        String filteredJson = gson.toJson(devResponse);
+        JsonObject obj = gson.fromJson(filteredJson, JsonObject.class);
 
-            if (obj.get("rendezVous") != null) {
-                obj.add("Account.rendezVous", obj.get("rendezVous"));
-                obj.remove("rendezVous");
-            }
-            if (obj.get("upnpEnabled") != null) {
-                obj.add("Account.upnpEnabled", obj.get("upnpEnabled"));
-                obj.remove("upnpEnabled");
-            }
+        renameKeys(obj);
 
-            if (obj.get("turnEnabled") != null) {
-                obj.add("TURN.enable", obj.get("turnEnabled"));
-                obj.remove("turnEnabled");
-            }
-            if (obj.get("turnServer") != null) {
-                obj.add("TURN.server", obj.get("turnServer"));
-                obj.remove("turnServer");
-            }
-            if (obj.get("turnServerUserName") != null) {
-                obj.add("TURN.username", obj.get("turnServerUserName"));
-                obj.remove("turnServerUserName");
-            }
-            if (obj.get("turnServerPassword") != null) {
-                obj.add("TURN.password", obj.get("turnServerPassword"));
-                obj.remove("turnServerPassword");
-            }
-            if (obj.get("proxyEnabled") != null) {
-                obj.add("Account.proxyEnabled", obj.get("proxyEnabled"));
-                obj.remove("proxyEnabled");
-            }
-            if (obj.get("proxyServer") != null) {
-                obj.add("Account.proxyServer", obj.get("proxyServer"));
-                obj.remove("proxyServer");
-            }
-            if (obj.get("dhtProxyListUrl") != null) {
-                obj.add("Account.dhtProxyListUrl", obj.get("dhtProxyListUrl"));
-                obj.remove("dhtProxyListUrl");
-            }
-            if (obj.get("displayName") != null) {
-                obj.add("Account.displayName", obj.get("displayName"));
-                obj.remove("displayName");
-            }
-            if (obj.get("defaultModerators") != null) {
-                obj.add("Account.defaultModerators", obj.get("defaultModerators"));
-                obj.remove("defaultModerators");
-            }
-            if (obj.get("uiCustomization") != null) {
-                obj.add("Account.uiCustomization", obj.get("uiCustomization"));
-                obj.remove("uiCustomization");
-            }
+        resp.getOutputStream().write((obj.toString()).getBytes());
+    }
 
-            resp.getOutputStream().write((obj.toString()).getBytes());
+    /**
+     * Modifies in place obj to rename a key
+     *
+     * @param obj
+     * @param oldKey
+     * @param newKey
+     */
+    private static void renameKey(JsonObject obj, String oldKey, String newKey) {
+        if (obj.get(oldKey) != null) {
+            obj.add(newKey, obj.get(oldKey));
+            obj.remove(oldKey);
         }
-        else TomcatCustomErrorHandler.sendCustomError(resp,500,"could not enroll a device due to server-side error");
+    }
+
+    /**
+     * Modifies in place obj to rename keys
+     *
+     * @param obj
+     */
+    public static void renameKeys(JsonObject obj) {
+        renameKey(obj, "videoEnabled", "Account.videoEnabled");
+        renameKey(obj, "publicInCalls", "DHTRelay.PublicInCalls");
+        renameKey(obj, "autoAnswer", "Account.autoAnswer");
+        renameKey(obj, "peerDiscovery", "Account.peerDiscovery");
+        renameKey(obj, "accountDiscovery", "Account.accountDiscovery");
+        renameKey(obj, "accountPublish", "Account.accountPublish");
+
+        renameKey(obj, "rendezVous", "Account.rendezVous");
+        renameKey(obj, "upnpEnabled", "Account.upnpEnabled");
+
+        renameKey(obj, "turnEnabled", "TURN.enable");
+        renameKey(obj, "turnServer", "TURN.server");
+        renameKey(obj, "turnServerUserName", "TURN.username");
+        renameKey(obj, "turnServerPassword", "TURN.password");
+        renameKey(obj, "proxyEnabled", "Account.proxyEnabled");
+        renameKey(obj, "proxyServer", "Account.proxyServer");
+        renameKey(obj, "dhtProxyListUrl", "Account.dhtProxyListUrl");
+        renameKey(obj, "displayName", "Account.displayName");
+        renameKey(obj, "defaultModerators", "Account.defaultModerators");
+        renameKey(obj, "uiCustomization", "Account.uiCustomization");
     }
 
     /**
@@ -221,18 +193,21 @@ public class DeviceServlet extends HttpServlet {
     @Override
     protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String username = req.getAttribute("username").toString();
-        String deviceId = req.getPathInfo().replace("/","");
+        String deviceId = req.getPathInfo().replace("/", "");
         String deviceName = req.getParameter("deviceName");
         StatementList update = new StatementList();
-        StatementElement st0 = new StatementElement("deviceName","=",deviceName,"");
+        StatementElement st0 = new StatementElement("deviceName", "=", deviceName, "");
         update.addStatement(st0);
         StatementList constraint = new StatementList();
-        StatementElement st1 = new StatementElement("owner","=",username,"AND");
-        StatementElement st2 = new StatementElement("deviceId","=",deviceId,"");
+        StatementElement st1 = new StatementElement("owner", "=", username, "AND");
+        StatementElement st2 = new StatementElement("deviceId", "=", deviceId, "");
         update.addStatement(st1);
         update.addStatement(st2);
-        if(dataStore.getDeviceDao().updateObject(update,constraint)) resp.setStatus(200);
-        else TomcatCustomErrorHandler.sendCustomError(resp,500,"could not update device information due to server-side error");
+        if (dataStore.getDeviceDao().updateObject(update, constraint))
+            resp.setStatus(200);
+        else
+            TomcatCustomErrorHandler.sendCustomError(resp, 500,
+                    "could not update device information due to server-side error");
     }
 
     /**
@@ -247,21 +222,25 @@ public class DeviceServlet extends HttpServlet {
      * @apiError (500) {null} null device could not be deactivated
      */
     @Override
-    @ScopedServletMethod(securityGroups = {AccessLevel.USER})
+    @ScopedServletMethod(securityGroups = { AccessLevel.USER })
     protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        String deviceId = req.getPathInfo().replace("/","");
-        //If the device does not belong to the user throw a 403
+        String deviceId = req.getPathInfo().replace("/", "");
+        // If the device does not belong to the user throw a 403
         StatementList statementList = new StatementList();
-        StatementElement statementElement = new StatementElement("owner","=",req.getAttribute("username").toString(),"");
+        StatementElement statementElement = new StatementElement("owner", "=", req.getAttribute("username").toString(),
+                "");
         statementList.addStatement(statementElement);
-        if(dataStore.getDeviceDao().getObjects(statementList).stream().filter(device ->
-                device.getDeviceId().equals(deviceId)).count() == 0){
-            TomcatCustomErrorHandler.sendCustomError(resp,403,"You do not have sufficient rights to revoke this device!");
+        if (dataStore.getDeviceDao().getObjects(statementList).stream()
+                .filter(device -> device.getDeviceId().equals(deviceId)).count() == 0) {
+            TomcatCustomErrorHandler.sendCustomError(resp, 403,
+                    "You do not have sufficient rights to revoke this device!");
             return;
         }
-        DeviceRevocationResponse devResponse = RevokeDeviceFlow.revokeDevice(req.getAttribute("username").toString(),deviceId);
-        if(devResponse != null) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes());
+        DeviceRevocationResponse devResponse = RevokeDeviceFlow.revokeDevice(req.getAttribute("username").toString(),
+                deviceId);
+        if (devResponse != null)
+            resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes());
         else
-            TomcatCustomErrorHandler.sendCustomError(resp,500,"could not revoke device due to server-side error");
+            TomcatCustomErrorHandler.sendCustomError(resp, 500, "could not revoke device due to server-side error");
     }
 }
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
index 8ca50f7e..057bed08 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
@@ -22,7 +22,23 @@
  */
 package net.jami.jams.server.servlets.api.auth.directory;
 
+import static net.jami.jams.server.Server.dataStore;
+import static net.jami.jams.server.Server.nameServer;
+import static net.jami.jams.server.Server.userAuthenticationModule;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.json.JSONObject;
+
 import com.jsoniter.JsonIterator;
+
 import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
@@ -34,16 +50,13 @@ import net.jami.jams.common.authentication.AuthenticationSourceType;
 import net.jami.jams.common.authmodule.AuthModuleKey;
 import net.jami.jams.common.dao.StatementElement;
 import net.jami.jams.common.dao.StatementList;
-import net.jami.jams.common.objects.user.*;
-import org.json.JSONObject;
-
-import java.io.IOException;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static net.jami.jams.server.Server.dataStore;
-import static net.jami.jams.server.Server.nameServer;
-import static net.jami.jams.server.Server.userAuthenticationModule;
+import net.jami.jams.common.objects.user.AccessLevel;
+import net.jami.jams.common.objects.user.Group;
+import net.jami.jams.common.objects.user.Policy;
+import net.jami.jams.common.objects.user.PolicyData;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.common.objects.user.UserGroupMapping;
+import net.jami.jams.common.objects.user.UserProfile;
 
 @Slf4j
 @WebServlet("/api/auth/directory/search")
@@ -75,10 +88,10 @@ public class SearchDirectoryServlet extends HttpServlet {
         StatementList statementList1 = new StatementList();
 
         statementList1.addStatement(new StatementElement("username", "=", req.getAttribute("username").toString(), ""));
-        if (dataStore.getUserGroupMappingsDao().getObjects(statementList1) != null && !dataStore.getUserGroupMappingsDao().getObjects(statementList1).isEmpty()) {
-            UserGroupMapping mapping = dataStore.getUserGroupMappingsDao().getObjects(statementList1).get(0);
+        List<UserGroupMapping> mappings = dataStore.getUserGroupMappingsDao().getObjects(statementList1);
+        if (mappings != null && !mappings.isEmpty()) {
             statementList1 = new StatementList();
-            statementList1.addStatement(new StatementElement("id", "=", mapping.getGroupId(), ""));
+            statementList1.addStatement(new StatementElement("id", "=", mappings.get(0).getGroupId(), ""));
             group =  dataStore.getGroupDao().getObjects(statementList1).get(0);
         }
 
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/image/FileHandlerServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/image/FileHandlerServlet.java
index 325d6dbe..6f9b108c 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/image/FileHandlerServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/image/FileHandlerServlet.java
@@ -6,6 +6,7 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 
 import jakarta.servlet.ServletException;
@@ -28,8 +29,8 @@ public class FileHandlerServlet extends HttpServlet {
 
     /**
      * @apiVersion 1.0.0
-     * @api {post} /api/image/filehandler/<blueprintName>/<imageType> Upload an
-     *      image
+     * @api {post} /api/image/filehandler/<blueprintName>/<imageType>
+     *      Upload an image
      * @apiName postImage
      * @apiGroup Images
      *
@@ -96,8 +97,8 @@ public class FileHandlerServlet extends HttpServlet {
 
     /**
      * @apiVersion 1.0.0
-     * @api {get} /api/image/filehandler/<blueprintName>/<imageType>/<fileName> Get
-     *      an image
+     * @api {get} /api/image/filehandler/<blueprintName>/<imageType>/<fileName>
+     *      Get an image
      * @apiName getImage
      * @apiGroup Images
      *
@@ -133,27 +134,19 @@ public class FileHandlerServlet extends HttpServlet {
             String imageType = pathParts[2];
             String fileName = pathParts[3];
 
-            File file = new File(IMAGES_DIR + File.separator + blueprintName + File.separator + imageType
-                    + File.separator + fileName);
+            Path imageFilePath = Paths.get(IMAGES_DIR, blueprintName, imageType, fileName);
+            byte[] imageData = Files.readAllBytes(imageFilePath);
 
-            String mimeType = Files.probeContentType(Paths.get(file.getAbsolutePath()));
+            String mimeType = Files.probeContentType(imageFilePath);
             if (mimeType == null) {
                 mimeType = "application/octet-stream";
             }
 
             response.setContentType(mimeType);
-            response.addHeader("Content-Disposition", "attachment; filename=" + fileName);
 
-            OutputStream out = response.getOutputStream();
-            FileInputStream in = new FileInputStream(file);
-            byte[] buffer = new byte[4096];
-            int length;
-
-            while ((length = in.read(buffer)) > 0) {
-                out.write(buffer, 0, length);
+            try (OutputStream out = response.getOutputStream()) {
+                out.write(imageData);
             }
-            in.close();
-            out.flush();
         } catch (FileNotFoundException e) {
             log.error("FileHandlerServlet: Error while processing request", e);
             response.sendError(HttpServletResponse.SC_NOT_FOUND);
-- 
GitLab