Cleanning repository

This commit is contained in:
Jérôme Schneider 2011-04-21 18:43:59 +02:00
parent 17223c80ca
commit 009bafa773
2119 changed files with 0 additions and 224693 deletions

View File

@ -1,46 +0,0 @@
prefix = /usr
config_prefix = /var
config_dir = $(config_prefix)/lib/larpe
INSTALL = /usr/bin/install -c
PYTHON = /usr/bin/python
LARPE_USER = www-data
LARPE_GROUP = www-data
APACHE_INIT_SCRIPT = /etc/init.d/apache2
larpe-reload-apache2: larpe-reload-apache2.c
install: larpe-reload-apache2
rm -rf build
$(MAKE) -C po install
$(PYTHON) setup.py install --root "$(DESTDIR)/" --prefix "$(prefix)"
$(INSTALL) -d $(DESTDIR)$(prefix)/sbin/
$(INSTALL) larpectl $(DESTDIR)$(prefix)/sbin/
$(INSTALL) larpe-reload-apache2-script $(DESTDIR)$(prefix)/sbin/
$(INSTALL) -m 4550 --group $(LARPE_GROUP) larpe-reload-apache2 $(DESTDIR)$(prefix)/sbin/
$(INSTALL) -d $(DESTDIR)$(prefix)/share/larpe/
$(INSTALL) -m 0644 apache2.conf $(DESTDIR)$(prefix)/share/larpe/
chown -R $(LARPE_USER):$(LARPE_GROUP) /usr/share/larpe/
$(INSTALL) --owner=$(LARPE_USER) --group=$(LARPE_GROUP) -d $(DESTDIR)$(config_dir)
$(INSTALL) --owner=$(LARPE_USER) --group=$(LARPE_GROUP) -d $(DESTDIR)$(config_dir)/vhosts.d
$(INSTALL) --owner=$(LARPE_USER) --group=$(LARPE_GROUP) -d $(DESTDIR)$(config_dir)/vhost-locations.d
$(INSTALL) --owner=$(LARPE_USER) --group=$(LARPE_GROUP) -d $(DESTDIR)$(config_dir)/vhosts.d.disabled
$(INSTALL) --owner=$(LARPE_USER) --group=$(LARPE_GROUP) -d $(DESTDIR)$(config_dir)/vhost-locations.d.disabled
uninstall:
$(MAKE) -C po uninstall
-rm -f $(DESTDIR)$(prefix)/sbin/larpe-reload-apache2
-rm -f $(DESTDIR)$(prefix)/sbin/larpe-reload-apache2-script
-rm -f $(DESTDIR)$(prefix)/sbin/larpectl
-rm -rf $(DESTDIR)$(prefix)/share/larpe/
@echo
@echo "Depending on your Python version, you will have to remove manually the files in /usr/lib/python(your_version)/site-packages/larpe/"
clean:
$(MAKE) -C po clean
$(MAKE) -C doc clean
-$(PYTHON) setup.py clean
-rm larpe-reload-apache2

View File

@ -1,2 +0,0 @@
NEWS
====

View File

@ -1,33 +0,0 @@
Larpe - Liberty Alliance Reverse Proxy
======================================
Description
-----------
Larpe is a Liberty Alliance Reverse Proxy. It allow any service provider (that
is a website) to use Liberty Alliance features (Identity federation, Single
Sign On and Single Sign Logout) without changing the code of the service
provider itself. It uses the Lasso library which is certified by the Liberty
Alliance consortium.
Documentation
-------------
* README, as you are doing;
* doc/en/ for English documentation, published on the website as
http://larpe.labs.libre-entreprise.org/doc/en/larpe-admin.html
Copyright
---------
Larpe is copyrighted by Entr'ouvert and is licensed through the GNU General
Public Licence. Artwork and administrative design are from DotClear and
released under the GNU General Public License by Olivier Meunier and others.
Some artwork comes from GTK+ (LGPL).
Read the COPYING file for the complete license text. Read the AUTHORS file for
additional credits.

View File

