Chapter 13. guacamole-ext

While not strictly part of the Java API provided by the Guacamole project, guacamole-ext is a subset of the API used by the Guacamole web application, exposed within a separate project such that extensions, specifically authentication providers, can be written to tweak Guacamole to fit well in existing deployments.

Common configuration

For the sake of ease of development and providing a common location for configuration of both Guacamole and its extensions, guacamole-ext provides utility classes for accessing the main configuration file, guacamole.properties, and for accessing the main root directory for housing configuration files: GUACAMOLE_HOME.

GuacamoleProperties

GuacamoleProperties is a utility class for accessing the properties declared within guacamole.properties. Each property is typesafe and handles its own parsing - retrieving a property is as simple as calling getProperty() or getRequiredProperty().

Because of this ease-of-access to guacamole.properties within Guacamole and all extensions, the guacamole.properties file is an ideal place to store unstructured, extension-specific configuration information.

GuacamoleHome

If you need more structured data than provided by simple properties, placing XML or some other separate file within GUACAMOLE_HOME (or a subdirectory thereof) is a decent way to achieve this. The GuacamoleHome class provides access to the GUACAMOLE_HOME directory, abstracting away the decision process that determines which directory is considered GUACAMOLE_HOME.

Authentication providers

The main use of guacamole-ext is to provide custom authentication for Guacamole through the implementation of authentication providers. An authentication provider is any class which implements the AuthenticationProvider interface, implementing the only function defined by that interface: getUserContext(). This function is required to return a "context" which provides access to only those users and configurations accessible with the given credentials, and enforces its own security model.

The credentials given are abstract and while Guacamole the web application implements a username/password driven login screen, you are not required to user usernames and passwords; the Credentials class given to the authentication provider provides access to all HTTP parameters in general, as well as cookies and SSL information.

The Guacamole web application includes a basic authentication provider implementation which parses an XML file to determine which users exist, their corresponding passwords, and what configurations those users have access to. This is the part of Guacamole that reads the user-mapping.xml file. If you use a custom authentication provider for your authentication, this file will probably not be required.

The community has implemented authentication providers which access databases, use LDAP, or even perform no authentication at all, redirecting all users to a single configuration specified in guacamole.properties.

A minimal authentication provider is implemented in the tutorials later, and the upstream authentication provider implemented within Guacamole, as well as the authentication providers implemented by the community, are good examples for how authentication can be extended without having to implement a whole new web application.

SimpleAuthenticationProvider

The SimpleAuthenticationProvider class provides a much simpler means of implementing authentication when you do not require the ability to add and remove users and connections. It is an abstract class and requires only one function implementation: getAuthorizedConfigurations().

This function is required to return a Map of unique IDs to configurations, where these configurations are all configurations accessible with the provided credentials. As before, the credentials given are abstract. You are not required to use usernames and passwords.

The configurations referred to by the function name are instances of GuacamoleConfiguration (part of guacamole-common), which is just a wrapper around a protocol name and set of parameter name/value pairs. The name of the protocol to use and a set of parameters is the minimum information required for other parts of the Guacamole API to complete the handshake required by the Guacamole protocol.

When a class that extends SimpleAuthenticationProvider is asked for more advanced operations by the web application, SimpleAuthenticationProvider simply returns that there is no permission to do so. This effectively disables all administrative functionality within the web interface.

If you choose to go the simple route, most of the rest of this chapter is irrelevant. Permissions, security model, and various classes will be discussed that are all handled for you automatically by SimpleAuthenticationProvider.

The UserContext

The UserContext is the root of all operations. It is used to list, create, modify, or delete users and connections, as well as to query available permissions.

The Guacamole web application uses permissions queries against the UserContext to determine what operations to present, but beware that it is up to the UserContext to actually enforce these restrictions. The Guacamole web application will not enforce restrictions on behalf of the UserContext.

