doc/gug/event-listeners.html (445 lines of code) (raw):
<!DOCTYPE html>
<html class="writer-html5" lang="en" >
<head>
<meta charset="utf-8" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Event listeners — Apache Guacamole Manual v1.5.3</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="_static/tabs.css" type="text/css" />
<link rel="stylesheet" href="_static/gug.css" type="text/css" />
<!--[if lt IE 9]>
<script src="_static/js/html5shiv.min.js"></script>
<![endif]-->
<script src="_static/jquery.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/tabs.js"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Writing your own Guacamole application" href="writing-you-own-guacamole-app.html" />
<link rel="prev" title="Custom authentication" href="custom-auth.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
Apache Guacamole
</a>
<div class="version">
1.5.3
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Overview</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">User's Guide</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="guacamole-architecture.html">Implementation and architecture</a></li>
<li class="toctree-l1"><a class="reference internal" href="installing-guacamole.html">Installing Guacamole natively</a></li>
<li class="toctree-l1"><a class="reference internal" href="guacamole-docker.html">Installing Guacamole with Docker</a></li>
<li class="toctree-l1"><a class="reference internal" href="reverse-proxy.html">Proxying Guacamole</a></li>
<li class="toctree-l1"><a class="reference internal" href="configuring-guacamole.html">Configuring Guacamole</a></li>
<li class="toctree-l1"><a class="reference internal" href="jdbc-auth.html">Database authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="ldap-auth.html">LDAP authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="vault.html">Retrieving secrets from a vault</a></li>
<li class="toctree-l1"><a class="reference internal" href="duo-auth.html">Duo two-factor authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="totp-auth.html">TOTP two-factor authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="header-auth.html">HTTP header authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="json-auth.html">Encrypted JSON authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="cas-auth.html">CAS Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="openid-auth.html">OpenID Connect Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="saml-auth.html">SAML Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="radius-auth.html">RADIUS Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="adhoc-connections.html">Ad-hoc Connections</a></li>
<li class="toctree-l1"><a class="reference internal" href="using-guacamole.html">Using Guacamole</a></li>
<li class="toctree-l1"><a class="reference internal" href="recording-playback.html">Viewing session recordings in-browser</a></li>
<li class="toctree-l1"><a class="reference internal" href="administration.html">Administration</a></li>
<li class="toctree-l1"><a class="reference internal" href="troubleshooting.html">Troubleshooting</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Developer's Guide</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="guacamole-protocol.html">The Guacamole protocol</a></li>
<li class="toctree-l1"><a class="reference internal" href="libguac.html">libguac</a></li>
<li class="toctree-l1"><a class="reference internal" href="guacamole-common.html">guacamole-common</a></li>
<li class="toctree-l1"><a class="reference internal" href="guacamole-common-js.html">guacamole-common-js</a></li>
<li class="toctree-l1"><a class="reference internal" href="guacamole-ext.html">guacamole-ext</a></li>
<li class="toctree-l1"><a class="reference internal" href="custom-protocols.html">Adding new protocols</a></li>
<li class="toctree-l1"><a class="reference internal" href="custom-auth.html">Custom authentication</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Event listeners</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#a-guacamole-listener-extension-skeleton">A Guacamole listener extension skeleton</a></li>
<li class="toctree-l2"><a class="reference internal" href="#building-the-extension">Building the extension</a></li>
<li class="toctree-l2"><a class="reference internal" href="#handling-events">Handling events</a></li>
<li class="toctree-l2"><a class="reference internal" href="#influencing-guacamole-by-event-veto">Influencing Guacamole by event veto</a></li>
<li class="toctree-l2"><a class="reference internal" href="#installing-the-extension">Installing the extension</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="writing-you-own-guacamole-app.html">Writing your own Guacamole application</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Appendices</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="protocol-reference.html">Guacamole protocol reference</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">Apache Guacamole</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Event listeners</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/event-listeners.md.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<section id="event-listeners">
<h1>Event listeners<a class="headerlink" href="#event-listeners" title="Permalink to this heading"></a></h1>
<p>Guacamole supports the delivery of event notifications to custom extensions.
Developers can use listener extensions to integrate custom handling of events
such as successful and failed authentications, and requests to connect and
disconnect tunnels to desktop environments.</p>
<p>A listener extension could be used, for example, to record authentication
attempts in an external database for security auditing or alerting. By
listening to tunnel lifecycle events, a listener extension could be used to
help coordinate startup and shutdown of machine resources; particularly useful
in cloud environments where minimizing running-but-idle resources is an
important cost savings measure.</p>
<p>For certain <em>vetoable</em> events, an event listener can even influence Guacamole’s
behavior. For example, a listener can veto a successful authentication,
effectively causing the authentication to be considered failed. Similarly, a
listener can veto a tunnel connection, effectively preventing the tunnel from
being connected to a virtual desktop resource.</p>
<p>Custom event listeners are packaged using the same extension mechanism used for
custom authentication providers. A single listener extension can include any
number of classes that implement the listener interface. A single extension
module can also include any combination of authentication providers and
listeners, so developers can easily combine authentication providers with
listeners designed to support them.</p>
<p>To demonstrate the principles involved in receiving Guacamole event
notifications, we will implement a simple listener extension that logs
authentication events. While our approach simply writes event details to the
same log used by the Guacamole web application, a listener could process these
events in arbitrary ways, limited only by the imagination and ingenuity of the
developer.</p>
<section id="a-guacamole-listener-extension-skeleton">
<span id="custom-event-listener-skeleton"></span><h2>A Guacamole listener extension skeleton<a class="headerlink" href="#a-guacamole-listener-extension-skeleton" title="Permalink to this heading"></a></h2>
<p>For simplicity’s sake, and because this is how things are done upstream in the
Guacamole project, we will use Maven to build our extension.</p>
<p>The bare minimum required for a Guacamole listener extension is a <code class="docutils literal notranslate"><span class="pre">pom.xml</span></code>
file listing guacamole-ext as a dependency, a single <code class="docutils literal notranslate"><span class="pre">.java</span></code> file implementing
our stub of a listener, and a <code class="docutils literal notranslate"><span class="pre">guac-manifest.json</span></code> file describing the
extension and pointing to our listener class.</p>
<div class="highlight-xml notranslate"><div class="highlight"><pre><span></span><span class="nt"><project</span><span class="w"> </span><span class="na">xmlns=</span><span class="s">"http://maven.apache.org/POM/4.0.0"</span>
<span class="w"> </span><span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="w"> </span><span class="na">xsi:schemaLocation=</span><span class="s">"http://maven.apache.org/POM/4.0.0</span>
<span class="s"> http://maven.apache.org/maven-v4_0_0.xsd"</span><span class="nt">></span>
<span class="w"> </span><span class="nt"><modelVersion></span>4.0.0<span class="nt"></modelVersion></span>
<span class="w"> </span><span class="nt"><groupId></span>org.apache.guacamole<span class="nt"></groupId></span>
<span class="w"> </span><span class="nt"><artifactId></span>guacamole-listener-tutorial<span class="nt"></artifactId></span>
<span class="w"> </span><span class="nt"><packaging></span>jar<span class="nt"></packaging></span>
<span class="w"> </span><span class="nt"><version></span>1.5.3<span class="nt"></version></span>
<span class="w"> </span><span class="nt"><name></span>guacamole-listener-tutorial<span class="nt"></name></span>
<span class="w"> </span><span class="nt"><properties></span>
<span class="w"> </span><span class="nt"><project.build.sourceEncoding></span>UTF-8<span class="nt"></project.build.sourceEncoding></span>
<span class="w"> </span><span class="nt"></properties></span>
<span class="w"> </span><span class="nt"><build></span>
<span class="w"> </span><span class="nt"><plugins></span>
<span class="w"> </span><span class="cm"><!-- Written for Java 8 --></span>
<span class="w"> </span><span class="nt"><plugin></span>
<span class="w"> </span><span class="nt"><groupId></span>org.apache.maven.plugins<span class="nt"></groupId></span>
<span class="w"> </span><span class="nt"><artifactId></span>maven-compiler-plugin<span class="nt"></artifactId></span>
<span class="w"> </span><span class="nt"><version></span>3.3<span class="nt"></version></span>
<span class="w"> </span><span class="nt"><configuration></span>
<span class="w"> </span><span class="nt"><source></span>1.8<span class="nt"></source></span>
<span class="w"> </span><span class="nt"><target></span>1.8<span class="nt"></target></span>
<span class="w"> </span><span class="nt"></configuration></span>
<span class="w"> </span><span class="nt"></plugin></span>
<span class="w"> </span><span class="nt"></plugins></span>
<span class="w"> </span><span class="nt"></build></span>
<span class="w"> </span><span class="nt"><dependencies></span>
<span class="w"> </span><span class="cm"><!-- Guacamole Extension API --></span>
<span class="w"> </span><span class="nt"><dependency></span>
<span class="w"> </span><span class="nt"><groupId></span>org.apache.guacamole<span class="nt"></groupId></span>
<span class="w"> </span><span class="nt"><artifactId></span>guacamole-ext<span class="nt"></artifactId></span>
<span class="w"> </span><span class="nt"><version></span>1.5.3<span class="nt"></version></span>
<span class="w"> </span><span class="nt"><scope></span>provided<span class="nt"></scope></span>
<span class="w"> </span><span class="nt"></dependency></span>
<span class="w"> </span><span class="cm"><!-- Slf4j API --></span>
<span class="w"> </span><span class="cm"><!-- This is needed only if your listener wants to </span>
<span class="cm"> write to the Guacamole web application log --></span>
<span class="w"> </span><span class="nt"><dependency></span>
<span class="w"> </span><span class="nt"><groupId></span>org.slf4j<span class="nt"></groupId></span>
<span class="w"> </span><span class="nt"><artifactId></span>slf4j-api<span class="nt"></artifactId></span>
<span class="w"> </span><span class="nt"><version></span>1.7.7<span class="nt"></version></span>
<span class="w"> </span><span class="nt"><scope></span>provided<span class="nt"></scope></span>
<span class="w"> </span><span class="nt"></dependency></span>
<span class="w"> </span><span class="nt"></dependencies></span>
<span class="nt"></project></span>
</pre></div>
</div>
<p>Naturally, we need the actual listener extension skeleton code. While you can
put this in whatever file and package you want, for the sake of this tutorial,
we will assume you are using <code class="docutils literal notranslate"><span class="pre">org.apache.guacamole.event.TutorialListener</span></code>.</p>
<p>For now, we won’t actually do anything other than log the fact that an event
notification was received. At this point, we’re just creating the skeleton for
our listener extension.</p>
<div class="highlight-java notranslate"><div class="highlight"><pre><span></span><span class="kn">package</span><span class="w"> </span><span class="nn">org.apache.guacamole.event</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.GuacamoleException</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.net.event.listener.Listener</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.slf4j.Logger</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.slf4j.LoggerFactory</span><span class="p">;</span>
<span class="cm">/**</span>
<span class="cm"> * A Listener implementation intended to demonstrate basic use</span>
<span class="cm"> * of Guacamole's listener extension API.</span>
<span class="cm"> */</span>
<span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">TutorialListener</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Listener</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Logger</span><span class="w"> </span><span class="n">logger</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>
<span class="w"> </span><span class="n">LoggerFactory</span><span class="p">.</span><span class="na">getLogger</span><span class="p">(</span><span class="n">TutorialListener</span><span class="p">.</span><span class="na">class</span><span class="p">);</span>
<span class="w"> </span><span class="nd">@Override</span>
<span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">handleEvent</span><span class="p">(</span><span class="n">Object</span><span class="w"> </span><span class="n">event</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">GuacamoleException</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">logger</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">"received Guacamole event notification"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
<p>To conform with Maven, this skeleton file must be placed within
<code class="docutils literal notranslate"><span class="pre">src/main/java/org/apache/guacamole/event</span></code> as <code class="docutils literal notranslate"><span class="pre">TutorialListener.java</span></code>.</p>
<p>As you can see, implementing a listener is quite simple. There is a single
<code class="docutils literal notranslate"><span class="pre">Listener</span></code> interface to implement. All Guacamole event notifications will be
delivered to your code by invoking the handleEvent method. We will see shortly
how to use the passed event object to get the details of the event itself.</p>
<p>The only remaining piece for the overall skeleton to be complete is a
<code class="docutils literal notranslate"><span class="pre">guac-manifest.json</span></code> file. <em>This file is absolutely required for all Guacamole
extensions.</em> The <code class="docutils literal notranslate"><span class="pre">guac-manifest.json</span></code> format is described in more detail in
<a class="reference internal" href="guacamole-ext.html"><span class="doc std std-doc">guacamole-ext</span></a>. It provides for quite a few properties, but for our listener
extension we are mainly interested in the Guacamole version sanity check (to
make sure an extension built for the API of Guacamole version X is not
accidentally used against version Y) and telling Guacamole where to find our
listener class.</p>
<p>The Guacamole extension format requires that <code class="docutils literal notranslate"><span class="pre">guac-manifest.json</span></code> be placed in
the root directory of the extension <code class="docutils literal notranslate"><span class="pre">.jar</span></code> file. To accomplish this with Maven,
we place it within the <code class="docutils literal notranslate"><span class="pre">src/main/resources</span></code> directory. Maven will automatically
pick it up during the build and include it within the <code class="docutils literal notranslate"><span class="pre">.jar</span></code>.</p>
<div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
<span class="w"> </span><span class="nt">"guacamoleVersion"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"1.5.3"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Tutorial Listener Extension"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"namespace"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"guac-listener-tutorial"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"listeners"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="s2">"org.apache.guacamole.event.TutorialListener"</span>
<span class="w"> </span><span class="p">]</span>
<span class="p">}</span>
</pre></div>
</div>
</section>
<section id="building-the-extension">
<span id="custom-listener-building"></span><h2>Building the extension<a class="headerlink" href="#building-the-extension" title="Permalink to this heading"></a></h2>
<p>Once all three of the above files are in place, the extension should build
successfully even though it is just a skeleton at this point.</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>mvn<span class="w"> </span>package
<span class="go">[INFO] Scanning for projects...</span>
<span class="go">[INFO] ---------------------------------------------------------------</span>
<span class="go">[INFO] Building guacamole-listener-tutorial 1.5.3</span>
<span class="go">[INFO] ---------------------------------------------------------------</span>
<span class="go">...</span>
<span class="go">[INFO] ---------------------------------------------------------------</span>
<span class="go">[INFO] BUILD SUCCESS</span>
<span class="go">[INFO] ---------------------------------------------------------------</span>
<span class="go">[INFO] Total time: 1.297 s</span>
<span class="go">[INFO] Finished at: 2017-10-08T13:12:39-04:00</span>
<span class="go">[INFO] Final Memory: 19M/306M</span>
<span class="go">[INFO] ---------------------------------------------------------------</span>
<span class="gp">$</span>
</pre></div>
</div>
<p>Assuming you see the “<code class="docutils literal notranslate"><span class="pre">BUILD</span> <span class="pre">SUCCESS</span></code>” message when you build the extension,
there will be a new file, <code class="docutils literal notranslate"><span class="pre">target/guacamole-listener-tutorial-1.5.3.jar</span></code>, which
can be installed within Guacamole (see <a class="reference internal" href="#custom-listener-installing"><span class="std std-ref">Installing the extension</span></a> at the
end of this chapter). It should log event notifications that occur during, for
example, authentication attempts. If you changed the name or version of the
project in the <code class="docutils literal notranslate"><span class="pre">pom.xml</span></code> file, the name of this new <code class="docutils literal notranslate"><span class="pre">.jar</span></code> file will be
different, but it can still be found within <code class="docutils literal notranslate"><span class="pre">target/</span></code>.</p>
</section>
<section id="handling-events">
<span id="custom-listener-event-handling"></span><h2>Handling events<a class="headerlink" href="#handling-events" title="Permalink to this heading"></a></h2>
<p>The Guacamole <code class="docutils literal notranslate"><span class="pre">Listener</span></code> interface represents a low-level event handling API. A
listener is notified of every event generated by Guacamole. The listener must
examine the event type to determine whether the event is of interest, and if so
to dispatch the event to the appropriate entry point.</p>
<p>The event types that can be produced by Guacamole are described in the
<code class="docutils literal notranslate"><span class="pre">org.apache.guacamole.net.event</span></code> package of the guacamole-ext API. In this
package you will find several concrete event types as well as interfaces that
describe common characteristics of certain of event types. You can use any of
these types to distinguish the events received by your listener, and to examine
properties of an event of a given type.</p>
<p>Suppose we wish to log authentication success and failure events, while
ignoring all other event types. The <code class="docutils literal notranslate"><span class="pre">AuthenticationSuccessEvent</span></code> and
<code class="docutils literal notranslate"><span class="pre">AuthenticationFailureEvent</span></code> types are used to notify a listener of
authentication events. We can simply check whether a received event is of one
of these types and, if so, log an appropriate message.</p>
<div class="highlight-java notranslate"><div class="highlight"><pre><span></span><span class="kn">package</span><span class="w"> </span><span class="nn">org.apache.guacamole.event</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.GuacamoleException</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.net.event.AuthenticationFailureEvent</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.net.event.AuthenticationSuccessEvent</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.net.event.listener.Listener</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.slf4j.Logger</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.slf4j.LoggerFactory</span><span class="p">;</span>
<span class="cm">/**</span>
<span class="cm"> * A Listener that logs authentication success and failure events.</span>
<span class="cm"> */</span>
<span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">TutorialListener</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Listener</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Logger</span><span class="w"> </span><span class="n">logger</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>
<span class="w"> </span><span class="n">LoggerFactory</span><span class="p">.</span><span class="na">getLogger</span><span class="p">(</span><span class="n">TutorialListener</span><span class="p">.</span><span class="na">class</span><span class="p">);</span>
<span class="w"> </span><span class="nd">@Override</span>
<span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">handleEvent</span><span class="p">(</span><span class="n">Object</span><span class="w"> </span><span class="n">event</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">GuacamoleException</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">event</span><span class="w"> </span><span class="k">instanceof</span><span class="w"> </span><span class="n">AuthenticationSuccessEvent</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">logger</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">"successful authentication for user {}"</span><span class="p">,</span><span class="w"> </span>
<span class="w"> </span><span class="p">((</span><span class="n">AuthenticationSuccessEvent</span><span class="p">)</span><span class="w"> </span><span class="n">event</span><span class="p">)</span>
<span class="w"> </span><span class="p">.</span><span class="na">getCredentials</span><span class="p">().</span><span class="na">getUsername</span><span class="p">());</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">event</span><span class="w"> </span><span class="k">instanceof</span><span class="w"> </span><span class="n">AuthenticationFailureEvent</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">logger</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">"failed authentication for user {}"</span><span class="p">,</span><span class="w"> </span>
<span class="w"> </span><span class="p">((</span><span class="n">AuthenticationFailureEvent</span><span class="p">)</span><span class="w"> </span><span class="n">event</span><span class="p">)</span>
<span class="w"> </span><span class="p">.</span><span class="na">getCredentials</span><span class="p">().</span><span class="na">getUsername</span><span class="p">());</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
<p>In our example, we use <code class="docutils literal notranslate"><span class="pre">instanceof</span></code> to check for the two event types of
interest to our listener. Once we have identified an event of interest, we can
safely cast the event type to access properties of the event.</p>
<p>The extension is now complete and can be built as described earlier in
<a class="reference internal" href="#custom-listener-building"><span class="std std-ref">Building the extension</span></a> and installed as described below in
<a class="reference internal" href="#custom-listener-installing"><span class="std std-ref">Installing the extension</span></a>.</p>
</section>
<section id="influencing-guacamole-by-event-veto">
<span id="custom-listener-veto"></span><h2>Influencing Guacamole by event veto<a class="headerlink" href="#influencing-guacamole-by-event-veto" title="Permalink to this heading"></a></h2>
<p>An implementation of the handleEvent method is permitted to throw any
<code class="docutils literal notranslate"><span class="pre">GuacamoleException</span></code>. For certain <em>vetoable</em> event types, throwing a
<code class="docutils literal notranslate"><span class="pre">GuacamoleException</span></code> serves to effectively veto the action that resulted in the
event notification. See the API documentation for guacamole-ext to learn more
about vetoable event types.</p>
<p>As an (admittedly contrived) example, suppose we want to prevent a user named
“guacadmin” from accessing Guacamole. For whatever reason, we don’t wish to
remove or disable the auth database entry for this user. In this case we can
use a listener to block this user, preventing access to Guacamole. In the
listener, when we get an <code class="docutils literal notranslate"><span class="pre">AuthenticationSuccessEvent</span></code> we can check to see if
the user is “guacadmin” and, if so, throw an exception to prevent this user
from logging in to Guacamole.</p>
<div class="highlight-java notranslate"><div class="highlight"><pre><span></span><span class="kn">package</span><span class="w"> </span><span class="nn">org.apache.guacamole.event</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.GuacamoleException</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.GuacamoleSecurityException</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.net.event.AuthenticationFailureEvent</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.net.event.AuthenticationSuccessEvent</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.guacamole.net.event.listener.Listener</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.slf4j.Logger</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.slf4j.LoggerFactory</span><span class="p">;</span>
<span class="cm">/**</span>
<span class="cm"> * A Listener that logs authentication success and failure events</span>
<span class="cm"> * and prevents the "guacadmin" user from logging in by throwing</span>
<span class="cm"> * a GuacamoleSecurityException.</span>
<span class="cm"> */</span>
<span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">TutorialListener</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Listener</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Logger</span><span class="w"> </span><span class="n">logger</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>
<span class="w"> </span><span class="n">LoggerFactory</span><span class="p">.</span><span class="na">getLogger</span><span class="p">(</span><span class="n">TutorialListener</span><span class="p">.</span><span class="na">class</span><span class="p">);</span>
<span class="w"> </span><span class="nd">@Override</span>
<span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">handleEvent</span><span class="p">(</span><span class="n">Object</span><span class="w"> </span><span class="n">event</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">GuacamoleException</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">event</span><span class="w"> </span><span class="k">instanceof</span><span class="w"> </span><span class="n">AuthenticationSuccessEvent</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">username</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">((</span><span class="n">AuthenticationSuccessEvent</span><span class="p">)</span><span class="w"> </span><span class="n">event</span><span class="p">)</span>
<span class="w"> </span><span class="p">.</span><span class="na">getCredentials</span><span class="p">().</span><span class="na">getUsername</span><span class="p">();</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="s">"guacadmin"</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">username</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">logger</span><span class="p">.</span><span class="na">warn</span><span class="p">(</span><span class="s">"user {} has been blocked"</span><span class="p">,</span><span class="w"> </span><span class="n">username</span><span class="p">);</span>
<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">GuacamoleSecurityException</span><span class="p">(</span>
<span class="w"> </span><span class="s">"User '"</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">username</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">"' is currently blocked"</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">logger</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">"successful authentication for user {}"</span><span class="p">,</span><span class="w"> </span><span class="n">username</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">event</span><span class="w"> </span><span class="k">instanceof</span><span class="w"> </span><span class="n">AuthenticationFailureEvent</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">logger</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">"failed authentication for user {}"</span><span class="p">,</span><span class="w"> </span>
<span class="w"> </span><span class="p">((</span><span class="n">AuthenticationFailureEvent</span><span class="p">)</span><span class="w"> </span><span class="n">event</span><span class="p">)</span>
<span class="w"> </span><span class="p">.</span><span class="na">getCredentials</span><span class="p">().</span><span class="na">getUsername</span><span class="p">());</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
<p>If our Guacamole user database contains a user named “guacadmin”, and we build
and install this listener extension, we will find that an attempt to log in as
this user now results in a message in the UI indicating that the user is
blocked. If we examine the Guacamole log, we will see the message indicating
that the user was blocked. Because the successful authentication was vetoed,
Guacamole sends a subsequent authentication failure notification, which we see
logged as well.</p>
</section>
<section id="installing-the-extension">
<span id="custom-listener-installing"></span><h2>Installing the extension<a class="headerlink" href="#installing-the-extension" title="Permalink to this heading"></a></h2>
<p>Guacamole extensions are self-contained <code class="docutils literal notranslate"><span class="pre">.jar</span></code> files which are installed by
being placed within <code class="docutils literal notranslate"><span class="pre">GUACAMOLE_HOME/extensions</span></code>, and this extension is no
different. As described in <a class="reference internal" href="configuring-guacamole.html"><span class="doc std std-doc">Configuring Guacamole</span></a>, <code class="docutils literal notranslate"><span class="pre">GUACAMOLE_HOME</span></code> is a
placeholder used to refer to the directory that Guacamole uses to locate its
configuration files and extensions. Typically, this will be the <code class="docutils literal notranslate"><span class="pre">.guacamole</span></code>
directory within the home directory of the user running Tomcat.</p>
<p>To install your extension, copy the
<code class="docutils literal notranslate"><span class="pre">target/guacamole-listener-tutorial-1.5.3.jar</span></code> file into
<code class="docutils literal notranslate"><span class="pre">GUACAMOLE_HOME/extensions</span></code> and restart Tomcat. Guacamole will automatically
load your extension, logging an informative message that it has done so:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Extension "Tutorial Listener Extension" loaded.
</pre></div>
</div>
</section>
</section>
</div>
</div>
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
<a href="custom-auth.html" class="btn btn-neutral float-left" title="Custom authentication" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
<a href="writing-you-own-guacamole-app.html" class="btn btn-neutral float-right" title="Writing your own Guacamole application" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
</div>
<hr/>
<div role="contentinfo">
<p>Copyright © 2023 <a href="http://www.apache.org/">The Apache Software Foundation</a>,
Licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>.
Apache Guacamole, Guacamole, Apache, the Apache feather logo, and the Apache Guacamole project logo are
trademarks of The Apache Software Foundation.</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
<!-- Google Analytics -->
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-75289145-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>