From 6e69e54096f3b50999319ffbdd99edf75720ce13 Mon Sep 17 00:00:00 2001 From: Abba Soungui YOUNOUSS Date: Fri, 24 Oct 2025 11:36:35 +0100 Subject: [PATCH] Initial commit. The application can connect to the MariaDB database and Wallix Bastion. --- .gitignore | 5 + pom.xml | 47 ++++++++ .../ext/wallix/sync/Configuration.java | 88 ++++++++++++++ .../guacamole/ext/wallix/sync/Wallix.java | 113 ++++++++++++++++++ .../wallix/sync/WallixConfigSynchronizer.java | 60 ++++++++++ .../guacamole/ext/wallix/sync/db/DB.java | 79 ++++++++++++ .../guacamole/ext/wallix/sync/db/Entity.java | 37 ++++++ .../ext/wallix/sync/db/UserGroup.java | 29 +++++ .../listener/ApplicationStartedListener.java | 22 ++++ src/main/resources/guac-manifest.json | 12 ++ 10 files changed, 492 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/cm/soungui/guacamole/ext/wallix/sync/Configuration.java create mode 100644 src/main/java/cm/soungui/guacamole/ext/wallix/sync/Wallix.java create mode 100644 src/main/java/cm/soungui/guacamole/ext/wallix/sync/WallixConfigSynchronizer.java create mode 100644 src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/DB.java create mode 100644 src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/Entity.java create mode 100644 src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/UserGroup.java create mode 100644 src/main/java/cm/soungui/guacamole/ext/wallix/sync/listener/ApplicationStartedListener.java create mode 100644 src/main/resources/guac-manifest.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..657fe60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target/ +/.classpath +/.mvn/ +/.settings/ +/.project diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..93b1af6 --- /dev/null +++ b/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + cm.soungui.guacamole + guacamole-ext-wallix-sync + 0.0.1-SNAPSHOT + + guacamole-ext-wallix-sync + Guacamole extension for synchronizing parameters from Wallix Bastion. + + + UTF-8 + 11 + 11 + + + + + junit + junit + 3.8.1 + test + + + + + org.apache.guacamole + guacamole-ext + 1.6.0 + provided + + + + + + org.slf4j + slf4j-api + 1.7.7 + provided + + + + diff --git a/src/main/java/cm/soungui/guacamole/ext/wallix/sync/Configuration.java b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/Configuration.java new file mode 100644 index 0000000..cc21fe6 --- /dev/null +++ b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/Configuration.java @@ -0,0 +1,88 @@ +package cm.soungui.guacamole.ext.wallix.sync; + +import org.apache.guacamole.properties.IntegerGuacamoleProperty; +import org.apache.guacamole.properties.LongGuacamoleProperty; +import org.apache.guacamole.properties.StringGuacamoleProperty; + +public class Configuration { + + public static LongGuacamoleProperty UPDATE_INTERVAL = new LongGuacamoleProperty() { + + @Override + public String getName() { + return "wallix-sync-interval"; + } + }; + + public static StringGuacamoleProperty MYSQL_HOSTNAME = new StringGuacamoleProperty() { + + @Override + public String getName() { + return "mysql-hostname"; + } + }; + + public static IntegerGuacamoleProperty MYSQL_PORT = new IntegerGuacamoleProperty() { + + @Override + public String getName() { + return "mysql-port"; + } + }; + + public static StringGuacamoleProperty MYSQL_DB = new StringGuacamoleProperty() { + + @Override + public String getName() { + return "mysql-database"; + } + }; + + public static StringGuacamoleProperty MYSQL_USERNAME = new StringGuacamoleProperty() { + + @Override + public String getName() { + return "mysql-username"; + } + }; + + public static StringGuacamoleProperty MYSQL_PASSWORD = new StringGuacamoleProperty() { + + @Override + public String getName() { + return "mysql-password"; + } + }; + + public static StringGuacamoleProperty MYSQL_SSL_MODE = new StringGuacamoleProperty() { + + @Override + public String getName() { + return "mysql-ssl-mode"; + } + }; + + public static StringGuacamoleProperty WALLIX_URL = new StringGuacamoleProperty() { + + @Override + public String getName() { + return "wallix-url"; + } + }; + + public static StringGuacamoleProperty WALLIX_USER = new StringGuacamoleProperty() { + + @Override + public String getName() { + return "wallix-user"; + } + }; + + public static StringGuacamoleProperty WALLIX_AUTH_KEY = new StringGuacamoleProperty() { + + @Override + public String getName() { + return "wallix-auth-key"; + } + }; +} diff --git a/src/main/java/cm/soungui/guacamole/ext/wallix/sync/Wallix.java b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/Wallix.java new file mode 100644 index 0000000..ac52b8c --- /dev/null +++ b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/Wallix.java @@ -0,0 +1,113 @@ +package cm.soungui.guacamole.ext.wallix.sync; + +import java.net.Socket; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedTrustManager; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.environment.LocalEnvironment; + +public class Wallix { + + private static Wallix INSTANCE; + + private static String HEADER_AUTH_USER = "X-Auth-User"; + + private static String HEADER_AUTH_KEY = "X-Auth-Key"; + + private String url; + + private String user; + + private String authKey; + + private Wallix() throws GuacamoleException { + Environment environment = LocalEnvironment.getInstance(); + url = environment.getRequiredProperty(Configuration.WALLIX_URL); + user = environment.getRequiredProperty(Configuration.WALLIX_USER); + authKey = environment.getRequiredProperty(Configuration.WALLIX_AUTH_KEY); + } + + TrustManager[] trustAllCerts = new X509ExtendedTrustManager[]{ + new X509ExtendedTrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; // Not used + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + // Trust all client certificates + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + // Trust all server certificates + } + + @Override + public void checkClientTrusted(X509Certificate[] arg0, String arg1, Socket arg2) + throws CertificateException { + // TODO Auto-generated method stub + + } + + @Override + public void checkClientTrusted(X509Certificate[] arg0, String arg1, SSLEngine arg2) + throws CertificateException { + // TODO Auto-generated method stub + + } + + @Override + public void checkServerTrusted(X509Certificate[] arg0, String arg1, Socket arg2) + throws CertificateException { + // TODO Auto-generated method stub + + } + + @Override + public void checkServerTrusted(X509Certificate[] arg0, String arg1, SSLEngine arg2) + throws CertificateException { + // TODO Auto-generated method stub + + } + } + }; + + public static Wallix getInstance() throws GuacamoleException { + if (INSTANCE == null) { + INSTANCE = new Wallix(); + } + return INSTANCE; + } + + public String get(String query) throws Exception { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url + query)) + .setHeader(HEADER_AUTH_USER, user) + .setHeader(HEADER_AUTH_KEY, authKey) + .build(); + + // TODO Disable this section before release and make this configurable + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new SecureRandom()); + + HttpClient client = HttpClient.newBuilder().sslContext(sslContext).build(); + try { + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + return response.body(); + } catch (Exception e) { + throw e; + } + } + +} diff --git a/src/main/java/cm/soungui/guacamole/ext/wallix/sync/WallixConfigSynchronizer.java b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/WallixConfigSynchronizer.java new file mode 100644 index 0000000..3d593e2 --- /dev/null +++ b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/WallixConfigSynchronizer.java @@ -0,0 +1,60 @@ +package cm.soungui.guacamole.ext.wallix.sync; + +import java.util.List; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.LocalEnvironment; + +import cm.soungui.guacamole.ext.wallix.sync.db.DB; +import cm.soungui.guacamole.ext.wallix.sync.db.UserGroup; + +public class WallixConfigSynchronizer implements Runnable { + + private long sleepDuration; + private LocalEnvironment environment; + + public WallixConfigSynchronizer() throws GuacamoleException { + System.out.println("Wazuh synchronizer Thread started"); + environment = LocalEnvironment.getInstance(); + sleepDuration = 1000 * environment.getProperty(Configuration.UPDATE_INTERVAL, Long.valueOf(900)); + } + + @Override + public void run() { + while (true) { + try { + System.out.println("Fetching config"); + synchronizeGroups(); + Thread.sleep(sleepDuration); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + private void synchronizeGroups() throws Exception { + System.out.println("Synchronizing groups"); + System.out.println("Getting Wallix groups"); + printWallixVersion(); + getWallixGroups(); + + DB db = DB.getInstance(); + List groups = db.getUserGroups(); + for (UserGroup group : groups) { + System.out.println("ID : " + group.getId() + " - Name : " + group.getName()); + } + } + + private void printWallixVersion() throws Exception { + Wallix wallix = Wallix.getInstance(); + String output = wallix.get("/version"); + System.out.println(output); + } + + private void getWallixGroups() throws Exception { + Wallix wallix = Wallix.getInstance(); + String output = wallix.get("/usergroups"); + System.out.println(output); + } + +} diff --git a/src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/DB.java b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/DB.java new file mode 100644 index 0000000..6a5a916 --- /dev/null +++ b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/DB.java @@ -0,0 +1,79 @@ +package cm.soungui.guacamole.ext.wallix.sync.db; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.environment.LocalEnvironment; + +import cm.soungui.guacamole.ext.wallix.sync.Configuration; + +public class DB { + + private static DB instance = new DB(); + + private Connection mysqlConnection; + + public Connection getMySQLConnection() throws GuacamoleException { + try { + if (mysqlConnection == null) { + Environment environment = LocalEnvironment.getInstance(); + String hostname = environment.getProperty(Configuration.MYSQL_HOSTNAME, "localhost"); + int port = environment.getProperty(Configuration.MYSQL_PORT, 3306); + String database = environment.getProperty(Configuration.MYSQL_DB, "guacamole_db"); + String username = environment.getProperty(Configuration.MYSQL_USERNAME); + String password = environment.getProperty(Configuration.MYSQL_PASSWORD); + String sslMode = environment.getProperty(Configuration.MYSQL_SSL_MODE, "disable"); + + String url = new StringBuffer("jdbc:mariadb://") + .append(hostname) + .append(":").append(port).append("/") + .append(database).append("?") + .append(username == null ? "" : "user=" + username + "&") + .append(password == null ? "" : "password=" + password + "&") + .append(sslMode == null ? "" : "sslMode=" + sslMode + "&") + .toString(); + + System.out.println("JDBC URL : " + url); + + Class.forName("org.mariadb.jdbc.Driver"); + mysqlConnection = DriverManager.getConnection(url); + + System.out.println("Connected to Database"); + } + + return mysqlConnection; + } catch (Exception e) { + throw new GuacamoleException(e); + } + } + + public static DB getInstance() { + return instance; + } + + public List getUserGroups() throws GuacamoleException, SQLException { + DB db = DB.getInstance(); + Connection connection = db.getMySQLConnection(); + ResultSet rs = connection + .prepareStatement("select entity_id,name from guacamole_entity where type='USER_GROUP'") + .executeQuery(); + + ArrayList groups = new ArrayList<>(); + while (rs.next()) { + UserGroup group = new UserGroup(); + group.setId(rs.getInt("entity_id")); + group.setName(rs.getString("name")); + + groups.add(group); + } + + return groups; + } + +} diff --git a/src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/Entity.java b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/Entity.java new file mode 100644 index 0000000..eab0f76 --- /dev/null +++ b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/Entity.java @@ -0,0 +1,37 @@ +package cm.soungui.guacamole.ext.wallix.sync.db; + +public class Entity { + + public static enum EntityType { USER, GROUP }; + + private int id; + + private String name; + + public EntityType type; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public EntityType getType() { + return type; + } + + public void setType(EntityType type) { + this.type = type; + } + +} diff --git a/src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/UserGroup.java b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/UserGroup.java new file mode 100644 index 0000000..f2ae275 --- /dev/null +++ b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/db/UserGroup.java @@ -0,0 +1,29 @@ +package cm.soungui.guacamole.ext.wallix.sync.db; + +import java.util.ArrayList; +import java.util.List; + +public class UserGroup extends Entity { + + private int groupId; + + private List members = new ArrayList<>(); + + @Override + public EntityType getType() { + return EntityType.GROUP; + } + + public int getGroupId() { + return groupId; + } + + public void setGroupId(int groupId) { + this.groupId = groupId; + } + + public List getMembers() { + return members; + } + +} diff --git a/src/main/java/cm/soungui/guacamole/ext/wallix/sync/listener/ApplicationStartedListener.java b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/listener/ApplicationStartedListener.java new file mode 100644 index 0000000..8fb6845 --- /dev/null +++ b/src/main/java/cm/soungui/guacamole/ext/wallix/sync/listener/ApplicationStartedListener.java @@ -0,0 +1,22 @@ +package cm.soungui.guacamole.ext.wallix.sync.listener; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.event.ApplicationStartedEvent; +import org.apache.guacamole.net.event.listener.Listener; + +import cm.soungui.guacamole.ext.wallix.sync.WallixConfigSynchronizer; + +public class ApplicationStartedListener implements Listener { + + private WallixConfigSynchronizer synchronizer; + + @Override + public void handleEvent(Object event) throws GuacamoleException { + if (event instanceof ApplicationStartedEvent) { + synchronizer = new WallixConfigSynchronizer(); + Thread thread = new Thread(synchronizer); + thread.start(); + } + } + +} diff --git a/src/main/resources/guac-manifest.json b/src/main/resources/guac-manifest.json new file mode 100644 index 0000000..47affc7 --- /dev/null +++ b/src/main/resources/guac-manifest.json @@ -0,0 +1,12 @@ +{ + + "guacamoleVersion" : "1.6.0", + + "name" : "Soungui Wallix Synchronizer extension", + "namespace" : "soungui-wallix-synchronizer-extension", + + "listeners" : [ + "cm.soungui.guacamole.ext.wallix.sync.listener.ApplicationStartedListener" + ] + +} \ No newline at end of file