[X2go-Commits] x2gobroker.git - build-main (branch) updated: 0.0.0.5-71-g8676299

X2Go dev team git-admin at x2go.org
Wed Mar 6 11:47:12 CET 2013


The branch, build-main has been updated
       via  8676299cde99da2c19307431c2a81f46b16b31dc (commit)
       via  bea0b745fc4d5842b715684d00b8fea7ea79a5f8 (commit)
       via  620cf778695448fb38394e5b476974aa1815c639 (commit)
       via  cc0ae3ddb38a1212ab5e63715344e7befdc4b544 (commit)
       via  aa84ed46e7d201c60b72b70b2be26c02bdbcecee (commit)
       via  d1b123d29807d11997c79697ac7b998264ed2fea (commit)
       via  83b347f272687471a93d46449704893256ca88de (commit)
       via  1333f57a70b1c03a127d6995cdc0ddd171c19b84 (commit)
       via  f395518f6ce01794e96aae35014225d29c594505 (commit)
       via  eb322fcd5727c3e9d6f80da831f9e0f177f589c9 (commit)
       via  dcdbce376f33afe485535d4bca6cb39f06b91c12 (commit)
       via  ea189372b4d143c6937ffb5a682a9ba0c3eb281c (commit)
       via  63ca4fa3a6bccff85f1ebc51548cd8a1b95c62af (commit)
       via  f744b3940f0b8cf705b9add326ed67703ee07243 (commit)
       via  ff5809f40d1735d77dc0f97936934c0f9927f433 (commit)
       via  30c5d7913cd73b6172673983ee37160f3a5c9f71 (commit)
       via  e9fd1798964afb34fdd2d11a1ec5b0d23628ff65 (commit)
       via  93a9f3f41318e472eb0ac964126a7189f4c80897 (commit)
       via  4a05d3411c0da68f013201e68b396f77a082344e (commit)
       via  01f3a1d9c79f9bf60a28e1c20250afdb5abc09f0 (commit)
       via  391fa36de255a6e17e817fe7127d85f81db015ee (commit)
       via  b6a0588c151b322dfcf2418b6d6df289946710d1 (commit)
       via  9fcd2186c96f07d41302fae68112e9b3dd7fb66e (commit)
       via  838bdf9fccfd43bae4b23ccc141d75fccaeaa7de (commit)
       via  e468013decdfd46a467b5dc7f66757f5a91fdd1b (commit)
       via  20c45b24317270cad9d201b307f2a2041bf8b57d (commit)
       via  fee39f5a3c8de2ab8ac1d3c32e534a568aa50f74 (commit)
       via  fa1b06962977c3c10d90af3544e2d1aa8a7ac4e4 (commit)
       via  f74dbc86da2fee649b2c8e75104e7b45885742d4 (commit)
       via  fdfc849b6c698e4008356fc4b35c206d7fceb73c (commit)
       via  a72d1e92b9dfdde11637cf1a10022a8685e16075 (commit)
       via  1cd026fbcc45b4688fcc88ea7be6dcc52e6488a7 (commit)
       via  41fd2fad0c72117701608c83ced9b6e35221e9fd (commit)
       via  54b7e70d862107b1d2142c7313b3d06335cace2b (commit)
       via  58096be962fdb8663cd5e99d7bd1307c904049fe (commit)
       via  ccf7a7ca308499ef909dcad891ecef4957f01abc (commit)
       via  341213001583b07248fad92073941705891eb53f (commit)
       via  d46aa2e207f31deda85dc0f896d53989cc244fe3 (commit)
       via  993c8cda4b2aa59cf181cb54d2fa5dfc90102c53 (commit)
       via  cacfe491f405d6db68079b6166d729f4c502d0dd (commit)
       via  8b21bebbe426c5fc64b692933072e99724f479b2 (commit)
       via  428601765689aafc634b6be05b4619e7583cf2e6 (commit)
       via  fd53c4a42a79daf8dd3dc209dea8a475599beed6 (commit)
       via  f23bc42116e94fc7893668f247363d753cc9fd77 (commit)
       via  679f5a07c67afada94887db1c009458107e00209 (commit)
       via  e388fd69d4fd75c8e417eee4ffdc6dd2bb1169de (commit)
       via  185d624e21315ba72cfafac8fa31ac84b8e759b0 (commit)
       via  a026dbd05c0a0d63997c23a9d471141345c56263 (commit)
       via  f91a06f160bde0b4da9bcd320c0885252bbe689a (commit)
       via  ef21a2cdd0fdba3efcd769c93f63d69a2bb377da (commit)
       via  5c50b38d3bba839076228f8aec49823092c3c0f6 (commit)
       via  8efcb47747dd16ce1ad139c0cc47a5e4ebea6e9a (commit)
       via  687fc56ef8103ed2404a29ad00d14e5e7eaf5cf7 (commit)
       via  cf4527bfd7d98fc9c46339c61d1239522f9cbdf9 (commit)
       via  762de5b6b9eea0241adbb7c72495368c16d32605 (commit)
       via  82844d78ad379d8d3e90cdcb1c0808dd7995a045 (commit)
       via  0d33c73d2733c7e7d11d1aa262caa25014a1d523 (commit)
       via  e7f633c94f471c930d27d8fb95b523f701842e33 (commit)
       via  65db3d550c384ff0afbc0d9e97855ac1bf4bbea7 (commit)
       via  c6fbb4cf743a450b4a251bf39e86822662b17a73 (commit)
       via  51cb5974db42ea58b28b4b520337fa00a0a80eab (commit)
       via  6c69ef04e6fda090552dfb0fd09152b2a8aee96a (commit)
       via  5347ab0c3c00f99e3f30e9f274f5082c63b94b10 (commit)
       via  94323f6767cc8f3258f03a4160dba4cf6e166d07 (commit)
       via  74481c6695dfabb5d98b3eba35956268854e0a0b (commit)
       via  a62fb92ed282ca25ded2e7782ee79386f9e68e7d (commit)
       via  8d008dbadaab7527ccd85580056eb3cbcc16b9fe (commit)
       via  c671ef9817d26b7a8cf0350558dd6887f9ad1787 (commit)
       via  ef381eb4cb55fbdfb842f5f9fdcf9037e3f8caa6 (commit)
       via  7aa900c55a6c37ef74d4affe47d120fd488e1511 (commit)
       via  0955ff2dbaa37f3f0d7cfa95004e635b083116d6 (commit)
      from  7e5e1ad8a8a887538da50ace3749a61316b28e99 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 8676299cde99da2c19307431c2a81f46b16b31dc
Author: Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
Date:   Wed Mar 6 11:46:30 2013 +0100

    release 0.0.0.6

-----------------------------------------------------------------------

Summary of changes:
 debian/changelog                            |   26 +++-
 debian/control                              |    6 +-
 debian/python-x2gobroker.default            |   13 +-
 debian/python-x2gobroker.postrm             |   39 ------
 debian/x2gobroker-agent.postinst            |   16 +--
 debian/x2gobroker-agent.postrm              |   10 +-
 debian/x2gobroker-authservice.default       |    9 ++
 debian/x2gobroker-daemon.default            |   11 ++
 debian/x2gobroker-daemon.postinst           |   20 ++-
 debian/x2gobroker-daemon.postrm             |   10 +-
 etc/broker/x2gobroker-sessionprofiles.conf  |    6 +
 etc/x2gobroker.conf                         |   61 +++++++--
 lib/x2gobroker-agent.pl                     |   79 +++++++----
 sbin/x2gobroker                             |   67 ++++++----
 sbin/x2gobroker-pubkeyauthorizer            |    2 +-
 x2gobroker/__init__.py                      |    2 +-
 x2gobroker/agent.py                         |  144 +++++++++++++++++++-
 x2gobroker/authmechs/pam_authmech.py        |   17 ++-
 x2gobroker/brokers/base_broker.py           |  189 ++++++++++++++++++++-------
 x2gobroker/brokers/inifile_broker.py        |   13 +-
 x2gobroker/defaults.py                      |    8 +-
 x2gobroker/loggers.py                       |   19 +++
 x2gobroker/tests/test_web_plain_base.py     |   27 ++--
 x2gobroker/tests/test_web_plain_inifile.py  |    6 +-
 x2gobroker/tests/test_web_plain_zeroconf.py |   16 +--
 x2gobroker/web/extras.py                    |    9 +-
 x2gobroker/web/html.py                      |   45 +++----
 x2gobroker/web/json.py                      |    4 +-
 x2gobroker/web/plain.py                     |   71 +++++-----
 29 files changed, 656 insertions(+), 289 deletions(-)
 delete mode 100755 debian/python-x2gobroker.postrm