The UserContext is the sole means of entry and the sole means of modification available to a logged-in user. If the UserContext refuses to perform an operation (by throwing an exception), the user cannot perform the operation at all.

Directory classes

Access to users and connections is given through Directory classes. These Directory classes are similar to Java collections, but they also embody object update semantics. Objects can be retrieved from a Directory using its get() function and added or removed with add() and remove() respectively, but objects already in the set can also be updated by passing an updated object to its update() function.

An implementation of a Directory can rely on these functions to define the semantics surrounding all operations. The add() function is called only when creating new objects, the update() function is called only when updating an object previously retrieved with get(), and remove() is called only when removing an existing object by its identifier.

When implementing an AuthenticationProvider, you must ensure that the UserContext will only return Directory classes that automatically enforce the permissions associated with all objects and the associated user.

Permissions

The permissions system within guacamole-ext is the means with which an authentication module communicates with the web application, informing it of what the user is allowed to do. The presence or lack of permissions for certain operations dictates how the web interface displays itself: whether the "Manage" button is displayed, whether the user or connection management sections (or both) are displayed, etc.

Permissions are not the means through which access is restricted. An implementation may use the permission objects to define restrictions, but this is not required. It is up to the implementation to enforce its own restrictions by throwing exceptions when an operation is not allowed.

System permissions

System permissions grant access to operations that manipulate the system as a whole, rather than specific objects. This includes the creation of new objects, as object creation directly affects the system, and per-object controls cannot exist before the object is actually created.

ADMINISTER

Allows manipulation of system-level permissions. The semantics of the system-level ADMINISTER permission are up to the implementor of the authentication module, but in general this permission implies all other permissions. A user having this permission can implicitly create and manage any object.

CREATE_CONNECTION

Allows creation of new connections. If a user has this permission, they will see the connection management interface and the "Manage" button. Within this interface, they will be able to create new connections.

CREATE_CONNECTION_GROUP

Allows creation of new connections groups. If a user has this permission, they will see the connection management interface and the "Manage" button. Within this interface, they will be able to create new connection groups.

CREATE_USER

Allows creation of new users. If a user has this permission, they will see the user management interface and the "Manage" button.

User permissions

User permissions grant access to operations that affect a specific user. Each user permission has a definite and single associated user that is the object of the operation.

ADMINISTER

Allows changing visibility of the user. A user with ADMINISTER permission on another user can add and remove permissions related to that user. Note that adding or removing permissions on a user implicitly requires UPDATE permission on that user.

DELETE

Allows deletion of the associated user. This is distinct from the ADMINISTER permission which deals only with modification to the permissions associated with a user. A user with this permission will see the "Delete" button when editing the corresponding user.

READ

Allows the user to be read. A particular user will not appear in the user management section unless the user viewing it has READ permission on that specific user.

UPDATE

Allows the user to be updated. This means altering the user's password or adding or removing permissions from that user. Note the difference between UPDATE and ADMINISTER: the UPDATE permission allows the permissions of a user to be changed, while ADMINISTER permission allows permissions relating to a user to be changed. A user with this permission will see the "Manage" button.

Connection permissions

Connection permissions grant access to operations that affect a specific connection. Each connection permission has a definite and single associated connection that is the object of the operation. The semantics of each operation differ slightly from the similar user permissions, but the general principles behind them are the same.

ADMINISTER

Allows changing visibility of the connection. A user with ADMINISTER permission on a connection can add and remove permissions related to that connection, and will see the "Manage" button.

DELETE

Allows deletion of the associated connection. This is distinct from the ADMINISTER permission which deals only with modification to the permissions associated with a connection. A user with this permission will see the "Delete" button when editing the corresponding connection.

READ

Allows the connection to be read. A particular connection will not appear in the connection list nor in the connection management section unless the user viewing it has READ permission on that specific connection. READ permission is required for a user to have permission to actually use a connection, unless that connection is part of a balancing connection group for which the user has READ permission.

