diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/.gitignore b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/.gitignore
new file mode 100644
index 000000000..42f4a1a64
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/.gitignore
@@ -0,0 +1,2 @@
+target/
+*~
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/pom.xml
new file mode 100644
index 000000000..82776f7d0
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/pom.xml
@@ -0,0 +1,128 @@
+
+
+
+
+ 4.0.0
+ org.apache.guacamole
+ guacamole-auth-jdbc-sqlserver
+ jar
+ guacamole-auth-jdbc-sqlserver
+ http://guacamole.incubator.apache.org/
+
+
+ UTF-8
+
+
+
+ org.apache.guacamole
+ guacamole-auth-jdbc
+ 0.9.13-incubating
+ ../../
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.3
+
+ 1.6
+ 1.6
+
+ -Xlint:all
+ -Werror
+
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 2.10
+
+
+ unpack-dependencies
+ prepare-package
+
+ unpack-dependencies
+
+
+ runtime
+ ${project.build.directory}/classes
+
+
+
+
+
+
+
+ org.apache.rat
+ apache-rat-plugin
+ 0.12
+
+
+
+ **/*.json
+
+
+
+
+
+
+ validate
+ validate
+
+ check
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.guacamole
+ guacamole-ext
+ provided
+
+
+
+
+ org.apache.guacamole
+ guacamole-auth-jdbc-base
+ 0.9.13-incubating
+
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/001-create-schema.sql
new file mode 100644
index 000000000..df95800ec
Binary files /dev/null and b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/001-create-schema.sql differ
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/002-create-admin-user.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/002-create-admin-user.sql
new file mode 100644
index 000000000..08cce3f07
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/002-create-admin-user.sql
@@ -0,0 +1,43 @@
+/**
+ * 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.
+ */
+
+INSERT INTO [guacamole].[user] (username, password_hash, password_salt, password_date)
+VALUES ('guacadmin', 0xCA458A7D494E3BE824F5E1E175A1556C0F8EEF2C2D7DF3633BEC4A29C4411960,
+0xCA458A7D494E3BE824F5E1E175A1556C0F8EEF2C2D7DF3633BEC4A29C4411960, getdate());
+
+INSERT INTO [guacamole].[system_permission]
+SELECT user_id, permission
+FROM (
+ SELECT 'guacadmin' AS username, 'CREATE_CONNECTION' AS permission
+ UNION SELECT 'guacadmin' AS username, 'CREATE_CONNECTION_GROUP' AS permission
+ UNION SELECT 'guacadmin' AS username, 'CREATE_SHARING_PROFILE' AS permission
+ UNION SELECT 'guacadmin' AS username, 'CREATE_USER' AS permission
+ UNION SELECT 'guacadmin' AS username, 'ADMINISTER' AS permission)
+ permissions
+ JOIN [guacamole].[user] ON permissions.username = [guacamole].[user].[username];
+
+INSERT INTO [guacamole].[user_permission]
+SELECT [guacamole].[user].[user_id], [affected].[user_id], permission
+FROM (
+ SELECT 'guacadmin' AS username, 'guacadmin' AS affected_username, 'READ' AS permission
+ UNION SELECT 'guacadmin' AS username, 'guacadmin' AS affected_username, 'UPDATE' AS permission
+ UNION SELECT 'guacadmin' AS username, 'guacadmin' AS affected_username, 'ADMINISTER' AS permission)
+ permissions
+ JOIN [guacamole].[user] ON permissions.username = [guacamole].[user].[username]
+ JOIN [guacamole].[user] affected ON permissions.affected_username = affected.username;
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerAuthenticationProvider.java
new file mode 100644
index 000000000..ef5d61d90
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerAuthenticationProvider.java
@@ -0,0 +1,50 @@
+/*
+ * 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.guacamole.auth.sqlserver;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.jdbc.InjectedAuthenticationProvider;
+import org.apache.guacamole.auth.jdbc.JDBCAuthenticationProviderService;
+
+/**
+ * Provides a SQLServer-based implementation of the AuthenticationProvider
+ * functionality.
+ */
+public class SQLServerAuthenticationProvider extends InjectedAuthenticationProvider {
+
+ /**
+ * Creates a new SQLServerAuthenticationProvider that reads and writes
+ * authentication data to a SQLServer database defined by properties in
+ * guacamole.properties.
+ *
+ * @throws GuacamoleException
+ * If a required property is missing, or an error occurs while parsing
+ * a property.
+ */
+ public SQLServerAuthenticationProvider() throws GuacamoleException {
+ super(new SQLServerInjectorProvider(), JDBCAuthenticationProviderService.class);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return "sqlserver";
+ }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerAuthenticationProviderModule.java
new file mode 100644
index 000000000..ebb1a0678
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerAuthenticationProviderModule.java
@@ -0,0 +1,91 @@
+/*
+ * 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.guacamole.auth.sqlserver;
+
+import com.google.inject.Binder;
+import com.google.inject.Module;
+import com.google.inject.name.Names;
+import java.util.Properties;
+import org.apache.guacamole.GuacamoleException;
+import org.mybatis.guice.datasource.helper.JdbcHelper;
+
+/**
+ * Guice module which configures SQLServer-specific injections.
+ */
+public class SQLServerAuthenticationProviderModule implements Module {
+
+ /**
+ * MyBatis-specific configuration properties.
+ */
+ private final Properties myBatisProperties = new Properties();
+
+ /**
+ * SQLServer-specific driver configuration properties.
+ */
+ private final Properties driverProperties = new Properties();
+
+ /**
+ * Creates a new SQLServer authentication provider module that configures
+ * driver and MyBatis properties using the given environment.
+ *
+ * @param environment
+ * The environment to use when configuring MyBatis and the underlying
+ * JDBC driver.
+ *
+ * @throws GuacamoleException
+ * If a required property is missing, or an error occurs while parsing
+ * a property.
+ */
+ public SQLServerAuthenticationProviderModule(SQLServerEnvironment environment)
+ throws GuacamoleException {
+
+ // Set the SQLServer-specific properties for MyBatis.
+ myBatisProperties.setProperty("mybatis.environment.id", "guacamole");
+ myBatisProperties.setProperty("JDBC.host", environment.getSQLServerHostname());
+ myBatisProperties.setProperty("JDBC.port", String.valueOf(environment.getSQLServerPort()));
+ myBatisProperties.setProperty("JDBC.schema", environment.getSQLServerDatabase());
+ myBatisProperties.setProperty("JDBC.username", environment.getSQLServerUsername());
+ myBatisProperties.setProperty("JDBC.password", environment.getSQLServerPassword());
+ myBatisProperties.setProperty("JDBC.autoCommit", "false");
+ myBatisProperties.setProperty("mybatis.pooled.pingEnabled", "true");
+ myBatisProperties.setProperty("mybatis.pooled.pingQuery", "SELECT 1");
+
+ // Use UTF-8 in database
+ driverProperties.setProperty("characterEncoding", "UTF-8");
+
+ }
+
+ @Override
+ public void configure(Binder binder) {
+
+ // Bind SQLServer-specific properties
+ JdbcHelper.SQL_Server_2005_MS_Driver.configure(binder);
+
+ // Bind MyBatis properties
+ Names.bindProperties(binder, myBatisProperties);
+
+ // Bind JDBC driver properties
+ binder.bind(Properties.class)
+ .annotatedWith(Names.named("JDBC.driverProperties"))
+ .toInstance(driverProperties);
+
+ }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerEnvironment.java
new file mode 100644
index 000000000..67d882719
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerEnvironment.java
@@ -0,0 +1,306 @@
+/*
+ * 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.guacamole.auth.sqlserver;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
+
+/**
+ * A SQLServer-specific implementation of JDBCEnvironment provides database
+ * properties specifically for SQLServer.
+ */
+public class SQLServerEnvironment extends JDBCEnvironment {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(SQLServerEnvironment.class);
+
+ /**
+ * The default host to connect to, if SQLSERVER_HOSTNAME is not specified.
+ */
+ private static final String DEFAULT_HOSTNAME = "localhost";
+
+ /**
+ * The default port to connect to, if SQLSERVER_PORT is not specified.
+ */
+ private static final int DEFAULT_PORT = 1433;
+
+ /**
+ * Whether a database user account is required by default for authentication
+ * to succeed.
+ */
+ private static final boolean DEFAULT_USER_REQUIRED = true;
+
+ /**
+ * The default value for the maximum number of connections to be
+ * allowed to the Guacamole server overall.
+ */
+ private final int DEFAULT_ABSOLUTE_MAX_CONNECTIONS = 0;
+
+ /**
+ * The default value for the default maximum number of connections to be
+ * allowed per user to any one connection. Note that, as long as the
+ * legacy "disallow duplicate" and "disallow simultaneous" properties are
+ * still supported, these cannot be constants, as the legacy properties
+ * dictate the values that should be used in the absence of the correct
+ * properties.
+ */
+ private int DEFAULT_MAX_CONNECTIONS_PER_USER = 1;
+
+ /**
+ * The default value for the default maximum number of connections to be
+ * allowed per user to any one connection group. Note that, as long as the
+ * legacy "disallow duplicate" and "disallow simultaneous" properties are
+ * still supported, these cannot be constants, as the legacy properties
+ * dictate the values that should be used in the absence of the correct
+ * properties.
+ */
+ private int DEFAULT_MAX_GROUP_CONNECTIONS_PER_USER = 1;
+
+ /**
+ * The default value for the default maximum number of connections to be
+ * allowed to any one connection. Note that, as long as the legacy
+ * "disallow duplicate" and "disallow simultaneous" properties are still
+ * supported, these cannot be constants, as the legacy properties dictate
+ * the values that should be used in the absence of the correct properties.
+ */
+ private int DEFAULT_MAX_CONNECTIONS = 0;
+
+ /**
+ * The default value for the default maximum number of connections to be
+ * allowed to any one connection group. Note that, as long as the legacy
+ * "disallow duplicate" and "disallow simultaneous" properties are still
+ * supported, these cannot be constants, as the legacy properties dictate
+ * the values that should be used in the absence of the correct properties.
+ */
+ private int DEFAULT_MAX_GROUP_CONNECTIONS = 0;
+
+ /**
+ * Constructs a new SQLServerEnvironment, providing access to SQLServer-specific
+ * configuration options.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while setting up the underlying JDBCEnvironment
+ * or while parsing legacy SQLServer configuration options.
+ */
+ public SQLServerEnvironment() throws GuacamoleException {
+
+ // Init underlying JDBC environment
+ super();
+
+ // Read legacy concurrency-related property
+ Boolean disallowSimultaneous = getProperty(SQLServerGuacamoleProperties.SQLSERVER_DISALLOW_SIMULTANEOUS_CONNECTIONS);
+ Boolean disallowDuplicate = getProperty(SQLServerGuacamoleProperties.SQLSERVER_DISALLOW_DUPLICATE_CONNECTIONS);
+
+ // Legacy "simultaneous" property dictates only the maximum number of
+ // connections per connection
+ if (disallowSimultaneous != null) {
+
+ // Translate legacy property
+ if (disallowSimultaneous) {
+ DEFAULT_MAX_CONNECTIONS = 1;
+ DEFAULT_MAX_GROUP_CONNECTIONS = 0;
+ }
+ else {
+ DEFAULT_MAX_CONNECTIONS = 0;
+ DEFAULT_MAX_GROUP_CONNECTIONS = 0;
+ }
+
+ // Warn of deprecation
+ logger.warn("The \"{}\" property is deprecated. Use \"{}\" and \"{}\" instead.",
+ SQLServerGuacamoleProperties.SQLSERVER_DISALLOW_SIMULTANEOUS_CONNECTIONS.getName(),
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_CONNECTIONS.getName(),
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_GROUP_CONNECTIONS.getName());
+
+ // Inform of new equivalent
+ logger.info("To achieve the same result of setting \"{}\" to \"{}\", set \"{}\" to \"{}\" and \"{}\" to \"{}\".",
+ SQLServerGuacamoleProperties.SQLSERVER_DISALLOW_SIMULTANEOUS_CONNECTIONS.getName(), disallowSimultaneous,
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_CONNECTIONS.getName(), DEFAULT_MAX_CONNECTIONS,
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_GROUP_CONNECTIONS.getName(), DEFAULT_MAX_GROUP_CONNECTIONS);
+
+ }
+
+ // Legacy "duplicate" property dictates whether connections and groups
+ // may be used concurrently only by different users
+ if (disallowDuplicate != null) {
+
+ // Translate legacy property
+ if (disallowDuplicate) {
+ DEFAULT_MAX_CONNECTIONS_PER_USER = 1;
+ DEFAULT_MAX_GROUP_CONNECTIONS_PER_USER = 1;
+ }
+ else {
+ DEFAULT_MAX_CONNECTIONS_PER_USER = 0;
+ DEFAULT_MAX_GROUP_CONNECTIONS_PER_USER = 0;
+ }
+
+ // Warn of deprecation
+ logger.warn("The \"{}\" property is deprecated. Use \"{}\" and \"{}\" instead.",
+ SQLServerGuacamoleProperties.SQLSERVER_DISALLOW_DUPLICATE_CONNECTIONS.getName(),
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_CONNECTIONS_PER_USER.getName(),
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_GROUP_CONNECTIONS.getName());
+
+ // Inform of new equivalent
+ logger.info("To achieve the same result of setting \"{}\" to \"{}\", set \"{}\" to \"{}\" and \"{}\" to \"{}\".",
+ SQLServerGuacamoleProperties.SQLSERVER_DISALLOW_DUPLICATE_CONNECTIONS.getName(), disallowDuplicate,
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_CONNECTIONS_PER_USER.getName(), DEFAULT_MAX_CONNECTIONS_PER_USER,
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_GROUP_CONNECTIONS_PER_USER.getName(), DEFAULT_MAX_GROUP_CONNECTIONS_PER_USER);
+
+ }
+
+ }
+
+ @Override
+ public boolean isUserRequired() throws GuacamoleException {
+ return getProperty(
+ SQLServerGuacamoleProperties.SQLSERVER_USER_REQUIRED,
+ DEFAULT_USER_REQUIRED
+ );
+ }
+
+ @Override
+ public int getAbsoluteMaxConnections() throws GuacamoleException {
+ return getProperty(SQLServerGuacamoleProperties.SQLSERVER_ABSOLUTE_MAX_CONNECTIONS,
+ DEFAULT_ABSOLUTE_MAX_CONNECTIONS
+ );
+ }
+
+ @Override
+ public int getDefaultMaxConnections() throws GuacamoleException {
+ return getProperty(
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_CONNECTIONS,
+ DEFAULT_MAX_CONNECTIONS
+ );
+ }
+
+ @Override
+ public int getDefaultMaxGroupConnections() throws GuacamoleException {
+ return getProperty(
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_GROUP_CONNECTIONS,
+ DEFAULT_MAX_GROUP_CONNECTIONS
+ );
+ }
+
+ @Override
+ public int getDefaultMaxConnectionsPerUser() throws GuacamoleException {
+ return getProperty(
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_CONNECTIONS_PER_USER,
+ DEFAULT_MAX_CONNECTIONS_PER_USER
+ );
+ }
+
+ @Override
+ public int getDefaultMaxGroupConnectionsPerUser() throws GuacamoleException {
+ return getProperty(
+ SQLServerGuacamoleProperties.SQLSERVER_DEFAULT_MAX_GROUP_CONNECTIONS_PER_USER,
+ DEFAULT_MAX_GROUP_CONNECTIONS_PER_USER
+ );
+ }
+
+ @Override
+ public PasswordPolicy getPasswordPolicy() {
+ return new SQLServerPasswordPolicy(this);
+ }
+
+ /**
+ * Returns the hostname of the SQLServer server hosting the Guacamole
+ * authentication tables. If unspecified, this will be "localhost".
+ *
+ * @return
+ * The URL of the SQLServer server.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the property value.
+ */
+ public String getSQLServerHostname() throws GuacamoleException {
+ return getProperty(
+ SQLServerGuacamoleProperties.SQLSERVER_HOSTNAME,
+ DEFAULT_HOSTNAME
+ );
+ }
+
+ /**
+ * Returns the port number of the SQLServer server hosting the Guacamole
+ * authentication tables. If unspecified, this will be the default
+ * SQLServer port of 5432.
+ *
+ * @return
+ * The port number of the SQLServer server.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the property value.
+ */
+ public int getSQLServerPort() throws GuacamoleException {
+ return getProperty(
+ SQLServerGuacamoleProperties.SQLSERVER_PORT,
+ DEFAULT_PORT
+ );
+ }
+
+ /**
+ * Returns the name of the SQLServer database containing the Guacamole
+ * authentication tables.
+ *
+ * @return
+ * The name of the SQLServer database.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the property value, or if the
+ * value was not set, as this property is required.
+ */
+ public String getSQLServerDatabase() throws GuacamoleException {
+ return getRequiredProperty(SQLServerGuacamoleProperties.SQLSERVER_DATABASE);
+ }
+
+ /**
+ * Returns the username that should be used when authenticating with the
+ * SQLServer database containing the Guacamole authentication tables.
+ *
+ * @return
+ * The username for the SQLServer database.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the property value, or if the
+ * value was not set, as this property is required.
+ */
+ public String getSQLServerUsername() throws GuacamoleException {
+ return getRequiredProperty(SQLServerGuacamoleProperties.SQLSERVER_USERNAME);
+ }
+
+ /**
+ * Returns the password that should be used when authenticating with the
+ * SQLServer database containing the Guacamole authentication tables.
+ *
+ * @return
+ * The password for the SQLServer database.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the property value, or if the
+ * value was not set, as this property is required.
+ */
+ public String getSQLServerPassword() throws GuacamoleException {
+ return getRequiredProperty(SQLServerGuacamoleProperties.SQLSERVER_PASSWORD);
+ }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerGuacamoleProperties.java
new file mode 100644
index 000000000..e45f50268
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerGuacamoleProperties.java
@@ -0,0 +1,200 @@
+/*
+ * 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.guacamole.auth.sqlserver;
+
+import org.apache.guacamole.properties.BooleanGuacamoleProperty;
+import org.apache.guacamole.properties.IntegerGuacamoleProperty;
+import org.apache.guacamole.properties.StringGuacamoleProperty;
+
+/**
+ * Properties used by the SQLServer Authentication plugin.
+ */
+public class SQLServerGuacamoleProperties {
+
+ /**
+ * This class should not be instantiated.
+ */
+ private SQLServerGuacamoleProperties() {}
+
+ /**
+ * The URL of the SQLServer server hosting the Guacamole authentication tables.
+ */
+ public static final StringGuacamoleProperty SQLSERVER_HOSTNAME =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-hostname"; }
+
+ };
+
+ /**
+ * The port of the SQLServer server hosting the Guacamole authentication
+ * tables.
+ */
+ public static final IntegerGuacamoleProperty SQLSERVER_PORT =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-port"; }
+
+ };
+
+ /**
+ * The name of the SQLServer database containing the Guacamole
+ * authentication tables.
+ */
+ public static final StringGuacamoleProperty SQLSERVER_DATABASE =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-database"; }
+
+ };
+
+ /**
+ * The username used to authenticate to the SQLServer database containing
+ * the Guacamole authentication tables.
+ */
+ public static final StringGuacamoleProperty SQLSERVER_USERNAME =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-username"; }
+
+ };
+
+ /**
+ * The password used to authenticate to the SQLServer database containing
+ * the Guacamole authentication tables.
+ */
+ public static final StringGuacamoleProperty SQLSERVER_PASSWORD =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-password"; }
+
+ };
+
+ /**
+ * Whether a user account within the database is required for authentication
+ * to succeed, even if the user has been authenticated via another
+ * authentication provider.
+ */
+ public static final BooleanGuacamoleProperty
+ SQLSERVER_USER_REQUIRED = new BooleanGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-user-required"; }
+
+ };
+
+ /**
+ * Whether or not multiple users accessing the same connection at the same
+ * time should be disallowed.
+ */
+ public static final BooleanGuacamoleProperty
+ SQLSERVER_DISALLOW_SIMULTANEOUS_CONNECTIONS =
+ new BooleanGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-disallow-simultaneous-connections"; }
+
+ };
+
+ /**
+ * Whether or not the same user accessing the same connection or connection
+ * group at the same time should be disallowed.
+ */
+ public static final BooleanGuacamoleProperty
+ SQLSERVER_DISALLOW_DUPLICATE_CONNECTIONS =
+ new BooleanGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-disallow-duplicate-connections"; }
+
+ };
+
+ /**
+ * The maximum number of concurrent connections to allow overall. Zero
+ * denotes unlimited.
+ */
+ public static final IntegerGuacamoleProperty
+ SQLSERVER_ABSOLUTE_MAX_CONNECTIONS =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-absolute-max-connections"; }
+
+ };
+
+ /**
+ * The maximum number of concurrent connections to allow to any one
+ * connection. Zero denotes unlimited.
+ */
+ public static final IntegerGuacamoleProperty
+ SQLSERVER_DEFAULT_MAX_CONNECTIONS =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-default-max-connections"; }
+
+ };
+
+ /**
+ * The maximum number of concurrent connections to allow to any one
+ * connection group. Zero denotes unlimited.
+ */
+ public static final IntegerGuacamoleProperty
+ SQLSERVER_DEFAULT_MAX_GROUP_CONNECTIONS =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-default-max-group-connections"; }
+
+ };
+
+ /**
+ * The maximum number of concurrent connections to allow to any one
+ * connection by an individual user. Zero denotes unlimited.
+ */
+ public static final IntegerGuacamoleProperty
+ SQLSERVER_DEFAULT_MAX_CONNECTIONS_PER_USER =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-default-max-connections-per-user"; }
+
+ };
+
+ /**
+ * The maximum number of concurrent connections to allow to any one
+ * connection group by an individual user. Zero denotes
+ * unlimited.
+ */
+ public static final IntegerGuacamoleProperty
+ SQLSERVER_DEFAULT_MAX_GROUP_CONNECTIONS_PER_USER =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-default-max-group-connections-per-user"; }
+
+ };
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerInjectorProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerInjectorProvider.java
new file mode 100644
index 000000000..32d12f6e2
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerInjectorProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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.guacamole.auth.sqlserver;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.jdbc.JDBCAuthenticationProviderModule;
+import org.apache.guacamole.auth.jdbc.JDBCInjectorProvider;
+
+/**
+ * JDBCInjectorProvider implementation which configures Guice injections for
+ * connecting to a SQLServer database based on SQLServer-specific options
+ * provided via guacamole.properties.
+ */
+public class SQLServerInjectorProvider extends JDBCInjectorProvider {
+
+ @Override
+ protected Injector create() throws GuacamoleException {
+
+ // Get local environment
+ SQLServerEnvironment environment = new SQLServerEnvironment();
+
+ // Set up Guice injector
+ return Guice.createInjector(
+ new JDBCAuthenticationProviderModule(environment),
+ new SQLServerAuthenticationProviderModule(environment)
+ );
+
+ }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerPasswordPolicy.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerPasswordPolicy.java
new file mode 100644
index 000000000..f30b180bb
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerPasswordPolicy.java
@@ -0,0 +1,194 @@
+/*
+ * 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.guacamole.auth.sqlserver;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
+import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
+import org.apache.guacamole.properties.BooleanGuacamoleProperty;
+import org.apache.guacamole.properties.IntegerGuacamoleProperty;
+
+/**
+ * PasswordPolicy implementation which reads the details of the policy from
+ * SQLServer-specific properties in guacamole.properties.
+ */
+public class SQLServerPasswordPolicy implements PasswordPolicy {
+
+ /**
+ * The property which specifies the minimum length required of all user
+ * passwords. By default, this will be zero.
+ */
+ private static final IntegerGuacamoleProperty MIN_LENGTH =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-user-password-min-length"; }
+
+ };
+
+ /**
+ * The property which specifies the minimum number of days which must
+ * elapse before a user may reset their password. If set to zero, the
+ * default, then this restriction does not apply.
+ */
+ private static final IntegerGuacamoleProperty MIN_AGE =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-user-password-min-age"; }
+
+ };
+
+ /**
+ * The property which specifies the maximum number of days which may
+ * elapse before a user is required to reset their password. If set to zero,
+ * the default, then this restriction does not apply.
+ */
+ private static final IntegerGuacamoleProperty MAX_AGE =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-user-password-max-age"; }
+
+ };
+
+ /**
+ * The property which specifies the number of previous passwords remembered
+ * for each user. If set to zero, the default, then this restriction does
+ * not apply.
+ */
+ private static final IntegerGuacamoleProperty HISTORY_SIZE =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-user-password-history-size"; }
+
+ };
+
+ /**
+ * The property which specifies whether all user passwords must have at
+ * least one lowercase character and one uppercase character. By default,
+ * no such restriction is imposed.
+ */
+ private static final BooleanGuacamoleProperty REQUIRE_MULTIPLE_CASE =
+ new BooleanGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-user-password-require-multiple-case"; }
+
+ };
+
+ /**
+ * The property which specifies whether all user passwords must have at
+ * least one numeric character (digit). By default, no such restriction is
+ * imposed.
+ */
+ private static final BooleanGuacamoleProperty REQUIRE_DIGIT =
+ new BooleanGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-user-password-require-digit"; }
+
+ };
+
+ /**
+ * The property which specifies whether all user passwords must have at
+ * least one non-alphanumeric character (symbol). By default, no such
+ * restriction is imposed.
+ */
+ private static final BooleanGuacamoleProperty REQUIRE_SYMBOL =
+ new BooleanGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-user-password-require-symbol"; }
+
+ };
+
+ /**
+ * The property which specifies whether users are prohibited from including
+ * their own username in their password. By default, no such restriction is
+ * imposed.
+ */
+ private static final BooleanGuacamoleProperty PROHIBIT_USERNAME =
+ new BooleanGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "sqlserver-user-password-prohibit-username"; }
+
+ };
+
+ /**
+ * The Guacamole server environment.
+ */
+ private final JDBCEnvironment environment;
+
+ /**
+ * Creates a new SQLServerPasswordPolicy which reads the details of the
+ * policy from the properties exposed by the given environment.
+ *
+ * @param environment
+ * The environment from which password policy properties should be
+ * read.
+ */
+ public SQLServerPasswordPolicy(JDBCEnvironment environment) {
+ this.environment = environment;
+ }
+
+ @Override
+ public int getMinimumLength() throws GuacamoleException {
+ return environment.getProperty(MIN_LENGTH, 0);
+ }
+
+ @Override
+ public int getMinimumAge() throws GuacamoleException {
+ return environment.getProperty(MIN_AGE, 0);
+ }
+
+ @Override
+ public int getMaximumAge() throws GuacamoleException {
+ return environment.getProperty(MAX_AGE, 0);
+ }
+
+ @Override
+ public int getHistorySize() throws GuacamoleException {
+ return environment.getProperty(HISTORY_SIZE, 0);
+ }
+
+ @Override
+ public boolean isMultipleCaseRequired() throws GuacamoleException {
+ return environment.getProperty(REQUIRE_MULTIPLE_CASE, false);
+ }
+
+ @Override
+ public boolean isNumericRequired() throws GuacamoleException {
+ return environment.getProperty(REQUIRE_DIGIT, false);
+ }
+
+ @Override
+ public boolean isNonAlphanumericRequired() throws GuacamoleException {
+ return environment.getProperty(REQUIRE_SYMBOL, false);
+ }
+
+ @Override
+ public boolean isUsernameProhibited() throws GuacamoleException {
+ return environment.getProperty(PROHIBIT_USERNAME, false);
+ }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerSharedAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerSharedAuthenticationProvider.java
new file mode 100644
index 000000000..0a3c8d31f
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerSharedAuthenticationProvider.java
@@ -0,0 +1,50 @@
+/*
+ * 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.guacamole.auth.sqlserver;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.jdbc.InjectedAuthenticationProvider;
+import org.apache.guacamole.auth.jdbc.sharing.SharedAuthenticationProviderService;
+
+/**
+ * Provides a implementation of AuthenticationProvider which interacts with the
+ * SQLServer AuthenticationProvider, accepting share keys as credentials and
+ * providing access to the shared connections.
+ */
+public class SQLServerSharedAuthenticationProvider extends InjectedAuthenticationProvider {
+
+ /**
+ * Creates a new SQLServerSharedAuthenticationProvider that provides access
+ * to shared connections exposed by the SQLServerAuthenticationProvider.
+ *
+ * @throws GuacamoleException
+ * If a required property is missing, or an error occurs while parsing
+ * a property.
+ */
+ public SQLServerSharedAuthenticationProvider() throws GuacamoleException {
+ super(new SQLServerInjectorProvider(), SharedAuthenticationProviderService.class);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return "sqlserver-shared";
+ }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/package-info.java
new file mode 100644
index 000000000..7bbe1b2e3
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * The SQLServer authentication provider.
+ */
+package org.apache.guacamole.auth.sqlserver;
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/guac-manifest.json
new file mode 100644
index 000000000..ee61ab578
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/guac-manifest.json
@@ -0,0 +1,28 @@
+{
+
+ "guacamoleVersion" : "0.9.13-incubating",
+
+ "name" : "SQLServer Authentication",
+ "namespace" : "guac-sqlserver",
+
+ "authProviders" : [
+ "org.apache.guacamole.auth.sqlserver.SQLServerAuthenticationProvider",
+ "org.apache.guacamole.auth.sqlserver.SQLServerSharedAuthenticationProvider"
+ ],
+
+ "css" : [
+ "styles/jdbc.css"
+ ],
+
+ "html" : [
+ "html/shared-connection.html"
+ ],
+
+ "translations" : [
+ "translations/en.json",
+ "translations/fr.json",
+ "translations/ru.json"
+ ]
+
+}
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml
new file mode 100644
index 000000000..24008fcc4
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml
@@ -0,0 +1,235 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[connection]
+ WHERE connection_id = #{identifier,jdbcType=INTEGER}
+
+
+
+
+
+ INSERT INTO [guacamole].[connection] (
+ connection_name,
+ parent_id,
+ protocol,
+ max_connections,
+ max_connections_per_user,
+ proxy_hostname,
+ proxy_port,
+ proxy_encryption_method,
+ connection_weight,
+ failover_only
+ )
+ VALUES (
+ #{object.name,jdbcType=VARCHAR},
+ #{object.parentIdentifier,jdbcType=INTEGER},
+ #{object.protocol,jdbcType=VARCHAR},
+ #{object.maxConnections,jdbcType=INTEGER},
+ #{object.maxConnectionsPerUser,jdbcType=INTEGER},
+ #{object.proxyHostname,jdbcType=VARCHAR},
+ #{object.proxyPort,jdbcType=INTEGER},
+ #{object.proxyEncryptionMethod,jdbcType=VARCHAR},
+ #{object.connectionWeight,jdbcType=INTEGER},
+ #{object.failoverOnly,jdbcType=INTEGER}
+ )
+
+
+
+
+
+ UPDATE [guacamole].[connection]
+ SET connection_name = #{object.name,jdbcType=VARCHAR},
+ parent_id = #{object.parentIdentifier,jdbcType=INTEGER},
+ protocol = #{object.protocol,jdbcType=VARCHAR},
+ max_connections = #{object.maxConnections,jdbcType=INTEGER},
+ max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER},
+ proxy_hostname = #{object.proxyHostname,jdbcType=VARCHAR},
+ proxy_port = #{object.proxyPort,jdbcType=INTEGER},
+ proxy_encryption_method = #{object.proxyEncryptionMethod,jdbcType=VARCHAR},
+ connection_weight = #{object.connectionWeight,jdbcType=INTEGER},
+ failover_only = #{object.failoverOnly,jdbcType=INTEGER}
+ WHERE connection_id = #{object.objectID,jdbcType=INTEGER}
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionParameterMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionParameterMapper.xml
new file mode 100644
index 000000000..de1ab97c1
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionParameterMapper.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[connection_parameter]
+ WHERE connection_id = #{identifier,jdbcType=INTEGER}
+
+
+
+
+
+ INSERT INTO [guacamole].[connection_parameter] (
+ connection_id,
+ parameter_name,
+ parameter_value
+ )
+ VALUES
+
+ (#{parameter.connectionIdentifier,jdbcType=INTEGER},
+ #{parameter.name,jdbcType=VARCHAR},
+ #{parameter.value,jdbcType=VARCHAR})
+
+
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml
new file mode 100644
index 000000000..ec077db7f
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO [guacamole].[connection_history] (
+ connection_id,
+ connection_name,
+ remote_host,
+ sharing_profile_id,
+ sharing_profile_name,
+ user_id,
+ username,
+ start_date,
+ end_date
+ )
+ VALUES (
+ #{record.connectionIdentifier,jdbcType=INTEGER},
+ #{record.connectionName,jdbcType=VARCHAR},
+ #{record.remoteHost,jdbcType=VARCHAR},
+ #{record.sharingProfileIdentifier,jdbcType=INTEGER},
+ #{record.sharingProfileName,jdbcType=VARCHAR},
+ (SELECT user_id FROM [guacamole].[user]
+ WHERE username = #{record.username,jdbcType=VARCHAR}),
+ #{record.username,jdbcType=VARCHAR},
+ #{record.startDate,jdbcType=TIMESTAMP},
+ #{record.endDate,jdbcType=TIMESTAMP}
+ )
+
+
+
+
+
+
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml
new file mode 100644
index 000000000..47a3e63d5
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[connection_group]
+ WHERE connection_group_id = #{identifier,jdbcType=INTEGER}
+
+
+
+
+
+ INSERT INTO [guacamole].[connection_group] (
+ connection_group_name,
+ parent_id,
+ type,
+ max_connections,
+ max_connections_per_user,
+ enable_session_affinity
+ )
+ VALUES (
+ #{object.name,jdbcType=VARCHAR},
+ #{object.parentIdentifier,jdbcType=INTEGER},
+ #{object.type,jdbcType=VARCHAR},
+ #{object.maxConnections,jdbcType=INTEGER},
+ #{object.maxConnectionsPerUser,jdbcType=INTEGER},
+ #{object.sessionAffinityEnabled,jdbcType=INTEGER}
+ )
+
+
+
+
+
+ UPDATE [guacamole].[connection_group]
+ SET connection_group_name = #{object.name,jdbcType=VARCHAR},
+ parent_id = #{object.parentIdentifier,jdbcType=INTEGER},
+ type = #{object.type,jdbcType=VARCHAR},
+ max_connections = #{object.maxConnections,jdbcType=INTEGER},
+ max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER},
+ enable_session_affinity = #{object.sessionAffinityEnabled,jdbcType=INTEGER}
+ WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER}
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.xml
new file mode 100644
index 000000000..2890ab31d
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[connection_group_permission]
+ WHERE (user_id, permission, connection_group_id) IN
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR},
+ #{permission.objectIdentifier,jdbcType=INTEGER})
+
+
+
+
+
+
+
+ INSERT INTO [guacamole].[connection_group_permission] (
+ user_id,
+ permission,
+ connection_group_id
+ )
+ SELECT DISTINCT
+ permissions.user_id,
+ permissions.permission,
+ permissions.connection_group_id
+ FROM
+
+ SELECT #{permission.userID,jdbcType=INTEGER} AS user_id,
+ #{permission.type,jdbcType=VARCHAR} AS permission,
+ #{permission.objectIdentifier,jdbcType=INTEGER} AS connection_group_id
+
+ AS permissions
+ WHERE (user_id, permission, connection_group_id) NOT IN (
+ SELECT
+ [guacamole].[connection_group_permission].user_id,
+ [guacamole].[connection_group_permission].permission,
+ [guacamole].[connection_group_permission].connection_group_id
+ FROM [guacamole].[connection_group_permission]
+ );
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.xml
new file mode 100644
index 000000000..8ea85bc3c
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[connection_permission]
+ WHERE (user_id, permission, connection_id) IN
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR},
+ #{permission.objectIdentifier,jdbcType=INTEGER})
+
+
+
+
+
+
+
+ INSERT INTO [guacamole].[connection_permission] (
+ user_id,
+ permission,
+ connection_id
+ )
+ SELECT DISTINCT
+ permissions.user_id,
+ permissions.permission,
+ permissions.connection_id
+ FROM
+
+ SELECT #{permission.userID,jdbcType=INTEGER} AS user_id,
+ #{permission.type,jdbcType=VARCHAR} AS permission,
+ #{permission.objectIdentifier,jdbcType=INTEGER} AS connection_id
+
+ AS permissions
+ WHERE (user_id, permission, connection_id) NOT IN (
+ SELECT
+ [guacamole].[connection_permission].user_id,
+ [guacamole].[connection_permission].permission,
+ [guacamole].[connection_permission].connection_id
+ FROM [guacamole].[connection_permission]
+ );
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/SharingProfilePermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/SharingProfilePermissionMapper.xml
new file mode 100644
index 000000000..cb706b8d4
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/SharingProfilePermissionMapper.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[sharing_profile_permission]
+ WHERE (user_id, permission, sharing_profile_id) IN
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR},
+ #{permission.objectIdentifier,jdbcType=INTEGER})
+
+
+
+
+
+
+
+ INSERT INTO [guacamole].[sharing_profile_permission] (
+ user_id,
+ permission,
+ sharing_profile_id
+ )
+ SELECT DISTINCT
+ permissions.user_id,
+ permissions.permission,
+ permissions.sharing_profile_id
+ FROM
+
+ SELECT #{permission.userID,jdbcType=INTEGER} AS user_id,
+ #{permission.type,jdbcType=VARCHAR} AS permission,
+ #{permission.objectIdentifier,jdbcType=INTEGER} AS sharing_profile_id
+
+ AS permissions
+ WHERE (user_id, permission, sharing_profile_id) NOT IN (
+ SELECT
+ [guacamole].[sharing_profile_permission].user_id,
+ [guacamole].[sharing_profile_permission].permission,
+ [guacamole].[sharing_profile_permission].sharing_profile_id
+ FROM [guacamole].[sharing_profile_permission]
+ );
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/SystemPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/SystemPermissionMapper.xml
new file mode 100644
index 000000000..d9e622b69
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/SystemPermissionMapper.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[system_permission]
+ WHERE (user_id, permission) IN
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR})
+
+
+
+
+
+
+
+ INSERT INTO [guacamole].[system_permission] (
+ user_id,
+ permission
+ )
+ SELECT DISTINCT
+ permissions.user_id,
+ permissions.permission
+ FROM
+
+ SELECT #{permission.userID,jdbcType=INTEGER} AS user_id,
+ #{permission.type,jdbcType=VARCHAR} AS permission
+
+ AS permissions
+ WHERE (user_id, permission) NOT IN (
+ SELECT
+ [guacamole].[system_permission].user_id,
+ [guacamole].[system_permission].permission
+ FROM [guacamole].[system_permission]
+ );
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml
new file mode 100644
index 000000000..595c3263d
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[user_permission]
+ USING [guacamole].[user] affected
+ WHERE
+ [guacamole].[user_permission].affected_user_id = affected.user_id
+ AND ([guacamole].[user_permission].user_id, permission, affected.username) IN
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR},
+ #{permission.objectIdentifier,jdbcType=INTEGER})
+
+
+
+
+
+
+
+ INSERT INTO [guacamole].[user_permission] (
+ user_id,
+ permission,
+ affected_user_id
+ )
+ SELECT DISTINCT
+ permissions.user_id,
+ permissions.permission,
+ [guacamole].[user].user_id
+ FROM
+
+ SELECT #{permission.userID,jdbcType=INTEGER} AS user_id,
+ #{permission.type,jdbcType=VARCHAR} AS permission,
+ #{permission.objectIdentifier,jdbcType=INTEGER} AS username
+
+ AS permissions
+ JOIN [guacamole].[user] ON [guacamole].[user].username = permissions.username
+ WHERE (permissions.user_id, permissions.permission, [guacamole].[user].user_id) NOT IN (
+ SELECT
+ [guacamole].[user_permission].user_id,
+ [guacamole].[user_permission].permission,
+ [guacamole].[user_permission].affected_user_id
+ FROM [guacamole].[user_permission]
+ );
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileMapper.xml
new file mode 100644
index 000000000..9d7d45abd
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileMapper.xml
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[sharing_profile]
+ WHERE sharing_profile_id = #{identifier,jdbcType=INTEGER}
+
+
+
+
+
+ INSERT INTO [guacamole].[sharing_profile] (
+ sharing_profile_name,
+ primary_connection_id
+ )
+ VALUES (
+ #{object.name,jdbcType=VARCHAR},
+ #{object.parentIdentifier,jdbcType=INTEGER}
+ )
+
+
+
+
+
+ UPDATE [guacamole].[sharing_profile]
+ SET sharing_profile_name = #{object.name,jdbcType=VARCHAR},
+ primary_connection_id = #{object.parentIdentifier,jdbcType=INTEGER}
+ WHERE sharing_profile_id = #{object.objectID,jdbcType=INTEGER}
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileParameterMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileParameterMapper.xml
new file mode 100644
index 000000000..8835350b7
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileParameterMapper.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[sharing_profile_parameter]
+ WHERE sharing_profile_id = #{identifier,jdbcType=INTEGER}
+
+
+
+
+
+ INSERT INTO [guacamole].[sharing_profile_parameter] (
+ sharing_profile_id,
+ parameter_name,
+ parameter_value
+ )
+ VALUES
+
+ (#{parameter.sharingProfileIdentifier,jdbcType=INTEGER}
+ #{parameter.name,jdbcType=VARCHAR},
+ #{parameter.value,jdbcType=VARCHAR})
+
+
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
new file mode 100644
index 000000000..9ad67a6f3
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO [guacamole].[user_password_history] (
+ user_id,
+ password_hash,
+ password_salt,
+ password_date
+ )
+ VALUES (
+ #{record.userID,jdbcType=INTEGER},
+ #{record.passwordHash,jdbcType=BINARY},
+ #{record.passwordSalt,jdbcType=BINARY},
+ #{record.passwordDate,jdbcType=TIMESTAMP}
+ );
+
+ DELETE FROM [guacamole].[user_password_history]
+ WHERE password_history_id IN (
+ SELECT password_history_id
+ FROM [guacamole].[user_password_history]
+ WHERE user_id = #{record.userID,jdbcType=INTEGER}
+ ORDER BY password_date DESC
+ OFFSET #{maxHistorySize}
+ );
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml
new file mode 100644
index 000000000..a4ceea79e
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM [guacamole].[user]
+ WHERE username = #{identifier,jdbcType=VARCHAR}
+
+
+
+
+
+ INSERT INTO [guacamole].[user] (
+ username,
+ password_hash,
+ password_salt,
+ password_date,
+ disabled,
+ expired,
+ access_window_start,
+ access_window_end,
+ valid_from,
+ valid_until,
+ timezone,
+ full_name,
+ email_address,
+ organization,
+ organizational_role
+ )
+ VALUES (
+ #{object.identifier,jdbcType=VARCHAR},
+ #{object.passwordHash,jdbcType=BINARY},
+ #{object.passwordSalt,jdbcType=BINARY},
+ #{object.passwordDate,jdbcType=TIMESTAMP},
+ #{object.disabled,jdbcType=INTEGER},
+ #{object.expired,jdbcType=INTEGER},
+ #{object.accessWindowStart,jdbcType=TIME},
+ #{object.accessWindowEnd,jdbcType=TIME},
+ #{object.validFrom,jdbcType=DATE},
+ #{object.validUntil,jdbcType=DATE},
+ #{object.timeZone,jdbcType=VARCHAR},
+ #{object.fullName,jdbcType=VARCHAR},
+ #{object.emailAddress,jdbcType=VARCHAR},
+ #{object.organization,jdbcType=VARCHAR},
+ #{object.organizationalRole,jdbcType=VARCHAR}
+ )
+
+
+
+
+
+ UPDATE [guacamole].[user]
+ SET password_hash = #{object.passwordHash,jdbcType=BINARY},
+ password_salt = #{object.passwordSalt,jdbcType=BINARY},
+ password_date = #{object.passwordDate,jdbcType=TIMESTAMP},
+ disabled = #{object.disabled,jdbcType=INTEGER},
+ expired = #{object.expired,jdbcType=INTEGER},
+ access_window_start = #{object.accessWindowStart,jdbcType=TIME},
+ access_window_end = #{object.accessWindowEnd,jdbcType=TIME},
+ valid_from = #{object.validFrom,jdbcType=DATE},
+ valid_until = #{object.validUntil,jdbcType=DATE},
+ timezone = #{object.timeZone,jdbcType=VARCHAR},
+ full_name = #{object.fullName,jdbcType=VARCHAR},
+ email_address = #{object.emailAddress,jdbcType=VARCHAR},
+ organization = #{object.organization,jdbcType=VARCHAR},
+ organizational_role = #{object.organizationalRole,jdbcType=VARCHAR}
+ WHERE user_id = #{object.objectID,jdbcType=VARCHAR}
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/pom.xml b/extensions/guacamole-auth-jdbc/pom.xml
index 2a5ef5b2b..7869c8650 100644
--- a/extensions/guacamole-auth-jdbc/pom.xml
+++ b/extensions/guacamole-auth-jdbc/pom.xml
@@ -70,6 +70,7 @@
modules/guacamole-auth-jdbc-mysqlmodules/guacamole-auth-jdbc-postgresql
+ modules/guacamole-auth-jdbc-sqlserver