The diff of changes is:
diff --git a/debian/changelog b/debian/changelog
index ceb4877..872f3aa 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,27 @@
+x2gobroker (0.0.0.6-0~x2go1) unstable; urgency=low
+
+  [ Mike Gabriel ]
+  * New upstream version (0.0.0.6):
+    - Rewrite empty user parameter in session profile. Fill in
+      the UID that has been used for broker authentication.
+    - Implement session autologin feature. (Fixes: #134).
+    - If X2Go Session Broker's PAM Auth Service is not available, try to
+      fallback to direct PAM authentication (only works if x2gobroker
+      runs as super-user root).
+    - Switch from webpy to using tornado as http engine.
+    - Divert tornado log requests into the broker's logger instances.
+    - Add workaround to handle bug #138 in x2goclient.
+  * /debian/*.default:
+    - Be more explanatory about the X2GOBROKER_DEBUG option and allow
+      to enable debug mode for the different services independently.
+      (Fixes: #126).
+  * /debian/x2gobroker-daemon.postinst:
+    - Fix home path for user x2gobroker. (Fixes: #127).
+  * /debian/control:
+    - Dependency for python-x2gobroker: python-gevent.
+
+ -- Mike Gabriel <mike.gabriel at das-netzwerkteam.de>  Wed, 06 Mar 2013 11:46:06 +0100
+
 x2gobroker (0.0.0.5-0~x2go1) unstable; urgency=low
 
   [ Mike Gabriel ]
@@ -22,7 +46,7 @@ x2gobroker (0.0.0.5-0~x2go1) unstable; urgency=low
 x2gobroker (0.0.0.4-0~x2go1) unstable; urgency=low
 
   * New upstream version (0.0.0.4):
-    - Capture DNS resolver failures on client ACLs in case the
+    - Capture DNS resolver failures on client ACLs in cases where
       one of the listed hostnames in one client ACL definition is
       not resolvable. Such a failure will deny access to the
       corresponding session profile.
diff --git a/debian/control b/debian/control
index 3190f5c..ece5439 100644
--- a/debian/control
+++ b/debian/control
@@ -9,7 +9,7 @@ Build-Depends:
  debhelper (>= 7.0.50~),
  cdbs (>= 0.4.90~),
  dpkg-dev (>= 1.16.1~),
- python (>= 2.6.6-3~),
+ python (>= 2.6.5-0~),
  python-setuptools,
 Standards-Version: 3.9.3
 XS-Python-Version: >= 2.4
@@ -22,7 +22,7 @@ Depends:
  python,
  python-argparse,
  python-setproctitle,
- python-webpy (>= 1:0.37),
+ python-tornado,
  python-x2gobroker (>= ${source:Version}), python-x2gobroker (<< ${source:Version}.1~),
 Suggests:
  apache2 | httpd,
@@ -150,7 +150,7 @@ Depends:
  ${python:Depends},
  python-pampy,
  python-netaddr,
- python-webpy (>= 1:0.37),
+ python-tornado,
  python-paramiko,
 Suggests:
  apache2 | httpd,
diff --git a/debian/python-x2gobroker.default b/debian/python-x2gobroker.default
index 0907b79..67b93cb 100644
--- a/debian/python-x2gobroker.default
+++ b/debian/python-x2gobroker.default
@@ -11,10 +11,15 @@
 # remote X2Go Servers.
 #X2GOBROKER_AGENT_USER=x2gobroker
 
-# Run X2Go Session Broker in debug mode, this will make the broker
-# available through http GET method calls (otherwise: POST method
-# only) and you will be able to test the broker through your web
-# browser (0=disable, 1=enable).
+# Control debug mode (0=disable, 1=enable).
+#
+# Apart from verbose logging in /var/log/x2gobroker/*.log, this will
+# also make the broker available through http GET method requests
+# (otherwise: POST method requests only) and you will be able to test
+# the broker through your web browser
+#
+# This value has an effect on all (Python-based) X2Go Session Broker
+# services and can be overridden in /etc/default/x2gobroker-* files.
 #X2GOBROKER_DEBUG=0
 
 # Default X2Go Session Broker backend (available: zeroconf, inifile)
diff --git a/debian/python-x2gobroker.postrm b/debian/python-x2gobroker.postrm
deleted file mode 100755
index 22abe93..0000000
--- a/debian/python-x2gobroker.postrm
+++ /dev/null
@@ -1,39 +0,0 @@
-#! /bin/sh
-# postrm script for x2gobroker-agent
-#
-# see: dh_installdeb(1)
-# summary of how this script can be called:
-#        * <postrm> `remove'
-#        * <postrm> `purge'
-#        * <old-postrm> `upgrade' <new-version>
-#        * <new-postrm> `failed-upgrade' <old-version>
-#        * <new-postrm> `abort-install'
-#        * <new-postrm> `abort-install' <old-version>
-#        * <new-postrm> `abort-upgrade' <old-version>
-#        * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
-# for details, see /usr/share/doc/packaging-manual/
-
-set -e
-
-
-case "$1" in
-	purge)
-		if dpkg-statoverride --list /var/log/x2gobroker >/dev/null; then
-			dpkg-statoverride --remove /var/log/x2gobroker
-		fi
-
-		;;
-	remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
-		;;
-	*)
-		echo "postrm called with unknown argument \`$1'" >&2
-		exit 0
-		;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
diff --git a/debian/x2gobroker-agent.postinst b/debian/x2gobroker-agent.postinst
index 3f7c1ed..a9c761b 100755
--- a/debian/x2gobroker-agent.postinst
+++ b/debian/x2gobroker-agent.postinst
@@ -25,19 +25,19 @@ case "$1" in
 	configure)
 
 		# setup user/group x2gobroker
-		if ! getent group x2gobroker >/dev/null; then
-			echo "Creating x2gobroker group." >&2
+		if ! getent group x2gobroker 1>/dev/null; then
+			echo "Creating x2gobroker group." 1>&2
 			addgroup --system x2gobroker
 		else
-			echo "Group x2gobroker already exists." >&2
+			echo "Group x2gobroker already exists." 1>&2
 		fi
-		if ! getent passwd x2gobroker >/dev/null; then
-			echo "Creating x2gobroker user." >&2
+		if ! getent passwd x2gobroker 1>/dev/null; then
+			echo "Creating x2gobroker user." 1>&2
 			adduser --system \
 			        --disabled-password --disabled-login \
 			        --shell /bin/bash --group --home /var/lib/x2gobroker x2gobroker
 		else
-			echo "User x2gobroker already exists." >&2
+			echo "User x2gobroker already exists." 1>&2
 			# make sure all settings are appropriate
 			if getent passwd x2gobroker | grep /dev/null 1>/dev/null 2>/dev/null; then
 				mkdir -p /var/lib/x2gobroker
@@ -51,7 +51,7 @@ case "$1" in
 		fi
 
 		# the x2gobroker-agent has to be installed setuid root
-		if ! dpkg-statoverride --list /usr/lib/x2go/x2gobroker-agent >/dev/null; then
+		if ! dpkg-statoverride --list /usr/lib/x2go/x2gobroker-agent 1>/dev/null; then
 		    dpkg-statoverride --add --update root x2gobroker 4750 /usr/lib/x2go/x2gobroker-agent
 		fi
 
@@ -61,7 +61,7 @@ case "$1" in
 		;;
 
 	*)
-		echo "postinst called with unknown argument \`$1'" >&2
+		echo "postinst called with unknown argument \`$1'" 1>&2
 		exit 1
 		;;
 esac
diff --git a/debian/x2gobroker-agent.postrm b/debian/x2gobroker-agent.postrm
index a9dc429..bc535ee 100755
--- a/debian/x2gobroker-agent.postrm
+++ b/debian/x2gobroker-agent.postrm
@@ -18,15 +18,15 @@ set -e
 
 case "$1" in
 	purge)
-		if dpkg-statoverride --list /usr/lib/x2go/x2gobroker-agent >/dev/null; then
+		if dpkg-statoverride --list /usr/lib/x2go/x2gobroker-agent 1>/dev/null; then
 			dpkg-statoverride --remove /usr/lib/x2go/x2gobroker-agent
 		fi
 
 		if [ ! -d /usr/share/doc/x2gobroker-daemon ]; then
 			# remove user/group x2gobroker from system (if not in use by x2gobroker-daemon
-			getent passwd x2gobroker >/dev/null && deluser x2gobroker
-			getent group x2gobroker >/dev/null && delgroup x2gobroker
-			getent group x2gobroker >/dev/null && delgroup x2gobroker
+			getent passwd x2gobroker 1>/dev/null && deluser x2gobroker
+			getent group x2gobroker 1>/dev/null && delgroup x2gobroker
+			getent group x2gobroker 1>/dev/null && delgroup x2gobroker
 			rm -Rfv /var/lib/x2gobroker
 		fi
 
@@ -34,7 +34,7 @@ case "$1" in
 	remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
 		;;
 	*)
-		echo "postrm called with unknown argument \`$1'" >&2
+		echo "postrm called with unknown argument \`$1'" 1>&2
 		exit 0
 		;;
 esac
diff --git a/debian/x2gobroker-authservice.default b/debian/x2gobroker-authservice.default
index 017b75c..e8b5f7b 100644
--- a/debian/x2gobroker-authservice.default
+++ b/debian/x2gobroker-authservice.default
@@ -8,3 +8,12 @@
 # you can disable the authentication service here.
 START_AUTHSERVICE=true
 
+# Control debug mode (0=disable, 1=enable) of the X2Go Broker Authentication
+# Service.
+#
+# Logging is (by default) written to /var/log/x2gobroker/*log.
+#
+# This option can also be configured in /etc/default/python-x2go.
+# The value configured here overrides the value from python-x2go
+# defaults and only sets the x2gobroker-authservice into debug mode.
+#X2GOBROKER_DEBUG=0
diff --git a/debian/x2gobroker-daemon.default b/debian/x2gobroker-daemon.default
index eb76c1b..0eb0a81 100644
--- a/debian/x2gobroker-daemon.default
+++ b/debian/x2gobroker-daemon.default
@@ -6,6 +6,17 @@ START_BROKER=true
 # Bind standalone daemon to this address:port
 #DAEMON_BIND_ADDRESS=127.0.0.1:8080
 
+# Control debug mode (0=disable, 1=enable).
+#
+# Apart from verbose logging in /var/log/x2gobroker/*.log, this will
+# also make the broker available through http GET method requests
+# (otherwise: POST method requests only) and you will be able to
+# test the broker through your web browser.
+#
+# This option can also be configured in /etc/default/python-x2go.
+# The value configured here overrides the value from python-x2go
+# defaults and only sets the x2gobroker-daemon into debug mode.
+#X2GOBROKER_DEBUG=0
 
 ##########################################################
 ###                                                    ###
diff --git a/debian/x2gobroker-daemon.postinst b/debian/x2gobroker-daemon.postinst
index b37c2f5..cc5fb54 100755
--- a/debian/x2gobroker-daemon.postinst
+++ b/debian/x2gobroker-daemon.postinst
@@ -25,19 +25,19 @@ case "$1" in
 	configure)
 
 		# setup user/group x2gobroker
-		if ! getent group x2gobroker >/dev/null; then
-			echo "Creating x2gobroker group." >&2
+		if ! getent group x2gobroker 1>/dev/null; then
+			echo "Creating x2gobroker group." 1>&2
 			addgroup --system x2gobroker
 		else
-			echo "Group x2gobroker already exists." >&2
+			echo "Group x2gobroker already exists." 1>&2
 		fi
-		if ! getent passwd x2gobroker >/dev/null; then
-			echo "Creating x2gobroker user." >&2
+		if ! getent passwd x2gobroker 1>/dev/null; then
+			echo "Creating x2gobroker user." 1>&2
 			adduser --system \
 			        --disabled-password --disabled-login \
-			        --shell /bin/bash --group --home /var/lib/x2go x2gobroker
+			        --shell /bin/bash --group --home /var/lib/x2gobroker x2gobroker
 		else
-			echo "User x2gobroker already exists." >&2
+			echo "User x2gobroker already exists." 1>&2
 			# make sure all settings are appropriate
 			if getent passwd x2gobroker | grep /dev/null 1>/dev/null 2>/dev/null; then
 				mkdir -p /var/lib/x2gobroker
@@ -51,7 +51,7 @@ case "$1" in
 		fi
 
 		# the x2gobroker-daemon needs special permissions on its log directory
-		if ! dpkg-statoverride --list /var/log/x2gobroker >/dev/null; then
+		if ! dpkg-statoverride --list /var/log/x2gobroker 1>/dev/null; then
 			dpkg-statoverride --add --update x2gobroker adm 2750 /var/log/x2gobroker
 		fi
 
@@ -61,7 +61,7 @@ case "$1" in
 		;;
 
 	*)
-		echo "postinst called with unknown argument \`$1'" >&2
+		echo "postinst called with unknown argument \`$1'" 1>&2
 		exit 1
 		;;
 esac
@@ -72,5 +72,3 @@ esac
 #DEBHELPER#
 
 exit 0
-
-
diff --git a/debian/x2gobroker-daemon.postrm b/debian/x2gobroker-daemon.postrm
index 42f3752..34a8687 100755
--- a/debian/x2gobroker-daemon.postrm
+++ b/debian/x2gobroker-daemon.postrm
@@ -19,15 +19,15 @@ set -e
 case "$1" in
 	purge)
 
-		if dpkg-statoverride --list /var/log/x2gobroker >/dev/null; then
+		if dpkg-statoverride --list /var/log/x2gobroker 1>/dev/null; then
 			dpkg-statoverride --remove /var/log/x2gobroker
 		fi
 
 		if [ ! -d /usr/share/doc/x2gobroker-agent ]; then
 			# remove user/group x2gobroker from system (only if not in use by x2gobroker-agent
-			getent passwd x2gobroker >/dev/null && deluser x2gobroker
-			getent group x2gobroker >/dev/null && delgroup x2gobroker
-			getent group x2gobroker >/dev/null && delgroup x2gobroker
+			getent passwd x2gobroker 1>/dev/null && deluser x2gobroker
+			getent group x2gobroker 1>/dev/null && delgroup x2gobroker
+			getent group x2gobroker 1>/dev/null && delgroup x2gobroker
 			rm -Rf /var/lib/x2gobroker
 		fi
 
@@ -35,7 +35,7 @@ case "$1" in
 	remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
 		;;
 	*)
-		echo "postrm called with unknown argument \`$1'" >&2
+		echo "postrm called with unknown argument \`$1'" 1>&2
 		exit 0
 		;;
 esac
diff --git a/etc/broker/x2gobroker-sessionprofiles.conf b/etc/broker/x2gobroker-sessionprofiles.conf
index f3b4e8b..dc79a6b 100644
--- a/etc/broker/x2gobroker-sessionprofiles.conf
+++ b/etc/broker/x2gobroker-sessionprofiles.conf
@@ -92,6 +92,7 @@ acl-groups-deny=ALL
 acl-clients-deny=ALL
 acl-clients-allow=10.1.0.0/16
 acl-any-order=deny-allow
+broker-session-autologin=true
 
 [pool-A-server-B]
 user=
@@ -103,6 +104,7 @@ acl-groups-deny=ALL
 acl-clients-deny=ALL
 acl-clients-allow=10.2.0.0/16
 acl-any-order=deny-allow
+broker-session-autologin=true
 
 [pool-A-server-C]
 user=
@@ -112,6 +114,7 @@ command=KDE
 acl-groups-allow=kde-users,admins
 acl-groups-deny=ALL
 acl-any-order=deny-allow
+broker-session-autologin=true
 
 ##
 ## EXAMPLE: pool-B (e.g. webserver in the DMZ or on the internet)
@@ -170,3 +173,6 @@ acl-users-deny=ALL
 acl-groups-allow=students,admins
 acl-groups-deny=ALL
 acl-any-order=deny-allow
+# this server pool has a special broker setup for SSH authorized_keys
+broker-session-autologin=true
+broker-authorized-keys=/var/lib/x2gobroker/ssh/%u/authorized_keys
diff --git a/etc/x2gobroker.conf b/etc/x2gobroker.conf
index a1fc81a..24a530f 100644
--- a/etc/x2gobroker.conf
+++ b/etc/x2gobroker.conf
@@ -50,20 +50,20 @@
 # profile mapping in LDAP
 
 
-# Allow unauthenticated connections? Then set check_credentials to false.
+# Allow unauthenticated connections? Then set check-credentials to false.
 #check-credentials = true
 
 # To secure server-client communication the client can start the communication
-# with a pre-set, agreed on authentication ID. Set the below value to 1 to make
-# use of this feature
-#use-authid = false
+# with a pre-set, agreed on authentication ID. Set the below value to true
+# to make the X2Go Session Broker require this feature
+#require-cookie-auth = false
 
-# X2Go supports two different auth ID modes (static and dynamic), for now set
-# the below value to true
-#use-static-authid = true
+# X2Go supports two different cookie authentication modes (static and dynamic).
+#use-static-cookie = false
 
-# Make up your own authid below...
-#authid = <aaaavveeeerrrrryyyyylooonnnnggggssttrrriiinnnggg>
+# Every server-client communication (between X2Go Client and broker) has to be
+# accompanied by this initial authentication cookie.
+#my-cookie = <aaaavveeeerrrrryyyyylooonnnnggggssttrrriiinnnggg>
 
 # X2Go Session Broker knows about two output formats: a text/html based output
 # and a text/json based output. The different outputs run under different URLs
@@ -90,7 +90,48 @@
 # detection can be quite CPU intensive on the X2Go Broker server.
 #ignore-primary-group-memberships = true
 
-# default X2Go Broker Agent query mode:
+# X2Go session autologin via X2Go Session Broker
+#
+# Once authenticated against the session
+# broker, the user becomes a trusted user. That is, the X2Go session login can
+# be automatized by a very temporary SSH pub/priv key pair. Prior to the session
+# login the key is generated, after successful session login, the key is dropped
+# immediately.
+#
+# This option can be overridden by the session profile parameter
+# broker-session-autologin=<file-location>
+
+#default-session-autologin=false
+
+# X2Go's authorized_keys file for broker mediated autologin sessions
+#
+# For the X2Go auto-login via X2Go Session Broker feature to work thoroughly,
+# the X2Go Session Broker has to place the temporary public SSH key into the
+# user's home directory. It is not recommended to use SSH's default
+# authorized_keys file for this but a separate and X2Go-specific authorized_keys
+# file ($HOME/.x2go/authorized_keys).
+#
+# Of course, the SSH daemon has to be made aware of this. This can be configured
+# in /etc/ssh/sshd_config like this:
+#
+#    --- /etc/ssh/sshd_config.no-x2go        2013-03-01 09:57:04.000000000 +0100
+#    +++ /etc/ssh/sshd_config        2013-03-01 09:56:57.000000000 +0100
+#    @@ -28,7 +28,8 @@
+#
+#    RSAAuthentication yes
+#    PubkeyAuthentication yes
+#    AuthorizedKeysFile     %h/.ssh/authorized_keys
+#   +AuthorizedKeysFile2    %h/.x2go/authorized_keys
+#
+#    # Don't read the user's ~/.rhosts and ~/.shosts files
+#    IgnoreRhosts yes
+#
+# This option can be overridden by the session profile parameter
+# broker-authorized-keys=<file-location>
+
+#default-authorized-keys=%h/.x2go/authorized_keys
+
+# X2Go Broker Agent query mode
 #
 # The X2Go Broker Agent is needed for multi-server sites configured for
 # load balancing. Multi-server sites require a setup that uses the
diff --git a/lib/x2gobroker-agent.pl b/lib/x2gobroker-agent.pl
index 3155837..12f38d2 100755
--- a/lib/x2gobroker-agent.pl
+++ b/lib/x2gobroker-agent.pl
@@ -22,6 +22,8 @@
 
 use strict;
 
+use File::Basename;
+
 sub InitX2GoUser
 {
 	my ($user, $uidNumber, $gidNumber, $home)=@_;
@@ -49,32 +51,44 @@ sub InitX2GoUser
 	}
 }
 
-sub CreateKey
+sub AddAuthKey
 {
-	###
-	### FIXME: This bit of code looks dangerous... My
-	###        authorized_keys file is starting to get
-	###        scared... (Mike)
-	###
-	#my ($uidNumber, $gidNumber, $home)=@_;
-	#if ( ! -d "$home/.ssh" )
-	#{
-	#	mkdir ("$home/.ssh", 0700);
-	#	chown ($uidNumber, $gidNumber, "$home/.ssh");
-	#}
-	#if( -e "$home/.ssh/authorized_keys" )
-	#{
-	#	unlink("$home/.ssh/authorized_keys");
-	#}
-	#open my $save_out, ">&STDOUT";
-	#close (STDOUT);
-	#system "/usr/bin/ssh-keygen", "-t", "dsa", "-N","","-f","$home/.ssh/authorized_keys";
-	#open STDOUT, ">&", $save_out;
-	#open (F,"<$home/.ssh/authorized_keys");
-	#print <F>;
-	#close (F);
-	#unlink("$home/.ssh/authorized_keys");
-	#rename("$home/.ssh/authorized_keys.pub", "$home/.ssh/authorized_keys");
+	my ($uid, $uidNumber, $gidNumber, $home, $pubkey, $authkeyfile)=@_;
+
+	# rewrite %%, %u and %h in authkeyfile string
+	$authkeyfile =~ s/%u/$uid/;
+	$authkeyfile =~ s/%h/$home/;
+	$authkeyfile =~ s/%%/%/;
+
+	my $authkeydir = dirname($authkeyfile);
+	if ( ! $authkeyfile =~ m/\/.*/ )
+	{
+		$authkeyfile = "$home/$authkeyfile";
+	}
+
+	# make sure dir and file for authorized_keys do exist
+	system ("su - $uid -c \"mkdir -p $authkeydir\"");
+	system ("su - $uid -c \"touch $authkeyfile\"");
+	my $authorized_keys = `su - $uid -c \"cat $authkeyfile\"`;
+	if ( ! ( $authorized_keys =~ m/$pubkey/ ) )
+	{
+		system("su - $uid -c \"echo $pubkey >> $authkeyfile\"");
+	}
+}
+
+sub DelAuthKey
+{
+	my ($uid, $uidNumber, $gidNumber, $home, $pubkey, $authkeyfile)=@_;
+
+	# rewrite %%, %u and %h in authkeyfile string
+	$authkeyfile =~ s/%u/$uid/;
+	$authkeyfile =~ s/%h/$home/;
+	$authkeyfile =~ s/%%/%/;
+	if ( ! $authkeyfile =~ m/\/.*/ )
+	{
+		$authkeyfile = "$home/$authkeyfile";
+	}
+	system("su - $uid -c \"sed -e s\!'$pubkey'\!''\! -e '/^\$/d' -i $authkeyfile\" 1>/dev/null 2>/dev/null");
 }
 
 $< = $>;
