From 3c73db2d98d79057dddfcf0188c42e1fdf6bde5d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?L=C3=A9o=20Banno-Cloutier?=
 <leo.banno-cloutier@savoirfairelinux.com>
Date: Wed, 19 Jul 2023 11:02:37 -0400
Subject: [PATCH] jams-server: create /api/auth/policyData route

Change-Id: Ic7236e6978a631a1079e840594b78a105d814571
---
 .gitignore                                    |  1 -
 .vscode/launch.json                           | 16 ++++
 Dockerfile                                    |  6 +-
 README.md                                     |  6 +-
 .../jami/jams/server/core/TomcatLauncher.java |  6 +-
 .../core/workflows/RegisterDeviceFlow.java    | 78 ++++++++++--------
 .../auth/policyData/PolicyDataServlet.java    | 82 +++++++++++++++++++
 7 files changed, 151 insertions(+), 44 deletions(-)
 create mode 100644 .vscode/launch.json
 create mode 100644 jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/policyData/PolicyDataServlet.java

diff --git a/.gitignore b/.gitignore
index 4ea1475c..d1ec1641 100644
--- a/.gitignore
+++ b/.gitignore
@@ -101,7 +101,6 @@ jams-react-client/package-lock.json
 jams-server/src/main/resources/webapp/
 
 # VScode
-.vscode/
 *.factorypath
 
 jams-server/doc/
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..fccd0d13
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,16 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "java",
+            "name": "Attach Debugger",
+            "request": "attach",
+            "hostName": "localhost",
+            "port": 35000,
+            "projectName": "jams-server"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index c0a41452..8e0d9912 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -31,8 +31,10 @@ RUN mkdir -p /app/jams-server/src/main/resources/webapp \
     > /app/jams-server/src/main/resources/webapp/index.html
 RUN mvn package
 WORKDIR /app/jams
-EXPOSE 3000 8080
-CMD java -jar jams-launcher.jar & npm start --prefix ../jams-react-client
+EXPOSE 3000 8080 35000
+ENV JAVA_TOOL_OPTIONS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:35000
+CMD java -jar jams-server.jar 8080 \
+    & npm start --prefix ../jams-react-client
 
 FROM build as prod
 WORKDIR /app/jams-react-client
diff --git a/README.md b/README.md
index 4fe3cec7..5db4eb58 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,10 @@ In order to generate a pair of pem and key use the following command using opens
 
 `openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout server.key -out server.pem`
 
+## Run with the debugger enabled
+
+`java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=localhost:35000 -jar jams-server.jar 8080`
+
 ## Generate documentation
 
 To generate the documentation you will need `apidoc` installed on your system.
