You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by mj...@apache.org on 2017/10/01 02:10:14 UTC

[02/28] incubator-guacamole-client git commit: GUACAMOLE-363: Initial commit of SQLServer authentication module for JDBC.

GUACAMOLE-363: Initial commit of SQLServer authentication module for JDBC.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/b6e88d33
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/b6e88d33
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/b6e88d33

Branch: refs/heads/master
Commit: b6e88d33b3967b42020aa8dbc8429f41f2a0afb8
Parents: 81ffa5c
Author: Nick Couchman <vn...@apache.org>
Authored: Mon Aug 14 22:14:15 2017 -0400
Committer: Nick Couchman <vn...@apache.org>
Committed: Thu Sep 28 07:00:51 2017 -0400

----------------------------------------------------------------------
 .../guacamole-auth-jdbc-sqlserver/.gitignore    |   2 +
 .../guacamole-auth-jdbc-sqlserver/pom.xml       | 128 ++++++++
 .../schema/001-create-schema.sql                | Bin 0 -> 35118 bytes
 .../schema/002-create-admin-user.sql            |  43 +++
 .../SQLServerAuthenticationProvider.java        |  50 +++
 .../SQLServerAuthenticationProviderModule.java  |  91 ++++++
 .../auth/sqlserver/SQLServerEnvironment.java    | 306 +++++++++++++++++++
 .../sqlserver/SQLServerGuacamoleProperties.java | 200 ++++++++++++
 .../sqlserver/SQLServerInjectorProvider.java    |  49 +++
 .../auth/sqlserver/SQLServerPasswordPolicy.java | 194 ++++++++++++
 .../SQLServerSharedAuthenticationProvider.java  |  50 +++
 .../guacamole/auth/sqlserver/package-info.java  |  23 ++
 .../src/main/resources/guac-manifest.json       |  28 ++
 .../auth/jdbc/connection/ConnectionMapper.xml   | 235 ++++++++++++++
 .../connection/ConnectionParameterMapper.xml    |  68 +++++
 .../jdbc/connection/ConnectionRecordMapper.xml  | 216 +++++++++++++
 .../connectiongroup/ConnectionGroupMapper.xml   | 232 ++++++++++++++
 .../ConnectionGroupPermissionMapper.xml         | 130 ++++++++
 .../permission/ConnectionPermissionMapper.xml   | 130 ++++++++
 .../SharingProfilePermissionMapper.xml          | 130 ++++++++
 .../jdbc/permission/SystemPermissionMapper.xml  | 101 ++++++
 .../jdbc/permission/UserPermissionMapper.xml    | 137 +++++++++
 .../sharingprofile/SharingProfileMapper.xml     | 126 ++++++++
 .../SharingProfileParameterMapper.xml           |  68 +++++
 .../auth/jdbc/user/PasswordRecordMapper.xml     |  79 +++++
 .../guacamole/auth/jdbc/user/UserMapper.xml     | 216 +++++++++++++
 extensions/guacamole-auth-jdbc/pom.xml          |   1 +
 27 files changed, 3033 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/.gitignore