@@ -150,11 +164,22 @@ if($mode eq 'getservers')
 	exec ("/bin/su - $uid -c \"x2gogetservers\"");
 }
 
-if($mode eq 'key')
+if($mode eq 'addauthkey')
+{
+	my $pubkey = shift or die;
+	my $authkeyfile = shift or die;
+	InitX2GoUser($uid, $uidNumber, $gidNumber, $home);
+	print "OK\n";
+	AddAuthKey($uid, $uidNumber, $gidNumber, $home, $pubkey, $authkeyfile);
+}
+
+if($mode eq 'delauthkey')
 {
+	my $pubkey = shift or die;
+	my $authkeyfile = shift or die;
 	InitX2GoUser($uid, $uidNumber, $gidNumber, $home);
 	print "OK\n";
-	createKey($uidNumber, $gidNumber, $home);
+	DelAuthKey($uid, $uidNumber, $gidNumber, $home, $pubkey, $authkeyfile);
 }
 
 if($mode eq 'suspend')
diff --git a/sbin/x2gobroker b/sbin/x2gobroker
index 66c51da..3951539 100755
--- a/sbin/x2gobroker
+++ b/sbin/x2gobroker
@@ -26,8 +26,10 @@ import sys
 import argparse
 import socket
 import logging
-import web
-from web.wsgiserver import CherryPyWSGIServer
+import thread
+import tornado.web
+import tornado.httpserver
+import tornado.ioloop
 
 try:
     import x2gobroker.defaults