@ -1,94 +0,0 @@
- Tests
- egroupware
- http://labs.libre-entreprise.org/
- logs.entrouvert.org
====== Roadmap de Larpe ======
===== 0.2 =====
* Vérifier la compatibilité avec egroupware
* Mettre le vhosts générés dans /var/lib/larpe/vhosts.d et mettre un include /var/lib/larpe/vhosts.d/* dans la conf générale
* Supprimer debconf
* Vérification des formulaires de configuration d'hôtes
* Tests de valeurs erronés diverses
* Erreur si on donne un label qui existe deja
* Ne plus inclure le binaire larpe-reload-apache2 dans les sources
* Corriger les avertissements debian
* Ne pas demander la clé publique de l'idp
* Compléter les traductions
* Ajouter la possibilité de changer la langue dans l'interface d'administration
* Mettre à jour la documentation
* Ajouter un chapitre sur les sites testés et leurs options de configuration particulières
* Traduire la documentation en français
===== 0.3 =====
* Implémenter le SLO en SOAP
* Supprimer un /liberty/ des urls
* Voir comment activer le SSLProxyEngine quand on utilise un sous répertoire
* Faire un site web pour présenter Larpe
* Ajouter la possibilité d'envoyer les exceptions par courriel à l'administrateur
* Améliorer la journalisation des accès et des erreurs
===== 1.0 =====
* Support de SAML 2.0
* Implémenter l'accès à un site nécessitant une authentification préalable avant tout accès
* Choix de cette fonctionnalité par une option de configuration par site
* Lors de la création d'un site, choix d'un moteur de site connu (mediawiki, squirrelmail, ...) qui pré-remplirait un ensemble d'options nécessaire à ce moteur
* Documentation technique pour les développeurs ?
===== Non classés =====
* Support des sites qui ont une authentification HTTP (à priori, nécessite de charger toute la configuration de larpe dans le filtre python d'apache)
* Création de nouveaux comptes pour les sites, avec des jetons (déjà implémenté en partie ; est-ce utile ?)
Fait
====
- Serveur python principal
- Fonctionnalités liberty
- SSO (depuis le sp et depuis l'idp)
- Fédération
- SLO (depuis le sp et depuis l'idp)
- Défédération (depuis l'idp) en SOAP et redirect
- Support https
- Possibilité d'utiliser toutes les combinaisons de sous domaines et de sous répertoires
- RP par vhost (appli1.example.com, rp de appli1.interne)
- RP par repertoire (www.example.com/appli1, rp de appl1.interne)
- Récupère la configuration de l'IP des vhosts
- Administration
- Authentification liberty sur l'admin
- Créer de nouveaux sites (+ modifier, supprimer)
- Écrire les vhosts correspondants
- Rechargement de la configuration d'apache
- Script + wrapper en C suid root
- Gestion d'utilisateurs pour administrer le RP (Authentification http)
- Gestion des traductions
- Filtre Python branché en sortie sur Apache à la suite du filtre de réécriture html (proxy_html)
- Générique
- Personalisable par site pour une meilleure intégration dans les pages
- Sites testés
- Dotclear
- Linuxfr
- listes.entrouvert.com
- https://listes.libre-entreprise.org/
- all4dev.libre-entreprise.org
- www.libre-entreprise.org
- http://www.besancon.com/
- quintine.entrouvert.org/egroupware/
- squirrelmail
- Documentation
- Paquets Debian
- Debconf pour demander le nom de domaine et le courriel de l'admin, ainsi que le compte administrateur
- Installation sur lupin
- Batterie de tests de non-regression

View File

@ -1,12 +0,0 @@
<VirtualHost *>
ServerName localhost
ServerAdmin root@localhost
include /usr/share/larpe/apache2.conf
include /var/lib/larpe/vhost-locations.d
CustomLog /var/log/apache2/larpe-access.log combined
ErrorLog /var/log/apache2/larpe-error.log
</VirtualHost>
include /var/lib/larpe/vhosts.d

View File

@ -1,31 +0,0 @@
# Static files
DocumentRoot /usr/share/larpe/web/
# Python application
SCGIMount / 127.0.0.1:3007
# Don't change static files
<Location /css/>
SCGIHandler off
</Location>
<Location /images/>
SCGIHandler off
</Location>
<Location /js/>
SCGIHandler off
</Location>
<Location /larpe/>
ProxyPass !
SCGIHandler off
</Location>
<Location /liberty/>
ProxyPass !
</Location>
# No gzip compression
RequestHeader unset Accept-Encoding
# HTML url rewriting module and customized Python module
SetOutputFilter OURFILTER

View File

@ -1,48 +0,0 @@
larpe (0.2.1-1) unstable; urgency=low
* New release
-- Damien Laniel <dlaniel@entrouvert.com> Wed, 20 Jun 2007 15:43:16 +0200
larpe (0.2.0-1) unstable; urgency=low
* New release
-- Damien Laniel <dlaniel@entrouvert.com> Tue, 30 Jan 2007 18:07:04 +0100
larpe (0.1.1-2) unstable; urgency=low
* Use python2.4
-- Damien Laniel <dlaniel@entrouvert.com> Tue, 19 Dec 2006 17:21:05 +0100
larpe (0.1.1-1) unstable; urgency=low
* New release
-- Damien Laniel <dlaniel@entrouvert.com> Thu, 5 Oct 2006 11:47:53 +0200
larpe (0.1.0-1) unstable; urgency=low
* New release
-- Damien Laniel <dlaniel@entrouvert.com> Wed, 4 Oct 2006 10:19:26 +0200
larpe (0.0.4-1) unstable; urgency=low
* New version, many improvements, more compatible sites, some bug fixes
-- Damien Laniel <dlaniel@entrouvert.com> Tue, 3 Oct 2006 20:44:06 +0200
larpe (0.0.3-1) unstable; urgency=low
* New version, many improvements, more compatible sites, some bug fixes
-- Damien Laniel <dlaniel@entrouvert.com> Mon, 25 Sep 2006 11:11:36 +0200
larpe (0.0.2-1) unstable; urgency=low
* Initial package.
-- Damien Laniel <dlaniel@entrouvert.com> Fri, 08 Sep 2006 16:00:00 +0200

View File

@ -1,17 +0,0 @@
Source: larpe
Section: web
Priority: optional
Maintainer: Damien Laniel <dlaniel@entrouvert.com>
Build-Depends: debhelper (>= 5.0.37.2), python, python-central (>= 0.5), gettext
Standards-Version: 3.7.2.0
XS-Python-Version: current
Package: larpe
Architecture: any
XB-Python-Version: ${python:Versions}
Depends: ${python:Depends}, python-quixote | quixote (>= 2.0), python-lasso (>= 0.6.5), python-scgi, python-libxml2, apache2, libapache2-mod-scgi, libapache2-mod-python, libapache2-mod-proxy-html
Description: Liberty Alliance Reverse Proxy
Larpe allows any service provider (that is a website) to use Liberty Alliance
identity management and Single Sign On features without changing the code of
the service provider itself.
.

View File

@ -1,3 +0,0 @@
etc/apache2/sites-available
usr/sbin
var/lib/larpe

View File

@ -1 +0,0 @@
README

View File

@ -1,103 +0,0 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: larpe
# Required-Start: $local_fs $network
# Required-Stop: $local_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start Larpe Liberty Alliance reverse proxy
# Description: Start Larpe Liberty Alliance reverse proxy
### END INIT INFO
set -e
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="larpe"
NAME=larpe
DAEMON=/usr/sbin/larpectl
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
. /lib/lsb/init-functions
# Read config file if it is present.
if [ -r /etc/default/$NAME ]
then
. /etc/default/$NAME
fi
#
# Function that starts the daemon/service.
#
d_start() {
start-stop-daemon --start --quiet --pidfile $PIDFILE --oknodo \
--chuid www-data:www-data --make-pidfile --background --exec $DAEMON -- start $OPTIONS
}
#
# Function that stops the daemon/service.
#
d_stop() {
start-stop-daemon --stop --quiet --pidfile $PIDFILE --oknodo
rm -f $PIDFILE
}
#
# Function that sends a SIGHUP to the daemon/service.
#
d_reload() {
start-stop-daemon --stop --quiet --pidfile $PIDFILE \
--make-pidfile --background --signal 1
}
case "$1" in
start)
log_begin_msg "Starting $DESC: $NAME"
d_start
log_end_msg $?
;;
stop)
log_begin_msg "Stopping $DESC: $NAME"
d_stop
log_end_msg $?
;;
#reload)
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
# If the daemon responds to changes in its config file
# directly anyway, make this an "exit 0".
#
# echo -n "Reloading $DESC configuration..."
# d_reload
# echo "done."
#;;
restart|force-reload)
#
# If the "reload" option is implemented, move the "force-reload"
# option to the "reload" entry above. If not, "force-reload" is
# just the same as "restart".
#
log_begin_msg "Restarting $DESC: $NAME"
d_stop
sleep 1
d_start
log_end_msg $?
;;
*)
# echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0

View File

@ -1,64 +0,0 @@
#!/usr/bin/make -f
# GNU copyright 1997 to 1999 by Joey Hess.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
PYTHON=/usr/bin/python2.4
ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
CFLAGS += -g
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
INSTALL_PROGRAM += -s
endif
build: build-stamp
build-stamp:
dh_testdir
touch build-stamp
clean:
dh_testdir
dh_testroot
rm -f build-stamp
make clean
dh_clean
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
make install prefix=/usr DESTDIR=$(CURDIR)/debian/larpe/
dh_install apache2-vhost-larpe etc/apache2/sites-available
find debian/larpe -name "*.pyc" -exec rm -f {} \;
# Build architecture-independent files here.
binary-indep: build install
# We have nothing to do by default.
# Build architecture-dependent files here.
binary-arch: build install
dh_testdir
dh_testroot
dh_installdocs
dh_installinit
dh_installchangelogs
# dh_installdebconf
dh_link
dh_strip
dh_compress
dh_fixperms -X /var/lib/larpe -X /usr/sbin/larpe-reload-apache2
dh_pycentral
dh_installdeb
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install

View File

@ -1,37 +0,0 @@
Template: larpe/hostname
Type: string
Default: localhost
Description: Hostname :
This is the name by which this reverse-proxy will be known on the network.
Your DNS server must have been configured accordingly.
Template: larpe/enable_vhost
Type: boolean
Default: false
Description: Enable this vhost :
A new virtual host for Larpe will be created with the hostname you chose. It may break
your Apache2 configuration.
.
If you didn't tweak your Apache2 configuration a lot
and you don't have vital websites on the same server, you can safely say "yes" here
to enable it, and fix it later if needed.
.
If you prefer checking this vhost will fit well with your Apache2 configuration first,
and enable it by yourself later, say "no".
Template: larpe/admin_username
Type: string
Default: admin
Description: Administrator login :
This is the login which will be used to connect to the administrator interface
Template: larpe/admin_password
Type: password
Description: Administrator password :
This is the password which will be used to connect to the administrator interface
Template: larpe/admin_email
Type: string
Default: root@localhost
Description: Administrator email address :
This is the email address to which problem reports will be sent

View File

@ -1,10 +0,0 @@
all:
$(MAKE) -C en
# $(MAKE) -C fr
clean:
$(MAKE) -C en clean
# $(MAKE) -C fr clean
.PHONY: clean

View File

@ -1,4 +0,0 @@
APP_DIR = "/var/lib/larpe"
DATA_DIR = "/usr/share/larpe"
ERROR_LOG = None #"/var/log/larpe.log"
WEB_ROOT = '/larpe'

View File

@ -1,14 +0,0 @@
import sys
import os
sys.path.insert(0, os.path.dirname(__file__))
import qommon
try:
import lasso
except ImportError:
lasso = None
if lasso and not hasattr(lasso, 'SAML2_SUPPORT'):
lasso.SAML2_SUPPORT = False

View File

@ -1 +0,0 @@
from root import RootDirectory

View File

@ -1,299 +0,0 @@
import os
import re
import urllib
import base64
from quixote import get_publisher, get_request
from larpe.hosts import Host
from Defaults import APP_DIR
def write_apache2_vhosts():
hosts = Host.select(lambda x: x.name != 'larpe')
hosts.sort()
vhosts_dir = os.path.join(APP_DIR, 'vhosts.d')
vhost_locations_dir = os.path.join(APP_DIR, 'vhost-locations.d')
vhosts_dir_disabled = os.path.join(APP_DIR, 'vhosts.d.disabled')
vhost_locations_dir_disabled = os.path.join(APP_DIR, 'vhost-locations.d.disabled')
vhost_file_name = get_request().get_server().split(':')[0]
vhost_file = None
reversed_hostname = ''
other_locations_hosts = []
vhost = None
if get_publisher().cfg.get(str('allow_config_generation'), True):
vhost_file = open(os.path.join(vhosts_dir, vhost_file_name), 'w')
locations_file = open(os.path.join(vhost_locations_dir, vhost_file_name), 'w')
else:
vhost_file = open(os.path.join(vhosts_dir_disabled, vhost_file_name), 'w')
locations_file = open(os.path.join(vhost_locations_dir_disabled, vhost_file_name), 'w')
try:
main_vhost = open('/etc/apache2/sites-available/apache2-vhost-larpe', 'r')
file_content = main_vhost.read()
regexp = re.compile('<VirtualHost (.*?)>')
vhost_ip = regexp.findall(file_content)[0]
except:
vhost_ip = '*'
for host in hosts:
if host.orig_site is None:
# This site hasn't been fully configured
continue
if host.reversed_hostname != reversed_hostname and host.reversed_hostname != get_publisher().cfg['proxy_hostname']:
if vhost is not None:
vhost.close()
vhost = Vhost(host, vhost_ip)
vhost.write(vhost_file)
reversed_hostname = host.reversed_hostname
if host.reversed_hostname == get_publisher().cfg['proxy_hostname']:
conf_file = locations_file
else:
conf_file = vhost_file
Location(host).write(conf_file)
if vhost_file is not None:
if vhost is not None:
vhost.close()
vhost_file.close()
if locations_file is not None:
locations_file.close()
if get_publisher().cfg.get(str('allow_config_generation'), True):
os.system('/usr/sbin/larpe-reload-apache2')
class Vhost:
def __init__(self, host, main_ip_port):
self.host = host
self.main_ip_port = main_ip_port
self.conf_file = None
def get_ip_port(self):
if self.host.scheme == 'https':
return self.main_ip_port.replace(':80', ':443')
else:
return self.main_ip_port.replace(':443', ':80')
ip_port = property(get_ip_port)
def get_proxy_url(self):
if get_publisher().cfg.get('use_proxy', False) and self.host.use_proxy == True:
return 'http://%(proxy_ip)s:%(proxy_port)s' % get_publisher().cfg
return None
proxy_url = property(get_proxy_url)
def get_proxy_auth(self):
if self.get_proxy_url() and get_publisher().cfg.get('proxy_user'):
credentials = base64.encodestring(
'%(proxy_user)s:%(proxy_password)s' % get_publisher().cfg)[:-1]
return '"Basic %s"' % credentials
return None
proxy_auth = property(get_proxy_auth)
def get_cfg(self):
return { 'ip_port': self.ip_port,
'reversed_hostname': self.host.reversed_hostname,
'proxy_url': self.proxy_url,
'proxy_auth': self.proxy_auth }
cfg = property(get_cfg)
def write(self, conf_file):
self.conf_file = conf_file
conf_lines = []
# Start Virtual Host
conf_lines.append('<VirtualHost %(ip_port)s>' % self.cfg)
# Server name and administrator
conf_lines.append('ServerName %(reversed_hostname)s' % self.cfg)
conf_lines.append('# ServerAdmin root@localhost\n')
# Include common vhost configuration
conf_lines.append('include /usr/share/larpe/apache2.conf\n')
# SSL
if self.host.scheme == 'https':
conf_lines.append('SSLEngine On\n')
if self.host.orig_site.startswith('https'):
conf_lines.append('SSLProxyEngine On\n')
# Remote proxy configuration
if self.proxy_url is not None:
conf_lines.append('ProxyRemote * %(proxy_url)s' % self.cfg)
if self.proxy_auth is not None:
conf_lines.append('RequestHeader set Proxy-Authorization %(proxy_auth)s\n' % self.cfg)
# Write it all
conf_file.write('\n\t'.join(conf_lines))
def close(self):
if self.conf_file:
self.conf_file.write('</VirtualHost>\n\n')
def apache_escape_chars(url):
special_characters = ('\\', '.', '?', '*', '+', '^', '$', '|', '(', ')', '[', ']')
for char in special_characters:
url = url.replace(char, '\%s' % char)
return url
class Location:
def __init__(self, host):
self.host = host
def get_reversed_directory(self):
if not self.host.reversed_directory:
return '%s/' % get_request().environ['SCRIPT_NAME']
else:
return '%s/%s/' % (get_request().environ['SCRIPT_NAME'], self.host.reversed_directory)
reversed_directory = property(get_reversed_directory)
def get_python_path(self):
if hasattr(self.host, 'apache_output_python_filters') and \
hasattr(self.host, 'apache_python_paths') and self.host.apache_python_paths:
python_path = 'PythonPath "sys.path'
for path in self.host.apache_python_paths:
python_path += "+['%s']" % path
python_path += '"'
return python_path
else:
return None
python_path = property(get_python_path)
def get_output_filters(self):
python_filters = ''
output_filters = []
if hasattr(self.host, 'apache_output_python_filters'):
i = 0
for filter_file in self.host.apache_output_python_filters:
filter_name = 'filter%d' % i
python_filters += 'PythonOutputFilter %s %s\n\t\t' % (filter_file, filter_name)
output_filters.append(filter_name)
i += 1
if hasattr(self.host, 'apache_output_filters'):
for filter in self.host.apache_output_filters:
output_filters.append(filter)
if output_filters:
return python_filters + 'SetOutputFilter ' + ';'.join(output_filters)
else:
return None
output_filters = property(get_output_filters)
def get_old_auth_url(self):
old_auth_url = None
if self.host.initiate_sso_url:
old_auth_url = self.host.initiate_sso_url
elif self.host.auth_url is not None:
if self.host.auth_url.startswith('http://'):
chars_to_skip = 5
else:
chars_to_skip = 6
regexp = re.compile(self.host.orig_site[chars_to_skip:])
old_auth_url_short = regexp.sub('', self.host.auth_url[chars_to_skip:])
if old_auth_url_short.startswith('/'):
old_auth_url = old_auth_url_short
else:
old_auth_url = '/' + old_auth_url_short
if old_auth_url:
old_auth_url = apache_escape_chars(old_auth_url)
return old_auth_url
old_auth_url = property(get_old_auth_url)
def get_new_auth_url(self):
if not hasattr(self.host, 'base_url'):
return None
base_url_tokens = self.host.base_url.split('/')
base_url_tokens[-1] = 'login'
return '/'.join(base_url_tokens)
new_auth_url = property(get_new_auth_url)
def get_old_logout_url(self):
old_logout_url = None
if self.host.logout_url is not None:
if self.host.logout_url.startswith('http://'):
chars_to_skip = 5
else:
chars_to_skip = 6
regexp = re.compile(self.host.orig_site[chars_to_skip:])
old_logout_url_short = regexp.sub('', self.host.logout_url[chars_to_skip:])
if old_logout_url_short.startswith('/'):
old_logout_url = old_logout_url_short
else:
old_logout_url = '/' + old_logout_url_short
old_logout_url = apache_escape_chars(old_logout_url)
return old_logout_url
old_logout_url = property(get_old_logout_url)
def get_new_logout_url(self):
if not hasattr(self.host, 'base_url'):
return None
base_url_tokens = self.host.base_url.split('/')
base_url_tokens[-1] = 'logout'
return '/'.join(base_url_tokens)
new_logout_url = property(get_new_logout_url)
def get_orig_site_url_and_dir(self):
# Split url
if self.host.orig_site.startswith('http://'):
orig_host, orig_query = urllib.splithost(self.host.orig_site[5:])
else:
orig_host, orig_query = urllib.splithost(self.host.orig_site[6:])
# Add a trailing slash if necessary
if self.host.orig_site.endswith('/'):
orig_url = self.host.orig_site
orig_dir = orig_query
else:
orig_url = self.host.orig_site + '/'
orig_dir = orig_query + '/'
return orig_url, orig_dir
def get_orig_url(self):
orig_url, orig_dir = self.get_orig_site_url_and_dir()
return orig_url
orig_url = property(get_orig_url)
def get_orig_dir(self):
orig_url, orig_dir = self.get_orig_site_url_and_dir()
return orig_dir
orig_dir = property(get_orig_dir)
def get_cfg(self):
return { 'reversed_directory': self.reversed_directory,
'old_auth_url': self.old_auth_url,
'new_auth_url': self.new_auth_url,
'old_logout_url': self.old_logout_url,
'new_logout_url': self.new_logout_url,
'orig_url': self.orig_url,
'orig_dir': self.orig_dir }
cfg = property(get_cfg)
def write(self, conf_file):
conf_lines = []
# Start Location
conf_lines.append('\n\t<Location %(reversed_directory)s>' % self.cfg)
# No user restriction
conf_lines.append('Allow from all')
# Apache output filters
if self.python_path:
conf_lines.append(self.python_path)
if self.output_filters:
conf_lines.append(self.output_filters)
# Enable rewrite module
if self.old_auth_url is not None or self.old_logout_url is not None:
conf_lines.append('RewriteEngine On')
# Redirect old authentication url to the new one
if self.old_auth_url is not None and self.host.auth_form_places == 'form_once':
conf_lines.append(
'RewriteRule %(old_auth_url)s %(new_auth_url)s [R]' % self.cfg)
# Redirect old logout url to the new one
if self.old_logout_url is not None:
conf_lines.append(
'RewriteRule %(old_logout_url)s %(new_logout_url)s [R]' % self.cfg)
# Redirect the home page to the login page
if self.host.redirect_root_to_login is True:
conf_lines.append('RewriteRule /$ %(new_auth_url)s [R]' % self.cfg)
# Convert urls in http headers to/from the new domain
conf_lines.append('ProxyPass %(orig_url)s' % self.cfg)
conf_lines.append('ProxyPassReverse %(orig_url)s' % self.cfg)
# Convert urls in html pages to/from the new domain
conf_lines.append('ProxyHTMLURLMap %(orig_dir)s %(reversed_directory)s' % self.cfg)
conf_lines.append('ProxyHTMLURLMap %(orig_url)s %(reversed_directory)s' % self.cfg)
# Write it all and close the Location
conf_file.write('\n\t\t'.join(conf_lines))
conf_file.write('\n\t</Location>\n')

View File

@ -1,130 +0,0 @@
from quixote import get_response, redirect
from quixote.directory import Directory
from qommon.form import *
from field_prefill import FieldPrefill
from menu import html_top, command_icon
class FieldUI:
def __init__(self, field_prefill):
self.field_prefill = field_prefill
def form_edit(self):
form = Form(enctype='multipart/form-data')
form.add(StringWidget, 'name', title = _('Field name'), required = True,
size = 50, value = self.field_prefill.name)
form.add(StringWidget, 'xpath', title = _('Xpath of the attribute'), required = True,
size = 50, value = self.field_prefill.xpath, hint=_('Example: /pp:PP/pp:InformalName'))
form.add(IntWidget, 'number', title = _('Number of the field in the data'), required = True,
size = 3, value = self.field_prefill.number,
hint=_('Change it if there are multiple fields corresponding to the same Xpath and you want to get another than the first one'))
form.add(CheckboxWidget, 'raw_xml', title=_('Get raw XML value'),
value = self.field_prefill.raw_xml)
form.add(StringWidget, 'regexp_match', title = _('Python regexp of a string to match'),
size = 50, value = self.field_prefill.regexp_match)
form.add(StringWidget, 'regexp_replacing', title = _('Python regexp of the replacing string'),
size = 50, value = self.field_prefill.regexp_replacing)
form.add(WidgetDict, 'select_options', title = _('Options mapping for a select field'),
value = self.field_prefill.select_options, add_element_label = _('Add item'))
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
def submit_edit(self, form):
for f in ('name', 'xpath', 'number', 'raw_xml', 'regexp_match', 'regexp_replacing', 'select_options'):
widget = form.get_widget(f)
setattr(self.field_prefill, f, widget.parse())
self.field_prefill.store()
class FieldPage(Directory):
_q_exports = ['', 'delete']
def __init__(self, field_id):
self.field_prefill = FieldPrefill.get(field_id)
self.field_ui = FieldUI(self.field_prefill)
get_response().breadcrumb.append((field_id + '/', field_id))
def _q_index [html] (self):
form = self.field_ui.form_edit()
redo = False
if form.get_widget('cancel').parse():
return redirect('..')
if form.get_widget('select_options') and form.get_widget('select_options').get_widget('add_element').parse():
form.clear_errors()
redo = True
if redo is False and form.is_submitted() and not form.has_errors():
self.field_ui.submit_edit(form)
return redirect('..')
get_response().breadcrumb.append( ('edit', _('Edit')) )
html_top('edit', title = _('Edit'))
'<h2>%s</h2>' % _('Edit')
form.render()
def delete [html] (self):
form = Form(enctype='multipart/form-data')
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
'You are about to irrevocably delete this field.')))
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append(('delete', _('Delete')))
html_top('delete_form', title = _('Delete Field'))
'<h2>%s : %s</h2>' % (_('Delete Field'), self.field_prefill.id)
form.render()
else:
self.field_prefill.remove_self()
return redirect('..')
class FieldsDirectory(Directory):
_q_exports = ['', 'new']
def __init__(self, form_prefill):
get_response().breadcrumb.append(('fields/', _('Fields')))
self.form_prefill = form_prefill
def _q_lookup(self, component):
return FieldPage(component)
def _q_index [html] (self):
html_top('fields', title = _('Fields'))
"""<ul id="nav-fields-admin">
<li><a href="new">%s</a></li>
</ul>""" % _('New Field')
'<ul class="biglist">'
for field_prefill in FieldPrefill.select(lambda x: x.form_id == self.form_prefill.id):
if not field_prefill.name:
continue
# Split too long xpath
xpath = field_prefill.xpath
xpath_tokens = xpath.split(str('/'))
if len(xpath_tokens) > 3:
xpath = str('.../') + str('/').join(xpath_tokens[-3:])
'<li>'
'<strong class="label">%s</strong>' % field_prefill.name
'<br />%s' % xpath
'<p class="commands">'
command_icon('%s/' % field_prefill.id, 'edit')
command_icon('%s/delete' % field_prefill.id, 'remove')
'</p></li>'
'</ul>'
def new [html] (self):
get_response().breadcrumb.append(('new', _('New')) )
field_prefill = FieldPrefill()
field_prefill.form_id = self.form_prefill.id
field_prefill.store()
return redirect('%s/' % field_prefill.id)

View File

@ -1,127 +0,0 @@
from quixote import get_response, redirect
from quixote.directory import Directory
from qommon.form import *
from form_prefill import FormPrefill
from fields_prefill import FieldsDirectory
from menu import html_top, command_icon
class FormUI:
def __init__(self, form_prefill):
self.form_prefill = form_prefill
def form_edit(self):
form = Form(enctype='multipart/form-data')
form.add(StringWidget, 'name', title = _('Form name'), required = True,
size = 50, value = self.form_prefill.name, hint=_('Only used for display'))
form.add(UrlWidget, 'url', title = _('Form address'), required = True,
size = 50, value = self.form_prefill.url)
form.add(StringWidget, 'profile', title = _('ID-WSF data profile'), required = True,
size = 50, value = self.form_prefill.profile, hint=_('Example: urn:liberty:id-sis-pp:2005-05'))
form.add(StringWidget, 'prefix', title = _('ID-WSF data XML prefix'), required = True,
size = 50, value = self.form_prefill.prefix, hint=_('Example: pp'))
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
def submit_edit(self, form):
for f in ('name', 'url', 'profile', 'prefix'):
widget = form.get_widget(f)
setattr(self.form_prefill, f, widget.parse())
self.form_prefill.store()
class FormPage(Directory):
_q_exports = ['', 'edit', 'delete']
def __init__(self, form_id):
self.form_prefill = FormPrefill.get(form_id)
self.form_ui = FormUI(self.form_prefill)
get_response().breadcrumb.append((form_id + '/', form_id))
def _q_index [html] (self):
html_top('forms_prefill', title = 'Form prefilling')
'<h2>%s</h2>' % _('Form prefilling configuration')
'<dl>'
'<dt><a href="edit">%s</a></dt> <dd>%s</dd>' % (
_('Edit'), _('Configure this form'))
'<dt><a href="fields/">%s</a></dt> <dd>%s</dd>' % (
_('Fields'), _('Configure the fields of this form'))
'</dl>'
def _q_lookup(self, component):
if component == 'fields':
return FieldsDirectory(self.form_prefill)
def edit [html] (self):
form = self.form_ui.form_edit()
if form.get_widget('cancel').parse():
return redirect('.')
if form.is_submitted() and not form.has_errors():
self.form_ui.submit_edit(form)
return redirect('.')
get_response().breadcrumb.append( ('edit', _('Edit')) )
html_top('edit', title = _('Edit'))
'<h2>%s</h2>' % _('Edit')
form.render()
def delete [html] (self):
form = Form(enctype='multipart/form-data')
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
'You are about to irrevocably delete this form.')))
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append(('delete', _('Delete')))
html_top('delete_form', title = _('Delete Form'))
'<h2>%s : %s</h2>' % (_('Delete Form'), self.form_prefill.id)
form.render()
else:
self.form_prefill.remove_self()
return redirect('..')
class FormsDirectory(Directory):
_q_exports = ['', 'new']
def __init__(self, host):
get_response().breadcrumb.append(('forms_prefill/', _('Forms')))
self.host = host
def _q_lookup(self, component):
return FormPage(component)
def _q_index [html] (self):
html_top('forms', title = _('Forms'))
"""<ul id="nav-forms-admin">
<li><a href="new">%s</a></li>
</ul>""" % _('New Form')
'<ul class="biglist">'
for form_prefill in FormPrefill.select(lambda x: x.host_id == self.host.id):
if not form_prefill.name:
continue
'<li>'
'<strong class="label">%s</strong>' % form_prefill.name
url = form_prefill.url
'<br /><a href="%s">%s</a>' % (url, url)
'<p class="commands">'
command_icon('%s/' % form_prefill.id, 'edit')
command_icon('%s/delete' % form_prefill.id, 'remove')
'</p></li>'
'</ul>'
def new [html] (self):
get_response().breadcrumb.append(('new', _('New')) )
form_prefill = FormPrefill()
form_prefill.host_id = self.host.id
form_prefill.store()
return redirect('%s/edit' % form_prefill.id)

File diff suppressed because it is too large Load Diff

View File

@ -1,129 +0,0 @@
import os
def set_provider_keys(private_key_path, public_key_path):
# use system calls for openssl since PyOpenSSL doesn't expose the
# necessary functions.
if os.system('openssl version > /dev/null 2>&1') == 0:
os.system('openssl genrsa -out %s 2048' % private_key_path)
os.system('openssl rsa -in %s -pubout -out %s' % (private_key_path, public_key_path))
def get_metadata(cfg):
prologue = """\
<?xml version="1.0"?>
<EntityDescriptor
providerID="%(provider_id)s"
xmlns="urn:liberty:metadata:2003-08">""" % cfg
sp_head = """
<SPDescriptor protocolSupportEnumeration="urn:liberty:iff:2003-08">"""
signing_public_key = ''
if cfg.has_key('signing_public_key') and cfg['signing_public_key']:
if 'CERTIF' in cfg['signing_public_key']:
signing_public_key = """
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data><ds:X509Certificate>%s</ds:X509Certificate></ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>""" % cfg['signing_public_key']
elif 'KEY' in cfg['signing_public_key']:
signing_public_key = """
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:KeyValue>%s</ds:KeyValue>
</ds:KeyInfo>
</KeyDescriptor>""" % cfg['signing_public_key']
sp_body = """
<AssertionConsumerServiceURL id="AssertionConsumerServiceURL1" isDefault="true">%(base_url)s/assertionConsumer</AssertionConsumerServiceURL>
<SoapEndpoint>%(base_url)s/soapEndpoint</SoapEndpoint>
<SingleLogoutServiceURL>%(base_url)s/singleLogout</SingleLogoutServiceURL>
<SingleLogoutServiceReturnURL>%(base_url)s/singleLogoutReturn</SingleLogoutServiceReturnURL>
<SingleLogoutProtocolProfile>http://projectliberty.org/profiles/slo-idp-http</SingleLogoutProtocolProfile>
<SingleLogoutProtocolProfile>http://projectliberty.org/profiles/slo-sp-soap</SingleLogoutProtocolProfile>
<SingleLogoutProtocolProfile>http://projectliberty.org/profiles/slo-sp-http</SingleLogoutProtocolProfile>
<FederationTerminationServiceURL>%(base_url)s/federationTermination</FederationTerminationServiceURL>
<FederationTerminationServiceReturnURL>%(base_url)s/federationTerminationReturn</FederationTerminationServiceReturnURL>
<FederationTerminationNotificationProtocolProfile>http://projectliberty.org/profiles/fedterm-idp-soap</FederationTerminationNotificationProtocolProfile>
<FederationTerminationNotificationProtocolProfile>http://projectliberty.org/profiles/fedterm-idp-http</FederationTerminationNotificationProtocolProfile>
<FederationTerminationNotificationProtocolProfile>http://projectliberty.org/profiles/fedterm-sp-soap</FederationTerminationNotificationProtocolProfile>
<FederationTerminationNotificationProtocolProfile>http://projectliberty.org/profiles/fedterm-sp-http</FederationTerminationNotificationProtocolProfile>
<AuthnRequestsSigned>true</AuthnRequestsSigned>
</SPDescriptor>""" % cfg
orga = ''
if cfg.get('organization_name'):
orga = """
<Organization>
<OrganizationName>%s</OrganizationName>
</Organization>""" % unicode(cfg['organization_name'], 'iso-8859-1').encode('utf-8')
epilogue = """
</EntityDescriptor>"""
return '\n'.join([prologue, sp_head, signing_public_key, sp_body, orga, epilogue])
def get_saml2_metadata(cfg):
prologue = """\
<?xml version="1.0"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
entityID="%(saml2_provider_id)s">""" % cfg
sp_head = """
<SPSSODescriptor
AuthnRequestsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">"""
signing_public_key = ''
if cfg.has_key('signing_public_key') and cfg['signing_public_key']:
if 'CERTIF' in cfg['signing_public_key']:
signing_public_key = """
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data><ds:X509Certificate>%s</ds:X509Certificate></ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>""" % cfg['signing_public_key']
elif 'KEY' in cfg['signing_public_key']:
signing_public_key = """
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:KeyValue>%s</ds:KeyValue>
</ds:KeyInfo>
</KeyDescriptor>""" % cfg['signing_public_key']
sp_body = """
<AssertionConsumerService isDefault="true" index="0"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
Location="%(saml2_base_url)s/singleSignOnArtifact" />
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
Location="%(saml2_base_url)s/singleLogoutSOAP" />
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="%(saml2_base_url)s/singleLogout"
ResponseLocation="%(saml2_base_url)s/singleLogoutReturn" />
</SPSSODescriptor>""" % cfg
orga = ''
if cfg.get('organization_name'):
orga = """
<Organization>
<OrganizationName>%s</OrganizationName>
</Organization>""" % unicode(cfg['organization_name'], 'iso-8859-1').encode('utf-8')
epilogue = """
</EntityDescriptor>"""
return '\n'.join([prologue, sp_head, signing_public_key, sp_body, orga, epilogue])

View File

@ -1,161 +0,0 @@
import random
from quixote import get_publisher, get_request, get_response, redirect
from quixote.directory import Directory
from larpe.qommon.form import *
from larpe.qommon import template
from qommon import logger
from larpe import misc
from larpe.users import User
from menu import *
class ByUserDirectory(Directory):
def _q_lookup(self, component):
return ByUserPages(component)
class LoggerDirectory(Directory):
_q_exports = ['', 'download', 'by_user']
by_user = ByUserDirectory()
def _q_index [html] (self):
get_response().breadcrumb.append( ('logger/', _('Logs')) )
html_top('logger', title = _('Logs'))
request = get_request()
logfile = request.get_field('logfile', 'larpe.log')
if not logfile.startswith(str('larpe.log')) or str('/') in str(logfile):
return template.error_page(_('Bad log file: %s') % logfile)
logfilename = str(os.path.join(get_publisher().app_dir, logfile))
if not os.path.exists(logfilename):
_('Nothing to show')
else:
if logfile:
'<a href="download?logfile=%s">%s</a>' % (logfile, _('Download Raw Log File'))
else:
'<a href="download">%s</a>' % _('Download Raw Log File')
user_color_keys = {}
last_date = None
'<table id="logs">\n'
'<thead> <tr>'
' <th>%s</th>' % _('Time')
' <th>%s</th>' % _('User')
' <th>%s</th>' % _('Message')
'<tr></thead>\n'
'<tbody>\n'
for line in open(logfilename):
d = logger.readline(line)
user_color_key = d['user_id']
if user_color_key == 'anonymous':
user_color_key += d['ip']
if not user_color_keys.has_key(user_color_key):
user_color_keys[user_color_key] = ''.join(
['%x' % random.randint(0xc, 0xf) for x in range(3)])
'<tr class="level-%s" style="background: #%s;">' % (
d['level'].lower(), user_color_keys[user_color_key])
if (last_date != d['date']):
' <td class="time">%s&nbsp;%s</td>' % (d['date'], d['hour'][:-4])
last_date = d['date']
else:
' <td class="time">%s</td>' % (d['hour'][:-4])
if d['user_id'] == 'anonymous':
userlabel = _('Anonymous')
ip = d['ip']
' <td class="userlabel"><span title="%s">%s</span></td>' % (ip, userlabel)
else:
try:
user = User.get(d['user_id'])
except KeyError:
userlabel = _('Unknown')
else:
if user.name is not None:
userlabel = htmltext(user.name.replace(str(' '), str('&nbsp;')))
else:
userlabel = _('Unknown')
' <td class="userlabel">%s</td>' % userlabel
' <td class="message">%s</td>' % d['message']
'</tr>\n'
'</tbody>\n'
'</table>\n'
logfiles = [x for x in os.listdir(get_publisher().app_dir) if x.startswith(str('larpe.log'))]
if len(logfiles) > 1:
options = []
for lfile in logfiles:
firstline = open(os.path.join(get_publisher().app_dir, lfile)).readline()
d = logger.readline(firstline)
if not d:
continue
if logfile == lfile:
selected = 'selected="selected" '
else:
selected = ''
options.append({'selected': selected, 'lfile': lfile,
'date': '%s %s' % (d['date'], d['hour'])})
'<form id="other-log-select">'
_('Select another logfile:')
'<select name="logfile">'
options.sort(lambda x,y: cmp(x['date'], y['date']))
options.reverse()
for option in options:
option['since'] = str(_('Since: %s') % option['date'])[:-4]
'<option value="%(lfile)s"%(selected)s>%(since)s</option>' % option
'</select>'
'<input type="submit" value="%s" />' % _('Submit')
def download(self):
request = get_request()
logfile = request.get_field('logfile', 'larpe.log')
if not logfile.startswith(str('larpe.log')) or str('/') in logfile:
return template.error_page(_('Bad log file: %s') % logfile)
logfilename = os.path.join(get_publisher().app_dir, logfile)
response = get_response()
response.set_content_type('text/x-log', 'iso-8859-1')
response.set_header('content-disposition', 'attachment; filename=%s' % logfile)
return open(logfilename).read()
class ByUserPages(Directory):
_q_exports = ['']
def __init__(self, component):
try:
self.user = User.get(component)
except KeyError:
raise TraversalError()
def _q_index [html] (self):
html_top('logger', title = _('Logs'))
'<h2>%s - %s</h2>' % (_('User'), self.user.name)
last_date = None
'<table id="logs">'
'<thead> <tr>'
' <th>%s</th>' % _('Time')
' <th>%s</th>' % _('Message')
'<tr></thead>'
'<tbody>'
logfile = get_publisher().get_app_logger_filename()
if os.path.exists(logfile):
for line in open(logfile):
d = logger.readline(line)
if d['user_id'] != str(self.user.id):
continue
'<tr>'
if (last_date != d['date']):
' <td class="time">%s&nbsp;%s</td>' % (d['date'], d['hour'][:-4])
last_date = d['date']
else:
' <td class="time">%s</td>' % (d['hour'][:-4])
' <td><a href="%s">%s</a></td>' % (d['url'], d['message'])
'</tr>'
'</tbody>'
'</table>'

View File

@ -1,107 +0,0 @@
from quixote import get_request, get_response, get_session, get_publisher
from larpe import storage
from larpe import misc
from larpe.users import User
from larpe.hosts import Host
items = [
('hosts', N_('Hosts')),
('users', N_('Users')),
('settings', N_('Settings')),
('logger', N_('Logs')),
# FIXME : use get_request().environ['SCRIPT_NAME']) ?
('/', N_('Liberty Alliance Reverse Proxy'))]
def generate_header_menu [html] (selected = None):
s = ["""<ul id="menu">\n"""]
base_url = get_request().environ['SCRIPT_NAME'] + '/admin'
features = get_publisher().cfg.get('misc', {}).get('features', 'both')
show_logger = get_publisher().cfg.get('debug', {}).get('logger', False)
for k, v in items:
if k == '/':
continue # skip root
if k == 'logger' and not show_logger:
continue
if k == selected:
s.append('<li class="active">')
else:
s.append('<li>')
s.append('<a href="%s/%s/">%s</a></li>\n' % (base_url, k, _(v)))
s.append('</ul>\n')
return ''.join(s)
def generate_user_info [html] ():
hosts = Host.select(lambda x: x.name == 'larpe')
if not hosts:
return
host = hosts[0]
user = get_session().get_user(host.provider_id)
if user and user.name:
logout_url = '%s/liberty/%s/logout' % (get_request().environ['SCRIPT_NAME'], host.name)
"""<ul class="user-info">
<li class="ui-name">%s</li>
<li class="ui-logout"><a href="%s">%s</a></li>
</ul>""" % (user.name, logout_url, _('logout'))
def html_top [html] (section, title = None, scripts = None):
header_menu = generate_header_menu(section)
user_info = generate_user_info()
subtitle = ''
for s in items:
if s[0] == section:
subtitle = _(s[1])
if not title:
title = ''
else:
title = ' - ' + title
if not scripts:
scripts = ''
else:
scripts = '\n'.join(['<script src="%s" type="text/javascript"></script>' % x for x in scripts])
sitetitle = _('Larpe Administration')
# if title:
# sitetitle += ' - '
admin_ezt = True
get_response().filter.update(locals())
def error_page [html] (section, error):
html_top(section, title = _('Error'))
'<div id="error-page">'
'<h2>%s</h2>' % _('Error')
'<p>%s</p>' % error
'</div>'
def command_icon [html] (url, type, label = None, icon = None):
script_name = get_request().environ['SCRIPT_NAME']
icons = {
'edit': 'stock_edit_16.png',
'add': 'stock_add_16.png',
'remove': 'stock_remove_16.png',
'duplicate': 'stock_copy_16.png',
'view': 'view_16.png',
}
labels = {
'add': N_('Add'),
'edit': N_('Edit'),
'remove': N_('Remove'),
'duplicate': N_('Duplicate'),
'view': N_('View'),
}
if not label:
label = _(labels[str(type)])
if not icon:
icon = icons[str(type)]
if url:
'''<span class="%(type)s">
<a href="%(url)s"><img src="%(script_name)s/larpe/images/%(icon)s" alt="%(label)s" title="%(label)s" /></a>
</span>''' % locals()
else:
# no url -> image button
'''<span class="%(type)s">
<input type="image" src="%(script_name)s/larpe/images/%(icon)s" alt="%(label)s" title="%(label)s" />
</span>''' % locals()

View File

@ -1,76 +0,0 @@
import os
from quixote import get_session, get_session_manager, get_publisher, get_request, get_response
from quixote.directory import Directory, AccessControlled
import hosts
import users
import settings
import logger
from larpe.admin.menu import html_top
from larpe import errors
from larpe import misc
def gpl [html] ():
"""<p>This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.</p>
<p>This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.</p>
<p>You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.</p>
"""
class RootDirectory(AccessControlled, Directory):
_q_exports = ['', 'hosts', 'users', 'settings', 'logger']
hosts = hosts.HostsDirectory()
users = users.UsersDirectory()
settings = settings.SettingsDirectory()
logger = logger.LoggerDirectory()
def _q_access(self):
# FIXME : this block should be moved somewhere else
get_publisher().reload_cfg()
if not get_publisher().cfg.has_key('proxy_hostname'):
get_publisher().cfg['proxy_hostname'] = get_request().get_server().split(':')[0]
get_publisher().write_cfg()
response = get_response()
if not hasattr(response, 'breadcrumb'):
response.breadcrumb = [ ('../admin/', _('Administration')) ]
# Cheater
if os.path.exists(os.path.join(get_publisher().app_dir, 'ADMIN_FOR_ALL')):
return
# No admin user created yet, free access
user_list = users.User.select(lambda x: x.is_admin)
if not user_list:
return
host_list = hosts.Host.select(lambda x: x.name == 'larpe')
if host_list:
host = host_list[0]
else:
raise errors.AccessForbiddenError()
user = get_session().get_user(host.provider_id)
if user:
if not user.name or not user.is_admin:
raise errors.AccessForbiddenError()
else:
raise errors.AccessUnauthorizedError()
def _q_index [html] (self):
html_top('/')
gpl()

View File

@ -1,566 +0,0 @@
import cStringIO
import cPickle
import re
import os
import lasso
import glob
import zipfile
from quixote import get_publisher, get_request, get_response, redirect
from quixote.directory import Directory, AccessControlled
from menu import *
from larpe.qommon.form import *
from larpe.qommon.misc import get_abs_path
from larpe.qommon.admin.emails import EmailsDirectory
from larpe import misc
from larpe.hosts import Host
from larpe.admin.liberty_utils import *
class LibertyIDPDir(Directory):
_q_exports = ['', ('metadata.xml', 'metadata')]
def _q_index [html] (self):
form = Form(enctype="multipart/form-data")
form.add(FileWidget, "metadata", title = _("Metadata"), required=True)
form.add(FileWidget, "publickey", title = _("Public Key"), required=False)
form.add(FileWidget, "cacertchain", title = _("CA Certificate Chain"), required=False)
form.add_submit("submit", _("Submit"))
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('New Identity Provider'))
"<h2>%s</h2>" % _('New Identity Provider')
form.render()
else:
self.submit_new(form)
def submit_new(self, form, key_provider_id = None):
metadata, publickey, cacertchain = None, None, None
if form.get_widget('metadata').parse():
metadata = form.get_widget('metadata').parse().fp.read()
if form.get_widget('publickey').parse():
publickey = form.get_widget('publickey').parse().fp.read()
if form.get_widget('cacertchain').parse():
cacertchain = form.get_widget('cacertchain').parse().fp.read()
if not key_provider_id:
try:
provider_id = re.findall(r'(provider|entity)ID="(.*?)"', metadata)[0][1]
except IndexError:
return error_page(_('Bad metadata'))
key_provider_id = provider_id.replace(str('://'), str('-')).replace(str('/'), str('-'))
dir = get_abs_path(os.path.join('idp', key_provider_id))
if not os.path.isdir(dir):
os.makedirs(dir)
if metadata:
metadata_fn = os.path.join(dir, 'metadata.xml')
open(metadata_fn, 'w').write(metadata)
if publickey:
publickey_fn = os.path.join(dir, 'public_key')
open(publickey_fn, 'w').write(publickey)
else:
publickey_fn = None
if cacertchain:
cacertchain_fn = os.path.join(dir, 'ca_cert_chain.pem')
open(cacertchain_fn, 'w').write(cacertchain)
else:
cacertchain_fn = None
p = lasso.Provider(lasso.PROVIDER_ROLE_IDP, metadata_fn, publickey_fn, None)
try:
misc.get_provider_label(p)
get_publisher().cfg['idp'] = key_provider_id
get_publisher().write_cfg()
except TypeError:
if metadata:
os.unlink(metadata_fn)
if publickey:
os.unlink(publickey_fn)
if cacertchain:
os.unlink(cacertchain_fn)
return error_page(_('Bad metadata'))
redirect('..')
def metadata(self):
response = get_response()
response.set_content_type('text/xml', 'utf-8')
get_publisher().reload_cfg()
if get_publisher().cfg['idp']:
idp_metadata = os.path.join(get_abs_path('idp'), get_publisher().cfg['idp'], 'metadata.xml')
return unicode(open(idp_metadata).read(), 'utf-8')
return 'No IDP is configured'
class EmailsDirectory(Directory):
emails = []
def __init__(self):
self._q_exports = ['', 'options'] + [x[0] for x in self.emails]
def options [html] (self):
form = Form(enctype="multipart/form-data")
emails = get_publisher().cfg.get('emails', {})
form.add(StringWidget, 'smtp_server', title = _('SMTP Server'),
required = False, value = emails.get('smtp_server', ''))
form.add(StringWidget, 'from', title = _('Email Sender'),
required = True, value = emails.get('from', 'larpe@localhost'))
form.add(StringWidget, 'reply_to', title = _('Reply-To Address'),
required = False, value = emails.get('reply_to'))
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('Emails'))
"<h2>%s</h2>" % _('General Options')
form.render()
else:
self.options_submit(form)
redirect('.')
def options_submit(self, form):
get_publisher().reload_cfg()
if not get_publisher().cfg.has_key('emails'):
get_publisher().cfg['emails'] = {}
for k in ('smtp_server', 'from', 'reply_to'):
get_publisher().cfg['emails'][k] = form.get_widget(k).parse()
get_publisher().write_cfg()
def _q_index [html] (self):
html_top('settings', title = _('Emails'))
'<h2>%s</h2>' % _('Emails')
'<ul>'
'<li><a href="options">%s</a></li>' % _('General Options')
for email_key, email_label in self.emails:
'<li><a href="%s">%s %s</a></li>' % (email_key,
_('Custom Email:'), _(email_label))
'</ul>'
'<p>'
'<a href="..">%s</a>' % _('Back')
'</p>'
def email [html] (self, email_key, email_label, hint = None, check_template = None,
enabled = True):
emails_cfg = get_publisher().cfg.get('emails', {})
cfg_key = 'email-%s' % email_key
form = Form(enctype='multipart/form-data')
form.add(CheckboxWidget, cfg_key + '_enabled', title = _('Enabled Email'),
value = emails_cfg.get(cfg_key + '_enabled', True), default = enabled)
form.add(StringWidget, cfg_key + '_subject', title = _('Subject'),
value = emails_cfg.get(cfg_key + '_subject', ''))
form.add(TextWidget, cfg_key, title = email_label, value = emails_cfg.get(cfg_key),
cols = 80, rows = 10, hint = hint)
form.add_submit('submit', _('Submit'))
form.add_submit('restore-default', _('Restore default email'))
form.add_submit('cancel', _('Cancel'))
if form.get_submit() == 'cancel':
return redirect('.')
if form.get_submit() == 'restore-default':
self.email_submit(None, cfg_key)
return redirect('.')
if form.is_submitted() and not form.has_errors():
if self.email_submit(form, cfg_key, check_template):
return redirect('.')
form.set_error(cfg_key, _('Invalid template'))
html_top('settings', title = _('Emails'))
'<h2>%s - %s</h2>' % (_('Email'), email_label)
form.render()
def email_submit(self, form, cfg_key, check_template = None):
get_publisher().reload_cfg()
if not get_publisher().cfg.has_key('emails'):
get_publisher().cfg['emails'] = {}
if form:
template = form.get_widget(cfg_key).parse()
if check_template and not check_template(template):
return False
get_publisher().cfg['emails'][str(cfg_key)] = template
get_publisher().cfg['emails'][str(cfg_key + '_enabled')] = form.get_widget(
cfg_key + '_enabled').parse()
get_publisher().cfg['emails'][str(cfg_key + '_subject')] = form.get_widget(
cfg_key + '_subject').parse()
else:
get_publisher().cfg['emails'][str(cfg_key)] = None
get_publisher().write_cfg()
return True
class SettingsDirectory(AccessControlled, Directory):
_q_exports = ['', 'liberty_sp', 'liberty_idp', 'domain_names', 'apache2_configuration_generation',
'language', 'emails', 'proxy' ]
liberty_idp = LibertyIDPDir()
emails = EmailsDirectory()
def _q_access(self):
get_response().breadcrumb.append( ('settings/', _('Settings')) )
def _q_index [html] (self):
get_publisher().reload_cfg()
html_top('settings', title = _('Settings'))
if lasso.SAML2_SUPPORT:
'<h2>%s</h2>' % _('Liberty Alliance & SAML 2.0 Service Provider')
else:
'<h2>%s</h2>' % _('Liberty Alliance Service Provider')
'<dl> <dt><a href="liberty_sp">%s</a></dt> <dd>%s</dd>' % (
_('Service Provider'), _('Configure Larpe as a Service Provider'))
hosts = Host.select(lambda x: x.name == 'larpe')
if hosts:
self.host = hosts[0]
if lasso.SAML2_SUPPORT and self.host.saml2_metadata is not None:
metadata_url = '%s/metadata.xml' % self.host.saml2_base_url
'<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
metadata_url,
_('Service Provider Metadata'),
_('Download Service Provider SAML 2.0 Metadata file'))
if self.host.metadata is not None:
metadata_url = '%s/metadata.xml' % self.host.base_url
'<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
metadata_url,
_('Service Provider Metadata'),
_('Download Service Provider ID-FF 1.2 Metadata file'))
if self.host.public_key is not None:
public_key_url = '%s/public_key' % self.host.base_url
'<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
public_key_url,
_('Public key'),
_('Download Service Provider SSL Public Key file'))
if lasso.SAML2_SUPPORT:
'<h2>%s</h2>' % _('Liberty Alliance & SAML 2.0 Identity Provider')
else:
'<h2>%s</h2>' % _('Liberty Alliance Identity Provider')
'<dl>'
'<dt><a href="liberty_idp/">%s</a></dt> <dd>%s</dd>' % (
_('Identity Provider'), _('Configure an identity provider'))
if get_publisher().cfg.has_key('idp'):
'<dt><a href="liberty_idp/metadata.xml">%s</a></dt> <dd>%s</dd>' % (
_('Identity Provider metadatas'), _('See current identity provider metadatas'))
'</dl>'
'<h2>%s</h2>' % _('Global parameters for the sites')
'<dl>'
'<dt><a href="domain_names">%s</a></dt> <dd>%s</dd>' % (
_('Domain name'), _('Configure the base domain name for the sites'))
'<dt><a href="apache2_configuration_generation">%s</a></dt> <dd>%s</dd>' % (
_('Apache 2 configuration generation'), _('Customise Apache 2 configuration generation'))
'<dt><a href="proxy">%s</a></dt> <dd>%s</dd>' % (
_('Proxy'), _('Connect to the sites through a web proxy'))
'</dl>'
'<h2>%s</h2>' % _('Customisation')
'<dl>'
'<dt><a href="language">%s</a></dt> <dd>%s</dd>' % (
_('Language'), _('Configure site language'))
'<dt><a href="emails/">%s</a></dt> <dd>%s</dd>' % (
_('Emails'), _('Configure email settings'))
'</dl>'
# '<h2>%s</h2>' % _('Misc')
# '<dl>'
# '<dt><a href="misc">%s</a></dt> <dd>%s</dd>' % (
# _('Misc'), _('Configure misc options'))
# '<dt><a href="debug_options">%s</a></dt> <dd>%s</dd>' % (
# _('Debug Options'), _('Configure options useful for debugging'))
# '</dl>'
def liberty_sp [html] (self):
get_publisher().reload_cfg()
# Get the host object for the reverse proxy
hosts = Host.select(lambda x: x.name == 'larpe')
if hosts:
self.host = hosts[0]
else:
self.host = Host()
self.host.reversed_hostname = get_publisher().cfg[str('proxy_hostname')]
form = Form(enctype='multipart/form-data')
form.add(StringWidget, 'organization_name', title=_('Organisation Name'), size=50,
required = True, value = self.host.organization_name)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('Service Provider Configuration'))
'<h2>%s</h2>' % _('Service Provider Configuration')
form.render()
else:
self.liberty_sp_submit(form)
redirect('.')
def liberty_sp_submit(self, form):
get_publisher().reload_cfg()
metadata_cfg = {}
f = 'organization_name'
if form.get_widget(f):
setattr(self.host, f, form.get_widget(f).parse())
metadata_cfg['organization_name'] = self.host.organization_name
self.host.name = 'larpe'
# Liberty Alliance / SAML parameters
base_url = '%s/liberty/%s/liberty' % (misc.get_root_url(), self.host.name)
metadata_cfg['base_url'] = base_url
self.host.base_url = base_url
if lasso.SAML2_SUPPORT:
saml2_base_url = '%s/liberty/%s/saml' % (misc.get_root_url(), self.host.name)
metadata_cfg['saml2_base_url'] = saml2_base_url
self.host.saml2_base_url = saml2_base_url
provider_id = '%s/metadata' % base_url
metadata_cfg['provider_id'] = provider_id
self.host.provider_id = provider_id
if lasso.SAML2_SUPPORT:
saml2_provider_id = '%s/metadata' % saml2_base_url
metadata_cfg['saml2_provider_id'] = saml2_provider_id
self.host.saml2_provider_id = saml2_provider_id
# Storage directories
site_dir = os.path.join(get_publisher().app_dir, 'sp',
self.host.reversed_hostname, self.host.name)
user_dir = os.path.join(site_dir, 'users')
token_dir = os.path.join(site_dir, 'tokens')
for dir in (site_dir, user_dir, token_dir):
if not os.path.isdir(dir):
os.makedirs(dir)
metadata_cfg['site_dir'] = site_dir
self.host.site_dir = site_dir
# Generate SSL keys
private_key_path = os.path.join(site_dir, 'private_key.pem')
public_key_path = os.path.join(site_dir, 'public_key')
if not os.path.isfile(private_key_path) or not os.path.isfile(public_key_path):
set_provider_keys(private_key_path, public_key_path)
self.host.private_key = private_key_path
metadata_cfg['signing_public_key'] = open(public_key_path).read()
self.host.public_key = public_key_path
# Write metadatas
metadata_path = os.path.join(site_dir, 'metadata.xml')
open(metadata_path, 'w').write(get_metadata(metadata_cfg))
self.host.metadata = metadata_path
if hasattr(self.host, 'saml2_provider_id'):
saml2_metadata_path = os.path.join(site_dir, 'saml2_metadata.xml')
open(saml2_metadata_path, 'w').write(get_saml2_metadata(metadata_cfg))
self.host.saml2_metadata = saml2_metadata_path
self.host.root_url = '%s/' % misc.get_root_url()
self.host.return_url = '%s/admin/' % misc.get_root_url()
self.host.store()
def domain_names [html] (self):
form = self.form_domain_name()
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('Domain name'))
'<h2>%s</h2>' % _('Domain name')
form.render()
else:
self.submit_domain_name(form)
redirect('.')
def form_domain_name(self):
get_publisher().reload_cfg()
if get_cfg('domain_names'):
domain_name = get_cfg('domain_names')[0]
else:
domain_name = None
form = Form(enctype='multipart/form-data')
form.add(StringWidget, 'domain_name',
title=_('Domain name for the sites'),
value = domain_name)
# TODO: Add the option "Both" and handle it in hosts configuration
form.add(SingleSelectWidget, 'sites_url_scheme', title = _('Use HTTP or HTTPS'),
value = get_cfg('sites_url_scheme'),
options = [ (None, _('Same as the site')),
('http', 'HTTP'),
('https', 'HTTPS') ] )
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
def submit_domain_name(self, form):
get_publisher().reload_cfg()
get_publisher().cfg['domain_names'] = [ form.get_widget('domain_name').parse() ]
get_publisher().cfg['sites_url_scheme'] = form.get_widget('sites_url_scheme').parse()
get_publisher().write_cfg()
def apache2_configuration_generation [html] (self):
get_publisher().reload_cfg()
form = Form(enctype='multipart/form-data')
form.add(CheckboxWidget, 'allow_config_generation',
title=_('Automatically generate Apache 2 configuration for new hosts and reload Apache 2 after changes'),
value = get_publisher().cfg.get(str('allow_config_generation'), True))
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('Apache 2 configuration generation'))
'<h2>%s</h2>' % _('Apache 2 configuration generation')
form.render()
else:
self.apache2_configuration_generation_submit(form)
redirect('.')
def apache2_configuration_generation_submit(self, form):
get_publisher().reload_cfg()
f = 'allow_config_generation'
get_publisher().cfg[f] = form.get_widget(f).parse()
get_publisher().write_cfg()
def language [html] (self):
form = Form(enctype='multipart/form-data')
language_cfg = get_publisher().cfg.get('language', {})
form.add(SingleSelectWidget, 'language', title = _('Language'),
value = language_cfg.get('language'),
options = [ (None, _('System Default')),
(str('en'), _('English')),
(str('fr'), _('French')) ] )
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('Language'))
'<h2>%s</h2>' % _('Language')
form.render()
else:
self.language_submit(form)
redirect('.')
def language_submit(self, form):
get_publisher().reload_cfg()
if not get_publisher().cfg.has_key('language'):
get_publisher().cfg['language'] = {}
for k in ('language', ):
get_publisher().cfg['language'][k] = form.get_widget(k).parse()
get_publisher().write_cfg()
def proxy [html] (self):
get_publisher().reload_cfg()
form = Form(enctype='multipart/form-data')
form.add(CheckboxWidget, 'use_proxy',
title=_('Use a web proxy'),
value = get_publisher().cfg.get(str('use_proxy'), False))
form.add(StringWidget, 'proxy_ip',
title=_('Proxy IP address or domain name'),
value = get_publisher().cfg.get(str('proxy_ip')))
form.add(StringWidget, 'proxy_port',
title=_('Proxy port'),
value = get_publisher().cfg.get(str('proxy_port')))
form.add(StringWidget, 'proxy_user',
title=_('User name'),
value = get_publisher().cfg.get(str('proxy_user')))
form.add(PasswordWidget, 'proxy_password',
title=_('User password'),
value = get_publisher().cfg.get(str('proxy_password')))
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('Proxy'))
'<h2>%s</h2>' % _('Proxy')
form.render()
else:
self.proxy_submit(form)
redirect('.')
def proxy_submit(self, form):
get_publisher().reload_cfg()
for f in ('use_proxy', 'proxy_ip', 'proxy_port', 'proxy_user', 'proxy_password'):
get_publisher().cfg[f] = form.get_widget(f).parse()
get_publisher().write_cfg()
# def debug_options [html] (self):
# form = Form(enctype="multipart/form-data")
# debug_cfg = get_publisher().cfg.get('debug', {})
# form.add(StringWidget, 'error_email', title = _('Email for Tracebacks'),
# value = debug_cfg.get('error_email', ''))
# form.add(SingleSelectWidget, 'display_exceptions', title = _('Display Exceptions'),
# value = debug_cfg.get('display_exceptions', ''),
# options = [ (str(''), _('No display')),
# (str('text'), _('Display as Text')),
# (str('text-in-html'), _('Display as Text in HTML an error page')),
# (str('html'), _('Display as HTML')) ])
# form.add(CheckboxWidget, 'logger', title = _('Logger'),
# value = debug_cfg.get('logger', False))
# form.add_submit("submit", _("Submit"))
# form.add_submit("cancel", _("Cancel"))
# if form.get_widget('cancel').parse():
# return redirect('.')
# if not form.is_submitted() or form.has_errors():
# html_top('settings', title = _('Debug Options'))
# '<h2>%s</h2>' % _('Debug Options')
# form.render()
# else:
# self.debug_options_submit(form)
# redirect('.')
# def debug_options_submit(self, form):
# get_publisher().reload_cfg()
# if not get_publisher().cfg.has_key('debug'):
# get_publisher().cfg['debug'] = {}
# for k in ('error_email', 'display_exceptions', 'logger'):
# get_publisher().cfg['debug'][k] = form.get_widget(k).parse()
# get_publisher().write_cfg()
# get_publisher().set_config()
def error_page [html] (error_message):
html_top(_('Error'))
'<h1>%s</h1>' % _('Error')
'<div class="error-page">'
'<p>%s</p>' % error_message
'</div>'

View File

@ -1,276 +0,0 @@
import random
import lasso
from quixote import get_request, get_session, redirect, get_publisher
from quixote.directory import Directory
from larpe.qommon.form import *
from larpe.qommon import emails
from larpe import errors
from larpe import misc
from larpe import storage
from larpe.users import User
from larpe.hosts import Host
from menu import *
class UserUI:
def __init__(self, user):
self.user = user
def form_new(self):
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "name", title = _('User Name'), required = True, size=30)
form.add(StringWidget, "email", title = _('Email'), required = False, size=30)
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def form_edit(self):
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "id", title = _('User Id'), required = False, size=30,
value = self.user.id, readonly = 'readonly')
form.add(StringWidget, "name", title = _('User Name'), required = True, size=30,
value = self.user.name)
form.add(StringWidget, "email", title = _('Email'), required = False, size=30,
value = self.user.email)
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def submit_form(self, form):
if not self.user:
self.user = User()
for f in ('name', 'email'):
widget = form.get_widget(f)
if widget:
setattr(self.user, f, widget.parse())
self.user.is_admin = True
self.user.store()
class UserPage(Directory):
_q_exports = ['', 'edit', 'delete', 'token']
def __init__(self, component):
self.user = User.get(component)
self.user_ui = UserUI(self.user)
get_response().breadcrumb.append((component + '/', self.user.name))
def _q_index [html] (self):
html_top('users', '%s - %s' % (_('User'), self.user.name))
'<h2>%s - %s</h2>' % (_('User'), self.user.name)
'<div class="form">'
'<div class="title">%s</div>' % _('Name')
'<div class="StringWidget content">%s</div>' % self.user.name
if self.user.email:
'<div class="title">%s</div>' % _('Email')
'<div class="StringWidget content">%s</div>' % self.user.email
# if self.user.lasso_dump:
# identity = lasso.Identity.newFromDump(self.user.lasso_dump)
# server = misc.get_lasso_server()
# if len(identity.providerIds) and server:
# '<h3>%s</h3>' % _('Liberty Alliance Details')
# '<div class="StringWidget content"><ul>'
# for pid in identity.providerIds:
# provider = server.getProvider(pid)
# label = misc.get_provider_label(provider)
# if label:
# label = '%s (%s)' % (label, pid)
# else:
# label = pid
# federation = identity.getFederation(pid)
# '<li>'
# _('Account federated with %s') % label
# '<br />'
# if federation.localNameIdentifier:
# _("local: ") + federation.localNameIdentifier.content
# if federation.remoteNameIdentifier:
# _("remote: ") + federation.remoteNameIdentifier.content
# '</li>'
# '</ul></div>'
# # XXX: only display this in debug mode:
# '<h4>%s</h4>' % _('Lasso Identity Dump')
# '<pre>%s</pre>' % self.user.lasso_dump
'</div>'
def debug [html] (self):
get_response().breadcrumb.append( ('debug', _('Debug')) )
html_top('users', 'Debug')
"<h2>Debug - %s</h2>" % self.user.name
"<pre>"
self.user.lasso_dump
"</pre>"
def edit [html] (self):
form = self.user_ui.form_edit()
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append( ('edit', _('Edit')) )
html_top('users', title = _('Edit User'))
'<h2>%s</h2>' % _('Edit User')
form.render()
else:
self.user_ui.submit_form(form)
return redirect('..')
def delete [html] (self):
form = Form(enctype="multipart/form-data")
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
"You are about to irrevocably delete this user.")))
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append(('delete', _('Delete')))
html_top('users', title = _('Delete User'))
'<h2>%s %s</h2>' % (_('Deleting User :'), self.user.name)
form.render()
else:
self.user.remove_self()
return redirect('..')
def token [html] (self):
form = Form(enctype="multipart/form-data", use_tokens = False)
form.add_submit("submit", _("Generate"))
form.add_submit("cancel", _("Cancel"))
request = get_request()
if request.form.has_key('cancel') or request.form.has_key('done'):
return redirect('..')
get_response().breadcrumb.append(('token', _('Identification Token')))
if not form.is_submitted() or form.has_errors():
html_top('users', title = _('Identification Token'))
'<h2>%s</h2>' % _('Identification Token')
'<p>%s</p>' % _('You are about to generate a token than can be used to federate the account.')
'<p>%s</p>' % _('After that, you will have the choice to send it to the user by email so that he can federate his accounts.')
if self.user.identification_token:
'<p>%s</p>' % _('Note that user has already been issued an identification token : %s') % self.user.identification_token
form.render()
else:
if request.form.has_key('submit'):
html_top('users', title = _('Identification Token'))
token = '-'.join(['%04d' % random.randint(1, 9999) for x in range(4)])
self.user.identification_token = str(token)
self.user.store()
'<p>'
_('Identification Token for %s') % self.user.name
' : %s</p>' % self.user.identification_token
form = Form(enctype="multipart/form-data", use_tokens = False)
form.add_submit('done', _('Done'))
if self.user.email:
form.add_submit("submit-email", _("Send by email"))
form.render()
else:
site_url = '%s://%s%s/token?token=%s' \
% (request.get_scheme(), request.get_server(),
get_request().environ['SCRIPT_NAME'], self.user.identification_token)
body = _("""You have been given an identification token.
Your token is %(token)s
Click on %(url)s to use it.
""") % {'token': self.user.identification_token, 'url': site_url}
try:
emails.email(_('Identification Token'), body, self.user.email)
except errors.EmailError, e:
html_top('users', title = _('Identification Token'))
_('Failed sending email. Check your email configuration.')
'<div class="buttons"><a href=".."><input type="button" value="%s" /></a></div><br />' % _('Back')
else:
return redirect('..')
class UsersDirectory(Directory):
_q_exports = ['', 'new']
def _q_index [html] (self):
get_publisher().reload_cfg()
get_response().breadcrumb.append( ('users/', _('Users')) )
html_top('users', title = _('Users'))
if not list(Host.select(lambda x: x.name == 'larpe')):
'<p>%s</p>' % _('Liberty support must be setup before creating users.')
else:
"""<ul id="nav-users-admin">
<li><a href="new">%s</a></li>
</ul>""" % _('New User')
debug_cfg = get_publisher().cfg.get('debug', {})
users = User.select(lambda x: x.name is not None, order_by = 'name')
'<ul class="biglist">'
for user in users:
'<li>'
'<strong class="label">%s</strong>' % user.name
if user.email:
'<p class="details">'
user.email
'</p>'
'<p class="commands">'
command_icon('%s/' % user.id, 'view')
if not user.name_identifiers:
if not user.identification_token:
command_icon('%s/token' % user.id, 'token',
label = _('Identification Token'), icon = 'stock_exec_16.png')
else:
command_icon('%s/token' % user.id, 'token',
label = _('Identification Token (current: %s)') % \
user.identification_token,
icon = 'stock_exec_16.png')
command_icon('%s/edit' % user.id, 'edit')
command_icon('%s/delete' % user.id, 'remove')
if debug_cfg.get('logger', False):
command_icon('../logger/by_user/%s/' % user.id, 'logs',
label = _('Logs'), icon = 'stock_harddisk_16.png')
'</p></li>'
'</ul>'
def new [html] (self):
get_response().breadcrumb.append( ('users/', _('Users')) )
get_response().breadcrumb.append( ('new', _('New')) )
hosts = list(Host.select(lambda x: x.name == 'larpe'))
if not hosts:
return error_page('users', _('Liberty support must be setup before creating users.'))
host = hosts[0]
# XXX: user must be logged in to get here
user_ui = UserUI(None)
# FIXME : should be able to use User.count(). Track fake user creations.
users = User.select(lambda x: x.name is not None)
first_user = (len(users) == 0)
form = user_ui.form_new()
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('users', title = _('New User'))
'<h2>%s</h2>' % _('New User')
form.render()
else:
user_ui.submit_form(form)
if first_user:
req = get_request()
if hasattr(req, str('user')) and req.user:
user_ui.user.name_identifiers = req.user.name_identifiers
user_ui.user.lasso_dump = req.user.lasso_dump
user_ui.user.store()
# TODO : SAML 2.0
get_session().set_user(user_ui.user.id, host.provider_id)
return redirect('.')
def _q_lookup(self, component):
get_response().breadcrumb.append( ('users/', _('Users')) )
try:
return UserPage(component)
except KeyError:
raise errors.TraversalError()

View File

@ -1,34 +0,0 @@
import socket
import sys
from qommon.scgi_server import run
import publisher
def start(args):
port = 3007
script_name = ''
i = 0
while i < len(args):
if args[i] == '--port':
port = int(args[i+1])
i += 1
elif args[i] == '--silent':
sys.stdout = open('/dev/null', 'w')
sys.stderr = open('/dev/null', 'w')
elif args[i] == '--script-name':
script_name = args[i+1]
i += 1
i += 1
try:
run(publisher.LarpePublisher.create_publisher, port=port, script_name=script_name)
except socket.error, e:
if e[0] == 98:
print >> sys.stderr, 'address already in use'
sys.exit(1)
raise
except KeyboardInterrupt:
sys.exit(1)

View File

@ -1,12 +0,0 @@
from quixote import get_session, get_request, redirect
from qommon.errors import *
class AccessUnauthorizedError(AccessError):
def render [html] (self):
session = get_session()
request = get_request()
session.after_url = str('%s?%s' % (request.get_url(), request.get_query()))
# TODO : SAML2
login_url = '%s/liberty/larpe/login' % request.environ['SCRIPT_NAME']
redirect(login_url)

View File

@ -1,35 +0,0 @@
from storage import StorableObject
class Federation(StorableObject):
_names = 'federations'
username = None
password = None
host_id = None
name_identifiers = None
cookies = None
select_fields = {}
def __init__(self, username, password, host_id, name_identifier, cookies=None, select={}):
StorableObject.__init__(self)
self.username = username
self.password = password
self.host_id = host_id
self.name_identifiers = [ name_identifier ]
self.cookies = cookies
self.select_fields = select
# def add_name_identifier(self, name_identifier):
# self.name_identifiers.append(name_identifier)
def remove_name_identifier(self, name_identifier):
self.name_identifiers.remove(name_identifier)
if not self.name_identifiers:
self.remove_self()
def set_cookies(self, cookies):
self.cookies = cookies
def __str__(self):
return 'Federation username : %s, name identifiers : %s, cookies : %s' \
% (self.username, self.name_identifiers, self.cookies)

View File

@ -1,13 +0,0 @@
from storage import StorableObject
class FieldPrefill(StorableObject):
_names = 'field_prefill'
form_id = 0
name = None
xpath = None
number = 1
raw_xml = False
regexp_match = None
regexp_replacing = None
select_options = {}

View File

@ -1,164 +0,0 @@
import os
import re
from mod_python import apache
#import larpe.hosts
app_dir = '/var/lib/larpe'
def outputfilter(filter):
# Only filter html code
if filter.req.content_type is not None:
is_html = re.search('text/html', filter.req.content_type)
if filter.req.content_type is not None and not is_html:
filter.pass_on()
else:
if not hasattr(filter.req, 'temp_doc'): # the start
filter.req.temp_doc = [] # create new attribute to hold document
# If content-length ended up wrong, Gecko browsers truncated data, so
if 'Content-Length' in filter.req.headers_out:
del filter.req.headers_out['Content-Length']
# filter.write(filter.req.headers_in['Cookie'])
# delete_cookies(filter)
#filter.req.headers_out['Set-Cookie'] = 'dc_admin="deleted"; max-age=0; expires=Thu, 01-Jan-1970 00:00:00 GMT; path=/'
temp_doc = filter.req.temp_doc
s = filter.read()
while s: # could get '' at any point, but only get None at end
temp_doc.append(s)
s = filter.read()
if s is None: # the end
page = ''.join(temp_doc)
page = filter_dispatch(filter, page)
filter.write(page)
filter.close()
def filter_dispatch(filter, page):
# host = get_host_from_url(filter)
# if host is None:
# apache.log_error('Host not found')
# return page
try:
# function_name = 'filter_' + host.label.lowercase()
host_name = filter.req.hostname.split('.')[-3]
function_name = 'filter_' + host_name
return eval(function_name + '(filter, page)')
except:
return page
# return filter_default(filter, page)
def filter_default(filter, page):
# host = get_host_from_url(filter)
# if host is None:
# apache.log_error('Host not found')
# return page
# if host.auth_url is not None or host.auth_form is None:
# return page
form = find_auth_form(page)
if form is not None:
try:
host_name = filter.req.hostname.split('.')[-3]
return page.replace(form, """
<form method="post" action="/liberty/%s/login">
<input type="submit" value="Connexion" />
</form>""" % host_name)
except:
pass
return page
def find_auth_form(page):
regexp = re.compile("""<form.*?</form>""", re.DOTALL | re.IGNORECASE)
found_forms = regexp.findall(page)
for found_form in found_forms:
regexp = re.compile("""<input[^>]*?type="password"[^>]*?>""", re.DOTALL | re.IGNORECASE)
if regexp.search(found_form) is not None:
return found_form
return None
#def get_host_from_url(filter):
# try:
# return list(Host.select(lambda x: x.reversed_hostname == filter.req.hostname \
# and x.reversed_directory == get_proxied_site_name(filter)))[0]
# except:
# return None
def filter_linuxfr(filter, page):
str_to_replace = re.compile(str('<form method="post" action="/login.html" id="formulaire">.*?</form>'), re.DOTALL)
return str_to_replace.sub(str(r"""<form method="post" action="/liberty/linuxfr/login" id="formulaire">
<div style="text-align: center; font-size: 13px;" class="loginbox">
<input type="submit" value="Connexion" />
<br />
<a href="/user_new.html">Cr&eacute;er un compte</a>
</div>
</form>"""
), page)
def filter_dotclear(filter, page):
if filter.req.uri == '/dot/ecrire/redac_list.php':
str_to_replace = re.compile(str('(\[[^\?]+\?id=)([^"]+)(">[^\]]*)\]'), re.DOTALL)
return str_to_replace.sub(str(r'\1\2\3 - <a href="/liberty/dot/admin_token?id=\2">token</a> ]'), page)
if filter.req.uri == '/dot/ecrire/redacteur.php':
str_to_replace = re.compile(str('(<form action=")redacteur.php'))
page = str_to_replace.sub(str(r'\1/liberty/dot/admin_new_user'), page)
str_to_replace = re.compile(str('<p class="field"><label class="float" for="user_pwd">.*?</p>'), re.DOTALL)
return str_to_replace.sub(r'', page)
return page
def filter_concerto(filter, page):
str_to_replace = re.compile(str('<form action="login.do" method="post">.*?</form>'), re.DOTALL)
return str_to_replace.sub(str(r"""<form method="post" action="/liberty/concerto/login" id="formulaire">
<div style="text-align: center; font-size: 13px;" class="loginbox">
<input type="submit" value="Connexion" />
</div>
</form>"""
), page)
def get_abs_path(s):
if not s:
return s
if s[0] == '/':
return s
return os.path.join(app_dir, s)
def get_proxied_site_path(filter):
proxy_domain_name = filter.req.hostname
proxied_site_dir = get_proxied_site_name(filter)
return get_abs_path(os.path.join('sp', proxy_domain_name, proxied_site_dir))
def get_proxied_site_name(filter):
uri_tokens = filter.req.uri.split('/')
if uri_tokens[1] != 'liberty':
return uri_tokens[1]
return uri_tokens[2]
def delete_cookies(filter):
success = False
cookies_file_name = get_abs_path(os.path.join(get_proxied_site_path(filter), 'cookies_to_delete'))
cookies_file = open(cookies_file_name, 'r')
for cookie in cookies_file.read().split():
if filter.req.headers_in.has_key('Cookie'):
cookies_header = filter.req.headers_in['Cookie']
# filter.req.temp_doc.append(filter.req.headers_in['Cookie'])
#filter.req.temp_doc.append(cookie[len(cookie) -1:])
cookies_match = re.findall(cookie[:len(cookie) -1], cookies_header)
if len(cookies_match) > 0:
filter.req.temp_doc.append('User-Agent')
# filter.req.temp_doc.append('%s="deleted"; max-age=0; expires=Thu, 01-Jan-1970 00:00:00 GMT; path=/' % cookie.split('=')[0])
filter.req.headers_out['Set-Cookie'] = '%s="deleted"; max-age=0; expires=Thu, 01-Jan-1970 00:00:00 GMT; path=/' % cookie.split('=')[0]
# cookies_file.close()
# cookies_file = open(cookies_file_name, 'w')
break
# else:
# filter.req.temp_doc.append('dommage')
cookies_file.close()
# if success:
# cookies_file = open(cookies_file_name, 'w')
# cookies_file.close()

View File

@ -1,10 +0,0 @@
from storage import StorableObject
class FormPrefill(StorableObject):
_names = 'form_prefill'
host_id = 0
name = None
url = None
profile = None
prefix = None

View File

@ -1,125 +0,0 @@
import os
from shutil import rmtree
from quixote import get_request
import misc
from storage import StorableObject
from Defaults import APP_DIR
class Host(StorableObject):
_names = 'hosts'
# Main settings
label = None
name = None
orig_site = None
new_url = None
scheme = None
auth_url = None
auth_form_places = 'form_once'
auth_form_page_url = None
auth_form = None
auth_form_url = None
logout_url = None
reversed_hostname = None
reversed_directory = None
organization_name = None
use_ssl = False
private_key = None
public_key = None
site_dir = None
# Auto detected settings
auth_mode = 'form'
auth_form_action = None
auth_check_url = None
login_field_name = None
password_field_name = None
select_fields = {}
post_parameters = {}
http_headers = {}
# Advanced settings
return_url = '/'
root_url = '/'
auth_system = 'password'
auth_match_text = ''
send_hidden_fields = True
initiate_sso_url = None
redirect_root_to_login = False
# Other attributes
provider_id = None
# Default value that indicates the proxy (if configured) is not disabled for this host yet
use_proxy = True
valid_username = None
valid_password = None
# Plugins
# If name is set to None, use the default site authentication class
site_authentication_plugin = None
def get_host_from_url(cls):
try:
host = list(Host.select(lambda x: x.name == misc.get_proxied_site_name()))[0]
if hasattr(host, 'site_authentication_instance'):
del host.site_authentication_instance
return list(Host.select(lambda x: x.name == misc.get_proxied_site_name()))[0]
except Exception, e:
return None
get_host_from_url = classmethod(get_host_from_url)
def get_host_with_provider_id(cls, provider_id):
try:
return list(Host.select(lambda x: x.provider_id == provider_id))[0]
except:
return None
get_host_with_provider_id = classmethod(get_host_with_provider_id)
def get_root_url(self):
if self.root_url.startswith('/'):
if self.reversed_directory:
return '%s/%s%s' % (get_request().environ['SCRIPT_NAME'], self.reversed_directory, self.root_url)
else:
return '%s%s' % (get_request().environ['SCRIPT_NAME'], self.root_url)
# In this case, must be a full url
return self.root_url
def get_return_url(self):
if self.return_url.startswith('/'):
if self.reversed_directory:
return '%s/%s%s' % (get_request().environ['SCRIPT_NAME'], self.reversed_directory, self.return_url)
else:
return '%s%s' % (get_request().environ['SCRIPT_NAME'], self.return_url)
# In this case, must be a full url
return self.return_url
def __cmp__(self, other):
hostname_cmp = cmp(self.reversed_hostname, other.reversed_hostname)
if hostname_cmp != 0:
return hostname_cmp
return cmp(self.reversed_directory, other.reversed_directory)
def remove_object(cls, id):
raise NotImplementedError()
remove_object = classmethod(remove_object)
def remove_self(self):
# Main configuration file
StorableObject.remove_self(self)
# Other generated files
if self.site_dir and os.path.exists(self.site_dir):
rmtree(self.site_dir, ignore_errors=1)
# Also remove hostname directory if empty (meaning there was no other subdirectory for this hostname)
try:
os.rmdir('/'.join(self.site_dir.split('/')[:-1]))
except OSError:
pass
# Virtual host directory
if self.reversed_hostname:
dir = os.path.join(APP_DIR, self.reversed_hostname)
if os.path.exists(dir):
rmtree(dir, ignore_errors=1)

View File

@ -1,430 +0,0 @@
import libxml2
import urllib
import urlparse
import httplib
import re
import os
from quixote import get_field, get_request, get_response, get_session, get_session_manager, redirect
from quixote.directory import Directory
from quixote.http_request import parse_header
import lasso
from qommon import get_logger
from qommon.form import *
from qommon.template import *
import misc
import storage
from users import User
from hosts import Host
from federations import Federation
import site_authentication
class Liberty(Directory):
_q_exports = ['', 'login', 'assertionConsumer', 'soapEndpoint',
'singleLogout', 'singleLogoutReturn',
'federationTermination', 'federationTerminationReturn',
('metadata.xml', 'metadata'), 'public_key', 'local_auth']
def perform_login(self, idp = None):
server = misc.get_lasso_server()
login = lasso.Login(server)
login.initAuthnRequest(idp, lasso.HTTP_METHOD_REDIRECT)
login.request.nameIdPolicy = 'federated'
login.request.forceAuthn = False
login.request.isPassive = False
login.request.consent = 'urn:liberty:consent:obtained'
login.buildAuthnRequestMsg()
return redirect(login.msgUrl)
def assertionConsumer(self):
server = misc.get_lasso_server()
if not server:
return error_page(_('Liberty support is not yet configured'))
login = lasso.Login(server)
request = get_request()
if request.get_method() == 'GET' or get_field('LAREQ'):
if request.get_method() == 'GET':
login.initRequest(request.get_query(), lasso.HTTP_METHOD_REDIRECT)
else:
login.initRequest(get_field('LAREQ'), lasso.HTTP_METHOD_POST)
login.buildRequestMsg()
try:
soap_answer = soap_call(login.msgUrl, login.msgBody)
except SOAPException:
return error_page(_('Failure to communicate with identity provider'))
try:
login.processResponseMsg(soap_answer)
except lasso.Error, error:
if error[0] == lasso.LOGIN_ERROR_STATUS_NOT_SUCCESS:
return error_page(_('Unknown authentication failure'))
if hasattr(lasso, 'LOGIN_ERROR_UNKNOWN_PRINCIPAL'):
if error[0] == lasso.LOGIN_ERROR_UNKNOWN_PRINCIPAL:
return error_page(_('Authentication failure; unknown principal'))
return error_page(_("Identity Provider didn't accept artifact transaction."))
else:
login.processAuthnResponseMsg(get_field('LARES'))
login.acceptSso()
session = get_session()
if login.isSessionDirty:
if login.session:
session.lasso_session_dumps[server.providerId] = login.session.dump()
else:
session.lasso_session_dumps[server.providerId] = None
# Look for an existing user
user = self.lookup_user(session, login)
# Check if it is for Larpe administration or token
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
if host.name == 'larpe':
session.name_identifier = login.nameIdentifier.content
session.lasso_dump = login.identity.dump()
session.provider_id = server.providerId
if user:
session.set_user(user.id, server.providerId)
if hasattr(session, 'after_url') and session.after_url is not None and \
session.after_url.find('admin') != -1:
return redirect(session.after_url)
else:
if not user or not user.is_admin:
if hasattr(session, 'after_url') and session.after_url is not None \
and session.after_url.find('token') != -1:
return redirect(session.after_url)
return redirect('%s/token' % get_request().environ['SCRIPT_NAME'])
return redirect('%s/admin/' % get_request().environ['SCRIPT_NAME'])
# Set session user
if not user:
user = User()
user.name_identifiers = [ login.nameIdentifier.content ]
user.lasso_dumps = [ login.identity.dump() ]
user.store()
session.set_user(user.id, server.providerId)
# elif request.user is not None:
# user = request.user
# session.set_user(user.id)
# else:
# session.set_user('anonymous-%s' % login.nameIdentifier.content)
# user = session.get_user(login.nameIdentifier.content)
# if not user.name_identifiers.has_key(server.providerId):
# user.name_identifiers[server.providerId] = [ login.nameIdentifier.content ]
# elif not login.nameIdentifier.content in user.name_identifiers[server.providerId]:
# user.name_identifiers[server.providerId].append(login.nameIdentifier.content)
# user.lasso_dumps[server.providerId] = login.identity.dump()
# user.store()
# request.user = user
federations = Federation.select(lambda x: host.id == x.host_id \
and user.name_identifiers[0] in x.name_identifiers)
if federations:
return site_authentication.get_site_authentication(host).sso_local_login(federations[0])
else:
if hasattr(session, 'token'):
# FIXME : this method doesn't exist anymore
self.federate_token(session.token)
else:
response = get_response()
if session.after_url:
after_url = session.after_url
session.after_url = None
return redirect(after_url)
response.set_status(303)
response.headers['location'] = urlparse.urljoin(request.get_url(), str('local_auth'))
response.content_type = 'text/plain'
return 'Your browser should redirect you'
def lookup_user(self, session, login):
found_users = list(User.select(lambda x: login.nameIdentifier.content in x.name_identifiers))
if found_users:
return found_users[0]
return None
def singleLogout(self):
request = get_request()
logout = lasso.Logout(misc.get_lasso_server())
if lasso.isLibertyQuery(request.get_query()):
try:
logout.processRequestMsg(request.get_query())
except lasso.Error, error:
if error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
return error_page(_('Failed to check single logout request signature.'))
raise
session = get_session()
if not session.id:
# session has not been found, this may be because the user has
# its browser configured so that cookies are not sent for
# remote queries and IdP is using image-based SLO.
# so we look up a session with the appropriate name identifier
for session in get_session_manager().values():
# This block differs from qommon
user = session.get_user(logout.server.providerId)
if user and logout.nameIdentifier.content in user.name_identifiers:
break
else:
session = get_session()
return self.slo_idp(logout, session)
else:
return self.slo_sp(logout, get_session())
def singleLogoutReturn(self):
logout = lasso.Logout(misc.get_lasso_server())
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
try:
logout.processResponseMsg(get_request().get_query())
except lasso.Error, error:
if error[0] == lasso.PROFILE_ERROR_INVALID_QUERY:
raise AccessError()
if error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
return error_page(_('Failed to check single logout request signature.'))
if hasattr(lasso, 'LOGOUT_ERROR_REQUEST_DENIED') and \
error[0] == lasso.LOGOUT_ERROR_REQUEST_DENIED:
return redirect(host.get_root_url()) # ignore silently
elif error[0] == lasso.ERROR_UNDEFINED:
# XXX: unknown status; ignoring for now.
return redirect(host.get_root_url()) # ignore silently
raise
return redirect(host.get_root_url())
def slo_idp(self, logout, session):
'''Single Logout initiated by IdP'''
# This block differs from qommon
if session.lasso_session_dumps.has_key(logout.server.providerId):
logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
user = session.get_user(logout.server.providerId)
if user and user.lasso_dumps:
logout.setIdentityFromDump(user.lasso_dumps[0])
if user and logout.nameIdentifier.content not in user.name_identifiers:
raise 'No appropriate name identifier in user (%s and %s)' % (
logout.nameIdentifier.content, user.name_identifiers)
host = Host.get_host_with_provider_id(logout.server.providerId)
if host is not None:
site_authentication.get_site_authentication(host).local_logout(user=user)
try:
logout.validateRequest()
except lasso.Error, error:
if error[0] != lasso.PROFILE_ERROR_SESSION_NOT_FOUND:
raise
else:
get_session_manager().expire_session(logout.server.providerId)
logout.buildResponseMsg()
if logout.msgBody: # soap answer
return logout.msgBody
else:
return redirect(logout.msgUrl)
def slo_sp(self, logout, session):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
if not session.id or not session.users.has_key(logout.server.providerId) \
or not session.lasso_session_dumps.has_key(logout.server.providerId):
get_session_manager().expire_session(logout.server.providerId)
return redirect(host.get_root_url())
logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
user = session.get_user(logout.server.providerId)
site_authentication.get_site_authentication(host).local_logout(user=user)
if user and user.lasso_dumps:
logout.setIdentityFromDump(user.lasso_dumps[0])
return self.slo_sp_redirect(logout, host)
def slo_sp_redirect(self, logout, host):
try:
logout.initRequest(None, lasso.HTTP_METHOD_REDIRECT)
except lasso.Error, error:
if error[0] == lasso.PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND:
get_session_manager().expire_session()
return redirect(host.get_root_url())
raise
logout.buildRequestMsg()
get_session_manager().expire_session(logout.server.providerId)
return redirect(logout.msgUrl)
def soapEndpoint(self):
request = get_request()
ctype = request.environ.get('CONTENT_TYPE')
if not ctype:
return
ctype, ctype_params = parse_header(ctype)
if ctype != 'text/xml':
return
response = get_response()
response.set_content_type('text/xml')
length = int(request.environ.get('CONTENT_LENGTH'))
soap_message = request.stdin.read(length)
request_type = lasso.getRequestTypeFromSoapMsg(soap_message)
if request_type == lasso.REQUEST_TYPE_LOGOUT:
logout = lasso.Logout(misc.get_lasso_server())
logout.processRequestMsg(soap_message)
name_identifier = logout.nameIdentifier.content
for session in get_session_manager().values():
user = session.get_user(logout.server.providerId)
if user and logout.nameIdentifier.content in user.name_identifiers:
break
else:
session = None
return self.slo_idp(logout, session)
if request_type == lasso.REQUEST_TYPE_DEFEDERATION:
defederation = lasso.Defederation(misc.get_lasso_server())
defederation.processNotificationMsg(soap_message)
for session in get_session_manager().values():
user = session.get_user(defederation.server.providerId)
if user and defederation.nameIdentifier.content in user.name_identifiers:
break
else:
session = None
return self.fedterm(defederation, session)
def federationTermination(self):
request = get_request()
if not lasso.isLibertyQuery(request.get_query()):
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
defederation = lasso.Defederation(misc.get_lasso_server())
defederation.processNotificationMsg(request.get_query())
return self.fedterm(defederation, get_session())
def fedterm(self, defederation, session):
if session is not None:
host = Host.get_host_with_provider_id(defederation.server.providerId)
if host is not None:
site_authentication.get_site_authentication(host).local_defederate(session, defederation.server.providerId)
if session.lasso_session_dumps.has_key(defederation.server.providerId):
defederation.setSessionFromDump(session.lasso_session_dumps[defederation.server.providerId])
user = session.get_user(defederation.server.providerId)
if user and user.lasso_dumps:
defederation.setIdentityFromDump(user.lasso_dumps[0])
else:
user = None
try:
defederation.validateNotification()
except lasso.Error, error:
pass # ignore failure (?)
else:
if user:
if not defederation.identity:
# if it was the last federation the whole identity dump collapsed
del user.lasso_dumps[0]
else:
user.lasso_dumps[0] = defederation.identity.dump()
user.store()
if user and defederation.nameIdentifier.content:
user.remove_name_identifier(defederation.server.providerId, defederation.nameIdentifier.content)
user.store()
if defederation.isSessionDirty and session is not None:
if not defederation.session:
del session.lasso_session_dumps[defederation.server.providerId]
else:
session.lasso_session_dumps[defederation.server.providerId] = defederation.session.dump()
session.store()
get_session_manager().expire_session(defederation.server.providerId)
if defederation.msgUrl:
return redirect(defederation.msgUrl)
else:
response = get_response()
response.set_status(204)
return ''
def federationTerminationReturn(self):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
return redirect(host.get_return_url())
# def defederate(self, idp = None):
# session = get_session()
# user = get_request().user
# server = misc.get_lasso_server()
# self.local_defederate(session, server.providerId)
#
# defederation = lasso.Defederation(server)
# defederation.setSessionFromDump(session.lasso_session_dumps[defederation.server.providerId])
# if user and user.lasso_dumps.has_key(defederation.server.providerId):
# defederation.setIdentityFromDump(user.lasso_dumps[defederation.server.providerId])
# defederation.initNotification(idp, lasso.HTTP_METHOD_SOAP);
# defederation.buildNotificationMsg();
# try:
# soap_call(defederation.msgUrl, defederation.msgBody);
# except SOAPException:
# return error_page(_('Failure to communicate with identity provider'))
# rootUrl = '/' + misc.get_proxied_site_name() + '/'
# return redirect(rootUrl)
def local_auth(self):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
return site_authentication.get_site_authentication(host).local_auth
local_auth = property(local_auth)
def metadata(self):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
get_response().set_content_type('text/xml', 'utf-8')
metadata = unicode(open(host.metadata).read(), 'utf-8')
return metadata
def public_key(self):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
get_response().set_content_type('text/plain')
public_key = open(host.public_key).read()
return public_key
class SOAPException(Exception):
pass
def soap_call(url, msg):
if url.startswith('http://'):
host, query = urllib.splithost(url[5:])
conn = httplib.HTTPConnection(host)
else:
host, query = urllib.splithost(url[6:])
conn = httplib.HTTPSConnection(host)
conn.request('POST', query, msg, {'Content-Type': 'text/xml'})
response = conn.getresponse()
data = response.read()
conn.close()
if response.status not in (200, 204): # 204 ok for federation termination
get_logger().warn('SOAP error (%s) (on %s)' % (response.status, url))
raise SOAPException()
return data

View File

@ -1,109 +0,0 @@
import re
import os
import lasso
from quixote import get_publisher, get_request
from qommon.misc import get_abs_path
from hosts import Host
def get_root_url():
req = get_request()
return '%s://%s%s' % (req.get_scheme(), req.get_server(), req.environ['SCRIPT_NAME'])
def get_proxied_site_path():
host = Host.get_host_from_url()
if host is None:
return None
return host.site_dir
def get_proxied_site_domain():
return get_request().get_server().split(':')[0]
def get_proxied_site_name():
nb_subdirs = get_request().environ['SCRIPT_NAME'].count('/')
return get_request().get_path().split('/')[nb_subdirs + 2]
def get_identity_provider_config():
get_publisher().reload_cfg()
idps_dir = get_abs_path('idp')
if get_publisher().cfg.has_key('idp'):
idp_dir = os.path.join(idps_dir, get_publisher().cfg['idp'])
metadata_path = os.path.join(idp_dir, 'metadata.xml')
public_key_path = os.path.join(idp_dir, 'public_key')
if not os.path.isfile(public_key_path):
public_key_path = None
ca_cert_chain_path = os.path.join(idp_dir, 'ca_cert_chain.pem')
if not os.path.isfile(ca_cert_chain_path):
ca_cert_chain_path = None
return metadata_path, public_key_path, ca_cert_chain_path
return None, None, None
def get_lasso_server(protocol='liberty'):
proxied_site_path = get_proxied_site_path()
if proxied_site_path is None:
return None
if protocol == 'liberty':
server = lasso.Server(
os.path.join(proxied_site_path, 'metadata.xml'),
os.path.join(proxied_site_path, 'private_key.pem'),
None, None)
elif protocol == 'saml2':
server = lasso.Server(
os.path.join(proxied_site_path, 'saml2_metadata.xml'),
os.path.join(proxied_site_path, 'private_key.pem'),
None, None)
else:
raise 'XXX: unknown protocol'
metadata_path, public_key_path, ca_cert_chain_path = get_identity_provider_config()
if metadata_path:
try:
server.addProvider(
lasso.PROVIDER_ROLE_IDP,
metadata_path,
public_key_path,
ca_cert_chain_path)
except lasso.Error, error:
if error[0] == lasso.SERVER_ERROR_ADD_PROVIDER_PROTOCOL_MISMATCH:
return None
if error[0] == lasso.SERVER_ERROR_ADD_PROVIDER_FAILED:
return None
raise
return server
def get_provider_label(provider):
if not provider:
return None
if not hasattr(provider, str('getOrganization')):
return provider.providerId
organization = provider.getOrganization()
if not organization:
return provider.providerId
name = re.findall("<OrganizationDisplayName.*>(.*?)</OrganizationDisplayName>", organization)
if not name:
name = re.findall("<OrganizationName.*>(.*?)</OrganizationName>", organization)
if not name:
return provider.providerId
return name[0]
def get_current_protocol():
metadata_path, public_key_path, ca_cert_chain_path = get_identity_provider_config()
if not metadata_path:
return None
try:
provider = lasso.Provider(lasso.PROVIDER_ROLE_IDP, metadata_path, public_key_path, None)
except lasso.Error:
return None
else:
return provider.getProtocolConformance()

View File

@ -1,137 +0,0 @@
import re
import urllib
from quixote import get_request, get_response, get_session
from qommon.misc import http_post_request
from qommon import get_logger
from larpe.qommon.misc import http_get_page
import site_authentication
class AgirheSiteAuthentication(site_authentication.SiteAuthentication):
plugin_name = 'agirhe'
def auto_detect_site(cls, html_doc):
if re.search("""<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/""", html_doc):
return True
return False
auto_detect_site = classmethod(auto_detect_site)
def local_auth_check_post(self, username, password, select={}, session_cookies=False):
url = self.host.auth_check_url
# Build request body
body = '%s=%s&%s=%s' % (self.host.login_field_name, username, self.host.password_field_name, password)
# Add select fields to the body
for name, value in select.iteritems():
body += '&%s=%s' % (name, value)
# Get the authentication page
try:
response, status, page, auth_header = http_get_page(self.host.auth_form_url, use_proxy=self.host.use_proxy)
except Exception, err:
print err
return None, None
# Get current hidden fields everytime
self.parse_forms(page)
if self.host.auth_form is not None:
input_fields = self.parse_input_fields()
self.parse_other_fields(input_fields)
# Add hidden fields to the body
for key, value in self.host.other_fields.iteritems():
value = urllib.quote_plus(value)
body += '&%s=%s' % (key, value)
# Build request HTTP headers
headers = {'Content-Type': 'application/x-www-form-urlencoded',
'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
'X-Forwarded-Host': self.host.reversed_hostname}
# Send request
response, status, data, auth_headers = http_post_request(url, body, headers, self.host.use_proxy)
cookies = response.getheader('Set-Cookie', None)
self.host.cookies = []
if cookies is not None:
cookies_list = []
cookies_set_list = []
for cookie in cookies.split(', '):
# Drop the path and other attributes
cookie_only = cookie.split('; ')[0]
regexp = re.compile('=')
if regexp.search(cookie_only) is None:
continue
# Split name and value
cookie_split = cookie_only.split('=')
cookie_name = cookie_split[0]
cookie_value = cookie_split[1]
cookies_list.append('%s=%s' % (cookie_name, cookie_value))
set_cookie = '%s=%s; path=/' % (cookie_name, cookie_value)
cookies_set_list.append(set_cookie)
self.host.cookies.append(cookie_name)
cookies_headers = '\r\nSet-Cookie: '.join(cookies_set_list)
get_response().set_header('Set-Cookie', cookies_headers)
self.host.store()
get_session().cookies = '; '.join(cookies_list)
else:
get_logger().warn('No cookie from local authentication')
return response.status, data
# The 3 following functions have been copied from admin/hosts.ptl
def parse_forms(self, page):
'''Search for an authentication form'''
# Get all forms
regexp = re.compile("""<form.*?</form>""", re.DOTALL | re.IGNORECASE)
found_forms = regexp.findall(page)
if not found_forms:
return
#raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to find any form'), self.host.auth_form_url))
# Get the first form with a password field
for found_form in found_forms:
regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
if regexp.search(found_form) is not None:
self.host.auth_form = found_form
break
def parse_input_fields(self):
'''Get all input fields'''
regexp = re.compile("""<input[^>]*?>""", re.DOTALL | re.IGNORECASE)
return regexp.findall(self.host.auth_form)
def parse_other_fields(self, input_fields):
'''Get the default value of all other fields'''
self.host.other_fields = {}
# Get hidden fields
regexp = re.compile("""<input[^>]*?type=["']?hidden["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
other_fields = regexp.findall(self.host.auth_form)
# Only get first submit field
regexp = re.compile("""<input[^>]*?type=["']?submit["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
found = regexp.findall(self.host.auth_form)
if found:
if other_fields:
other_fields.append(found[0])
else:
other_fields = found[0]
for field in other_fields:
try:
regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
name = regexp.findall(field)[0]
regexp = re.compile("""value=["'](.*?)["'][\s/>]""", re.DOTALL | re.IGNORECASE)
value = regexp.findall(field)[0]
self.host.other_fields[name] = value
if not self.host.post_parameters.has_key(name):
self.host.post_parameters[name] = { 'enabled': True, 'value': value, 'immutable': False }
except IndexError, e:
continue
site_authentication.register_site_authentication_class(AgirheSiteAuthentication)

View File

@ -1,38 +0,0 @@
import re
from quixote import get_request, get_response, get_session, redirect
from qommon.misc import http_post_request
from qommon import get_logger
import site_authentication
class CirilSiteAuthentication(site_authentication.SiteAuthentication):
plugin_name = 'ciril'
def auto_detect_site(cls, html_doc):
if re.search("""<form name="myForm" id="myForm" method="post" target="choixAppli" action="/cgi-bin/acces.exe" """, html_doc):
print 'ok'
return True
return False
auto_detect_site = classmethod(auto_detect_site)
def check_auth(self, status, data):
success = False
return_content = ''
# If status is 500, fail without checking other criterias
if status // 100 == 5:
success = False
return_content = redirect(self.host.get_return_url())
regexp = re.compile("""javascript\:window\.open\('(/net_rh/accueil.php\?.*?)', '_blank'\)""", re.DOTALL | re.IGNORECASE)
match = regexp.findall(data)
if match:
success = True
return_content = redirect(match[0])
return success, return_content
site_authentication.register_site_authentication_class(CirilSiteAuthentication)

View File

@ -1,83 +0,0 @@
import re
from quixote import get_request, get_response, get_session
from qommon.misc import http_post_request
from qommon import get_logger
import site_authentication
class ConcertoSiteAuthentication(site_authentication.SiteAuthentication):
plugin_name = 'concerto'
def auto_detect_site(cls, html_doc):
if re.search("""<meta name="description" content="Page d'accueil du site Espace-Famille" />""", html_doc):
return True
return False
auto_detect_site = classmethod(auto_detect_site)
def local_auth_check_post(self, username, password, select={}, session_cookies=False):
url = self.host.auth_check_url
# Build request body
body = '%s=%s&%s=%s' % (self.host.login_field_name, username, self.host.password_field_name, password)
# Add select fields to the body
for name, value in select.iteritems():
body += '&%s=%s' % (name, value)
# Add hidden fields to the body
if self.host.send_hidden_fields:
for key, value in self.host.other_fields.iteritems():
body += '&%s=%s' % (key, value)
# Build request HTTP headers
headers = {'Content-Type': 'application/x-www-form-urlencoded',
'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
'X-Forwarded-Host': self.host.reversed_hostname}
# Add session id cookie
if session_cookies is True:
for key, value in self.host.other_fields.iteritems():
headers['Cookie'] = 'JSESSIONID=' + value
# Send request
response, status, data, auth_headers = http_post_request(url, body, headers, self.host.use_proxy)
cookies = response.getheader('Set-Cookie', None)
self.host.cookies = []
new_session_id = None
if cookies is not None:
cookies_list = []
cookies_set_list = []
for cookie in cookies.split(', '):
# Drop the path and other attributes
cookie_only = cookie.split('; ')[0]
regexp = re.compile('=')
if regexp.search(cookie_only) is None:
continue
# Split name and value
cookie_split = cookie_only.split('=')
cookie_name = cookie_split[0]
cookie_value = cookie_split[1]
if cookie_name == 'JSESSIONID':
new_session_id = cookie_value
cookies_list.append('%s=%s' % (cookie_name, cookie_value))
set_cookie = '%s=%s; path=/demo' % (cookie_name, cookie_value)
cookies_set_list.append(set_cookie)
self.host.cookies.append(cookie_name)
cookies_headers = '\r\nSet-Cookie: '.join(cookies_set_list)
get_response().set_header('Set-Cookie', cookies_headers)
self.host.store()
get_session().cookies = '; '.join(cookies_list)
else:
get_logger().warn('No cookie from local authentication')
if session_cookies is False:
# Change idSession hidden field with new session id
self.host.other_fields['idSession'] = new_session_id
# Retry the request with the new session id
return self.local_auth_check_post(username, password, select, session_cookies=True)
else:
return response.status, data
site_authentication.register_site_authentication_class(ConcertoSiteAuthentication)

View File

@ -1,87 +0,0 @@
import re
import urlparse
from quixote import get_request, get_response, get_session
from qommon.misc import http_post_request, http_get_page
from qommon import get_logger
import site_authentication
class EgroupwareSiteAuthentication(site_authentication.SiteAuthentication):
plugin_name = 'egroupware'
def auto_detect_site(cls, html_doc):
if re.search("""<meta name="description" content="eGroupWare" />""", html_doc):
return True
return False
auto_detect_site = classmethod(auto_detect_site)
def local_auth_check_post(self, username, password, select={}):
url = self.host.auth_check_url
# Build request body
body = '%s=%s&%s=%s' % (self.host.login_field_name, username, self.host.password_field_name, password)
# Add select fields to the body
for name, value in select.iteritems():
body += '&%s=%s' % (name, value)
# Add hidden fields to the body
if self.host.send_hidden_fields:
for key, value in self.host.other_fields.iteritems():
body += '&%s=%s' % (key, value)
# Build request HTTP headers
# FIXME : Add back {'Host': hostname}
headers = {'Content-Type': 'application/x-www-form-urlencoded',
'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
'X-Forwarded-Host': self.host.reversed_hostname}
# Send request
response, status, data, auth_headers = http_post_request(url, body, headers, self.host.use_proxy)
# The specific code is these 2 lines and the called function
if self.host.name.startswith('egroupware'):
data = self.get_data_after_redirects(response, data)
cookies = response.getheader('Set-Cookie', None)
self.host.cookies = []
if cookies is not None:
cookies_list = []
cookies_set_list = []
for cookie in cookies.split(', '):
# Drop the path and other attributes
cookie_only = cookie.split('; ')[0]
regexp = re.compile('=')
if regexp.search(cookie_only) is None:
continue
# Split name and value
cookie_split = cookie_only.split('=')
cookie_name = cookie_split[0]
cookie_value = cookie_split[1]
cookies_list.append('%s=%s' % (cookie_name, cookie_value))
set_cookie = '%s=%s; path=/' % (cookie_name, cookie_value)
cookies_set_list.append(set_cookie)
self.host.cookies.append(cookie_name)
cookies_headers = '\r\nSet-Cookie: '.join(cookies_set_list)
get_response().set_header('Set-Cookie', cookies_headers)
self.host.store()
get_session().cookies = '; '.join(cookies_list)
else:
get_logger().warn('No cookie from local authentication')
return response.status, data
def get_data_after_redirects(self, response, data):
status = response.status
headers = {'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
'X-Forwarded-Host': self.host.reversed_hostname}
while status == 302:
location = response.getheader('Location', None)
if location is not None:
url_tokens = urlparse.urlparse(self.host.auth_check_url)
url = '%s://%s%s'% (url_tokens[0], url_tokens[1], location)
response, status, data, auth_headers = http_get_page(url, headers, self.host.use_proxy)
return data
site_authentication.register_site_authentication_class(EgroupwareSiteAuthentication)

View File

@ -1,43 +0,0 @@
import re
from quixote import get_response, redirect
from quixote.html import htmltext
import site_authentication
class SympaSiteAuthentication(site_authentication.SiteAuthentication):
plugin_name = 'sympa'
def auto_detect_site(cls, html_doc):
if re.search("""<FORM ACTION="/wwsympa.fcgi" METHOD=POST>""", html_doc):
return True
return False
auto_detect_site = classmethod(auto_detect_site)
def check_auth(self, status, data):
success = False
return_content = ''
if self.host.auth_system == 'password':
# If there is a password field, authentication probably failed
regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
if not regexp.findall(data):
success = True
# The specific part is only these 2 lines
get_response().filter.update({'no_template': True})
return_content = htmltext(data)
elif self.host.auth_system == 'status':
match_status = int(self.host.auth_match_status)
if match_status == status:
success = True
return_content = redirect(self.host.return_url)
elif self.host.auth_system == 'match_text':
# If the auth_match_text is not matched, it means the authentication is successful
regexp = re.compile(self.host.auth_match_text, re.DOTALL)
if not regexp.findall(data):
success = True
return_content = redirect(self.host.get_return_url())
return success, return_content
site_authentication.register_site_authentication_class(SympaSiteAuthentication)

View File

@ -1,76 +0,0 @@
import os
import cPickle
from quixote import get_response, get_session, get_request
from Defaults import *
from qommon.publisher import set_publisher_class, QommonPublisher
from qommon import template
from root import RootDirectory
from admin import RootDirectory as AdminRootDirectory
from sessions import StorageSessionManager
from users import User
from hosts import Host
class LarpePublisher(QommonPublisher):
APP_NAME = 'larpe'
APP_DIR = APP_DIR
DATA_DIR = DATA_DIR
ERROR_LOG = ERROR_LOG
WEB_ROOT = '/larpe'
supported_languages = ['fr']
root_directory_class = RootDirectory
admin_directory_class = AdminRootDirectory
session_manager_class = StorageSessionManager
user_class = User
def get_web_root_url(self):
return get_request().environ['SCRIPT_NAME'] + WEB_ROOT + '/'
def _get_user_name(self, host):
user = get_session().get_user(host.provider_id)
if user and user.name:
return user.name
return None
def filter_output(self, request, output):
response = get_response()
if response.content_type != 'text/html':
return output
if not hasattr(response, 'filter') or not response.filter:
return output
org_name = 'Larpe'
user_name = None
host = Host.get_host_from_url()
if host is not None:
org_name = host.organization_name
# user_name = self._get_user_name(host)
return template.decorate(output, response)
def set_app_dir(self, request):
self.app_dir = os.path.join(self.APP_DIR, request.get_server().lower().split(':')[0])
self.reload_cfg()
if self.cfg.has_key('proxy_hostname'):
self.app_dir = os.path.join(self.APP_DIR, self.cfg['proxy_hostname'])
if self.app_dir is not None and not os.path.exists(self.app_dir):
os.mkdir(self.app_dir)
cfg = None
def write_cfg(self, directory=None):
s = cPickle.dumps(self.cfg)
if directory is None:
directory = self.app_dir
filename = os.path.join(directory, 'config.pck')
open(filename, 'w').write(s)
set_publisher_class(LarpePublisher)
LarpePublisher.register_extra_dir(os.path.join(os.path.dirname(__file__), 'plugins', 'site_authentication'))

View File

@ -1,111 +0,0 @@
import os
import httplib
import lasso
from quixote import get_request, get_response, get_session, redirect
from quixote.directory import Directory
from qommon.form import *
from qommon import template
import admin
import liberty_root
import errors
from hosts import Host
from users import User
from Defaults import WEB_ROOT
class RootDirectory(Directory):
_q_exports = ['', 'admin', 'liberty', 'token']
admin = admin.RootDirectory()
liberty = liberty_root.LibertyRootDirectory()
def _q_index [html] (self):
template.html_top(_('Welcome to Larpe reverse proxy'))
'<ul><li><a href="%s/admin/">%s</a></li></ul>' % (get_request().environ['SCRIPT_NAME'],
_('Configure Larpe'))
def _q_traverse(self, path):
response = get_response()
response.filter = {}
return Directory._q_traverse(self, path)
def _q_lookup(self, component):
return redirect(component + '/')
def token [html] (self):
session = get_session()
if not hasattr(session, str('name_identifier')) or not session.name_identifier \
or not hasattr(session, str('lasso_dump')) or not session.lasso_dump:
raise errors.AccessUnauthorizedError()
# If the token is in the query string, use it
query_string = get_request().get_query()
if query_string:
parameters = query_string.split(str('&'))
for param in parameters:
values = param.split(str('='))
if len(values) < 2:
continue
if values[0] == str('token'):
return self._federate_token(values[1])
# Otherwise, display a form to ask for the token
form = Form(enctype='multipart/form-data')
form.add(StringWidget, 'token', title = _('Identification Token'),
required = True, size = 30)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
template.html_top(_('Identification Token'))
'<p>'
_('Please enter your identification token. ')
_('Your local account will be federated with your Liberty Alliance account.')
'</p>'
form.render()
else:
session = get_session()
if not hasattr(session, str('name_identifier')) or not session.name_identifier \
or not hasattr(session, str('lasso_dump')) or not session.lasso_dump:
raise errors.AccessUnauthorizedError()
token = form.get_widget('token').parse()
return self._federate_token(token)
def _federate_token(self, token):
session = get_session()
# Get the user who owns this token
users_with_token = list(User.select(lambda x: x.identification_token == token))
if len(users_with_token) == 0:
return template.error_page(_('Unknown Token'))
# Fill user attributes
user = users_with_token[0]
user.name_identifiers.append(session.name_identifier)
user.lasso_dumps = [ session.lasso_dump ]
user.identification_token = None
user.is_admin = True
user.store()
# Set this user in the session
session.set_user(user.id, session.provider_id)
# Delete now useless session attributes
del session.name_identifier
del session.lasso_dump
del session.provider_id
return redirect('%s/admin/' % get_request().environ['SCRIPT_NAME'])

View File

@ -1,408 +0,0 @@
import os
import sys
import urlparse
try:
import lasso
except ImportError:
print >> sys.stderr, 'Missing Lasso module, SAMLv2 support disabled'
from quixote import get_publisher, get_request, get_response, get_session, get_session_manager, redirect
from qommon.liberty import SOAPException, soap_call
from qommon.saml2 import Saml2Directory
from qommon import template
from qommon import get_logger
import misc
from users import User
from hosts import Host
from federations import Federation
import site_authentication
class Saml2(Saml2Directory):
_q_exports = Saml2Directory._q_exports + ['local_auth']
def login(self):
return self.perform_login()
def perform_login(self, idp = None):
server = misc.get_lasso_server(protocol = 'saml2')
if not server:
return template.error_page(_('SAML 2.0 support not yet configured.'))
login = lasso.Login(server)
login.initAuthnRequest(idp, lasso.HTTP_METHOD_REDIRECT)
login.request.nameIDPolicy.format = lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT
login.request.nameIDPolicy.allowCreate = True
login.request.forceAuthn = False
login.request.isPassive = False
login.request.consent = 'urn:oasis:names:tc:SAML:2.0:consent:current-implicit'
login.buildAuthnRequestMsg()
return redirect(login.msgUrl)
def singleSignOnArtifact(self):
server = misc.get_lasso_server(protocol = 'saml2')
if not server:
return template.error_page(_('SAML 2.0 support not yet configured.'))
login = lasso.Login(server)
request = get_request()
try:
login.initRequest(request.get_query(), lasso.HTTP_METHOD_ARTIFACT_GET)
except lasso.Error, error:
if error[0] == lasso.PROFILE_ERROR_MISSING_ARTIFACT:
return template.error_page(_('Missing SAML Artifact'))
else:
raise
login.buildRequestMsg()
#remote_provider_cfg = get_cfg('idp', {}).get(misc.get_provider_key(login.remoteProviderId))
#client_cert = remote_provider_cfg.get('clientcertificate')
try:
soap_answer = soap_call(login.msgUrl, login.msgBody)
except SOAPException:
return template.error_page(_('Failure to communicate with identity provider'))
try:
login.processResponseMsg(soap_answer)
except lasso.Error, error:
if error[0] == lasso.LOGIN_ERROR_STATUS_NOT_SUCCESS:
return template.error_page(_('Unknown authentication failure'))
if error[0] == lasso.LOGIN_ERROR_UNKNOWN_PRINCIPAL:
return template.error_page(_('Authentication failure; unknown principal'))
if error[0] == lasso.LOGIN_ERROR_FEDERATION_NOT_FOUND:
return template.error_page('there was no federation')
raise
return self.sso_after_response(login)
def sso_after_response(self, login):
try:
assertion = login.response.assertion[0]
if assertion.subject.subjectConfirmation.subjectConfirmationData.recipient != \
get_request().get_url():
return template.error_page('SubjectConfirmation Recipient Mismatch')
except:
return template.error_page('SubjectConfirmation Recipient Mismatch')
assertions_dir = os.path.join(get_publisher().app_dir, 'assertions')
if not os.path.exists(assertions_dir):
os.mkdir(assertions_dir)
assertion_fn = os.path.join(assertions_dir, assertion.iD)
if os.path.exists(assertion_fn):
return template.error_page('Assertion replay')
try:
if assertion.subject.subjectConfirmation.method != \
'urn:oasis:names:tc:SAML:2.0:cm:bearer':
return template.error_page('Unknown SubjectConfirmation Method')
except:
return template.error_page('Unknown SubjectConfirmation Method')
try:
audience_ok = False
for audience_restriction in assertion.conditions.audienceRestriction:
if audience_restriction.audience != login.server.providerId:
return template.error_page('Incorrect AudienceRestriction')
audience_ok = True
if not audience_ok:
return template.error_page('Incorrect AudienceRestriction')
except:
return template.error_page('Incorrect AudienceRestriction')
# try:
# current_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
# not_before = assertion.subject.subjectConfirmation.subjectConfirmationData.notBefore
# not_on_or_after = assertion.subject.subjectConfirmation.subjectConfirmationData.notOnOrAfter
# if not_before and current_time < not_before:
# return template.error_page('Assertion received too early')
# if not_on_or_after and current_time > not_on_or_after:
# return template.error_page('Assertion expired')
# except:
# return template.error_page('Error checking Assertion Time')
# TODO: check for unknown conditions
login.acceptSso()
session = get_session()
if login.isSessionDirty:
if login.session:
session.lasso_session_dumps[login.server.providerId] = login.session.dump()
else:
session.lasso_session_dumps[login.server.providerId] = None
if assertion.authnStatement[0].sessionIndex:
session.lasso_session_index = assertion.authnStatement[0].sessionIndex
user = self.lookup_user(session, login)
# Check if it is for Larpe administration or token
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
if host.name == 'larpe':
session.name_identifier = login.nameIdentifier.content
session.lasso_dump = login.identity.dump()
session.provider_id = login.server.providerId
if user:
session.set_user(user.id, login.server.providerId)
if hasattr(session, 'after_url') and session.after_url is not None and \
session.after_url.find('admin') != -1:
return redirect(session.after_url)
else:
if not user or not user.is_admin:
if hasattr(session, 'after_url') and session.after_url is not None \
and session.after_url.find('token') != -1:
return redirect(session.after_url)
return redirect('%s/token' % get_request().environ['SCRIPT_NAME'])
return redirect('%s/admin/' % get_request().environ['SCRIPT_NAME'])
# Set session user
if not user:
user = User()
user.name_identifiers = [ login.nameIdentifier.content ]
user.lasso_dumps = [ login.identity.dump() ]
user.store()
session.set_user(user.id, login.server.providerId)
# Check if a federation already exist
federations = Federation.select(lambda x: host.id == x.host_id \
and user.name_identifiers[0] in x.name_identifiers)
if federations:
return site_authentication.get_site_authentication(host).sso_local_login(federations[0])
else:
# Build response redirection
response = get_response()
if session.after_url:
after_url = session.after_url
session.after_url = None
return redirect(after_url)
response.set_status(303)
response.headers['location'] = urlparse.urljoin(get_request().get_url(), str('local_auth'))
response.content_type = 'text/plain'
return 'Your browser should redirect you'
def slo_sp(self, method = None):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
if method is None:
method = lasso.HTTP_METHOD_REDIRECT
logout = lasso.Logout(misc.get_lasso_server(protocol = 'saml2'))
session = get_session()
if not session.id or not session.users.has_key(logout.server.providerId) \
or not session.lasso_session_dumps.has_key(logout.server.providerId):
get_session_manager().expire_session(logout.server.providerId)
return redirect(host.get_root_url())
logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
user = session.get_user(logout.server.providerId)
site_authentication.get_site_authentication(host).local_logout(user=user)
if user and user.lasso_dumps:
logout.setIdentityFromDump(user.lasso_dumps[0])
if method == lasso.HTTP_METHOD_REDIRECT:
return self.slo_sp_redirect(logout)
# Not implemented yet
if method == lasso.HTTP_METHOD_SOAP:
return self.slo_sp_soap(logout)
def slo_sp_redirect(self, logout):
session = get_session()
try:
logout.initRequest(None, lasso.HTTP_METHOD_REDIRECT)
except lasso.Error, error:
if error[0] == lasso.PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND:
get_session_manager().expire_session(logout.server.providerId)
return redirect(host.get_root_url())
if error[0] == lasso.PROFILE_ERROR_SESSION_NOT_FOUND:
get_session_manager().expire_session(logout.server.providerId)
return redirect(host.get_root_url())
raise
logout.buildRequestMsg()
return redirect(logout.msgUrl)
def singleLogoutReturn(self):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
logout = lasso.Logout(misc.get_lasso_server(protocol = 'saml2'))
session = get_session()
if not session.id or not session.users.has_key(logout.server.providerId) \
or not session.lasso_session_dumps.has_key(logout.server.providerId):
get_session_manager().expire_session(logout.server.providerId)
return redirect(host.get_root_url())
logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
message = get_request().get_query()
return self.slo_return(logout, message)
def slo_return(self, logout, message):
host = Host.get_host_from_url()
session = get_session()
try:
logout.processResponseMsg(message)
except lasso.Error, error:
if error[0] == lasso.PROFILE_ERROR_INVALID_QUERY:
get_logger().warn('Invalid response')
elif error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
get_logger().warn('Failed to check single logout request signature')
elif error[0] == lasso.LOGOUT_ERROR_REQUEST_DENIED:
get_logger().warn('Request Denied')
elif error[0] == lasso.LOGOUT_ERROR_UNKNOWN_PRINCIPAL:
get_logger().warn('Unknown principal on logout, probably session stopped already on IdP')
# XXX: wouldn't work when logged on two IdP
del session.lasso_session_dumps[logout.server.providerId]
else:
raise
if not session.lasso_session_dumps.has_key(logout.server.providerId):
get_session_manager().expire_session(logout.server.providerId)
return redirect(host.get_root_url())
def singleLogoutSOAP(self):
try:
soap_message = self.get_soap_message()
except:
return
response = get_response()
response.set_content_type('text/xml')
request_type = lasso.getRequestTypeFromSoapMsg(soap_message)
if request_type != lasso.REQUEST_TYPE_LOGOUT:
get_logger().warn('SOAP message on single logout url not a slo message')
return
logout = lasso.Logout(misc.get_lasso_server(protocol = 'saml2'))
logout.processRequestMsg(soap_message)
name_identifier = logout.nameIdentifier.content
for session in get_session_manager().values():
user = session.get_user(logout.server.providerId)
if user and logout.nameIdentifier.content in user.name_identifiers:
get_logger().info('SLO/SOAP from %s' % logout.remoteProviderId)
break
else:
# no session, build straight failure answer
logout.buildResponseMsg()
return logout.msgBody
return self.slo_idp(logout, session)
def singleLogout(self):
logout = lasso.Logout(misc.get_lasso_server(protocol = 'saml2'))
try:
logout.processRequestMsg(get_request().get_query())
except lasso.Error, error:
if error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
return template.error_page(_('Failed to check single logout request signature.'))
raise
session = get_session()
if not session.id:
# session has not been found, this may be because the user has
# its browser configured so that cookies are not sent for
# remote queries and IdP is using image-based SLO.
# so we look up a session with the appropriate name identifier
name_identifier = logout.nameIdentifier.content
for session in get_session_manager().values():
# This block differs from qommon
user = session.get_user(logout.server.providerId)
if user and logout.nameIdentifier.content in user.name_identifiers:
break
else:
session = get_session()
return self.slo_idp(logout, session)
def slo_idp(self, logout, session):
# This block differs from qommon
if session.lasso_session_dumps.has_key(logout.server.providerId):
logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
user = session.get_user(logout.server.providerId)
if user and user.lasso_dumps:
logout.setIdentityFromDump(user.lasso_dumps[0])
if user and logout.nameIdentifier.content not in user.name_identifiers:
raise 'no appropriate name identifier in session (%s and %s)' % (
logout.nameIdentifier.content, session.name_identifier)
try:
assertion = logout.session.getAssertions(logout.remoteProviderId)[0]
if logout.request.sessionIndex and (
assertion.authnStatement[0].sessionIndex != logout.request.sessionIndex):
logout.setSessionFromDump('<Session />')
except:
pass
try:
logout.validateRequest()
except lasso.Error, error:
if error[0] == lasso.PROFILE_ERROR_SESSION_NOT_FOUND:
pass
elif error[0] == lasso.PROFILE_ERROR_IDENTITY_NOT_FOUND:
pass
elif error[0] == lasso.PROFILE_ERROR_MISSING_ASSERTION:
pass
else:
raise
else:
get_session_manager().expire_session(logout.server.providerId)
try:
if not logout.request.sessionIndex:
for session2 in get_session_manager().values():
if name_identifier == session2.name_identifier:
del get_session_manager()[session2.id]
except:
# killing all session failed, ignoring silently
pass
logout.buildResponseMsg()
if logout.msgBody: # soap answer
return logout.msgBody
else:
return redirect(logout.msgUrl)
def lookup_user(self, session, login):
found_users = list(User.select(lambda x: login.nameIdentifier.content in x.name_identifiers))
if found_users:
return found_users[0]
return None
def local_auth(self):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
return site_authentication.get_site_authentication(host).local_auth
local_auth = property(local_auth)
def metadata(self):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
get_response().set_content_type('text/xml', 'utf-8')
metadata = unicode(open(host.saml2_metadata).read(), 'utf-8')
return metadata
def public_key(self):
host = Host.get_host_from_url()
if host is None:
return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
get_response().set_content_type('text/plain')
public_key = open(host.public_key).read()
return public_key

View File

@ -1,146 +0,0 @@
import random
from quixote.session import Session, SessionManager
from quixote import get_request
from quixote.html import htmltext
from storage import StorableObject
from users import User
class BasicSession(Session, StorableObject):
_names = 'sessions'
users = {}
# name_identifiers = {}
# name_identifier = None
after_url = None
anonymous_key = None
lasso_session_dumps = {}
# lasso_session_dump = None
# lasso_anonymous_identity_dump = None
tempfiles = None
magictokens = None
message = None
def has_info(self):
return self.users or self.after_url or self.anonymous_key or \
self.tempfiles or self.lasso_session_dumps or self.message or \
self.magictokens or Session.has_info(self)
is_dirty = has_info
def get_session_id(self):
return self.id
def set_session_id(self, session_id):
self.id = session_id
session_id = property(get_session_id, set_session_id)
def get_anonymous_key(self, generate = False):
if self.anonymous_key:
return self.anonymous_key
if generate:
self.anonymous_key = random.randint(0, 1000000000)
return self.anonymous_key
def display_message(self):
if not self.message:
return ''
s = htmltext('<div class="%snotice">%s</div>' % self.message)
self.message = None
return s
def add_magictoken(self, token, data):
if not self.magictokens:
self.magictokens = {}
self.magictokens[token] = data
def get_by_magictoken(self, token, default = None):
if not self.magictokens:
return default
return self.magictokens.get(token, default)
def get_user(self, provider_id):
user_id = self.users.get(provider_id, None)
if user_id:
try:
user = User.get(user_id)
except KeyError:
user = User()
# if str(user_id).startswith('anonymous-'):
user.id = user_id
# user.anonymous = True
# if self.name_identifiers.has_key(providerId):
# if not user.name_identifiers.has_key(providerId):
# user.name_identifiers[providerId] = [ self.name_identifiers[providerId] ]
# if self.name_identifiers.has_key(providerId):
# user.name_identifiers[providerId] = [ self.name_identifiers[providerId] ]
# else:
# user.name_identifiers[providerId] = []
# user.lasso_dumps[providerId] = self.lasso_anonymous_identity_dump
return user
return None
def set_user(self, user_id, provider_id):
self.users[provider_id] = user_id
class StorageSessionManager(SessionManager):
def __init__(self):
SessionManager.__init__(self, session_class=BasicSession)
def forget_changes(self, session):
pass
def __getitem__(self, session_id):
try:
return BasicSession.get(session_id)
except KeyError:
raise KeyError
def get(self, session_id, default = None):
try:
return BasicSession.get(session_id)
except KeyError:
return default
def commit_changes(self, session):
if session and session.id:
session.store()
def keys(self):
return BasicSession.keys()
def values(self):
return BasicSession.values()
def items(self):
return BasicSession.items()
def has_key(self, session_id):
return BasicSession.has_key(session_id)
def __setitem__(self, session_id, session):
session.store()
def __delitem__(self, session_id):
if not session_id:
return
try:
BasicSession.remove_object(session_id)
except OSError:
raise KeyError
def expire_session(self, provider_id=None):
session = get_request().session
if session.id is not None:
if provider_id:
if session.users.has_key(provider_id):
del session.users[provider_id]
if session.lasso_session_dumps.has_key(provider_id):
del session.lasso_session_dumps[provider_id]
session.store()
if not session.users:
SessionManager.expire_session(self)
# session.remove_self()

View File

@ -1,338 +0,0 @@
import libxml2
import urllib
import urlparse
import httplib
import re
import os
import socket
import base64
from quixote import get_request, get_response, get_session, redirect, get_publisher
from quixote.directory import Directory
from quixote.http_request import parse_header
import lasso
from qommon import get_logger
from qommon.form import *
from qommon.errors import ConnectionError, ConfigurationError, LoginError
from qommon.misc import http_post_request, http_get_page
from qommon.template import *
import misc
import storage
from users import User
from federations import Federation
class SiteAuthentication:
def __init__(self, host):
self.host = host
def federate(self, username, password, provider_id, cookies, select):
user = get_session().get_user(provider_id)
if user is not None:
Federation(username, password, self.host.id, user.name_identifiers[0], cookies, select).store()
# def federate_token(self, token):
# token_dir = os.path.join(misc.get_proxied_site_path(), 'tokens')
# token_file_name = os.path.join(token_dir, token)
# token_file = open(token_file_name, 'r')
# username, user_passwd = token_file.read().split()
# token_file.close()
def sso_local_login(self, federation):
status, data = self.local_auth_check_dispatch(
federation.username, federation.password, federation.select_fields)
success, return_content = self.check_auth(status, data)
if success:
session = get_session()
if hasattr(session, 'cookies'):
federation.set_cookies(session.cookies)
federation.store()
return return_content
else:
return redirect('local_auth')
def local_auth [html] (self, first_time=True):
response = get_response()
response.set_content_type('text/html')
if hasattr(get_response(), str('breadcrumb')):
del get_response().breadcrumb
get_response().filter['default_org'] = '%s - %s' % (self.host.label, _('Local authentication'))
get_response().filter['body_class'] = 'login'
form = self.form_local_auth()
form.add_submit('submit', _('Submit'))
#form.add_submit('cancel', _('Cancel'))
# if form.get_widget('cancel').parse():
# return redirect('.')
authentication_failure = None
if form.is_submitted() and not form.has_errors():
try:
return self.submit_local_auth_form(form)
except LoginError:
authentication_failure = _('Authentication failure')
get_logger().info('local auth page : %s' % authentication_failure)
except ConnectionError, err:
authentication_failure = _('Connection failed : %s') % err
get_logger().info('local auth page : %s' % authentication_failure)
except ConfigurationError, err:
authentication_failure = _('This service provider is not fully configured : %s') % err
get_logger().info('local auth page : %s' % authentication_failure)
except Exception, err:
authentication_failure = _('Unknown error : %s' % err)
get_logger().info('local auth page : %s' % authentication_failure)
if authentication_failure:
'<div class="errornotice">%s</div>' % authentication_failure
'<p>'
_('Please type your login and password for this Service Provider.')
_('Your local account will be federated with your Liberty Alliance account.')
'</p>'
form.render()
# Also used in admin/hosts.ptl
def form_local_auth(self):
form = Form(enctype='multipart/form-data')
form.add(StringWidget, 'username', title = _('Username'), required = True,
size = 30)
form.add(PasswordWidget, 'password', title = _('Password'), required = True,
size = 30)
for name, values in self.host.select_fields.iteritems():
options = []
if values:
for value in values:
options.append(value)
form.add(SingleSelectWidget, name, title = name.capitalize(),
value = values[0], options = options)
return form
def submit_local_auth_form(self, form):
username = form.get_widget('username').parse()
password = form.get_widget('password').parse()
select = {}
for name, values in self.host.select_fields.iteritems():
if form.get_widget(name):
select[name] = form.get_widget(name).parse()
return self.local_auth_check(username, password, select)
def local_auth_check(self, username, password, select={}):
status, data = self.local_auth_check_dispatch(username, password, select)
if status == 0:
raise
success, return_content = self.check_auth(status, data)
if success:
if misc.get_current_protocol() == lasso.PROTOCOL_SAML_2_0:
provider_id = self.host.saml2_provider_id
else:
provider_id = self.host.provider_id
session = get_session()
if hasattr(session, 'cookies'):
self.federate(username, password, provider_id, session.cookies, select)
else:
self.federate(username, password, provider_id, None, select)
return return_content
raise LoginError()
def local_auth_check_dispatch(self, username, password, select={}):
if self.host.auth_mode == 'http_basic':
return self.local_auth_check_http_basic(username, password)
elif self.host.auth_mode == 'form' and hasattr(self.host, 'auth_check_url') \
and self.host.auth_check_url is not None:
return self.local_auth_check_post(username, password, select)
else:
raise ConfigurationError('No authentication form was found')
def local_auth_check_post(self, username, password, select={}):
url = self.host.auth_check_url
# Build request body
if self.host.post_parameters:
body_params = {}
# Login field
if self.host.post_parameters[self.host.login_field_name]['enabled'] is True:
body_params[self.host.login_field_name] = username
# Password field
if self.host.post_parameters[self.host.password_field_name]['enabled'] is True:
body_params[self.host.password_field_name] = password
# Select fields
for name, value in select.iteritems():
if self.host.post_parameters[name]['enabled'] is True:
body_params[name] = value
# Other fields (hidden, submit and custom)
for name, value in self.host.other_fields.iteritems():
if self.host.post_parameters[name]['enabled'] is True:
body_params[name] = self.host.post_parameters[name]['value']
body = urllib.urlencode(body_params)
else:
# XXX: (to be removed later) Send all parameters for sites configured with a previous version of Larpe
body = '%s=%s&%s=%s' % (self.host.login_field_name, username, self.host.password_field_name, password)
# Add select fields to the body
for name, value in select.iteritems():
body += '&%s=%s' % (name, value)
# Add hidden fields to the body
if self.host.send_hidden_fields:
for name, value in self.host.other_fields.iteritems():
body += '&%s=%s' % (name, value)
# Build request HTTP headers
if self.host.http_headers:
headers = {}
if self.host.http_headers['X-Forwarded-For']['enabled'] is True:
headers['X-Forwarded-For'] = get_request().get_environ('REMOTE_ADDR', '-')
for name, value in self.host.http_headers.iteritems():
if value['enabled'] is True and value['immutable'] is False:
headers[name] = value['value']
else:
# XXX: (to be removed later) Send default headers for sites configured with a previous version of Larpe
headers = { 'Content-Type': 'application/x-www-form-urlencoded',
'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
'X-Forwarded-Host': self.host.reversed_hostname }
# Send request
response, status, data, auth_headers = http_post_request(url, body, headers, self.host.use_proxy)
cookies = response.getheader('Set-Cookie', None)
self.host.cookies = []
if cookies is not None:
cookies_list = []
cookies_set_list = []
for cookie in cookies.split(', '):
# Drop the path and other attributes
cookie_only = cookie.split('; ')[0]
regexp = re.compile('=')
if regexp.search(cookie_only) is None:
continue
# Split name and value
cookie_split = cookie_only.split('=')
cookie_name = cookie_split[0]
cookie_value = cookie_split[1]
cookies_list.append('%s=%s' % (cookie_name, cookie_value))
set_cookie = '%s=%s; path=/' % (cookie_name, cookie_value)
cookies_set_list.append(set_cookie)
self.host.cookies.append(cookie_name)
cookies_headers = '\r\nSet-Cookie: '.join(cookies_set_list)
get_response().set_header('Set-Cookie', cookies_headers)
self.host.store()
get_session().cookies = '; '.join(cookies_list)
else:
get_logger().warn('No cookie from local authentication')
return response.status, data
def local_auth_check_http_basic(self, username, password):
url = self.host.auth_form_url
hostname, query = urllib.splithost(url[5:])
conn = httplib.HTTPConnection(hostname)
auth_header = 'Basic %s' % base64.encodestring('%s:%s' % (username, password))
try:
conn.request('GET', query, headers={'Authorization': auth_header})
except socket.gaierror, err:
print err
conn.close()
return 0, None
else:
response = conn.getresponse()
conn.close()
return response.status, response.read()
def check_auth(self, status, data):
success = False
return_content = ''
# If status is 500, fail without checking other criterias
if status // 100 == 5:
success = False
return_content = redirect(self.host.get_return_url())
# For http auth, only check status code
elif self.host.auth_mode == 'http_basic':
# If failed, status code should be 401
if status // 100 == 2 or status // 100 == 3:
success = True
return_content = redirect(self.host.get_return_url())
else:
if self.host.auth_system == 'password':
# If there is a password field, authentication probably failed
regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
if not regexp.findall(data):
success = True
return_content = redirect(self.host.get_return_url())
elif self.host.auth_system == 'status':
match_status = int(self.host.auth_match_status)
if match_status == status:
success = True
return_content = redirect(self.host.get_return_url())
elif self.host.auth_system == 'match_text':
# If the auth_match_text is not matched, it means the authentication is successful
regexp = re.compile(self.host.auth_match_text, re.DOTALL)
if not regexp.findall(data):
success = True
return_content = redirect(self.host.get_return_url())
# if status == 302:
# success = True
# return_content = redirect(self.host.return_url)
# else:
# if self.host.orig_site.startswith('http://listes.entrouvert.com') \
# and re.search('You have logged in', data) is not None:
# success = True
# return_content = data
return success, return_content
def local_logout(self, federation=None, user=None):
if federation is None and user is not None:
federations = Federation.select(lambda x: user.name_identifiers[0] in x.name_identifiers)
if federations:
federation = federations[0]
# Logout request to the site
url = self.host.logout_url
if url is not None and federation is not None and federation.cookies is not None:
try:
http_get_page(url, {'Cookie': federation.cookies})
except:
pass
# Remove cookies from the browser
if hasattr(self.host, 'cookies'):
for cookie in self.host.cookies:
get_response().expire_cookie(cookie, path='/')
def local_defederate(self, session, provider_id):
if session is None:
return
user = session.get_user(provider_id)
if user is not None:
federations = Federation.select(lambda x: user.name_identifiers[0] in x.name_identifiers)
for federation in federations:
self.local_logout(provider_id, federation)
federation.remove_name_identifier(user.name_identifiers[0])
federation.store()
site_authentication_classes = {}
def register_site_authentication_class(klass):
site_authentication_classes[klass.plugin_name] = klass
def guess_site_authentication_class(html_doc):
for name, klass in site_authentication_classes.iteritems():
if klass.auto_detect_site(html_doc):
return klass.plugin_name
return None
def get_site_authentication(host):
if host.site_authentication_plugin is None:
return SiteAuthentication(host)
return site_authentication_classes[host.site_authentication_plugin](host)

View File

@ -1,119 +0,0 @@
import os
import cPickle
from quixote import get_publisher
def lax_int(s):
try:
return int(s)
except ValueError:
return -1
def fix_key(k):
# insure key can be inserted in filesystem
if not k: return k
return str(k).replace('/', '-')
class StorableObject(object):
def __init__(self):
if get_publisher():
self.id = self.get_new_id()
def get_table_name(cls):
return cls._names
get_table_name = classmethod(get_table_name)
def get_objects_dir(cls):
return os.path.join(get_publisher().app_dir, cls.get_table_name())
get_objects_dir = classmethod(get_objects_dir)
def keys(cls):
if not os.path.exists(cls.get_objects_dir()):
return []
return [fix_key(x) for x in os.listdir(cls.get_objects_dir())]
keys = classmethod(keys)
def values(cls):
return [cls.get(x) for x in cls.keys()]
values = classmethod(values)
def items(cls):
return [(x, cls.get(x)) for x in cls.keys()]
items = classmethod(items)
def count(cls):
return len(cls.keys())
count = classmethod(count)
def select(cls, clause = None, order_by = None):
objects = [cls.get(k) for k in cls.keys()]
if clause:
objects = [x for x in objects if clause(x)]
if order_by:
order_by = str(order_by)
if order_by[0] == '-':
reverse = True
order_by = order_by[1:]
else:
reverse = False
objects.sort(lambda x,y: cmp(getattr(x, order_by), getattr(y, order_by)))
if reverse:
objects.reverse()
return objects
select = classmethod(select)
def has_key(cls, id):
return fix_key(id) in cls.keys()
has_key = classmethod(has_key)
def get_new_id(cls):
keys = cls.keys()
if not keys:
id = 1
else:
id = max([lax_int(x) for x in keys]) + 1
if id == 0:
id = len(keys)+1
return id
get_new_id = classmethod(get_new_id)
def get(cls, id):
if id is None:
raise KeyError()
try:
s = open(os.path.join(cls.get_objects_dir(), fix_key(id))).read()
except IOError:
raise KeyError()
try:
o = cPickle.loads(s)
except EOFError:
raise KeyError()
o._names = cls._names
if hasattr(cls, 'migrate'):
o.migrate()
return o
get = classmethod(get)
def store(self):
if not os.path.exists(self.get_objects_dir()):
os.mkdir(self.get_objects_dir())
s = cPickle.dumps(self)
open(os.path.join(self.get_objects_dir(), fix_key(self.id)), 'w').write(s)
def volatile(cls):
o = cls()
o.id = None
return o
volatile = classmethod(volatile)
def remove_object(cls, id):
os.unlink(os.path.join(cls.get_objects_dir(), fix_key(id)))
remove_object = classmethod(remove_object)
def remove_self(self):
path = os.path.join(self.get_objects_dir(), fix_key(self.id))
try:
os.unlink(path)
except OSError, err:
print err

View File

@ -1,33 +0,0 @@
from storage import StorableObject
class User(StorableObject):
_names = 'users'
name = None
email = None
name_identifiers = None
identification_token = None
# lasso_dump = None
lasso_dumps = None
is_admin = False
anonymous = False
def __init__(self, name=None):
StorableObject.__init__(self)
self.name = name
self.name_identifiers = []
self.lasso_dumps = []
def migrate(self):
pass
def remove_name_identifier(self, provider_id, name_identifier):
self.name_identifiers.remove(name_identifier)
if not self.name_identifiers:
self.remove_self()
else:
self.store()
def __str__(self):
return 'User %s, name : %s, name identifiers : %s, lasso_dumps : %s, token : %s' \
% (self.id, self.name, self.name_identifiers, self.lasso_dumps, self.identification_token)

View File

@ -1,17 +0,0 @@
#!/bin/sh
VERSION="0.2.0"
svn export svn://labs.libre-entreprise.org/svnroot/larpe/larpe/tags/release-${VERSION}
cd release-${VERSION}
cvs -d :pserver:anonymous@labs.libre-entreprise.org:/cvsroot/wcs login
cvs -d :pserver:anonymous@labs.libre-entreprise.org:/cvsroot/wcs export -r HEAD -d larpe/qommon wcs/wcs/qommon
cd ..
mv release-${VERSION} larpe-${VERSION}
mv larpe-${VERSION}/debian .
tar czf larpe_${VERSION}.orig.tar.gz larpe-${VERSION}
mv debian larpe-${VERSION}
cd larpe-${VERSION}
debuild
cd ..
rm -rf larpe-${VERSION}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More