----------------------------------------------------------------------
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 0000000..42f4a1a
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/.gitignore
@@ -0,0 +1,2 @@
+target/
+*~

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/pom.xml
----------------------------------------------------------------------
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 0000000..82776f7
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/pom.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                        http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.guacamole</groupId>
+    <artifactId>guacamole-auth-jdbc-sqlserver</artifactId>
+    <packaging>jar</packaging>
+    <name>guacamole-auth-jdbc-sqlserver</name>
+    <url>http://guacamole.incubator.apache.org/</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <parent>
+        <groupId>org.apache.guacamole</groupId>
+        <artifactId>guacamole-auth-jdbc</artifactId>
+        <version>0.9.13-incubating</version>
+        <relativePath>../../</relativePath>
+    </parent>
+
+    <build>
+        <plugins>
+
+            <!-- Written for 1.6 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.3</version>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                    <compilerArgs>
+                        <arg>-Xlint:all</arg>
+                        <arg>-Werror</arg>
+                    </compilerArgs>
+                    <fork>true</fork>
+                </configuration>
+            </plugin>
+
+            <!-- Copy dependencies prior to packaging -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>2.10</version>
+                <executions>
+                    <execution>
+                        <id>unpack-dependencies</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>unpack-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <includeScope>runtime</includeScope>
+                            <outputDirectory>${project.build.directory}/classes</outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- Verify format using Apache RAT -->
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <version>0.12</version>
+
+                <configuration>
+                    <excludes>
+                        <exclude>**/*.json</exclude>
+                    </excludes>
+                </configuration>
+
+                <!-- Bind RAT to validate phase -->
+                <executions>
+                    <execution>
+                        <id>validate</id>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+
+            </plugin>
+
+        </plugins>
+    </build>
+
+    <dependencies>
+
+        <!-- Guacamole Extension API -->
+        <dependency>
+            <groupId>org.apache.guacamole</groupId>
+            <artifactId>guacamole-ext</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Guacamole JDBC Authentication -->
+        <dependency>
+            <groupId>org.apache.guacamole</groupId>
+            <artifactId>guacamole-auth-jdbc-base</artifactId>
+            <version>0.9.13-incubating</version>
+        </dependency>
+
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/001-create-schema.sql
----------------------------------------------------------------------
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 0000000..df95800
Binary files /dev/null and b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/001-create-schema.sql differ

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/002-create-admin-user.sql
----------------------------------------------------------------------
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 0000000..08cce3f
--- /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;

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerAuthenticationProvider.java
----------------------------------------------------------------------
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 0000000..ef5d61d
--- /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";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerAuthenticationProviderModule.java
----------------------------------------------------------------------
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 0000000..ebb1a06
--- /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);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerEnvironment.java
----------------------------------------------------------------------
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 0000000..67d8827
--- /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);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerGuacamoleProperties.java
----------------------------------------------------------------------
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 0000000..e45f502
--- /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"; }
+
+    };
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerInjectorProvider.java
----------------------------------------------------------------------
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 0000000..32d12f6
--- /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)
+        );
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerPasswordPolicy.java
----------------------------------------------------------------------
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 0000000..f30b180
--- /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);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerSharedAuthenticationProvider.java
----------------------------------------------------------------------
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 0000000..0a3c8d3
--- /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";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/package-info.java
----------------------------------------------------------------------
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 0000000..7bbe1b2
--- /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;

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/guac-manifest.json
----------------------------------------------------------------------
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 0000000..ee61ab5
--- /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"
+    ]
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml
----------------------------------------------------------------------
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 0000000..24008fc
--- /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 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+
+<!--
+    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.
+-->
+
+<mapper namespace="org.apache.guacamole.auth.jdbc.connection.ConnectionMapper" >
+
+    <!-- Result mapper for connection objects -->
+    <resultMap id="ConnectionResultMap" type="org.apache.guacamole.auth.jdbc.connection.ConnectionModel" >
+
+        <!-- Connection properties -->
+        <id     column="connection_id"            property="objectID"              jdbcType="INTEGER"/>
+        <result column="connection_name"          property="name"                  jdbcType="VARCHAR"/>
+        <result column="parent_id"                property="parentIdentifier"      jdbcType="INTEGER"/>
+        <result column="protocol"                 property="protocol"              jdbcType="VARCHAR"/>
+        <result column="max_connections"          property="maxConnections"        jdbcType="INTEGER"/>
+        <result column="max_connections_per_user" property="maxConnectionsPerUser" jdbcType="INTEGER"/>
+        <result column="proxy_hostname"           property="proxyHostname"         jdbcType="VARCHAR"/>
+        <result column="proxy_port"               property="proxyPort"             jdbcType="INTEGER"/>
+        <result column="proxy_encryption_method"  property="proxyEncryptionMethod" jdbcType="VARCHAR"
+                javaType="org.apache.guacamole.net.auth.GuacamoleProxyConfiguration$EncryptionMethod"/>
+        <result column="connection_weight"        property="connectionWeight"      jdbcType="INTEGER"/>
+        <result column="failover_only"            property="failoverOnly"          jdbcType="BOOLEAN"/>
+
+        <!-- Associated sharing profiles -->
+        <collection property="sharingProfileIdentifiers" resultSet="sharingProfiles" ofType="java.lang.String"
+                    column="connection_id" foreignColumn="primary_connection_id">
+            <result column="sharing_profile_id"/>
+        </collection>
+
+    </resultMap>
+
+    <!-- Select all connection identifiers -->
+    <select id="selectIdentifiers" resultType="string">
+        SELECT connection_id 
+        FROM [guacamole].[connection]
+    </select>
+
+    <!-- Select identifiers of all readable connections -->
+    <select id="selectReadableIdentifiers" resultType="string">
+        SELECT connection_id
+        FROM [guacamole].[connection_permission]
+        WHERE
+            user_id = #{user.objectID,jdbcType=INTEGER}
+            AND permission = 'READ'
+    </select>
+
+    <!-- Select all connection identifiers within a particular connection group -->
+    <select id="selectIdentifiersWithin" resultType="string">
+        SELECT connection_id 
+        FROM [guacamole].[connection]
+        WHERE
+            <if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}</if>
+            <if test="parentIdentifier == null">parent_id IS NULL</if>
+    </select>
+
+    <!-- Select identifiers of all readable connections within a particular connection group -->
+    <select id="selectReadableIdentifiersWithin" resultType="string">
+        SELECT [guacamole].[connection].connection_id
+        FROM [guacamole].[connection]
+        JOIN [guacamole].[connection_permission] ON [guacamole].[connection_permission].connection_id = [guacamole].[connection].connection_id
+        WHERE
+            <if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}</if>
+            <if test="parentIdentifier == null">parent_id IS NULL</if>
+            AND user_id = #{user.objectID,jdbcType=INTEGER}
+            AND permission = 'READ'
+    </select>
+
+    <!-- Select multiple connections by identifier -->
+    <select id="select" resultMap="ConnectionResultMap"
+            resultSets="connections,sharingProfiles">
+
+        SELECT
+            connection_id,
+            connection_name,
+            parent_id,
+            protocol,
+            max_connections,
+            max_connections_per_user,
+            proxy_hostname,
+            proxy_port,
+            proxy_encryption_method,
+            connection_weight,
+            failover_only
+        FROM [guacamole].[connection]
+        WHERE connection_id IN
+            <foreach collection="identifiers" item="identifier"
+                     open="(" separator="," close=")">
+                #{identifier,jdbcType=INTEGER}
+            </foreach>;
+
+        SELECT primary_connection_id, sharing_profile_id
+        FROM [guacamole].[sharing_profile]
+        WHERE primary_connection_id IN
+            <foreach collection="identifiers" item="identifier"
+                     open="(" separator="," close=")">
+                #{identifier,jdbcType=INTEGER}
+            </foreach>;
+
+    </select>
+
+    <!-- Select multiple connections by identifier only if readable -->
+    <select id="selectReadable" resultMap="ConnectionResultMap"
+            resultSets="connections,sharingProfiles">
+
+        SELECT
+            [guacamole].[connection].connection_id,
+            connection_name,
+            parent_id,
+            protocol,
+            max_connections,
+            max_connections_per_user,
+            proxy_hostname,
+            proxy_port,
+            proxy_encryption_method,
+            connection_weight,
+            failover_only
+        FROM [guacamole].[connection]
+        JOIN [guacamole].[connection_permission] ON [guacamole].[connection_permission].connection_id = [guacamole].[connection].connection_id
+        WHERE [guacamole].[connection].connection_id IN
+            <foreach collection="identifiers" item="identifier"
+                     open="(" separator="," close=")">
+                #{identifier,jdbcType=INTEGER}
+            </foreach>
+            AND user_id = #{user.objectID,jdbcType=INTEGER}
+            AND permission = 'READ';
+
+        SELECT primary_connection_id, [guacamole].[sharing_profile].sharing_profile_id
+        FROM [guacamole].[sharing_profile]
+        JOIN [guacamole].[sharing_profile_permission] ON [guacamole].[sharing_profile_permission].sharing_profile_id = [guacamole].[sharing_profile].sharing_profile_id
+        WHERE primary_connection_id IN
+            <foreach collection="identifiers" item="identifier"
+                     open="(" separator="," close=")">
+                #{identifier,jdbcType=INTEGER}
+            </foreach>
+            AND user_id = #{user.objectID,jdbcType=INTEGER}
+            AND permission = 'READ';
+
+    </select>
+
+    <!-- Select single connection by name -->
+    <select id="selectOneByName" resultMap="ConnectionResultMap">
+
+        SELECT
+            connection_id,
+            connection_name,
+            parent_id,
+            protocol,
+            max_connections,
+            max_connections_per_user,
+            proxy_hostname,
+            proxy_port,
+            proxy_encryption_method,
+            connection_weight,
+            failover_only
+        FROM [guacamole].[connection]
+        WHERE 
+            <if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}</if>
+            <if test="parentIdentifier == null">parent_id IS NULL</if>
+            AND connection_name = #{name,jdbcType=VARCHAR}
+
+    </select>
+
+    <!-- Delete single connection by identifier -->
+    <delete id="delete">
+        DELETE FROM [guacamole].[connection]
+        WHERE connection_id = #{identifier,jdbcType=INTEGER}
+    </delete>
+
+    <!-- Insert single connection -->
+    <insert id="insert" useGeneratedKeys="true" keyProperty="object.objectID"
+            parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionModel">
+
+        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}
+        )
+
+    </insert>
+
+    <!-- Update single connection -->
+    <update id="update" parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionModel">
+        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}
+    </update>
+
+</mapper>

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionParameterMapper.xml
----------------------------------------------------------------------
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 0000000..de1ab97
--- /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 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+
+<!--
+    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.
+-->
+
+<mapper namespace="org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper">
+
+    <!-- Result mapper for connection parameters -->
+    <resultMap id="ParameterResultMap" type="org.apache.guacamole.auth.jdbc.connection.ConnectionParameterModel">
+        <result column="connection_id"   property="connectionIdentifier" jdbcType="INTEGER"/>
+        <result column="parameter_name"  property="name"                 jdbcType="VARCHAR"/>
+        <result column="parameter_value" property="value"                jdbcType="VARCHAR"/>
+    </resultMap>
+
+    <!-- Select all parameters of a given connection -->
+    <select id="select" resultMap="ParameterResultMap">
+        SELECT
+            connection_id,
+            parameter_name,
+            parameter_value
+        FROM [guacamole].[connection_parameter]
+        WHERE
+            connection_id = #{identifier,jdbcType=INTEGER}
+    </select>
+
+    <!-- Delete all parameters of a given connection -->
+    <delete id="delete">
+        DELETE FROM [guacamole].[connection_parameter]
+        WHERE connection_id = #{identifier,jdbcType=INTEGER}
+    </delete>
+
+    <!-- Insert all given parameters -->
+    <insert id="insert" parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionParameterModel">
+
+        INSERT INTO [guacamole].[connection_parameter] (
+            connection_id,
+            parameter_name,
+            parameter_value
+        )
+        VALUES 
+            <foreach collection="parameters" item="parameter" separator=",">
+                (#{parameter.connectionIdentifier,jdbcType=INTEGER},
+                 #{parameter.name,jdbcType=VARCHAR},
+                 #{parameter.value,jdbcType=VARCHAR})
+            </foreach>
+
+    </insert>
+
+
+</mapper>

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/b6e88d33/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml
----------------------------------------------------------------------
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 0000000..ec077db
--- /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 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+
+<!--
+    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.
+-->
+
+<mapper namespace="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordMapper" >
+
+    <!-- Result mapper for system permissions -->
+    <resultMap id="ConnectionRecordResultMap" type="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
+        <result column="connection_id"        property="connectionIdentifier"     jdbcType="INTEGER"/>
+        <result column="connection_name"      property="connectionName"           jdbcType="VARCHAR"/>
+        <result column="remote_host"          property="remoteHost"               jdbcType="VARCHAR"/>
+        <result column="sharing_profile_id"   property="sharingProfileIdentifier" jdbcType="INTEGER"/>
+        <result column="sharing_profile_name" property="sharingProfileName"       jdbcType="VARCHAR"/>
+        <result column="user_id"              property="userID"                   jdbcType="INTEGER"/>
+        <result column="username"             property="username"                 jdbcType="VARCHAR"/>
+        <result column="start_date"           property="startDate"                jdbcType="TIMESTAMP"/>
+        <result column="end_date"             property="endDate"                  jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <!-- Select all connection records from a given connection -->
+    <select id="select" resultMap="ConnectionRecordResultMap">
+
+        SELECT
+            [guacamole].[connection_history].connection_id,
+            [guacamole].[connection_history].connection_name,
+            [guacamole].[connection_history].remote_host,
+            [guacamole].[connection_history].sharing_profile_id,
+            [guacamole].[connection_history].sharing_profile_name,
+            [guacamole].[connection_history].user_id,
+            [guacamole].[connection_history].username,
+            [guacamole].[connection_history].start_date,
+            [guacamole].[connection_history].end_date
+        FROM [guacamole].[connection_history]
+        WHERE
+            [guacamole].[connection_history].connection_id = #{identifier,jdbcType=INTEGER}
+        ORDER BY
+            [guacamole].[connection_history].start_date DESC,
+            [guacamole].[connection_history].end_date DESC
+
+    </select>
+
+    <!-- Insert the given connection record -->
+    <insert id="insert" parameterType="org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel">
+
+        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}
+        )
+
+    </insert>
+
+    <!-- Search for specific connection records -->
+    <select id="search" resultMap="ConnectionRecordResultMap">
+
+        SELECT
+            [guacamole].[connection_history].connection_id,
+            [guacamole].[connection_history].connection_name,
+            [guacamole].[connection_history].remote_host,
+            [guacamole].[connection_history].sharing_profile_id,
+            [guacamole].[connection_history].sharing_profile_name,
+            [guacamole].[connection_history].user_id,
+            [guacamole].[connection_history].username,
+            [guacamole].[connection_history].start_date,
+            [guacamole].[connection_history].end_date
+        FROM [guacamole].[connection_history]
+
+        <!-- Search terms -->
+        <foreach collection="terms" item="term"
+                 open="WHERE " separator=" AND ">
+            (
+
+                [guacamole].[connection_history].user_id IN (
+                    SELECT user_id
+                    FROM [guacamole].[user]
+                    WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0
+                )
+
+                OR [guacamole].[connection_history].connection_id IN (
+                    SELECT connection_id
+                    FROM [guacamole].[connection]
+                    WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0
+                )
+
+                <if test="term.startDate != null and term.endDate != null">
+                    OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}
+                </if>
+
+            )
+        </foreach>
+
+        <!-- Bind sort property enum values for sake of readability -->
+        <bind name="START_DATE"      value="@org.apache.guacamole.net.auth.ConnectionRecordSet$SortableProperty@START_DATE"/>
+
+        <!-- Sort predicates -->
+        <foreach collection="sortPredicates" item="sortPredicate"
+                 open="ORDER BY " separator=", ">
+            <choose>
+                <when test="sortPredicate.property == START_DATE">[guacamole].[connection_history].start_date</when>
+                <otherwise>1</otherwise>
+            </choose>
+            <if test="sortPredicate.descending">DESC</if>
+        </foreach>
+
+        LIMIT #{limit,jdbcType=INTEGER}
+
+    </select>
+
+    <!-- Search for specific connection records -->
+    <select id="searchReadable" resultMap="ConnectionRecordResultMap">
+
+        SELECT
+            [guacamole].[connection_history].connection_id,
+            [guacamole].[connection_history].connection_name,
+            [guacamole].[connection_history].remote_host,
+            [guacamole].[connection_history].sharing_profile_id,
+            [guacamole].[connection_history].sharing_profile_name,
+            [guacamole].[connection_history].user_id,
+            [guacamole].[connection_history].username,
+            [guacamole].[connection_history].start_date,
+            [guacamole].[connection_history].end_date
+        FROM [guacamole].[connection_history]
+        LEFT JOIN [guacamole].[connection]            ON [guacamole].[connection_history].connection_id = [guacamole].[connection].connection_id
+        LEFT JOIN [guacamole].[user]                  ON [guacamole].[connection_history].user_id       = [guacamole].[user].user_id
+
+        <!-- Restrict to readable connections -->
+        JOIN [guacamole].[connection_permission] ON
+                [guacamole].[connection_history].connection_id = [guacamole].[connection_permission].connection_id
+            AND [guacamole].[connection_permission].user_id    = #{user.objectID,jdbcType=INTEGER}
+            AND [guacamole].[connection_permission].permission = 'READ'
+
+        <!-- Restrict to readable users -->
+        JOIN [guacamole].[user_permission] ON
+                [guacamole].[connection_history].user_id = [guacamole].[user_permission].affected_user_id
+            AND [guacamole].[user_permission].user_id    = #{user.objectID,jdbcType=INTEGER}
+            AND [guacamole].[user_permission].permission = 'READ'
+
+        <!-- Search terms -->
+        <foreach collection="terms" item="term"
+                 open="WHERE " separator=" AND ">
+            (
+
+                [guacamole].[connection_history].user_id IN (
+                    SELECT user_id
+                    FROM [guacamole].[user]
+                    WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0
+                )
+
+                OR [guacamole].[connection_history].connection_id IN (
+                    SELECT connection_id
+                    FROM [guacamole].[connection]
+                    WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0
+                )
+
+                <if test="term.startDate != null and term.endDate != null">
+                    OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}
+                </if>
+
+            )
+        </foreach>
+
+        <!-- Bind sort property enum values for sake of readability -->
+        <bind name="START_DATE"      value="@org.apache.guacamole.net.auth.ConnectionRecordSet$SortableProperty@START_DATE"/>
+
+        <!-- Sort predicates -->
+        <foreach collection="sortPredicates" item="sortPredicate"
+                 open="ORDER BY " separator=", ">
+            <choose>
+                <when test="sortPredicate.property == START_DATE">[guacamole].[connection_history].start_date</when>
+                <otherwise>1</otherwise>
+            </choose>
+            <if test="sortPredicate.descending">DESC</if>
+        </foreach>
+
+        LIMIT #{limit,jdbcType=INTEGER}
+
+    </select>
+
+</mapper>