@@ -61,7 +65,7 @@ chmod +x .git/hooks/pre-commit
 A development environment with react hot reloading can be created using:
 ```
 docker build -f Dockerfile -t jams:dev --target dev .
-docker run -it -p 3000:3000 -p 8080:8080 \
+docker run -it -p 3000:3000 -p 8080:8080 -p 35000:35000 \
     -v $(pwd)/jams-react-client/src:/app/jams-react-client/src \
     -v $(pwd)/jams-react-client/public:/app/jams-react-client/public \
     --rm jams:dev
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java b/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java
index 9b52529a..fb0ab3d9 100644
--- a/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java
+++ b/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java
@@ -73,10 +73,8 @@ public class TomcatLauncher {
     public TomcatLauncher(int port, String certificateFile, String keyFile) {
         if (!Files.exists(Paths.get(System.getProperty("user.dir") + File.separator + certificateFile))
                 || !Files.exists(Paths.get(System.getProperty("user.dir") + File.separator + keyFile))) {
-            log.info("Could not find certificate or keyfile, starting in plain HTTP connector as fallback!");
-            tomcat.getService().addConnector(TomcatConnectorFactory.getNoSSLConnector(port));
-            this.startServer();
-            return;
+            log.info("Could not find certificate or keyfile, exiting");
+            System.exit(1);
         }
         if (Files.exists(Paths.get(System.getProperty("user.dir") + File.separator + "keystore.jks"))) {
             log.info("Found a valid trust store, injecting into tomcat!");
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 a23bf745..ac186055 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
@@ -22,9 +22,18 @@
 */
 package net.jami.jams.server.core.workflows;
 
-import com.jsoniter.JsonIterator;
-import lombok.extern.slf4j.Slf4j;
+import static net.jami.jams.authmodule.UserAuthenticationModule.datastore;
+import static net.jami.jams.server.Server.certificateAuthority;
+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.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
 
+import lombok.extern.slf4j.Slf4j;
 import net.jami.jams.authmodule.UserAuthenticationModule;
 import net.jami.jams.common.authmodule.AuthModuleKey;
 import net.jami.jams.common.dao.StatementElement;
@@ -32,24 +41,12 @@ import net.jami.jams.common.dao.StatementList;
 import net.jami.jams.common.objects.devices.Device;
 import net.jami.jams.common.objects.requests.DeviceRegistrationRequest;
 import net.jami.jams.common.objects.responses.DeviceRegistrationResponse;
-import net.jami.jams.common.objects.roots.X509Fields;
-import net.jami.jams.common.objects.user.*;
-import net.jami.jams.dht.DeviceReceiptGenerator;
 import net.jami.jams.common.objects.user.Group;
-import net.jami.jams.dht.ETHAddressGenerator;
-
-
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Optional;
-
-import static net.jami.jams.authmodule.UserAuthenticationModule.datastore;
-import static net.jami.jams.server.Server.certificateAuthority;
-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.Policy;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.common.objects.user.UserGroupMapping;
+import net.jami.jams.common.objects.user.UserProfile;
+import net.jami.jams.dht.DeviceReceiptGenerator;
 
 @Slf4j
 public class RegisterDeviceFlow {
@@ -60,6 +57,7 @@ public class RegisterDeviceFlow {
             StatementList statementList = new StatementList();
             statementList.addStatement(new StatementElement("username", "=", username, ""));
             User user = dataStore.getUserDao().getObjects(statementList).get(0);
+
             UserProfile userProfile = userAuthenticationModule.getAuthSources()
                     .get(new AuthModuleKey(user.getRealm(), user.getUserType()))
                     .searchUserProfiles(username, "LOGON_NAME", Optional.empty()).get(0);
@@ -69,7 +67,7 @@ public class RegisterDeviceFlow {
             }
 
             // Renew user certificate if expired with same private key
-            if(!user.getCertificate().getNotAfter().after(new Date())) {
+            if (!user.getCertificate().getNotAfter().after(new Date())) {
                 user = UserAuthenticationModule.certificateAuthority.getRefreshedCertificate(user);
                 datastore.updateUserCertificate(user);
             }
@@ -85,16 +83,7 @@ public class RegisterDeviceFlow {
             }
             dataStore.getDeviceDao().storeObject(device);
 
-            Group group = new Group();
-
-            statementList = new StatementList();
-            statementList.addStatement(new StatementElement("username", "=", username, ""));
-            if (dataStore.getUserGroupMappingsDao().getObjects(statementList) != null && !dataStore.getUserGroupMappingsDao().getObjects(statementList).isEmpty()) {
-                UserGroupMapping mapping = dataStore.getUserGroupMappingsDao().getObjects(statementList).get(0);
-                statementList = new StatementList();
-                statementList.addStatement(new StatementElement("id", "=", mapping.getGroupId(), ""));
-                group =  dataStore.getGroupDao().getObjects(statementList).get(0);
-            }
+            Group group = getGroupByUsername(username);
 
             DeviceRegistrationResponse response = new DeviceRegistrationResponse();
             String policyData = getPolicyData(group);
@@ -122,15 +111,32 @@ public class RegisterDeviceFlow {
         }
     }
 
-    private static String getPolicyData(Group group) {
+    public static Group getGroupByUsername(String username) {
+        Group group = new Group();
+
+        StatementList statementList = new StatementList();
+        statementList.addStatement(new StatementElement("username", "=", username, ""));
+        List<UserGroupMapping> userGroupMappings = dataStore.getUserGroupMappingsDao().getObjects(statementList);
+
+        if (userGroupMappings != null && !userGroupMappings.isEmpty()) {
+            UserGroupMapping mapping = userGroupMappings.get(0);
+            statementList = new StatementList();
+            statementList.addStatement(new StatementElement("id", "=", mapping.getGroupId(), ""));
+            group = dataStore.getGroupDao().getObjects(statementList).get(0);
+        }
+
+        return group;
+    }
+
+    public static String getPolicyData(Group group) {
         if (!group.isEmpty() && group.hasBlueprint()) {
-            StatementElement st2 = new StatementElement("name", "=", group.getBlueprint(), "");
-            StatementList statementList2 = new StatementList();
-            statementList2.addStatement((st2));
+            StatementElement statementElement = new StatementElement("name", "=", group.getBlueprint(), "");
+            StatementList statementList = new StatementList();
+            statementList.addStatement(statementElement);
             try {
-                Policy policy = dataStore.getPolicyDao().getObjects(statementList2).get(0);
+                Policy policy = dataStore.getPolicyDao().getObjects(statementList).get(0);
                 return policy.getPolicyData();
-            } catch (Exception e1) {
+            } catch (Exception e) {
                 log.warn("No policy available for user - not adding a policy component to response");
             }
         }
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/policyData/PolicyDataServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/policyData/PolicyDataServlet.java
new file mode 100644
index 00000000..c2e2d85d
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/policyData/PolicyDataServlet.java
@@ -0,0 +1,82 @@
+/*
+* Copyright (C) 2023 by Savoir-faire Linux
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+package net.jami.jams.server.servlets.api.auth.policyData;
+
+import java.io.IOException;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.objects.user.Group;
+import net.jami.jams.server.core.workflows.RegisterDeviceFlow;
+import net.jami.jams.server.servlets.api.auth.device.DeviceServlet;
+
+@WebServlet("/api/auth/policyData")
+public class PolicyDataServlet extends HttpServlet {
+
+    /**
+     * @apiVersion 1.0.0
+     * @api {get} /api/auth/policyData Get policy data
+     * @apiName getPolicyData
+     * @apiGroup Policy Data
+     *
+     *
+     * @apiSuccess (200) {body} Policy Data
+     * @apiSuccessExample {json} Success-Response:
+     *                    [{
+     *                    "allowCertFromHistory": true,
+     *                    "allowLookup": true,
+     *                    "allowCertFromContact": true,
+     *                    "allowCertFromTrusted": true,
+     *                    "Account.videoEnabled": true,
+     *                    "DHTRelay.PublicInCalls": false,
+     *                    "Account.autoAnswer": false,
+     *                    "Account.peerDiscovery": true,
+     *                    "Account.accountDiscovery": true,
+     *                    "Account.accountPublish": true,
+     *                    "Account.rendezVous": false,
+     *                    "Account.upnpEnabled": true,
+     *                    "Account.defaultModerators": "",
+     *                    "Account.uiCustomization":
+     *                    "{\"areTipsEnabled\":false,\"backgroundType\":\"default\"}"
+     *                    }]
+     * @apiError (500) {null} null Policy Data could not be retrieved
+     */
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        String username = req.getAttribute("username").toString();
+
+        Group group = RegisterDeviceFlow.getGroupByUsername(username);
+        String policyData = RegisterDeviceFlow.getPolicyData(group);
+
+        if (policyData == null) {
+            resp.setStatus(404);
+            return;
+        }
+
+        JsonObject obj = JsonParser.parseString(policyData).getAsJsonObject();
+        DeviceServlet.renameKeys(obj);
+
+        resp.getOutputStream().write(obj.toString().getBytes());
+        resp.setStatus(200);
+    }
+}
-- 
GitLab