UPDATE

Allows the connection to be updated. This means editing the connection's parameters or changing the connection's protocol. A user with this permission will see the "Manage" button.

Connection group permissions

Connection group permissions grant access to operations that affect a specific connection group. Connection group permissions are extremely similar to connection permissions, with some minor differences.

ADMINISTER

Allows changing visibility of the connection group. A user with ADMINISTER permission on a connection group can add and remove permissions related to that connection group, and will see the "Manage" button. Users with ADMINISTER permission on a balancing connection group can see the contents of that group. To users without ADMINISTER permission, a balancing group looks like a connection.

DELETE

Allows deletion of the associated connection group. This is distinct from the ADMINISTER permission which deals only with modification to the permissions associated with a connection group. A user with this permission will see the "Delete" button when editing the corresponding connection group.

READ

Allows the connection group to be read. A particular connection group will not appear in the connection list nor in the connection management section unless the user viewing it has READ permission on that specific connection group. READ permission is required for a user to have permission to actually use a connection.

A user with READ permission on a group will also be able to see any contained connections or groups for which they also have READ permission. READ permission is not sufficient to see the contents of a balancing group.

UPDATE

Allows the connection group to be updated. This means editing the connection group's name, type, or contents. A user with this permission will see the "Manage" button.

Connections and history

Authentication modules must return Connection objects which each implement a connect() function. When this function is called, the connection must be made if permission is available.

This new separation of concerns makes more sense when you consider that as connecting is an operation on a Connection, access to performing that operation must be restricted through the AuthenticationProvider, and thus must be enforced within the AuthenticationProvider. This separation also opens the door for things like load balancing of connections and restricting concurrent access to connections.

When a connection is made or terminated, it is also the duty of the authentication module to maintain the connection history. Each connection has a corresponding list of ConnectionRecord objects, each of which is associated with a past connection or a currently-active connection. This information will ultimately be exposed to the user as a history list when they view a connection in the management interface or as a simple active user count on the connection, advising the user of existing activity.

Event listeners

Although not used internally by the web application, the web application provides an event system which can be hooked into with listener objects, such that a class within the classpath of Guacamole can receive events when something noteworthy happens in the application layer, and take some sort of action.

Currently, the web application provides events for when the tunnel is opened or closed, and when an authentication attempt succeeds or fails. In most cases, the class listening for these events can also cancel whatever action just occurred.

TunnelConnectListener

When a tunnel is connected to by the JavaScript client, Guacamole informs all installed instances of TunnelConnectListener by calling their tunnelConnected() function with a new TunnelConnectEvent, which contains the tunnel that was just connected, as well as any associated credentials. If tunnelConnected() returns false, the connect attempt will be overridden and denied.

TunnelCloseListener

When a tunnel is connected to by the JavaScript client, Guacamole informs all installed instances of TunnelCloseListener by calling their tunnelClosed() function with a new TunnelCloseEvent, which contains the tunnel that is about to be closed, as well as any associated credentials. If tunnelClosed() returns false, the attempt close the tunnel will be overridden and denied, and the tunnel will remain open.

AuthenticationSuccessListener

If a user successfully authenticates with the web application, Guacamole informs all installed instances of AuthenticationSuccessListener by calling their authenticationSucceeded() function with a new AuthenticationSuccessEvent which contains the credentials used. The implementation of this function has the opportunity to cancel the authentication attempt, effectively denying access despite being otherwise valid, by returning false.

AuthenticationFailureListener

If a user fails to authenticate with the web application, Guacamole informs all installed instances of AuthenticationFailureListener by calling their authenticationFailed() function with a new AuthenticationFailureEvent which contains the credentials used. Unlike other listeners, this event cannot be canceled by returning false. All failed authentication attempts "succeed" in failing, and an implementation of AuthenticationFailureListener cannot force an authentication attempt to succeed by denying that failure.