Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dn0 committed Sep 30, 2013
0 parents commit eb62a72
Show file tree
Hide file tree
Showing 6 changed files with 780 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target/
.*.swp
470 changes: 470 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
guacamole-auth-redis
====================

[Guacamole](http://guac-dev.org/) Redis authentication plugin.


Build
-----

- Clone the [git repository](https://github.com/erigones/guacamole-auth-redis.git):

git clone https://github.com/erigones/guacamole-auth-redis.git

- Compile. This will create a new jar file `guacamole-auth-redis-VERSION.jar` in the `target/` folder:

cd guacamole-auth-redis
mvn package

Install
-------

- Copy `guacamole-auth-redis-VERSION.jar` into `webapps/guacamole/WEB-INF/lib/`. It does not work if you copy the jar into common/lib/ or shared/lib/.

- Download [redis.jar](https://github.com/xetorthio/jedis/downloads) into `webapps/guacamole/WEB-INF/lib/`.


Configure
---------

- Edit the Guacamole configuration file (`/etc/guacamole/guacamole.properties`):

# Auth provider class
auth-provider: com.erigones.guacamole.net.auth.redis.RedisAuthenticationProvider

# Redis properties
redis-host: localhost
redis-port: 6379
#redis-parent: myparent:
#redis-password: secret
#redis-timeout: 2

- Restart guacamole.


Use
---

- To create a user mapping for user "testuser" with one VNC connection named "connection1":

redis> HSET testuser password "SecretPassw0rd"

redis> HSET testuser connection1 "protocol=vnc\nhostname=localhost\nport=5900\npassword=VNCPASS"

- You can use redis *pipelines* for grouping operations into transactions and reducing overhead.
68 changes: 68 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<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>com.erigones.guacamole</groupId>
<artifactId>guacamole-auth-redis</artifactId>
<packaging>jar</packaging>
<version>0.1.0</version>
<name>guacamole-auth-redis</name>
<url>https://github.com/erigones/guacamole-auth-redis</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<build>
<plugins>
<!-- Written for 1.6 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<!-- SLF4J - logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jcl</artifactId>
<version>1.6.1</version>
<scope>runtime</scope>
</dependency>

<!-- Guacamole Java API -->
<dependency>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
<version>0.8.0</version>
</dependency>

<!-- Guacamole Extension API -->
<dependency>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
<version>0.8.1</version>
</dependency>

<!-- Jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.2.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package com.erigones.guacamole.net.auth.redis;


import java.util.Map;
import java.util.HashMap;
import java.util.Properties;
import java.io.StringReader;
import java.io.IOException;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import com.erigones.guacamole.net.auth.redis.properties.RedisGuacamoleProperties;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.simple.SimpleAuthenticationProvider;
import org.glyptodon.guacamole.properties.GuacamoleProperty;
import org.glyptodon.guacamole.properties.GuacamoleProperties;
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class RedisAuthenticationProvider extends SimpleAuthenticationProvider {

private static Logger logger = LoggerFactory.getLogger(RedisAuthenticationProvider.class);

private static String redisParent;
private static JedisPool pool;

private static final <T> T getRedisProperty(GuacamoleProperty<T> propName, T def) {
T redisProp = null;

try {
redisProp = GuacamoleProperties.getProperty(propName);

if (redisProp == null) {
throw new GuacamoleException("Configuration parameter \""+ propName.getName() +"\" is null.");
}
} catch (GuacamoleException e) {
logger.error("{}", e.getMessage());
logger.error("Using default value of \"{}\" for configuration parameter \"{}\".", def, propName.getName());
redisProp = def;
}

return redisProp;
}

static {
// Create jedis pool
pool = new JedisPool(new JedisPoolConfig(),
getRedisProperty(RedisGuacamoleProperties.REDIS_HOST, "localhost"),
getRedisProperty(RedisGuacamoleProperties.REDIS_PORT, 6379),
getRedisProperty(RedisGuacamoleProperties.REDIS_TIMEOUT, 2),
getRedisProperty(RedisGuacamoleProperties.REDIS_PASSWORD, null)
);

// Set key prefix
redisParent = getRedisProperty(RedisGuacamoleProperties.REDIS_PARENT, "");
}

private Map<String, String> getMap(String key) {
Jedis jedis = pool.getResource();

try {
logger.info("Fetching key \"{}\" from redis.", key);
return jedis.hgetAll(key);
} finally {
pool.returnResource(jedis);
}
}

private Map<String, GuacamoleConfiguration> getUserConfigurations(Map<String, String> cfgMap) throws GuacamoleException {
// Authorized configuration hash map
Map<String, GuacamoleConfiguration> configs = new HashMap<String, GuacamoleConfiguration>();

for (Map.Entry<String, String> item : cfgMap.entrySet()) {
// New empty configuration
GuacamoleConfiguration config = new GuacamoleConfiguration();

// Parse connection parameters from item value
Properties properties = new Properties();
try {
properties.load(new StringReader(item.getValue()));
} catch (IOException e) {
throw new GuacamoleException("Error reading basic user mapping string.", e);
}

boolean protocol_found = false;
// Iterate through properties
for (String cfg_key : properties.stringPropertyNames()) {
String cfg_val = properties.getProperty(cfg_key);

if (cfg_key.equals("protocol")) {
config.setProtocol(cfg_val);
protocol_found = true;
} else {
config.setParameter(cfg_key, cfg_val);
}
}

if (protocol_found) {
// Add user config to user config map
configs.put(item.getKey(), config);
protocol_found = false;
} else {
throw new GuacamoleException("Missing protocol in connection \""+ item.getKey() +"\".");
}
}

return configs;
}

@Override
public Map<String, GuacamoleConfiguration> getAuthorizedConfigurations(Credentials credentials) throws GuacamoleException {
// We are using the username as redis key
if (credentials.getUsername() == null) {
return null; // Unauthorized
}

// Create the key
String key = redisParent.concat(credentials.getUsername());

// Fetch the configuration from redis
Map<String, String> map = getMap(key);

// Check the value
if (map == null || map.isEmpty()) {
logger.warn("User mapping for key \"{}\" not found in redis.", key);
return null; // Unauthorized
}

// Check password
if (! credentials.getPassword().equals(map.get("password"))) {
logger.warn("Password mismatch in user mapping for key \"{}\".", key);
return null;
}

// Remove password (everything else is a connection)
map.remove("password");

try {
// Parse connections from remaining key/values in map
return getUserConfigurations(map);

} catch (GuacamoleException e) {
logger.error("User mapping for key \"{}\" could not be parsed. Error was: \"{}\".", key, e.getMessage());

// Return empty configuration map since we are already authenticated
return new HashMap<String, GuacamoleConfiguration>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.erigones.guacamole.net.auth.redis.properties;

import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty;
import org.glyptodon.guacamole.properties.StringGuacamoleProperty;

public class RedisGuacamoleProperties {

private RedisGuacamoleProperties() {}

public static final StringGuacamoleProperty REDIS_HOST = new StringGuacamoleProperty() {
@Override
public String getName() { return "redis-host"; }
};

public static final IntegerGuacamoleProperty REDIS_PORT = new IntegerGuacamoleProperty() {
@Override
public String getName() { return "redis-port"; }
};

public static final IntegerGuacamoleProperty REDIS_TIMEOUT = new IntegerGuacamoleProperty() {
@Override
public String getName() { return "redis-timeout"; }
};

public static final StringGuacamoleProperty REDIS_PASSWORD = new StringGuacamoleProperty() {
@Override
public String getName() { return "redis-password"; }
};

public static final StringGuacamoleProperty REDIS_PARENT = new StringGuacamoleProperty() {
@Override
public String getName() { return "redis-parent"; }
};
}

0 comments on commit eb62a72

Please sign in to comment.