@@ -37,7 +39,13 @@ except ImportError:
 
 from x2gobroker import __VERSION__
 from x2gobroker import __AUTHOR__
-from x2gobroker.loggers import logger_broker, logger_access, logger_error
+from x2gobroker.loggers import logger_broker, logger_access, logger_error, tornado_log_request
+
+# raise log level to DEBUG if requested...
+if x2gobroker.defaults.X2GOBROKER_DEBUG and not x2gobroker.defaults.X2GOBROKER_TESTSUITE:
+    logger_broker.setLevel(logging.DEBUG)
+    logger_access.setLevel(logging.DEBUG)
+    logger_error.setLevel(logging.DEBUG)
 
 logger_broker.info('X2Go Session Broker ({version}), written by {author}'.format(version=__VERSION__, author=__AUTHOR__))
 logger_broker.info('Setting up the broker\'s environment...')
@@ -49,12 +57,6 @@ logger_broker.info('  X2GOBROKER_DEFAULT_BACKEND: {value}'.format(value=x2gobrok
 logger_broker.info('  X2GOBROKER_SSL_CERTFILE: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_SSL_CERTFILE))
 logger_broker.info('  X2GOBROKER_SSL_KEYFILE: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_SSL_KEYFILE))
 
-# raise log level to DEBUG if requested...
-if x2gobroker.defaults.X2GOBROKER_DEBUG and not x2gobroker.defaults.X2GOBROKER_TESTSUITE:
-    logger_broker.setLevel(logging.DEBUG)
-    logger_access.setLevel(logging.DEBUG)
-    logger_error.setLevel(logging.DEBUG)
-
 # check effective UID the broker runs as and complain appropriately...
 if x2gobroker.defaults.X2GOBROKER_USER != x2gobroker.defaults.X2GOBROKER_DAEMON_USER and os.geteuid() != 0:
     logger_broker.warn('X2Go Session Broker has been started interactively by user {username}, better run as user {daemon_username}.'.format(username=x2gobroker.defaults.X2GOBROKER_USER, daemon_username=x2gobroker.defaults.X2GOBROKER_DAEMON_USER))
@@ -96,14 +98,15 @@ if __name__ == "__main__":
         x2gobroker.defaults.X2GOBROKER_DEBUG = cmdline_args.debug
 
     # some people just give the port but prepend a colon, webpy does not like this, so we strip if off
+    cmdline_args.bind = cmdline_args.bind.lstrip('*')
     cmdline_args.bind = cmdline_args.bind.lstrip(':')
 
-    # tweak cmdline args so that they become suitable for the web.py framework
-    if len(sys.argv) <= 1:
-        sys.argv.append('')
-        sys.argv.append('')
-    sys.argv[1] = cmdline_args.bind
-    sys.argv[2:] = []
+    if ':' in cmdline_args.bind:
+        bind_address, bind_port = cmdline_args.bind.split(':')[0:2]
+        bind_port = int(bind_port)
+    else:
+        bind_address = None
+        bind_port = int(cmdline_args.bind)
 
 # import classes serving the different web.py URLs
 import x2gobroker.web.plain
@@ -111,25 +114,33 @@ import x2gobroker.web.plain
 #import x2gobroker.web.html
 import x2gobroker.web.extras
 
-# switch on https:// mode
-if x2gobroker.defaults.X2GOBROKER_SSL_CERTFILE and x2gobroker.defaults.X2GOBROKER_SSL_KEYFILE:
-    CherryPyWSGIServer.ssl_certificate = x2gobroker.defaults.X2GOBROKER_SSL_CERTFILE
-    CherryPyWSGIServer.ssl_private_key = x2gobroker.defaults.X2GOBROKER_SSL_KEYFILE
-
 # define the web.py URLs
-urls = ( '/plain/(.*)', 'x2gobroker.web.plain.X2GoBrokerWeb',
-#         '/json/(.*)', 'x2gobroker.web.json.X2GoBrokerWeb',
-#         '/html/(.*)', 'x2gobroker.web.html.X2GoBrokerWeb',
-          '/pubkeys/', 'x2gobroker.web.extras.X2GoBrokerPubKeyService',
+urls = ( ('/plain/(.*)', x2gobroker.web.plain.X2GoBrokerWeb,),
+#        ('/json/(.*)', x2gobroker.web.json.X2GoBrokerWeb,),
+#        ('/html/(.*)', x2gobroker.web.html.X2GoBrokerWeb,),
+         ('/pubkeys/', x2gobroker.web.extras.X2GoBrokerPubKeyService,),
        )
 
-app = web.application(urls, globals(), autoreload=False)
-app.internalerror = web.debugerror
-application = app.wsgifunc()
+settings = {
+    'log_function': tornado_log_request,
+}
+application = tornado.web.Application(urls, **settings)
 
 # run the web.py standalone daemon...
 if __name__ == "__main__":
     try:
-        app.run()
+        if x2gobroker.defaults.X2GOBROKER_SSL_CERTFILE and x2gobroker.defaults.X2GOBROKER_SSL_KEYFILE:
+            # switch on https:// mode
+            http_server = tornado.httpserver.HTTPServer(application,
+                                                        ssl_options={
+                                                            "certfile": x2gobroker.defaults.X2GOBROKER_SSL_CERTFILE,
+                                                            "keyfile": x2gobroker.defaults.X2GOBROKER_SSL_KEYFILE,
+                                                        },
+            )
+        else:
+            # run without https
+            http_server = tornado.httpserver.HTTPServer(application)
+        http_server.listen(bind_port, address=bind_address)
+        tornado.ioloop.IOLoop.instance().start()
     except socket.error, e:
         print (e)
diff --git a/sbin/x2gobroker-pubkeyauthorizer b/sbin/x2gobroker-pubkeyauthorizer
index 55ec630..c1e9f41 100755
--- a/sbin/x2gobroker-pubkeyauthorizer
+++ b/sbin/x2gobroker-pubkeyauthorizer
@@ -36,7 +36,7 @@ import logging.config
 from pwd import getpwnam
 from grp import getgrnam
 
-__VERSION__ = '0.0.0.5'
+__VERSION__ = '0.0.0.6'
 __AUTHOR__ = 'Mike Gabriel (X2Go Project) <mike.gabriel at das-netzwerkteam.de>'
 
 PROG_NAME = os.path.basename(sys.argv[0])
diff --git a/x2gobroker/__init__.py b/x2gobroker/__init__.py
index f8ccf70..2c644a2 100644
--- a/x2gobroker/__init__.py
+++ b/x2gobroker/__init__.py
@@ -18,5 +18,5 @@
 # Free Software Foundation, Inc.,
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
-__VERSION__ = '0.0.0.5'
+__VERSION__ = '0.0.0.6'
 __AUTHOR__ = 'Mike Gabriel (X2Go Project) <mike.gabriel at das-netzwerkteam.de>'
diff --git a/x2gobroker/agent.py b/x2gobroker/agent.py
index 13dc2af..9b63f00 100644
--- a/x2gobroker/agent.py
+++ b/x2gobroker/agent.py
@@ -21,16 +21,37 @@
 
 import subprocess
 import paramiko
+import cStringIO
+import time
+import threading
 
 import x2gobroker._paramiko
 x2gobroker._paramiko.monkey_patch_paramiko()
 
 # X2Go Broker modules
 import x2gobroker.defaults
-from x2gobroker.loggers import logger_error
+from x2gobroker.loggers import logger_broker, logger_error
 
 
-def call_local_broker_agent(username, mode):
+class delayed_execution(threading.Thread):
+
+    def __init__(self, agent_func, delay=0, **kwargs):
+        threading.Thread.__init__(self)
+        self.agent_func = agent_func
+        self.delay = delay
+        self.kwargs = kwargs
+        self.daemon = True
+        self.start()
+
+    def run(self):
+        i = 0
+        while i < self.delay:
+            time.sleep(1)
+            i += 1
+        self.agent_func(**self.kwargs)
+
+
+def call_local_broker_agent(username, mode, cmdline_args=[]):
     """\
     Launch X2Go Broker Agent locally and process its output.
 
@@ -46,6 +67,10 @@ def call_local_broker_agent(username, mode):
         '{mode}'.format(mode=mode),
     ]
 
+    for cmdline_arg in cmdline_args:
+        cmd_line.append('"{arg}"'.format(arg=cmdline_arg))
+
+    logger_broker.debug('Executing agent command locally: {cmd}'.format(cmd=" ".join(cmd_line)))
     agent_process = subprocess.Popen(cmd_line,
                                      stdin=None,
                                      stdout=subprocess.PIPE,
@@ -57,7 +82,8 @@ def call_local_broker_agent(username, mode):
     if result[0].startswith('OK'):
         return [ r for r in result[1:] if r ]
 
-def call_remote_broker_agent(username, mode, remote_agent):
+
+def call_remote_broker_agent(username, mode, cmdline_args=[], remote_agent=None):
     """\
     Launch remote X2Go Broker Agent via SSH and process its output.
 
@@ -69,15 +95,24 @@ def call_remote_broker_agent(username, mode, remote_agent):
     @type remote_agent: C{dict}
 
     """
+    if remote_agent is None:
+        logger_error.error('With the SSH agent-query-mode remote agent (hostname, port) has to be specified!')
+
     cmd_line = [
         '{x2gobroker_agent_binary}'.format(x2gobroker_agent_binary=x2gobroker.defaults.X2GOBROKER_AGENT_CMD),
         '{username}'.format(username=username),
         '{mode}'.format(mode=mode),
     ]
 
+    for cmdline_arg in cmdline_args:
+        cmd_line.append('\'{arg}\''.format(arg=cmdline_arg))
+
     remote_username = x2gobroker.defaults.X2GOBROKER_AGENT_USER
     remote_hostname = remote_agent[u'hostname']
-    remote_port = int(remote_agent[u'port'])
+    if remote_agent.has_key(u'port'):
+        remote_port = int(remote_agent[u'port'])
+    else:
+        remote_port = 22
 
     # now, connect and use paramiko Client to negotiate SSH2 across the connection
     try:
@@ -88,9 +123,10 @@ def call_remote_broker_agent(username, mode, remote_agent):
 
         result = []
         ssh_transport = client.get_transport()
-        if ssh_transport.is_authenticated():
+        if ssh_transport and ssh_transport.is_authenticated():
             cmd = ' '.join(cmd_line)
             cmd = 'sh -c \"{cmd}\"'.format(cmd=cmd)
+            logger_broker.debug('Executing agent command on remote host ({remote_agent}): {cmd}'.format(remote_agent=remote_agent['hostname'], cmd=cmd))
             (stdin, stdout, stderr) = client.exec_command(cmd)
             result = stdout.read().split('\n')
         client.close()
@@ -99,6 +135,7 @@ def call_remote_broker_agent(username, mode, remote_agent):
     except paramiko.SSHException:
         logger_error.error('could not connect to remote X2Go Broker Agent (user: {user}, hostname: {hostname}, port: {port}'.format(user=remote_username, hostname=remote_hostname, port=remote_port))
 
+
 def list_sessions(username, query_mode='LOCAL', remote_agent=None):
     """\
     Query X2Go Broker Agent for a session list for a given username.
@@ -116,6 +153,7 @@ def list_sessions(username, query_mode='LOCAL', remote_agent=None):
     else:
         return call_remote_broker_agent(username, mode='listsessions', remote_agent=remote_agent)
 
+
 def find_busy_servers(username, query_mode='LOCAL', remote_agent=None):
     """\
     Query X2Go Broker Agent for a list of  servers with running
@@ -147,6 +185,61 @@ def find_busy_servers(username, query_mode='LOCAL', remote_agent=None):
 
     return server_usage
 
+
+def add_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/authorized_keys', query_mode='LOCAL', remote_agent=None):
+    """\
+    Add a public key hash to the user's authorized_keys file.
+
+    @param username: run the query on behalf of this username
+    @type username: C{unicode}
+    @param pubkey_hash: the public key hash as found in SSH authorized_keys files
+    @type pubkey_hash: C{unicode}
+    @param authorized_keys_file: the full path to the remote X2Go server's authorized_keys file
+    @type authorized_keys_file: C{unicode}
+    @param query_mode: query mode used when calling X2Go Broker Agent (C{LOCAL} or C{SSH})
+    @type query_mode: C{unicode}
+    @param remote_agent: information about the remote agent that is to be called.
+    @type remote_agent: C{dict}
+
+    """
+    if query_mode.upper() == u'LOCAL':
+        return call_local_broker_agent(username, mode='addauthkey', cmdline_args=[pubkey_hash, authorized_keys_file, ])
+    else:
+        return call_remote_broker_agent(username, mode='addauthkey', cmdline_args=[pubkey_hash, authorized_keys_file, ], remote_agent=remote_agent)
+
+
+def delete_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/authorized_keys', query_mode='LOCAL', remote_agent=None, delay_deletion=0):
+    """\
+    Remove a public key hash from the user's authorized_keys file.
+
+    @param username: run the query on behalf of this username
+    @type username: C{unicode}
+    @param pubkey_hash: the public key hash as found in SSH authorized_keys files
+    @type pubkey_hash: C{unicode}
+    @param authorized_keys_file: the full path to the remote X2Go server's authorized_keys file
+    @type authorized_keys_file: C{unicode}
+    @param query_mode: query mode used when calling X2Go Broker Agent (C{LOCAL} or C{SSH})
+    @type query_mode: C{unicode}
+    @param remote_agent: information about the remote agent that is to be called.
+    @type remote_agent: C{dict}
+
+    """
+    # this is for the logger output
+    if remote_agent is None:
+        _hostname = 'LOCAL'
+    else:
+        _hostname = remote_agent['hostname']
+
+    if delay_deletion > 0:
+        delayed_execution(delete_authorized_key, delay=delay_deletion, username=username, pubkey_hash=pubkey_hash, authorized_keys_file=authorized_keys_file, query_mode=query_mode, remote_agent=remote_agent, )
+        logger_broker.debug('Scheduled deletion of authorized key in {delay}s: user={user}, host={host}'.format(delay=delay_deletion, user=username, host=_hostname))
+    else:
+        if query_mode.upper() == u'LOCAL':
+            return call_local_broker_agent(username, mode='delauthkey', cmdline_args=[pubkey_hash, authorized_keys_file, ])
+        else:
+            return call_remote_broker_agent(username, mode='delauthkey', cmdline_args=[pubkey_hash, authorized_keys_file, ], remote_agent=remote_agent)
+
+
 def get_servers(username, query_mode='LOCAL', remote_agent=None):
     """\
     Query X2Go Broker Agent for the list of currently used servers.
@@ -166,3 +259,44 @@ def get_servers(username, query_mode='LOCAL', remote_agent=None):
     else:
         return call_local_broker_agent(username, mode='getservers', remote_agent=remote_agent)
 
+
+def genkeypair(local_username, client_address, key_type='RSA'):
+    """\
+    Generate an SSH pub/priv key pair without writing the private key to file.
+
+    @param local_username: the key is for this user
+    @type local_username: C{unicode}
+    @param client_address: the key is only valid for this client
+    @type client_address: C{unicode}
+    @param key_type: either of: RSA, DSA
+    @type key_type: C{unicode}
+
+    """
+    key = None
+    pubkey = None
+    privkey = None
+
+    # generate key pair
+    if unicode(key_type) == u'RSA':
+        key = paramiko.RSAKey.generate(2048)
+    elif unicode(key_type) == u'DSA':
+        key = paramiko.DSSKey.generate(1024)
+
+    if key:
+
+        # assemble the public key
+        if key_type == "RSA":
+            pubkey_type = 'ssh-rsa'
+        elif key_type == "DSA":
+            pubkey_type = 'ssh-dss'
+
+        # FIXME: the from option does not work properly by some reason. Fix it later
+        #pubkey = "from={client_address},no-X11-forwarding,no-pty,no-user-rc {pubkey_type} {pubkey} {local_username}@{client_address}".format(pubkey=key.get_base64(), pubkey_type=pubkey_type, local_username=local_username, client_address=client_address)
+        pubkey = "no-X11-forwarding,no-pty,no-user-rc {pubkey_type} {pubkey} {local_username}@{client_address}".format(pubkey=key.get_base64(), pubkey_type=pubkey_type, local_username=local_username, client_address=client_address)
+
+        # assemble the private key
+        privkey_obj = cStringIO.StringIO()
+        key.write_private_key(privkey_obj)
+        privkey = privkey_obj.getvalue()
+
+    return (pubkey, privkey)
diff --git a/x2gobroker/authmechs/pam_authmech.py b/x2gobroker/authmechs/pam_authmech.py
index b46d1a2..7ea2023 100644
--- a/x2gobroker/authmechs/pam_authmech.py
+++ b/x2gobroker/authmechs/pam_authmech.py
@@ -18,15 +18,26 @@
 # Free Software Foundation, Inc.,
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
+# modules
+from socket import error
+import pam
+
+# X2Go Session Broker modules
 import x2gobroker.authservice
 
 class X2GoBrokerAuthMech(object):
 
     def authenticate(self, username, password):
 
-        # do a simple PAM authentication against the PAM service ,,x2gobroker''
         if username and password:
-            if x2gobroker.authservice.authenticate(username, password, service="x2gobroker"):
-                return True
+            try:
+                # query the X2Go Session Broker's PAM Auth Service
+                if x2gobroker.authservice.authenticate(username, password, service="x2gobroker"):
+                    return True
+
+            except error:
+                # fallback to direct PAM authentication against the PAM service ,,x2gobroker''
+                if pam.authenticate(username, password, service="x2gobroker"):
+                    return True
 
         return False
diff --git a/x2gobroker/brokers/base_broker.py b/x2gobroker/brokers/base_broker.py
index ad3e3e7..9001039 100644
--- a/x2gobroker/brokers/base_broker.py
+++ b/x2gobroker/brokers/base_broker.py
@@ -66,7 +66,7 @@ class X2GoBroker(object):
         if config_defaults is None: config_defaults = x2gobroker.defaults.X2GOBROKER_CONFIG_DEFAULTS
         self.config = x2gobroker.config.X2GoBrokerConfigFile(config_files=self.config_file, defaults=config_defaults)
 
-        self._dynamic_authid_map = {}
+        self._dynamic_cookie_map = {}
         self._client_address = None
 
     def __del__(self):
@@ -220,7 +220,20 @@ class X2GoBroker(object):
         """\
         Get the session profile for profile ID <profile_id>.
 
-        @param profile_id: the ID of a profile, in other words the section name in the configuration file
+        @param profile_id: the ID of a profile
+        @type profile_id: C{unicode}
+
+        @return: a dictionary representing the session profile for ID <profile_id>
+        @rtype: C{dict}
+
+        """
+        return {}
+
+    def get_profile_broker(self, profile_id):
+        """\
+        Get broker-specific session profile options from the session profile with profile ID <profile_id>.
+
+        @param profile_id: the ID of a profile
         @type profile_id: C{unicode}
 
         @return: a dictionary representing the session profile for ID <profile_id>
@@ -233,7 +246,7 @@ class X2GoBroker(object):
         """\
         Get the ACLs for session profile with profile ID <profile_id>.
 
-        @param profile_id: the ID of a profile, in other words the section name in the configuration file
+        @param profile_id: the ID of a profile
         @type profile_id: C{unicode}
 
         @return: a dictionary representing the ACLs for session profile with ID <profile_id>
@@ -419,14 +432,15 @@ class X2GoBroker(object):
         """
         _default_auth_mech = "pam"
         _auth_mech = ""
-        if self.config.has_value('global', 'default-auth-mech'):
-            _default_auth_mech = self.config.get_value('global', 'default-auth-mech').lower()
-            logger_broker.debug('base_broker.X2GoBroker.get_authentication_mechanism(): found default-auth-mech in global config section: {value}'.format(value=_default_auth_mech))
 
-        if self.config.has_value(self.backend_name, 'auth-mech'):
+        if self.config.has_value(self.backend_name, 'auth-mech') and self.config.get_value(self.backend_name, 'auth-mech'):
             _auth_mech = self.config.get_value(self.backend_name, 'auth-mech').lower()
             logger_broker.debug('base_broker.X2GoBroker.get_authentication_mechanism(): found auth-mech in backend config section »{backend}«: {value}. This one has precendence over the default value.'.format(backend=self.backend_name, value=_auth_mech))
 
+        elif self.config.has_value('global', 'default-auth-mech'):
+            _default_auth_mech = self.config.get_value('global', 'default-auth-mech').lower()
+            logger_broker.debug('base_broker.X2GoBroker.get_authentication_mechanism(): found default-auth-mech in global config section: {value}'.format(value=_default_auth_mech))
+
         return unicode(_auth_mech) or unicode(_default_auth_mech)
 
     def get_agent_query_mode(self):
@@ -440,16 +454,64 @@ class X2GoBroker(object):
         """
         _default_agent_query_mode = "LOCAL"
         _agent_query_mode = ""
-        if self.config.has_value('global', 'default-agent-query-mode'):
-            _default_agent_query_mode = self.config.get_value('global', 'default-agent-query-mode').lower()
-            logger_broker.debug('base_broker.X2GoBroker.get_agent_query_mode(): found default-agent-query-mode in global config section: {value}'.format(value=_default_agent_query_mode))
-
-        if self.config.has_value(self.backend_name, 'agent-query-mode'):
+        if self.config.has_value(self.backend_name, 'agent-query-mode') and self.config.get_value(self.backend_name, 'agent-query-mode'):
             _agent_query_mode = self.config.get_value(self.backend_name, 'agent-query-mode').lower()
             logger_broker.debug('base_broker.X2GoBroker.get_agent_query_mode(): found agent-query-mode in backend config section »{backend}«: {value}. This one has precendence over the default value.'.format(backend=self.backend_name, value=_agent_query_mode))
 
+        elif self.config.has_value('global', 'default-agent-query-mode'):
+            _default_agent_query_mode = self.config.get_value('global', 'default-agent-query-mode').lower()
+            logger_broker.debug('base_broker.X2GoBroker.get_agent_query_mode(): found default-agent-query-mode in global config section: {value}'.format(value=_default_agent_query_mode))
+
         return unicode(_agent_query_mode) or unicode(_default_agent_query_mode)
 
+    def use_session_autologin(self, profile_id):
+        """\
+        Detect if the given profile is configured to try automatic session
+        logons.
+
+        @return: C{True} to denote that automatic session login should be attempted
+        @rtype: C{bool}
+
+        """
+        _default_session_autologin = False
+        _session_autologin = False
+        _profile = self.get_profile_broker(profile_id)
+        if _profile and _profile.has_key(u'broker-session-autologin') and _profile['broker-session-autologin']:
+            _session_autologin = _profile[u'broker-session-autologin']
+            logger_broker.debug('base_broker.X2GoBroker.get_session_autologin(): found broker-session-autologin in session profile with ID {id}: {value}. This one has precendence over the default value.'.format(id=profile_id, value=_session_autologin))
+
+        elif self.config.has_value('global', 'default-session-autologin'):
+            _default_session_autologin = self.config.get_value('global', 'default-session-autologin')
+            logger_broker.debug('base_broker.X2GoBroker.get_session_autologin(): found default-session-autologin in global config section: {value}'.format(value=_default_session_autologin))
+
+        return _session_autologin or _default_session_autologin
+
+    def get_authorized_keys_file(self, profile_id):
+        """\
+        Get the default location of server-side authorized_keys files used with
+        the X2Go Session Broker.
+
+        The file location can be configured broker-wide. It is also possible to
+        provide a broker-authorized-keys file in session profiles. The latter
+        will override the broker-wide conigured file location.
+
+        @return: authorized_keys location on the remote server
+        @rtype: C{unicode}
+
+        """
+        _default_authorized_keys_file = "%h/.x2go/authorized_keys"
+        _authorized_keys_file = ""
+        _profile = self.get_profile_broker(profile_id)
+        if _profile and _profile.has_key(u'broker-authorized-keys') and _profile['broker-authorized-keys']:
+            _authorized_keys_file = _profile[u'broker-authorized-keys']
+            logger_broker.debug('base_broker.X2GoBroker.get_authorized_keys_file(): found broker-authorized-keys in session profile with ID {id}: {value}. This one has precendence over the default value.'.format(id=profile_id, value=_authorized_keys_file))
+
+        elif self.config.has_value('global', 'default-authorized-keys'):
+            _default_authorized_keys_file = self.config.get_value('global', 'default-authorized-keys').lower()
+            logger_broker.debug('base_broker.X2GoBroker.get_authorized_keys_file(): found default-authorized-keys in global config section: {value}'.format(value=_default_authorized_keys_file))
+
+        return unicode(_authorized_keys_file) or unicode(_default_authorized_keys_file)
+
     def get_userdb_service(self):
         """\
         Get the name of the backend being used for retrieving user information from the
@@ -625,7 +687,7 @@ class X2GoBroker(object):
         else:
             return []
 
-    def check_access(self, username='', password='', authid=None, ):
+    def check_access(self, username='', password='', cookie=None, ):
         """\
         Check if a given user with a given password may gain access to the
         X2Go session broker.
@@ -634,6 +696,8 @@ class X2GoBroker(object):
         @type username: C{unicode}
         @param password: a password that authenticates the user against the X2Go session broker
         @type password: C{unicode}
+        @param cookie: an extra (static or dynamic) authentication token
+        @type cookie: C{unicode}
 
         @return: returns C{True} if the authentication has been successful
         @rtype: C{bool}
@@ -655,46 +719,46 @@ class X2GoBroker(object):
 
         ### HANDLING OF DYNAMIC AUTHENTICATION ID HASHES
 
-        # using authid as extra security?
-        if self.config.get_value('global', 'use-authid'):
+        # using cookie authentication as extra security?
+        if self.config.get_value('global', 'require-cookie-authentication'):
 
-            if type(authid) is types.StringType:
-                authid = unicode(authid)
+            if type(cookie) is types.StringType:
+                cookie = unicode(cookie)
 
-            if self.config.get_value('global', 'use-static-authid'):
+            if self.config.get_value('global', 'use-static-cookie'):
 
                 # evaluate access based on static authentication ID feature
-                access = access and ( authid == self.config.get_value('global', 'authid') )
+                access = access and ( cookie == self.config.get_value('global', 'my-cookie') )
 
             else:
 
                 # evaluate access based on dynamic authentication ID feature
-                if self._dynamic_authid_map.has_key(username):
-                    access = access and ( authid == self._dynamic_authid_map[username] )
+                if self._dynamic_cookie_map.has_key(username):
+                    access = access and ( cookie == self._dynamic_cookie_map[username] )
                     if access:
-                        self._dynamic_authid_map[username] = uuid.uuid5(namespace=authid, name=username)
+                        self._dynamic_cookie_map[username] = uuid.uuid5(namespace=cookie, name=username)
 
                 else:
-                    access = access and ( authid == self.config.get_value('global', 'authid') )
+                    access = access and ( cookie == self.config.get_value('global', 'my-cookie') )
                     if access:
                         # generate a first uuid, initialize the dynamic authencation ID security feature
-                        self._dynamic_authid_map[username] = uuid.uuid4()
+                        self._dynamic_cookie_map[username] = uuid.uuid4()
 
         return access
 
-    def get_next_authid(self, username):
+    def get_next_cookie(self, username):
         """\
-        Get the next expected authentication ID for the given user name.
+        Get the next expected authentication cookie for the given user name.
 
-        @param username: query next auth ID for this user
+        @param username: query next authentication cookie for this user
         @type username: C{unicode}
 
-        @return: returns next authentication ID for the given username, None if no auth ID has been generated, yet.
+        @return: returns next authentication cookie for the given username, None if no cookie has been generated, yet
         @rtype: C{unicode} or C{None}
 
         """
         try:
-            return self._dynamic_authid_map[username]
+            return self._dynamic_cookie_map[username]
         except KeyError:
             return None
 
@@ -712,6 +776,10 @@ class X2GoBroker(object):
         list_of_profiles = {}
         for profile_id in self.get_profile_ids():
             profile = self.get_profile(profile_id)
+
+            if self.use_session_autologin(profile_id):
+                profile['autologin'] = True
+
             acls = self.get_profile_acls(profile_id)
 
             if self.check_profile_acls(username, acls):
@@ -719,7 +787,7 @@ class X2GoBroker(object):
 
         return list_of_profiles
 
-    def select_session(self, profile_id, username):
+    def select_session(self, profile_id, username=None):
         """\
         Start/resume a session by selecting a profile name offered by the X2Go client.
 
@@ -738,14 +806,14 @@ class X2GoBroker(object):
         server_list = profile[u'host']
         agent_query_mode = self.get_agent_query_mode()
 
-        if len(server_list) >= 2:
+        remote_agent = None
+        if agent_query_mode.upper() == u'SSH':
+            random.shuffle(server_list)
+            remote_agent_server = server_list[0]
+            remote_agent_port = profile[u'sshport']
+            remote_agent = {u'hostname': remote_agent_server, u'port': remote_agent_port, }
 
-            remote_agent = None
-            if agent_query_mode.upper() == u'SSH':
-                random.shuffle(server_list)
-                remote_agent_server = server_list[0]
-                remote_agent_port = profile[u'sshport']
-                remote_agent = {u'hostname': remote_agent_server, u'port': remote_agent_port, }
+        if len(server_list) >= 2 and username:
 
             busy_servers = x2gobroker.agent.find_busy_servers(username=username, query_mode=agent_query_mode, remote_agent = remote_agent)
 
@@ -755,7 +823,6 @@ class X2GoBroker(object):
 
             busy_server_list = [ (load, server) for server, load in busy_servers.items() ]
             busy_server_list.sort()
-            print busy_server_list
 
             best_server = busy_server_list[0][1]
 
@@ -767,20 +834,50 @@ class X2GoBroker(object):
             'port': profile[u'sshport'],
         }
 
-        # do some load balancing if more than one server is configured
+        # find already running sessions and resume the first one found
         if len(server_list) >= 2 and username:
 
             session_list = x2gobroker.agent.list_sessions(username=username, query_mode=agent_query_mode, remote_agent=remote_agent)
             if session_list:
 
                 # if resuming, always select the first session in the list, there should only be one suspended session
-                server_name = session_list[0].split('|')[3]
-                session_info = session_list[0]
-
-                selected_session.update({
-                    'server': server_name,
-                    'session_info': session_info,
-                })
+                try:
+                    server_name = session_list[0].split('|')[3]
+                    session_info = session_list[0]
+
+                    selected_session.update({
+                        'server': server_name,
+                        'session_info': session_info,
+                    })
+
+                except IndexError:
+
+                    # FIXME: if we get here, we have to deal with a broker session info entry in the X2Go session database
+                    pass
+
+
+        # session autologin feature
+        if self.use_session_autologin(profile_id):
+
+            # FIXME: we somehow have to find out about the username of the person at the broker client-side...
+            # using the username used for broker login for now...
+            pubkey, privkey = x2gobroker.agent.genkeypair(local_username=username, client_address=self.get_client_address())
+            x2gobroker.agent.add_authorized_key(username=username,
+                                                pubkey_hash=pubkey,
+                                                authorized_keys_file=self.get_authorized_keys_file(profile_id),
+                                                query_mode=agent_query_mode,
+                                                remote_agent=remote_agent,
+            ),
+            selected_session.update({
+                'authentication_privkey': privkey,
+            })
+            x2gobroker.agent.delete_authorized_key(username=username,
+                                                   pubkey_hash=pubkey,
+                                                   authorized_keys_file=self.get_authorized_keys_file(profile_id),
+                                                   query_mode=agent_query_mode,
+                                                   remote_agent=remote_agent,
+                                                   delay_deletion=20,
+            ),
 
         return selected_session
 
diff --git a/x2gobroker/brokers/inifile_broker.py b/x2gobroker/brokers/inifile_broker.py
index af33bf2..3071a13 100644
--- a/x2gobroker/brokers/inifile_broker.py
+++ b/x2gobroker/brokers/inifile_broker.py
@@ -76,10 +76,22 @@ class X2GoBroker(base.X2GoBroker):
         for key in profile.keys():
             if key.startswith('acl-'):
                 del profile[key]
+            if key.startswith('broker-'):
+                del profile[key]
             if key == 'default':
                 del profile[key]
         return profile
 
+    def get_profile_broker(self, profile_id):
+
+        profile = self.session_profiles.get_section(profile_id)
+        for key in profile.keys():
+            if not key.startswith('broker-'):
+                del profile[key]
+            if key.startswith('brokerl-') and (profile[key] == '' or profile[key] == ['']):
+                del profile[key]
+        return profile
+
     def get_profile_acls(self, profile_id):
 
         profile = self.session_profiles.get_section(profile_id)
@@ -89,4 +101,3 @@ class X2GoBroker(base.X2GoBroker):
             if key.startswith('acl-') and (profile[key] == '' or profile[key] == ['']):
                 del profile[key]
         return profile
-
diff --git a/x2gobroker/defaults.py b/x2gobroker/defaults.py
index cd0dfd7..cfc7372 100644
--- a/x2gobroker/defaults.py
+++ b/x2gobroker/defaults.py
@@ -114,9 +114,9 @@ X2GOBROKER_CONFIG_DEFAULTS = {
     'global': {
         u'backend': u'zeroconf',
         u'check-credentials': True,
-        u'use-authid': False,
-        u'use-static-authid': True,
-        u'authid': uuid.uuid4(),
+        u'require-cookie-auth': False,
+        u'use-static-cookie': False,
+        u'my-cookie': uuid.uuid4(),
         u'enable-plain-output': True,
         u'enable-json-output': False,
         u'enable-html-output':  False,
@@ -124,6 +124,8 @@ X2GOBROKER_CONFIG_DEFAULTS = {
         u'default-user-db': u'libnss',
         u'default-group-db': u'libnss',
         u'ignore-primary-group-memberships': True,
+        u'default-session-autologin': False,
+        u'default-authorized-keys': u'%h/.x2go/authorized_keys',
         u'default-agent-query-mode': u'LOCAL',
     },
     'zeroconf': {
diff --git a/x2gobroker/loggers.py b/x2gobroker/loggers.py
index 5be50f3..e7eabe0 100644
--- a/x2gobroker/loggers.py
+++ b/x2gobroker/loggers.py
@@ -70,3 +70,22 @@ else:
     logger_error = logging.getLogger('error')
     logger_error.addHandler(stderr_handler)
     logger_error.propagate = 0
+
+def tornado_log_request(handler):
+    """\
+    Function for overriding the log_request method in
+    C{tornado.web.RequestHandler}.
+
+    @param handler: handler
+    @type handler: C{obj}
+
+    """
+    if handler.get_status() < 400:
+        log_method = logger_access.info
+    elif handler.get_status() < 500:
+        log_method = logger_access.warning
+    else:
+        log_method = logger_error.error
+    request_time = 1000.0 * handler.request.request_time()
+    log_method("%d %s %.2fms", handler.get_status(),
+               handler._request_summary(), request_time)
diff --git a/x2gobroker/tests/test_web_plain_base.py b/x2gobroker/tests/test_web_plain_base.py
index a31850f..dd5a3ba 100644
--- a/x2gobroker/tests/test_web_plain_base.py
+++ b/x2gobroker/tests/test_web_plain_base.py
@@ -21,21 +21,20 @@ import unittest
 import tempfile
 from paste.fixture import TestApp
 from nose.tools import *
+import tornado.wsgi
 
 # Python X2GoBroker modules
 import x2gobroker.defaults
+import x2gobroker.web.plain
 
-from x2gobroker.web.plain import *
-
-urls = ( '/plain/(.*)', 'X2GoBrokerWebPlain',)
-app = web.application(urls, globals())
+urls = ( ('/plain/(.*)', x2gobroker.web.plain.X2GoBrokerWeb,) ,)
+application = tornado.wsgi.WSGIApplication(urls)
 
 class TestX2GoBrokerWebPlainBase(unittest.TestCase):
 
     ### TEST RESPONSE: is enabled?
 
     def test_isenabled(self):
-        middleware = []
         _config = """
 [base]
 enable = false
@@ -43,8 +42,9 @@ enable = false
         tf = tempfile.NamedTemporaryFile()
         print >> tf, _config
         tf.seek(0)
+        _cf_bak = x2gobroker.defaults.X2GOBROKER_CONFIG
         x2gobroker.defaults.X2GOBROKER_CONFIG = tf.name
-        testApp = TestApp(app.wsgifunc(*middleware))
+        testApp = TestApp(application)
         r = testApp.get('/plain/base/', expect_errors=True)
         assert_equal(r.status, 404)
         tf.close()
@@ -56,17 +56,16 @@ enable = true
         print >> tf, _config
         tf.seek(0)
         x2gobroker.defaults.X2GOBROKER_CONFIG = tf.name
-        testApp = TestApp(app.wsgifunc(*middleware))
+        testApp = TestApp(application)
         r = testApp.get('/plain/base/', expect_errors=True)
         assert_equal(r.status, 401)
         tf.close()
-
+        x2gobroker.defaults.X2GOBROKER_CONFIG = _cf_bak
 
     ### TEST RESPONSE: simple authentication (check_access)
 
     def test_checkaccess(self):
-        middleware = []
-        testApp = TestApp(app.wsgifunc(*middleware))
+        testApp = TestApp(application)
         r = testApp.get('/plain/base/', expect_errors=True)
         assert_equal(r.status, 401)
         _config = """
@@ -77,15 +76,16 @@ auth-mech = testsuite
         tf = tempfile.NamedTemporaryFile()
         print >> tf, _config
         tf.seek(0)
+        _cf_bak = x2gobroker.defaults.X2GOBROKER_CONFIG
         x2gobroker.defaults.X2GOBROKER_CONFIG = tf.name
         r = testApp.get('/plain/base/', params={'user': 'test', 'password': 'sweet', }, expect_errors=True)
         assert_equal(r.status, 200)
         r.mustcontain('Access granted')
+        x2gobroker.defaults.X2GOBROKER_CONFIG = _cf_bak
 
     ### TEST TASK: listsessions (nothing should be returned for the base backend)
 
     def test_listsessions(self):
-        middleware = []
         _config = """
 [base]
 enable = true
@@ -94,8 +94,9 @@ auth-mech = testsuite
         tf = tempfile.NamedTemporaryFile()
         print >> tf, _config
         tf.seek(0)
+        _cf_bak = x2gobroker.defaults.X2GOBROKER_CONFIG
         x2gobroker.defaults.X2GOBROKER_CONFIG = tf.name
-        testApp = TestApp(app.wsgifunc(*middleware))
+        testApp = TestApp(application)
         r = testApp.get('/plain/base/', params={'user': 'test', 'password': 'sweet',  'task': 'listsessions', }, expect_errors=True)
         assert_equal(r.status, 200)
         r.mustcontain('Access granted')
@@ -105,6 +106,8 @@ auth-mech = testsuite
         r.mustcontain(no='<br>',)
         r.mustcontain(no='<BR />', )
         r.mustcontain(no='<br />', )
+        x2gobroker.defaults.X2GOBROKER_CONFIG = _cf_bak
+
 
 
 def test_suite():
diff --git a/x2gobroker/tests/test_web_plain_inifile.py b/x2gobroker/tests/test_web_plain_inifile.py
index e957ed7..114e73f 100644
--- a/x2gobroker/tests/test_web_plain_inifile.py
+++ b/x2gobroker/tests/test_web_plain_inifile.py
@@ -21,13 +21,13 @@ import unittest
 import tempfile
 from paste.fixture import TestApp
 from nose.tools import *
+import web
 
 # Python X2GoBroker modules
 import x2gobroker.defaults
+import x2gobroker.web.plain
 
-from x2gobroker.web.plain import *
-
-urls = ( '/plain/(.*)', 'X2GoBrokerWebPlain',)
+urls = ( '/plain/(.*)', 'x2gobroker.web.plain.X2GoBrokerWeb',)
 app = web.application(urls, globals())
 
 x2gobroker.defaults.X2GOBROKER_CONFIG_DEFAULTS.update({'base': {'enable': True, },})
diff --git a/x2gobroker/tests/test_web_plain_zeroconf.py b/x2gobroker/tests/test_web_plain_zeroconf.py
index 53fe01e..dad829b 100644
--- a/x2gobroker/tests/test_web_plain_zeroconf.py
+++ b/x2gobroker/tests/test_web_plain_zeroconf.py
@@ -21,21 +21,20 @@ import unittest
 import tempfile
 from paste.fixture import TestApp
 from nose.tools import *
+import tornado.wsgi
 
 # Python X2GoBroker modules
 import x2gobroker.defaults
+import x2gobroker.web.plain
 
-from x2gobroker.web.plain import *
-
-urls = ( '/plain/(.*)', 'X2GoBrokerWebPlain',)
-app = web.application(urls, globals())
+urls = ( ('/plain/(.*)', x2gobroker.web.plain.X2GoBrokerWeb,) ,)
+application = tornado.wsgi.WSGIApplication(urls)
 
 class TestX2GoBrokerWebPlainZeroconf(unittest.TestCase):
 
     ### TEST TASK: listsessions (you can influence the session command via the X2Go Broker's configurationfile)
 
     def test_listsessions_checkcommand(self):
-        middleware = []
         _config = """
 [zeroconf]
 enable = true
@@ -46,7 +45,7 @@ desktop-shell = KDE
         print >> tf, _config
         tf.seek(0)
         x2gobroker.defaults.X2GOBROKER_CONFIG = tf.name
-        testApp = TestApp(app.wsgifunc(*middleware))
+        testApp = TestApp(application)
         r = testApp.get('/plain/zeroconf/', params={'user': 'test', 'password': 'sweet',  'task': 'listsessions', }, expect_errors=True)
         assert_equal(r.status, 200)
         r.mustcontain('Access granted')
@@ -69,7 +68,7 @@ desktop-shell = GNOME
         print >> tf, _config
         tf.seek(0)
         x2gobroker.defaults.X2GOBROKER_CONFIG = tf.name
-        testApp = TestApp(app.wsgifunc(*middleware))
+        testApp = TestApp(application)
         r = testApp.get('/plain/zeroconf/', params={'user': 'test', 'password': 'sweet',  'task': 'listsessions', }, expect_errors=True)
         assert_equal(r.status, 200)
         r.mustcontain('Access granted')
@@ -85,7 +84,6 @@ desktop-shell = GNOME
     ### TEST TASK: selectsession (returns localhost as the only server, no SSH key, no session info)
 
     def test_selectsession(self):
-        middleware = []
         _config = """
 [zeroconf]
 enable = true
@@ -95,7 +93,7 @@ auth-mech = testsuite
         print >> tf, _config
         tf.seek(0)
         x2gobroker.defaults.X2GOBROKER_CONFIG = tf.name
-        testApp = TestApp(app.wsgifunc(*middleware))
+        testApp = TestApp(application)
         r = testApp.get('/plain/zeroconf/', params={'user': 'test', 'password': 'sweet',  'task': 'selectsession', 'sid': 'LOCALHOST',}, expect_errors=True)
         assert_equal(r.status, 200)
         r.mustcontain('Access granted')
diff --git a/x2gobroker/web/extras.py b/x2gobroker/web/extras.py
index 75571e6..df48008 100644
--- a/x2gobroker/web/extras.py
+++ b/x2gobroker/web/extras.py
@@ -23,13 +23,14 @@
 # modules
 import os.path
 import paramiko
+import tornado.web
 
 import x2gobroker._paramiko
 x2gobroker._paramiko.monkey_patch_paramiko()
 
 import x2gobroker.defaults
 
-class X2GoBrokerPubKeyService:
+class X2GoBrokerPubKeyService(tornado.web.RequestHandler):
 
     http_header_items = {
         'Content-Type': 'text/plain; charset=utf-8',
@@ -39,7 +40,7 @@ class X2GoBrokerPubKeyService:
     def _gen_http_header(self):
 
         for http_header_item in self.http_header_items.keys():
-            web.header(http_header_item, self.http_header_items[http_header_item])
+            self.add_header(http_header_item, self.http_header_items[http_header_item])
 
     def GET(self):
 
@@ -56,6 +57,6 @@ class X2GoBrokerPubKeyService:
             output += 'ssh-dss {pubkey} {user}@{hostname}\n'.format(pubkey=str(pubkey.get_base64()), user=x2gobroker.defaults.X2GOBROKER_DAEMON_USER, hostname=x2gobroker.defaults.X2GOBROKER_HOSTNAME)
 
         if not output:
-            return web.notfound()
+            raise tornado.web.HTTPError(404)
 
-        return output
+        self.write(output)
diff --git a/x2gobroker/web/html.py b/x2gobroker/web/html.py
index 1a0b1db..a604239 100644
--- a/x2gobroker/web/html.py
+++ b/x2gobroker/web/html.py
@@ -21,12 +21,13 @@
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
 # modules
-import web
+import tornado.web
 
 # Python X2Go Broker modules
 from x2gobroker.defaults import X2GOBROKER_DEFAULT_BACKEND as _X2GOBROKER_DEFAULT_BACKEND
 
-class X2GoBrokerWeb:
+
+class X2GoBrokerWeb(tornado.web.RequestHandler):
 
     http_header_items = {
         'Content-Type': 'text/html; charset=utf-8',
@@ -61,12 +62,13 @@ $output
     def _gen_http_header(self):
 
         for http_header_item in self.http_header_items.keys():
-            web.header(http_header_item, self.http_header_items[http_header_item])
+            self.add_header(http_header_item, self.http_header_items[http_header_item])
 
-    def GET(self, backend):
-        return self.POST(backend)
+    def get(self, backend):
+        self._gen_http_header()
+        return self.post(backend)
 
-    def POST(self, backend):
+    def post(self, backend):
 
         if not backend:
             backend = _X2GOBROKER_DEFAULT_BACKEND
@@ -81,25 +83,16 @@ $output
         backend_config = broker_backend.get_backend_config(backend)
 
         if not backend_config['enable']:
-            return web.notfound()
-
-        data = web.input()
-        try: username = data.user
-        except AttributeError: username = ''
-        try: password = data.password
-        except AttributeError: password = ''
-        try: authid = data.authid
-        except AttributeError: authid = ''
-        try: task = data.task
-        except AttributeError: task = ''
-        try: session_name = data.sid
-        except AttributeError: session_name = ''
-        try: new_password = data.newpass
-        except AttributeError: new_password = ''
+            raise tornado.web.HTTPError(404)
 
-        output = ''
+        username = self.get_argument('user', default='')
+        password = self.get_argument('password', default='')
+        cookie = self.get_argument('cookie', default='')
+        task = self.get_argument('task')
+        profile_id = self.get_argument('sid', default='')
+        new_password = self.get_argument('newpass', default='')
 
-        self._gen_http_header()
+        output = ''
 
         if broker_backend.check_access(username=username, password=password, authid=authid):
 
@@ -116,6 +109,8 @@ $output
                 ###
                 pass
 
-            return self.page(self.html_header_items, output)
+            self.write(output)
+            return
+
+        raise tornado.web.HTTPError(401)
 
-        return self.page(self.html_header_items, "<hr>Access denied")
diff --git a/x2gobroker/web/json.py b/x2gobroker/web/json.py
index ef35a0f..2e97f89 100644
--- a/x2gobroker/web/json.py
+++ b/x2gobroker/web/json.py
@@ -21,8 +21,10 @@
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
 # modules
+import tornado.web
 
-class X2GoBrokerWeb:
+
+class X2GoBrokerWeb(tornado.web.RequestHandler):
 
     # MUSIC OF THE FUTURE
     pass
\ No newline at end of file
diff --git a/x2gobroker/web/plain.py b/x2gobroker/web/plain.py
index 07c36b2..f85bc98 100644
--- a/x2gobroker/web/plain.py
+++ b/x2gobroker/web/plain.py
@@ -21,15 +21,16 @@
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
 # modules
-import web
 import types
+import tornado.web
+from tornado.escape import native_str, parse_qs_bytes
 
 # Python X2Go Broker modules
 import x2gobroker.defaults
 
 from x2gobroker.loggers import logger_broker, logger_error
 
-class X2GoBrokerWeb:
+class X2GoBrokerWeb(tornado.web.RequestHandler):
 
     http_header_items = {
         'Content-Type': 'text/plain; charset=utf-8',
@@ -39,15 +40,16 @@ class X2GoBrokerWeb:
     def _gen_http_header(self):
 
         for http_header_item in self.http_header_items.keys():
-            web.header(http_header_item, self.http_header_items[http_header_item])
+            self.add_header(http_header_item, self.http_header_items[http_header_item])
 
-    def GET(self, backend):
+    def get(self, backend):
         if x2gobroker.defaults.X2GOBROKER_DEBUG:
+            self._gen_http_header()
             logger_broker.warn('GET http request detected, if unwanted: disable X2GOBROKER_DEBUG')
-            return self.POST(backend)
-        return web.notfound()
+            return self.post(backend)
+        raise tornado.web.HTTPError(404)
 
-    def POST(self, backend):
+    def post(self, backend):
 
         if not backend:
             backend = x2gobroker.defaults.X2GOBROKER_DEFAULT_BACKEND
@@ -61,45 +63,43 @@ class X2GoBrokerWeb:
         exec("broker_backend = x2gobroker.brokers.{backend}_broker.X2GoBroker()".format(backend=backend))
         global_config = broker_backend.get_global_config()
 
+        # if the broker backend is disabled in the configuration, pretend to have nothing on offer
+        if not broker_backend.is_enabled():
+            raise tornado.web.HTTPError(404)
+
+        # FIXME: this is to work around a bug in X2Go Client (http://bugs.x2go.org/138)
+        content_type = self.request.headers.get("Content-Type", "")
+        if not content_type.startswith("application/x-www-form-urlencoded"):
+            for name, values in parse_qs_bytes(native_str(self.request.body)).iteritems():
+                self.request.arguments.setdefault(name, []).extend(values)
+
         # set the client address for the broker backend
-        ip = web.ctx.env.get('HTTP_X_FORWARDED_FOR', web.ctx.get('ip', ''))
+        ip = self.request.remote_ip
         if ip:
             logger_broker.info('client address is {address}'.format(address=ip))
             broker_backend.set_client_address(ip)
         elif not x2gobroker.defaults.X2GOBROKER_DEBUG:
             # if the client IP is not set, we pretend to have nothing on offer
             logger_error.error('client could not provide an IP address, pretending: 404 Not Found')
-            return web.notfound()
+            raise tornado.web.HTTPError(404)
 
-        # if the broker backend is disabled in the configuration, pretend to have nothing on offer
-        if not broker_backend.is_enabled():
-            return web.notfound()
-
-        data = web.input()
-        try: username = data.user
-        except AttributeError: username = ''
-        try: password = data.password
-        except AttributeError: password = ''
-        try: authid = data.authid
-        except AttributeError: authid = ''
-        try: task = data.task
-        except AttributeError: task = ''
-        try: profile_id = data.sid
-        except AttributeError: profile_id = ''
-        try: new_password = data.newpass
-        except AttributeError: new_password = ''
+        username = self.get_argument('user', default='')
+        password = self.get_argument('password', default='')
+        cookie = self.get_argument('cookie', default='')
+        task = self.get_argument('task', default='')
+        profile_id = self.get_argument('sid', default='')
+        new_password = self.get_argument('newpass', default='')
 
         output = ''
-        self._gen_http_header()
 
         logger_broker.debug ('username: {username}, password: {password}, task: {task}, profile_id: {profile_id}'.format(username=username, password='XXXXX', task=task, profile_id=profile_id))
-        if broker_backend.check_access(username=username, password=password, authid=authid):
+        if broker_backend.check_access(username=username, password=password, cookie=cookie):
 
             ###
             ### CONFIRM SUCCESSFUL AUTHENTICATION FIRST
             ###
 
-            if global_config['use-authid']:
+            if global_config['require-cookie-auth']:
 
                 ### FIXME: make up a nice protocol for this, disabled for now
                 #output += "AUTHID: {authid}<br />".format(authid=broker_backend.get_next_authid(username=data.user))
@@ -129,6 +129,8 @@ class X2GoBrokerWeb:
                     for profile_id in profile_ids:
                         output += "[{profile_id}]\n".format(profile_id=profile_id)
                         for key in profiles[profile_id].keys():
+                            if key == u'user' and not profiles[profile_id][key]:
+                                profiles[profile_id][key] = username
                             if type(profiles[profile_id][key]) in (types.UnicodeType, types.StringType):
                                 output += "{key}={value}".format(key=key, value=unicode(profiles[profile_id][key]))
                             elif type(profiles[profile_id][key]) in (types.ListType, types.TupleType):
@@ -141,7 +143,6 @@ class X2GoBrokerWeb:
 
                     output += "END_USER_SESSIONS\n"
 
-
             elif task == 'selectsession':
 
                 if profile_id:
@@ -153,12 +154,14 @@ class X2GoBrokerWeb:
                         if profile_info.has_key('port'):
                             output += ":{port}".format(port=profile_info['port'])
                         output += "\n"
-                        if profile_info.has_key('authentication_key'):
-                            output += ""
+                        if profile_info.has_key('authentication_privkey'):
+                            output += profile_info['authentication_privkey']
                         if profile_info.has_key('session_info'):
                             output += "SESSION_INFO:"
                             output += profile_info['session_info'] + "\n"
 
-            return output
+            self.write(output)
+            return
+
+        raise tornado.web.HTTPError(401)
 
-        return web.unauthorized()


hooks/post-receive
-- 
x2gobroker.git (HTTP(S) Session broker for X2Go)

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "x2gobroker.git" (HTTP(S) Session broker for X2Go).




More information about the x2go-commits mailing list