Cleanning repository
This commit is contained in:
parent
17223c80ca
commit
009bafa773
|
@ -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
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
NEWS
|
||||
====
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
.
|
|
@ -1,3 +0,0 @@
|
|||
etc/apache2/sites-available
|
||||
usr/sbin
|
||||
var/lib/larpe
|
|
@ -1 +0,0 @@
|
|||
README
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,10 +0,0 @@
|
|||
all:
|
||||
$(MAKE) -C en
|
||||
# $(MAKE) -C fr
|
||||
|
||||
clean:
|
||||
$(MAKE) -C en clean
|
||||
# $(MAKE) -C fr clean
|
||||
|
||||
.PHONY: clean
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
APP_DIR = "/var/lib/larpe"
|
||||
DATA_DIR = "/usr/share/larpe"
|
||||
ERROR_LOG = None #"/var/log/larpe.log"
|
||||
WEB_ROOT = '/larpe'
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
from root import RootDirectory
|
|
@ -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')
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
@ -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])
|
||||
|
|
@ -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 %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(' ')))
|
||||
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 %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>'
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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>'
|
||||
|
|
@ -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()
|
|
@ -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)
|
||||
|
|
@ -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)
|
|
@ -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)
|
|
@ -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 = {}
|
|
@ -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é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()
|
|
@ -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
|
|
@ -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)
|
||||
|
|
@ -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
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
|
@ -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'))
|
||||
|
|
@ -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'])
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
|
@ -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)
|
||||
|
|
@ -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
|
|
@ -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)
|
|
@ -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
Reference in New Issue