diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/.gitignore b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/.gitignore
new file mode 100644
index 000000000..42f4a1a64
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/.gitignore
@@ -0,0 +1,2 @@
+target/
+*~
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml
new file mode 100644
index 000000000..2329087cd
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml
@@ -0,0 +1,78 @@
+
+
+ 4.0.0
+ org.glyptodon.guacamole
+ guacamole-auth-jdbc-postgresql
+ jar
+ guacamole-auth-jdbc-postgresql
+ http://guac-dev.org/
+
+
+ UTF-8
+
+
+
+ org.glyptodon.guacamole
+ guacamole-auth-jdbc
+ 0.9.5
+ ../../
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.6
+ 1.6
+
+
+
+
+
+ maven-assembly-plugin
+ 2.2-beta-5
+
+
+ jar-with-dependencies
+ package
+
+ single
+
+
+ extension/${project.artifactId}-${project.version}
+ false
+
+ jar-with-dependencies
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.glyptodon.guacamole
+ guacamole-ext
+ provided
+
+
+
+
+ org.glyptodon.guacamole
+ guacamole-auth-jdbc-base
+ 0.9.5
+
+
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql
new file mode 100644
index 000000000..5c23bfc90
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql
@@ -0,0 +1,228 @@
+--
+-- Copyright (C) 2013 Glyptodon LLC
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+-- THE SOFTWARE.
+--
+
+--
+-- Table of connection groups. Each connection group has a name.
+--
+
+CREATE TABLE `guacamole_connection_group` (
+
+ `connection_group_id` int(11) NOT NULL AUTO_INCREMENT,
+ `parent_id` int(11),
+ `connection_group_name` varchar(128) NOT NULL,
+ `type` enum('ORGANIZATIONAL',
+ 'BALANCING') NOT NULL DEFAULT 'ORGANIZATIONAL',
+
+ PRIMARY KEY (`connection_group_id`),
+ UNIQUE KEY `connection_group_name_parent` (`connection_group_name`, `parent_id`),
+
+ CONSTRAINT `guacamole_connection_group_ibfk_1`
+ FOREIGN KEY (`parent_id`)
+ REFERENCES `guacamole_connection_group` (`connection_group_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Table of connections. Each connection has a name, protocol, and
+-- associated set of parameters.
+-- A connection may belong to a connection group.
+--
+
+CREATE TABLE `guacamole_connection` (
+
+ `connection_id` int(11) NOT NULL AUTO_INCREMENT,
+ `connection_name` varchar(128) NOT NULL,
+ `parent_id` int(11),
+ `protocol` varchar(32) NOT NULL,
+
+ PRIMARY KEY (`connection_id`),
+ UNIQUE KEY `connection_name_parent` (`connection_name`, `parent_id`),
+
+ CONSTRAINT `guacamole_connection_ibfk_1`
+ FOREIGN KEY (`parent_id`)
+ REFERENCES `guacamole_connection_group` (`connection_group_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Table of users. Each user has a unique username and a hashed password
+-- with corresponding salt.
+--
+
+CREATE TABLE `guacamole_user` (
+
+ `user_id` int(11) NOT NULL AUTO_INCREMENT,
+ `username` varchar(128) NOT NULL,
+ `password_hash` binary(32) NOT NULL,
+ `password_salt` binary(32) NOT NULL,
+
+ PRIMARY KEY (`user_id`),
+ UNIQUE KEY `username` (`username`)
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Table of connection parameters. Each parameter is simply a name/value pair
+-- associated with a connection.
+--
+
+CREATE TABLE `guacamole_connection_parameter` (
+
+ `connection_id` int(11) NOT NULL,
+ `parameter_name` varchar(128) NOT NULL,
+ `parameter_value` varchar(4096) NOT NULL,
+
+ PRIMARY KEY (`connection_id`,`parameter_name`),
+
+ CONSTRAINT `guacamole_connection_parameter_ibfk_1`
+ FOREIGN KEY (`connection_id`)
+ REFERENCES `guacamole_connection` (`connection_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Table of connection permissions. Each connection permission grants a user
+-- specific access to a connection.
+--
+
+CREATE TABLE `guacamole_connection_permission` (
+
+ `user_id` int(11) NOT NULL,
+ `connection_id` int(11) NOT NULL,
+ `permission` enum('READ',
+ 'UPDATE',
+ 'DELETE',
+ 'ADMINISTER') NOT NULL,
+
+ PRIMARY KEY (`user_id`,`connection_id`,`permission`),
+
+ CONSTRAINT `guacamole_connection_permission_ibfk_1`
+ FOREIGN KEY (`connection_id`)
+ REFERENCES `guacamole_connection` (`connection_id`) ON DELETE CASCADE,
+
+ CONSTRAINT `guacamole_connection_permission_ibfk_2`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `guacamole_user` (`user_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Table of connection group permissions. Each group permission grants a user
+-- specific access to a connection group.
+--
+
+CREATE TABLE `guacamole_connection_group_permission` (
+
+ `user_id` int(11) NOT NULL,
+ `connection_group_id` int(11) NOT NULL,
+ `permission` enum('READ',
+ 'UPDATE',
+ 'DELETE',
+ 'ADMINISTER') NOT NULL,
+
+ PRIMARY KEY (`user_id`,`connection_group_id`,`permission`),
+
+ CONSTRAINT `guacamole_connection_group_permission_ibfk_1`
+ FOREIGN KEY (`connection_group_id`)
+ REFERENCES `guacamole_connection_group` (`connection_group_id`) ON DELETE CASCADE,
+
+ CONSTRAINT `guacamole_connection_group_permission_ibfk_2`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `guacamole_user` (`user_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Table of system permissions. Each system permission grants a user a
+-- system-level privilege of some kind.
+--
+
+CREATE TABLE `guacamole_system_permission` (
+
+ `user_id` int(11) NOT NULL,
+ `permission` enum('CREATE_CONNECTION',
+ 'CREATE_CONNECTION_GROUP',
+ 'CREATE_USER',
+ 'ADMINISTER') NOT NULL,
+
+ PRIMARY KEY (`user_id`,`permission`),
+
+ CONSTRAINT `guacamole_system_permission_ibfk_1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `guacamole_user` (`user_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Table of user permissions. Each user permission grants a user access to
+-- another user (the "affected" user) for a specific type of operation.
+--
+
+CREATE TABLE `guacamole_user_permission` (
+
+ `user_id` int(11) NOT NULL,
+ `affected_user_id` int(11) NOT NULL,
+ `permission` enum('READ',
+ 'UPDATE',
+ 'DELETE',
+ 'ADMINISTER') NOT NULL,
+
+ PRIMARY KEY (`user_id`,`affected_user_id`,`permission`),
+
+ CONSTRAINT `guacamole_user_permission_ibfk_1`
+ FOREIGN KEY (`affected_user_id`)
+ REFERENCES `guacamole_user` (`user_id`) ON DELETE CASCADE,
+
+ CONSTRAINT `guacamole_user_permission_ibfk_2`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `guacamole_user` (`user_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Table of connection history records. Each record defines a specific user's
+-- session, including the connection used, the start time, and the end time
+-- (if any).
+--
+
+CREATE TABLE `guacamole_connection_history` (
+
+ `history_id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` int(11) NOT NULL,
+ `connection_id` int(11) NOT NULL,
+ `start_date` datetime NOT NULL,
+ `end_date` datetime DEFAULT NULL,
+
+ PRIMARY KEY (`history_id`),
+ KEY `user_id` (`user_id`),
+ KEY `connection_id` (`connection_id`),
+
+ CONSTRAINT `guacamole_connection_history_ibfk_1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `guacamole_user` (`user_id`) ON DELETE CASCADE,
+
+ CONSTRAINT `guacamole_connection_history_ibfk_2`
+ FOREIGN KEY (`connection_id`)
+ REFERENCES `guacamole_connection` (`connection_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/002-create-admin-user.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/002-create-admin-user.sql
new file mode 100644
index 000000000..997a48841
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/002-create-admin-user.sql
@@ -0,0 +1,38 @@
+--
+-- Copyright (C) 2013 Glyptodon LLC
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+-- THE SOFTWARE.
+--
+
+-- Create default user "guacadmin" with password "guacadmin"
+insert into guacamole_user values(1, 'guacadmin',
+ x'CA458A7D494E3BE824F5E1E175A1556C0F8EEF2C2D7DF3633BEC4A29C4411960', -- 'guacadmin'
+ x'FE24ADC5E11E2B25288D1704ABE67A79E342ECC26064CE69C5B3177795A82264');
+
+-- Grant this user create permissions
+insert into guacamole_system_permission values(1, 'CREATE_CONNECTION');
+insert into guacamole_system_permission values(1, 'CREATE_CONNECTION_GROUP');
+insert into guacamole_system_permission values(1, 'CREATE_USER');
+insert into guacamole_system_permission values(1, 'ADMINISTER');
+
+-- Grant admin permission to read/update/administer self
+insert into guacamole_user_permission values(1, 1, 'READ');
+insert into guacamole_user_permission values(1, 1, 'UPDATE');
+insert into guacamole_user_permission values(1, 1, 'ADMINISTER');
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java
new file mode 100644
index 000000000..65bd2270a
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.auth.postgresql;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
+import org.glyptodon.guacamole.net.auth.Credentials;
+import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule;
+import org.glyptodon.guacamole.auth.jdbc.socket.BalancedGuacamoleSocketService;
+import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService;
+import org.glyptodon.guacamole.auth.jdbc.socket.MultiseatGuacamoleSocketService;
+import org.glyptodon.guacamole.auth.jdbc.socket.SingleSeatGuacamoleSocketService;
+import org.glyptodon.guacamole.auth.jdbc.socket.UnrestrictedGuacamoleSocketService;
+import org.glyptodon.guacamole.auth.jdbc.user.UserContextService;
+import org.glyptodon.guacamole.environment.Environment;
+import org.glyptodon.guacamole.environment.LocalEnvironment;
+
+/**
+ * Provides a PostgreSQL-based implementation of the AuthenticationProvider
+ * functionality.
+ *
+ * @author James Muehlner
+ * @author Michael Jumper
+ */
+public class PostgreSQLAuthenticationProvider implements AuthenticationProvider {
+
+ /**
+ * Injector which will manage the object graph of this authentication
+ * provider.
+ */
+ private final Injector injector;
+
+ /**
+ * Returns the appropriate socket service class given the Guacamole
+ * environment. The class is chosen based on configuration options that
+ * dictate concurrent usage policy.
+ *
+ * @param environment
+ * The environment of the Guacamole server.
+ *
+ * @return
+ * The socket service class that matches the concurrent usage policy
+ * options set in the Guacamole environment.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while reading the configuration options.
+ */
+ private Class extends GuacamoleSocketService>
+ getSocketServiceClass(Environment environment)
+ throws GuacamoleException {
+
+ // Read concurrency-related properties
+ boolean disallowSimultaneous = environment.getProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_DISALLOW_SIMULTANEOUS_CONNECTIONS, false);
+ boolean disallowDuplicate = environment.getProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_DISALLOW_DUPLICATE_CONNECTIONS, true);
+
+ if (disallowSimultaneous) {
+
+ // Connections may not be used concurrently
+ if (disallowDuplicate)
+ return SingleSeatGuacamoleSocketService.class;
+
+ // Connections are reserved for a single user when in use
+ else
+ return BalancedGuacamoleSocketService.class;
+
+ }
+
+ else {
+
+ // Connections may be used concurrently, but only once per user
+ if (disallowDuplicate)
+ return MultiseatGuacamoleSocketService.class;
+
+ // Connection use is not restricted
+ else
+ return UnrestrictedGuacamoleSocketService.class;
+
+ }
+
+ }
+
+ /**
+ * Creates a new PostgreSQLAuthenticationProvider that reads and writes
+ * authentication data to a PostgreSQL database defined by properties in
+ * guacamole.properties.
+ *
+ * @throws GuacamoleException
+ * If a required property is missing, or an error occurs while parsing
+ * a property.
+ */
+ public PostgreSQLAuthenticationProvider() throws GuacamoleException {
+
+ // Get local environment
+ Environment environment = new LocalEnvironment();
+
+ // Set up Guice injector.
+ injector = Guice.createInjector(
+
+ // Configure PostgreSQL-specific authentication
+ new PostgreSQLAuthenticationProviderModule(environment),
+
+ // Configure JDBC authentication core
+ new JDBCAuthenticationProviderModule(environment, getSocketServiceClass(environment))
+
+ );
+
+ }
+
+ @Override
+ public UserContext getUserContext(Credentials credentials)
+ throws GuacamoleException {
+
+ // Create UserContext based on credentials, if valid
+ UserContextService userContextService = injector.getInstance(UserContextService.class);
+ return userContextService.getUserContext(credentials);
+
+ }
+
+ @Override
+ public UserContext updateUserContext(UserContext context,
+ Credentials credentials) throws GuacamoleException {
+
+ // No need to update the context
+ return context;
+
+ }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProviderModule.java
new file mode 100644
index 000000000..2decdf9a6
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProviderModule.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.auth.postgresql;
+
+import com.google.inject.Binder;
+import com.google.inject.Module;
+import com.google.inject.name.Names;
+import java.util.Properties;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.environment.Environment;
+import org.mybatis.guice.datasource.helper.JdbcHelper;
+
+/**
+ * Guice module which configures PostgreSQL-specific injections.
+ *
+ * @author James Muehlner
+ * @author Michael Jumper
+ */
+public class PostgreSQLAuthenticationProviderModule implements Module {
+
+ /**
+ * MyBatis-specific configuration properties.
+ */
+ private final Properties myBatisProperties = new Properties();
+
+ /**
+ * PostgreSQL-specific driver configuration properties.
+ */
+ private final Properties driverProperties = new Properties();
+
+ /**
+ * Creates a new PostgreSQL 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 PostgreSQLAuthenticationProviderModule(Environment environment)
+ throws GuacamoleException {
+
+ // Set the PostgreSQL-specific properties for MyBatis.
+ myBatisProperties.setProperty("mybatis.environment.id", "guacamole");
+ myBatisProperties.setProperty("JDBC.host", environment.getRequiredProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_HOSTNAME));
+ myBatisProperties.setProperty("JDBC.port", String.valueOf(environment.getRequiredProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_PORT)));
+ myBatisProperties.setProperty("JDBC.schema", environment.getRequiredProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_DATABASE));
+ myBatisProperties.setProperty("JDBC.username", environment.getRequiredProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_USERNAME));
+ myBatisProperties.setProperty("JDBC.password", environment.getRequiredProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_PASSWORD));
+ 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 PostgreSQL-specific properties
+ JdbcHelper.PostgreSQL.configure(binder);
+
+ // Bind MyBatis properties
+ Names.bindProperties(binder, myBatisProperties);
+
+ // Bing 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-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java
new file mode 100644
index 000000000..abb5a1249
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.auth.postgresql;
+
+import org.glyptodon.guacamole.properties.BooleanGuacamoleProperty;
+import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty;
+import org.glyptodon.guacamole.properties.StringGuacamoleProperty;
+
+/**
+ * Properties used by the PostgreSQL Authentication plugin.
+ *
+ * @author James Muehlner
+ * @author Michael Jumper
+ */
+public class PostgreSQLGuacamoleProperties {
+
+ /**
+ * This class should not be instantiated.
+ */
+ private PostgreSQLGuacamoleProperties() {}
+
+ /**
+ * The URL of the PostgreSQL server hosting the Guacamole authentication tables.
+ */
+ public static final StringGuacamoleProperty POSTGRESQL_HOSTNAME =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-hostname"; }
+
+ };
+
+ /**
+ * The port of the PostgreSQL server hosting the Guacamole authentication
+ * tables.
+ */
+ public static final IntegerGuacamoleProperty POSTGRESQL_PORT =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-port"; }
+
+ };
+
+ /**
+ * The name of the PostgreSQL database containing the Guacamole
+ * authentication tables.
+ */
+ public static final StringGuacamoleProperty POSTGRESQL_DATABASE =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-database"; }
+
+ };
+
+ /**
+ * The username used to authenticate to the PostgreSQL database containing
+ * the Guacamole authentication tables.
+ */
+ public static final StringGuacamoleProperty POSTGRESQL_USERNAME =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-username"; }
+
+ };
+
+ /**
+ * The password used to authenticate to the PostgreSQL database containing
+ * the Guacamole authentication tables.
+ */
+ public static final StringGuacamoleProperty POSTGRESQL_PASSWORD =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-password"; }
+
+ };
+
+ /**
+ * Whether or not multiple users accessing the same connection at the same
+ * time should be disallowed.
+ */
+ public static final BooleanGuacamoleProperty
+ POSTGRESQL_DISALLOW_SIMULTANEOUS_CONNECTIONS =
+ new BooleanGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-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
+ POSTGRESQL_DISALLOW_DUPLICATE_CONNECTIONS =
+ new BooleanGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-disallow-duplicate-connections"; }
+
+ };
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/package-info.java
new file mode 100644
index 000000000..1a939e1a6
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * The PostgreSQL authentication provider.
+ */
+package org.glyptodon.guacamole.auth.postgresql;
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.xml
new file mode 100644
index 000000000..2211da069
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.xml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM guacamole_connection
+ WHERE connection_id = #{identifier,jdbcType=VARCHAR}
+
+
+
+
+
+ INSERT INTO guacamole_connection (
+ connection_name,
+ parent_id,
+ protocol
+ )
+ VALUES (
+ #{object.name,jdbcType=VARCHAR},
+ #{object.parentIdentifier,jdbcType=VARCHAR},
+ #{object.protocol,jdbcType=VARCHAR}
+ )
+
+
+
+
+
+ UPDATE guacamole_connection
+ SET connection_name = #{object.name,jdbcType=VARCHAR},
+ parent_id = #{object.parentIdentifier,jdbcType=VARCHAR},
+ protocol = #{object.protocol,jdbcType=VARCHAR}
+ WHERE connection_id = #{object.objectID,jdbcType=INTEGER}
+
+
+
\ No newline at end of file
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml
new file mode 100644
index 000000000..b5775f607
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO guacamole_connection_history (
+ connection_id,
+ user_id,
+ start_date,
+ end_date
+ )
+ VALUES (
+ #{record.connectionIdentifier,jdbcType=VARCHAR},
+ #{record.userID,jdbcType=INTEGER},
+ #{record.startDate,jdbcType=TIMESTAMP},
+ #{record.endDate,jdbcType=TIMESTAMP}
+ )
+
+
+
+
\ No newline at end of file
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ParameterMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ParameterMapper.xml
new file mode 100644
index 000000000..ccd386c14
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ParameterMapper.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM guacamole_connection_parameter
+ WHERE connection_id = #{identifier,jdbcType=VARCHAR}
+
+
+
+
+
+ INSERT INTO guacamole_connection_parameter (
+ connection_id,
+ parameter_name,
+ parameter_value
+ )
+ VALUES
+
+ (#{parameter.connectionIdentifier,jdbcType=VARCHAR},
+ #{parameter.name,jdbcType=VARCHAR},
+ #{parameter.value,jdbcType=VARCHAR})
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml
new file mode 100644
index 000000000..4eb20da1c
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM guacamole_connection_group
+ WHERE connection_group_id = #{identifier,jdbcType=VARCHAR}
+
+
+
+
+
+ INSERT INTO guacamole_connection_group (
+ connection_group_name,
+ parent_id,
+ type
+ )
+ VALUES (
+ #{object.name,jdbcType=VARCHAR},
+ #{object.parentIdentifier,jdbcType=VARCHAR},
+ #{object.type,jdbcType=VARCHAR}
+ )
+
+
+
+
+
+ UPDATE guacamole_connection_group
+ SET connection_group_name = #{object.name,jdbcType=VARCHAR},
+ parent_id = #{object.parentIdentifier,jdbcType=VARCHAR},
+ type = #{object.type,jdbcType=VARCHAR}
+ WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER}
+
+
+
\ No newline at end of file
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.xml
new file mode 100644
index 000000000..40ada12a5
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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=VARCHAR})
+
+
+
+
+
+
+
+ INSERT IGNORE INTO guacamole_connection_group_permission (
+ user_id,
+ permission,
+ connection_group_id
+ )
+ VALUES
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR},
+ #{permission.objectIdentifier,jdbcType=VARCHAR})
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.xml
new file mode 100644
index 000000000..9935f3cfd
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM guacamole_connection_permission
+ WHERE (user_id, permission, connection_id) IN
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR},
+ #{permission.objectIdentifier,jdbcType=VARCHAR})
+
+
+
+
+
+
+
+ INSERT IGNORE INTO guacamole_connection_permission (
+ user_id,
+ permission,
+ connection_id
+ )
+ VALUES
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR},
+ #{permission.objectIdentifier,jdbcType=VARCHAR})
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionMapper.xml
new file mode 100644
index 000000000..55eacd072
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionMapper.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM guacamole_system_permission
+ WHERE (user_id, permission) IN
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR})
+
+
+
+
+
+
+
+ INSERT IGNORE INTO guacamole_system_permission (
+ user_id,
+ permission
+ )
+ VALUES
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR})
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionMapper.xml
new file mode 100644
index 000000000..038bb814f
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionMapper.xml
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM guacamole_user_permission
+ USING guacamole_user_permission
+ JOIN guacamole_user affected ON guacamole_user_permission.affected_user_id = affected.user_id
+ WHERE
+ (guacamole_user_permission.user_id, permission, affected.username) IN
+
+ (#{permission.userID,jdbcType=INTEGER},
+ #{permission.type,jdbcType=VARCHAR},
+ #{permission.objectIdentifier,jdbcType=VARCHAR})
+
+
+
+
+
+
+
+ INSERT IGNORE INTO guacamole_user_permission (
+ user_id,
+ permission,
+ affected_user_id
+ )
+ SELECT 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=VARCHAR} AS username
+
+ AS permissions
+ JOIN guacamole_user ON guacamole_user.username = permissions.username;
+
+
+
+
\ No newline at end of file
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml
new file mode 100644
index 000000000..5170d43ee
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DELETE FROM guacamole_user
+ WHERE username = #{identifier,jdbcType=VARCHAR}
+
+
+
+
+
+ INSERT INTO guacamole_user (
+ username,
+ password_hash,
+ password_salt
+ )
+ VALUES (
+ #{object.identifier,jdbcType=VARCHAR},
+ #{object.passwordHash,jdbcType=BINARY},
+ #{object.passwordSalt,jdbcType=BINARY}
+ )
+
+
+
+
+
+ UPDATE guacamole_user
+ SET password_hash = #{object.passwordHash,jdbcType=BINARY},
+ password_salt = #{object.passwordSalt,jdbcType=BINARY}
+ WHERE user_id = #{object.objectID,jdbcType=VARCHAR}
+
+
+
diff --git a/extensions/guacamole-auth-jdbc/pom.xml b/extensions/guacamole-auth-jdbc/pom.xml
index c6c622994..49fb5c124 100644
--- a/extensions/guacamole-auth-jdbc/pom.xml
+++ b/extensions/guacamole-auth-jdbc/pom.xml
@@ -20,8 +20,9 @@
modules/guacamole-auth-jdbc-base
-
+
modules/guacamole-auth-jdbc-mysql
+ modules/guacamole-auth-jdbc-postgresql