From 4dc72b40aa23d45730e06c083bca21dad7682e3b Mon Sep 17 00:00:00 2001 From: dlaniel Date: Mon, 9 Mar 2009 14:54:38 +0000 Subject: [PATCH] 1.0 release git-svn-id: svn+ssh://labs.libre-entreprise.org/svnroot/larpe@461 3ed937ae-f919-0410-9a43-8e6f19e4ba6e --- larpe/tags/release-1.0/AUTHORS | 5 + larpe/tags/release-1.0/COPYING | 339 ++ larpe/tags/release-1.0/MANIFEST.in | 13 + larpe/tags/release-1.0/Makefile | 45 + larpe/tags/release-1.0/NEWS | 17 + larpe/tags/release-1.0/README | 32 + larpe/tags/release-1.0/TODO | 101 + .../tags/release-1.0/conf/apache2-vhost-larpe | 12 + .../conf/apache2-vhost-larpe-common | 19 + .../release-1.0/conf/output_filter_base.py | 30 + larpe/tags/release-1.0/debian/changelog | 63 + larpe/tags/release-1.0/debian/compat | 1 + larpe/tags/release-1.0/debian/config | 24 + larpe/tags/release-1.0/debian/control | 17 + larpe/tags/release-1.0/debian/copyright | 27 + larpe/tags/release-1.0/debian/dirs | 4 + larpe/tags/release-1.0/debian/docs | 2 + larpe/tags/release-1.0/debian/init | 82 + .../debian/larpe-reload-apache2-script | 3 + larpe/tags/release-1.0/debian/postinst | 66 + larpe/tags/release-1.0/debian/prerm | 41 + larpe/tags/release-1.0/debian/pycompat | 1 + larpe/tags/release-1.0/debian/rules | 73 + larpe/tags/release-1.0/debian/templates | 37 + larpe/tags/release-1.0/doc/Makefile | 8 + larpe/tags/release-1.0/doc/en/Makefile | 35 + larpe/tags/release-1.0/doc/en/custom.tex | 45 + larpe/tags/release-1.0/doc/en/default.css | 143 + larpe/tags/release-1.0/doc/en/fncychap.sty | 490 +++ larpe/tags/release-1.0/doc/en/larpe-admin.rst | 202 ++ .../release-1.0/doc/scripts/removealpha.sh | 5 + .../tags/release-1.0/doc/scripts/rst2latex.py | 29 + larpe/tags/release-1.0/exclude_from_dist | 12 + .../fedora/larpe-reload-apache2-script | 22 + larpe/tags/release-1.0/fedora/larpe.init | 104 + larpe/tags/release-1.0/fedora/larpe.spec | 139 + larpe/tags/release-1.0/larpe-reload-apache2.c | 173 + larpe/tags/release-1.0/larpe/Defaults.py | 10 + larpe/tags/release-1.0/larpe/__init__.py | 16 + .../tags/release-1.0/larpe/admin/__init__.py | 1 + larpe/tags/release-1.0/larpe/admin/apache.py | 302 ++ .../larpe/admin/fields_prefill.ptl | 130 + .../release-1.0/larpe/admin/forms_prefill.ptl | 127 + larpe/tags/release-1.0/larpe/admin/hosts.ptl | 1336 ++++++++ .../release-1.0/larpe/admin/liberty_utils.py | 129 + larpe/tags/release-1.0/larpe/admin/root.ptl | 90 + .../tags/release-1.0/larpe/admin/settings.ptl | 361 +++ larpe/tags/release-1.0/larpe/admin/users.ptl | 275 ++ larpe/tags/release-1.0/larpe/ctl/__init__.py | 2 + larpe/tags/release-1.0/larpe/ctl/start.py | 34 + larpe/tags/release-1.0/larpe/errors.ptl | 14 + larpe/tags/release-1.0/larpe/federations.py | 35 + larpe/tags/release-1.0/larpe/field_prefill.py | 13 + .../release-1.0/larpe/filter/larpe-filter.py | 164 + larpe/tags/release-1.0/larpe/form_prefill.py | 10 + larpe/tags/release-1.0/larpe/hosts.py | 136 + larpe/tags/release-1.0/larpe/idwsf2.ptl | 199 ++ larpe/tags/release-1.0/larpe/liberty.ptl | 372 +++ larpe/tags/release-1.0/larpe/liberty_root.ptl | 9 + larpe/tags/release-1.0/larpe/liberty_site.ptl | 68 + larpe/tags/release-1.0/larpe/logger.py | 34 + larpe/tags/release-1.0/larpe/misc.py | 105 + .../release-1.0/larpe/plugins/__init__.py | 0 .../plugins/site_authentication/__init__.py | 0 .../plugins/site_authentication/agirhe.py | 142 + .../site_authentication/ciril_net_rh.py | 38 + .../plugins/site_authentication/concerto.py | 88 + .../plugins/site_authentication/egroupware.py | 90 + .../plugins/site_authentication/sympa.py | 44 + larpe/tags/release-1.0/larpe/publisher.py | 53 + larpe/tags/release-1.0/larpe/root.ptl | 106 + larpe/tags/release-1.0/larpe/saml2.ptl | 414 +++ larpe/tags/release-1.0/larpe/sessions.py | 76 + .../release-1.0/larpe/site_authentication.ptl | 324 ++ larpe/tags/release-1.0/larpe/users.py | 44 + larpe/tags/release-1.0/larpectl | 25 + larpe/tags/release-1.0/make_debian_package.sh | 17 + larpe/tags/release-1.0/po/Makefile | 49 + larpe/tags/release-1.0/po/fr.po | 2826 +++++++++++++++++ larpe/tags/release-1.0/po/larpe.pot | 2620 +++++++++++++++ larpe/tags/release-1.0/pylintrc | 310 ++ larpe/tags/release-1.0/root/index.html | 18 + .../root/larpe/css/arrow-right-2.0.png | Bin 0 -> 633 bytes .../release-1.0/root/larpe/css/bg-footer.png | Bin 0 -> 886 bytes .../release-1.0/root/larpe/css/dc2/admin.css | 283 ++ .../root/larpe/css/dc2/head-bg.png | Bin 0 -> 357 bytes .../root/larpe/css/dc2/head-logo-empty.png | Bin 0 -> 830 bytes .../root/larpe/css/dc2/head-logo.png | Bin 0 -> 3185 bytes .../root/larpe/css/dc2/page-bg.png | Bin 0 -> 225 bytes .../release-1.0/root/larpe/css/deg-top.png | Bin 0 -> 296 bytes .../release-1.0/root/larpe/css/dot999.png | Bin 0 -> 200 bytes .../tags/release-1.0/root/larpe/css/fond.jpg | Bin 0 -> 3034 bytes .../release-1.0/root/larpe/css/ico_user.png | Bin 0 -> 1332 bytes .../release-1.0/root/larpe/css/img/bulle.png | Bin 0 -> 200 bytes .../root/larpe/css/img/day-date.png | Bin 0 -> 87 bytes .../root/larpe/css/img/footer-500.png | Bin 0 -> 1750 bytes .../release-1.0/root/larpe/css/img/footer.jpg | Bin 0 -> 2586 bytes .../release-1.0/root/larpe/css/img/h2.png | Bin 0 -> 160 bytes .../release-1.0/root/larpe/css/img/li.png | Bin 0 -> 117 bytes .../root/larpe/css/img/linkscat.png | Bin 0 -> 102 bytes .../root/larpe/css/img/page-500.png | Bin 0 -> 216 bytes .../release-1.0/root/larpe/css/img/page.png | Bin 0 -> 181 bytes .../release-1.0/root/larpe/css/img/search.png | Bin 0 -> 136 bytes .../root/larpe/css/img/sidebarh2.png | Bin 0 -> 93 bytes .../root/larpe/css/img/top-500.png | Bin 0 -> 2443 bytes .../release-1.0/root/larpe/css/img/top.jpg | Bin 0 -> 3995 bytes .../release-1.0/root/larpe/css/img/top.png | Bin 0 -> 1462 bytes .../larpe/css/jscalendar/aqua/active-bg.gif | Bin 0 -> 89 bytes .../larpe/css/jscalendar/aqua/dark-bg.gif | Bin 0 -> 85 bytes .../larpe/css/jscalendar/aqua/hover-bg.gif | Bin 0 -> 89 bytes .../larpe/css/jscalendar/aqua/menuarrow.gif | Bin 0 -> 49 bytes .../larpe/css/jscalendar/aqua/normal-bg.gif | Bin 0 -> 110 bytes .../larpe/css/jscalendar/aqua/rowhover-bg.gif | Bin 0 -> 110 bytes .../larpe/css/jscalendar/aqua/status-bg.gif | Bin 0 -> 116 bytes .../root/larpe/css/jscalendar/aqua/theme.css | 236 ++ .../larpe/css/jscalendar/aqua/title-bg.gif | Bin 0 -> 116 bytes .../larpe/css/jscalendar/aqua/today-bg.gif | Bin 0 -> 1122 bytes .../root/larpe/css/larpe-admin.css | 410 +++ .../root/larpe/css/larpe-common.css | 260 ++ .../tags/release-1.0/root/larpe/css/larpe.css | 331 ++ .../root/larpe/css/onglet_left.png | Bin 0 -> 891 bytes .../root/larpe/css/onglet_right.png | Bin 0 -> 816 bytes .../release-1.0/root/larpe/css/required.png | Bin 0 -> 137 bytes .../root/larpe/css/user_info_bottom.png | Bin 0 -> 291 bytes .../root/larpe/css/user_info_top.png | Bin 0 -> 1629 bytes .../release-1.0/root/larpe/css/warning.png | Bin 0 -> 809 bytes .../release-1.0/root/larpe/images/bar.png | Bin 0 -> 2284 bytes .../root/larpe/images/stock_add_16.png | Bin 0 -> 341 bytes .../root/larpe/images/stock_copy_16.png | Bin 0 -> 341 bytes .../root/larpe/images/stock_edit_16.png | Bin 0 -> 492 bytes .../root/larpe/images/stock_exec_16.png | Bin 0 -> 501 bytes .../root/larpe/images/stock_file_16.png | Bin 0 -> 349 bytes .../root/larpe/images/stock_harddisk_16.png | Bin 0 -> 682 bytes .../root/larpe/images/stock_properties_16.png | Bin 0 -> 554 bytes .../root/larpe/images/stock_remove_16.png | Bin 0 -> 173 bytes .../release-1.0/root/larpe/images/view_16.png | Bin 0 -> 747 bytes .../root/larpe/js/jscalendar/README | 33 + .../root/larpe/js/jscalendar/calendar.js | 1806 +++++++++++ .../root/larpe/js/jscalendar/helpers.js | 118 + .../larpe/js/jscalendar/lang/calendar-en.js | 127 + .../larpe/js/jscalendar/lang/calendar-fr.js | 125 + .../tags/release-1.0/root/larpe/js/listing.js | 41 + .../release-1.0/root/larpe/js/prototype.js | 1041 ++++++ .../root/larpe/js/scriptaculous/controls.js | 699 ++++ .../root/larpe/js/scriptaculous/dragdrop.js | 545 ++++ .../root/larpe/js/scriptaculous/effects.js | 707 +++++ .../larpe/js/scriptaculous/scriptaculous.js | 26 + .../root/larpe/js/scriptaculous/unittest.js | 381 +++ .../root/larpe/js/scriptaculous/util.js | 429 +++ .../release-1.0/root/larpe/js/sorttable.js | 185 ++ larpe/tags/release-1.0/setup.py | 39 + .../tags/release-1.0/tests/all4dev/slo_check | 2 + .../tags/release-1.0/tests/all4dev/sso_check | 3 + .../release-1.0/tests/blueprint/slo_check | 1 + .../release-1.0/tests/blueprint/sso_check | 1 + larpe/tags/release-1.0/tests/check.sh | 93 + larpe/tags/release-1.0/tests/defederation | 1 + .../tags/release-1.0/tests/dotclear/slo_check | 2 + .../tags/release-1.0/tests/dotclear/sso_check | 4 + .../tests/dotclear_subdir/slo_check | 1 + .../tests/dotclear_subdir/sso_check | 3 + larpe/tags/release-1.0/tests/federation | 3 + larpe/tags/release-1.0/tests/gen_config.sh | 26 + larpe/tags/release-1.0/tests/idp_login | 3 + .../tests/libre-entreprise/slo_check | 3 + .../tests/libre-entreprise/sso_check | 4 + .../tests/listes_entrouvert/slo_check | 1 + .../tests/listes_entrouvert/sso_check | 2 + .../tests/listes_libre_entreprise/slo_check | 1 + .../tests/listes_libre_entreprise/sso_check | 2 + larpe/tags/release-1.0/tests/slo | 1 + larpe/tags/release-1.0/tests/sso | 1 + 172 files changed, 21697 insertions(+) create mode 100644 larpe/tags/release-1.0/AUTHORS create mode 100644 larpe/tags/release-1.0/COPYING create mode 100644 larpe/tags/release-1.0/MANIFEST.in create mode 100644 larpe/tags/release-1.0/Makefile create mode 100644 larpe/tags/release-1.0/NEWS create mode 100644 larpe/tags/release-1.0/README create mode 100644 larpe/tags/release-1.0/TODO create mode 100644 larpe/tags/release-1.0/conf/apache2-vhost-larpe create mode 100644 larpe/tags/release-1.0/conf/apache2-vhost-larpe-common create mode 100644 larpe/tags/release-1.0/conf/output_filter_base.py create mode 100644 larpe/tags/release-1.0/debian/changelog create mode 100644 larpe/tags/release-1.0/debian/compat create mode 100755 larpe/tags/release-1.0/debian/config create mode 100644 larpe/tags/release-1.0/debian/control create mode 100644 larpe/tags/release-1.0/debian/copyright create mode 100644 larpe/tags/release-1.0/debian/dirs create mode 100644 larpe/tags/release-1.0/debian/docs create mode 100644 larpe/tags/release-1.0/debian/init create mode 100755 larpe/tags/release-1.0/debian/larpe-reload-apache2-script create mode 100644 larpe/tags/release-1.0/debian/postinst create mode 100644 larpe/tags/release-1.0/debian/prerm create mode 100644 larpe/tags/release-1.0/debian/pycompat create mode 100755 larpe/tags/release-1.0/debian/rules create mode 100644 larpe/tags/release-1.0/debian/templates create mode 100644 larpe/tags/release-1.0/doc/Makefile create mode 100644 larpe/tags/release-1.0/doc/en/Makefile create mode 100644 larpe/tags/release-1.0/doc/en/custom.tex create mode 100644 larpe/tags/release-1.0/doc/en/default.css create mode 100644 larpe/tags/release-1.0/doc/en/fncychap.sty create mode 100644 larpe/tags/release-1.0/doc/en/larpe-admin.rst create mode 100755 larpe/tags/release-1.0/doc/scripts/removealpha.sh create mode 100755 larpe/tags/release-1.0/doc/scripts/rst2latex.py create mode 100644 larpe/tags/release-1.0/exclude_from_dist create mode 100755 larpe/tags/release-1.0/fedora/larpe-reload-apache2-script create mode 100755 larpe/tags/release-1.0/fedora/larpe.init create mode 100644 larpe/tags/release-1.0/fedora/larpe.spec create mode 100644 larpe/tags/release-1.0/larpe-reload-apache2.c create mode 100644 larpe/tags/release-1.0/larpe/Defaults.py create mode 100644 larpe/tags/release-1.0/larpe/__init__.py create mode 100644 larpe/tags/release-1.0/larpe/admin/__init__.py create mode 100644 larpe/tags/release-1.0/larpe/admin/apache.py create mode 100644 larpe/tags/release-1.0/larpe/admin/fields_prefill.ptl create mode 100644 larpe/tags/release-1.0/larpe/admin/forms_prefill.ptl create mode 100644 larpe/tags/release-1.0/larpe/admin/hosts.ptl create mode 100644 larpe/tags/release-1.0/larpe/admin/liberty_utils.py create mode 100644 larpe/tags/release-1.0/larpe/admin/root.ptl create mode 100644 larpe/tags/release-1.0/larpe/admin/settings.ptl create mode 100644 larpe/tags/release-1.0/larpe/admin/users.ptl create mode 100644 larpe/tags/release-1.0/larpe/ctl/__init__.py create mode 100644 larpe/tags/release-1.0/larpe/ctl/start.py create mode 100644 larpe/tags/release-1.0/larpe/errors.ptl create mode 100644 larpe/tags/release-1.0/larpe/federations.py create mode 100644 larpe/tags/release-1.0/larpe/field_prefill.py create mode 100755 larpe/tags/release-1.0/larpe/filter/larpe-filter.py create mode 100644 larpe/tags/release-1.0/larpe/form_prefill.py create mode 100644 larpe/tags/release-1.0/larpe/hosts.py create mode 100644 larpe/tags/release-1.0/larpe/idwsf2.ptl create mode 100644 larpe/tags/release-1.0/larpe/liberty.ptl create mode 100644 larpe/tags/release-1.0/larpe/liberty_root.ptl create mode 100644 larpe/tags/release-1.0/larpe/liberty_site.ptl create mode 100644 larpe/tags/release-1.0/larpe/logger.py create mode 100644 larpe/tags/release-1.0/larpe/misc.py create mode 100644 larpe/tags/release-1.0/larpe/plugins/__init__.py create mode 100644 larpe/tags/release-1.0/larpe/plugins/site_authentication/__init__.py create mode 100644 larpe/tags/release-1.0/larpe/plugins/site_authentication/agirhe.py create mode 100644 larpe/tags/release-1.0/larpe/plugins/site_authentication/ciril_net_rh.py create mode 100644 larpe/tags/release-1.0/larpe/plugins/site_authentication/concerto.py create mode 100644 larpe/tags/release-1.0/larpe/plugins/site_authentication/egroupware.py create mode 100644 larpe/tags/release-1.0/larpe/plugins/site_authentication/sympa.py create mode 100644 larpe/tags/release-1.0/larpe/publisher.py create mode 100644 larpe/tags/release-1.0/larpe/root.ptl create mode 100644 larpe/tags/release-1.0/larpe/saml2.ptl create mode 100644 larpe/tags/release-1.0/larpe/sessions.py create mode 100644 larpe/tags/release-1.0/larpe/site_authentication.ptl create mode 100644 larpe/tags/release-1.0/larpe/users.py create mode 100755 larpe/tags/release-1.0/larpectl create mode 100755 larpe/tags/release-1.0/make_debian_package.sh create mode 100644 larpe/tags/release-1.0/po/Makefile create mode 100644 larpe/tags/release-1.0/po/fr.po create mode 100644 larpe/tags/release-1.0/po/larpe.pot create mode 100644 larpe/tags/release-1.0/pylintrc create mode 100644 larpe/tags/release-1.0/root/index.html create mode 100644 larpe/tags/release-1.0/root/larpe/css/arrow-right-2.0.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/bg-footer.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/dc2/admin.css create mode 100644 larpe/tags/release-1.0/root/larpe/css/dc2/head-bg.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/dc2/head-logo-empty.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/dc2/head-logo.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/dc2/page-bg.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/deg-top.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/dot999.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/fond.jpg create mode 100644 larpe/tags/release-1.0/root/larpe/css/ico_user.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/bulle.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/day-date.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/footer-500.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/footer.jpg create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/h2.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/li.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/linkscat.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/page-500.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/page.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/search.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/sidebarh2.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/top-500.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/top.jpg create mode 100644 larpe/tags/release-1.0/root/larpe/css/img/top.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/active-bg.gif create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/dark-bg.gif create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/hover-bg.gif create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/menuarrow.gif create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/normal-bg.gif create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/rowhover-bg.gif create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/status-bg.gif create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/theme.css create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/title-bg.gif create mode 100644 larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/today-bg.gif create mode 100644 larpe/tags/release-1.0/root/larpe/css/larpe-admin.css create mode 100644 larpe/tags/release-1.0/root/larpe/css/larpe-common.css create mode 100644 larpe/tags/release-1.0/root/larpe/css/larpe.css create mode 100644 larpe/tags/release-1.0/root/larpe/css/onglet_left.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/onglet_right.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/required.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/user_info_bottom.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/user_info_top.png create mode 100644 larpe/tags/release-1.0/root/larpe/css/warning.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/bar.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/stock_add_16.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/stock_copy_16.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/stock_edit_16.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/stock_exec_16.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/stock_file_16.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/stock_harddisk_16.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/stock_properties_16.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/stock_remove_16.png create mode 100644 larpe/tags/release-1.0/root/larpe/images/view_16.png create mode 100644 larpe/tags/release-1.0/root/larpe/js/jscalendar/README create mode 100644 larpe/tags/release-1.0/root/larpe/js/jscalendar/calendar.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/jscalendar/helpers.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/jscalendar/lang/calendar-en.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/jscalendar/lang/calendar-fr.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/listing.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/prototype.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/scriptaculous/controls.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/scriptaculous/dragdrop.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/scriptaculous/effects.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/scriptaculous/scriptaculous.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/scriptaculous/unittest.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/scriptaculous/util.js create mode 100644 larpe/tags/release-1.0/root/larpe/js/sorttable.js create mode 100644 larpe/tags/release-1.0/setup.py create mode 100644 larpe/tags/release-1.0/tests/all4dev/slo_check create mode 100644 larpe/tags/release-1.0/tests/all4dev/sso_check create mode 100644 larpe/tags/release-1.0/tests/blueprint/slo_check create mode 100644 larpe/tags/release-1.0/tests/blueprint/sso_check create mode 100755 larpe/tags/release-1.0/tests/check.sh create mode 100644 larpe/tags/release-1.0/tests/defederation create mode 100644 larpe/tags/release-1.0/tests/dotclear/slo_check create mode 100644 larpe/tags/release-1.0/tests/dotclear/sso_check create mode 100644 larpe/tags/release-1.0/tests/dotclear_subdir/slo_check create mode 100644 larpe/tags/release-1.0/tests/dotclear_subdir/sso_check create mode 100644 larpe/tags/release-1.0/tests/federation create mode 100755 larpe/tags/release-1.0/tests/gen_config.sh create mode 100644 larpe/tags/release-1.0/tests/idp_login create mode 100644 larpe/tags/release-1.0/tests/libre-entreprise/slo_check create mode 100644 larpe/tags/release-1.0/tests/libre-entreprise/sso_check create mode 100644 larpe/tags/release-1.0/tests/listes_entrouvert/slo_check create mode 100644 larpe/tags/release-1.0/tests/listes_entrouvert/sso_check create mode 100644 larpe/tags/release-1.0/tests/listes_libre_entreprise/slo_check create mode 100644 larpe/tags/release-1.0/tests/listes_libre_entreprise/sso_check create mode 100644 larpe/tags/release-1.0/tests/slo create mode 100644 larpe/tags/release-1.0/tests/sso diff --git a/larpe/tags/release-1.0/AUTHORS b/larpe/tags/release-1.0/AUTHORS new file mode 100644 index 0000000..b0adfde --- /dev/null +++ b/larpe/tags/release-1.0/AUTHORS @@ -0,0 +1,5 @@ +Damien Laniel + +Artwork and administrave interface design taken from DotClear, version 1.2 and +2.0, released under the GNU General Public License; and GTK+, version 2.8, +released under the GNU Lesser General Public License. diff --git a/larpe/tags/release-1.0/COPYING b/larpe/tags/release-1.0/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/larpe/tags/release-1.0/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + 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. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/larpe/tags/release-1.0/MANIFEST.in b/larpe/tags/release-1.0/MANIFEST.in new file mode 100644 index 0000000..06c2ffc --- /dev/null +++ b/larpe/tags/release-1.0/MANIFEST.in @@ -0,0 +1,13 @@ +include Makefile +include setup.py +include larpectl +include apache2-vhost-larpe +include apache2.conf +include larpe-reload-apache2-script +include larpe-reload-apache2.c +include README COPYING MANIFEST.in MANIFEST NEWS AUTHORS +recursive-include larpe *.py *.ptl +recursive-include data * +recursive-include root * +recursive-include po *.po *.pot Makefile +recursive-include doc *.rst Makefile *.png *.sty custom.tex *.py *.sh *.css diff --git a/larpe/tags/release-1.0/Makefile b/larpe/tags/release-1.0/Makefile new file mode 100644 index 0000000..b0be307 --- /dev/null +++ b/larpe/tags/release-1.0/Makefile @@ -0,0 +1,45 @@ +prefix = /usr +config_prefix = /var +config_dir = $(config_prefix)/lib/larpe + +INSTALL = /usr/bin/install -c +PYTHON = /usr/bin/python + +LARPE_VERSION = 0.2.9 + +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)" --no-compile + $(INSTALL) -d $(DESTDIR)$(prefix)/sbin/ + $(INSTALL) larpectl $(DESTDIR)$(prefix)/sbin/ + $(INSTALL) -m 4550 larpe-reload-apache2 $(DESTDIR)$(prefix)/sbin/ + $(INSTALL) -d $(DESTDIR)/etc/larpe + $(INSTALL) -m 644 conf/apache2-vhost-larpe-common $(DESTDIR)/etc/larpe + $(INSTALL) -d $(DESTDIR)$(config_dir) + $(INSTALL) -d $(DESTDIR)$(config_dir)/vhosts.d + $(INSTALL) -d $(DESTDIR)$(config_dir)/vhost-locations.d + $(INSTALL) -d $(DESTDIR)$(config_dir)/vhosts.d.disabled + $(INSTALL) -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(version)/site-packages/larpe/" + +clean: + $(MAKE) -C po clean + $(MAKE) -C doc clean + -$(PYTHON) setup.py clean + -rm -f larpe-reload-apache2 + +dist: clean + tar czf dist/larpe-$(LARPE_VERSION).tar.gz -C .. --transform s/trunk/larpe-$(LARPE_VERSION)/ --exclude-from=exclude_from_dist trunk + +.PHONY: clean dist diff --git a/larpe/tags/release-1.0/NEWS b/larpe/tags/release-1.0/NEWS new file mode 100644 index 0000000..5e08d64 --- /dev/null +++ b/larpe/tags/release-1.0/NEWS @@ -0,0 +1,17 @@ +NEWS +==== + +Version 1.0 +----------- + +- Adds Liberty Alliance to some sites without modying sites code +- SAML 2.0 and ID-FF 1.2 support (authentication and logout) +- Form prefilling with ID-WSF 2.0 +- Configuration assistant (wizard-like) for configuring new sites +- Fully tested for several sites with very different behaviours +- Plugin system to handle specific behaviour of some sites +- Automatic Apache 2 configuration +- Automatic creation of Apache python filters to transform authentication boxes on the sites +- Support for proxies +- Logging and debug options +- Packages for Debian and Fedora (and children of both) distributions diff --git a/larpe/tags/release-1.0/README b/larpe/tags/release-1.0/README new file mode 100644 index 0000000..f135f76 --- /dev/null +++ b/larpe/tags/release-1.0/README @@ -0,0 +1,32 @@ +Larpe - Liberty Alliance Reverse Proxy +====================================== + +Description +----------- + +Larpe is a Liberty Alliance Reverse Proxy. It allows 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 as HTML on + http://larpe.labs.libre-entreprise.org/doc/en/larpe-admin.html + + +Copyright +--------- + +Larpe is copyrighted by Entr'ouvert and is licensed under 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. diff --git a/larpe/tags/release-1.0/TODO b/larpe/tags/release-1.0/TODO new file mode 100644 index 0000000..5564cc8 --- /dev/null +++ b/larpe/tags/release-1.0/TODO @@ -0,0 +1,101 @@ +- 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 + - Dacode + - linuxfr.org + - Sympa + - listes.entrouvert.com + - listes.libre-entreprise.org + - Mediawiki + - all4dev.libre-entreprise.org + - www.libre-entreprise.org + - www.besancon.com + - Egroupware + - quintine.entrouvert.org/egroupware/ + - squirrelmail + - Concerto Espace-famille + - Ciril Net RH + - Agirhe + +- 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 + diff --git a/larpe/tags/release-1.0/conf/apache2-vhost-larpe b/larpe/tags/release-1.0/conf/apache2-vhost-larpe new file mode 100644 index 0000000..d5ff56b --- /dev/null +++ b/larpe/tags/release-1.0/conf/apache2-vhost-larpe @@ -0,0 +1,12 @@ + + ServerName localhost + ServerAdmin root@localhost + + include /etc/larpe/apache2-vhost-larpe-common + include /var/lib/larpe/vhost-locations.d + + CustomLog /var/log/apache2/larpe-access.log combined + ErrorLog /var/log/apache2/larpe-error.log + + +include /var/lib/larpe/vhosts.d diff --git a/larpe/tags/release-1.0/conf/apache2-vhost-larpe-common b/larpe/tags/release-1.0/conf/apache2-vhost-larpe-common new file mode 100644 index 0000000..3e3d86d --- /dev/null +++ b/larpe/tags/release-1.0/conf/apache2-vhost-larpe-common @@ -0,0 +1,19 @@ +# Static files +DocumentRoot /usr/share/larpe/web/ + +# Python application +SCGIMount / 127.0.0.1:3007 + +# Static files for larpe + + ProxyPass ! + SCGIHandler off + + +# Larpe python application + + ProxyPass ! + + +# No gzip compression +RequestHeader unset Accept-Encoding diff --git a/larpe/tags/release-1.0/conf/output_filter_base.py b/larpe/tags/release-1.0/conf/output_filter_base.py new file mode 100644 index 0000000..aff82da --- /dev/null +++ b/larpe/tags/release-1.0/conf/output_filter_base.py @@ -0,0 +1,30 @@ +import re + +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 None or not is_html: + filter.pass_on() + else: + if not hasattr(filter.req, 'temp_doc'): + # Create a new attribute to hold the document + filter.req.temp_doc = [] + # If content-length ended up wrong, Gecko browsers truncated data + if 'Content-Length' in filter.req.headers_out: + del filter.req.headers_out['Content-Length'] + + temp_doc = filter.req.temp_doc + s = filter.read() + # Could get '' at any point, but only get None at end + while s: + temp_doc.append(s) + s = filter.read() + + # The end + if s is None: + page = ''.join(temp_doc) + page = filter_page(filter, page) + filter.write(page) + filter.close() + diff --git a/larpe/tags/release-1.0/debian/changelog b/larpe/tags/release-1.0/debian/changelog new file mode 100644 index 0000000..96298e2 --- /dev/null +++ b/larpe/tags/release-1.0/debian/changelog @@ -0,0 +1,63 @@ +larpe (1.0-1) unstable; urgency=low + + * New release + - SAML 2.0 and ID-FF 1.2 support (authentication and logout) + - Form prefilling with ID-WSF 2.0 + - Configuration assistant (wizard-like) for configuring new sites + - Fully tested for several sites with very different behaviours + - Plugin system to handle specific behaviour of some sites + - Automatic Apache 2 configuration + - Automatic creation of Apache python filters to transform authentication boxes on the sites + - Support for proxies + - Logging and debug options + + -- Damien Laniel Mon, 09 Mar 2009 11:19:49 +0100 + +larpe (0.2.1-1) unstable; urgency=low + + * New release + + -- Damien Laniel Wed, 20 Jun 2007 15:43:16 +0200 + +larpe (0.2.0-1) unstable; urgency=low + + * New release + + -- Damien Laniel Tue, 30 Jan 2007 18:07:04 +0100 + +larpe (0.1.1-2) unstable; urgency=low + + * Use python2.4 + + -- Damien Laniel Tue, 19 Dec 2006 17:21:05 +0100 + +larpe (0.1.1-1) unstable; urgency=low + + * New release + + -- Damien Laniel Thu, 5 Oct 2006 11:47:53 +0200 + +larpe (0.1.0-1) unstable; urgency=low + + * New release + + -- Damien Laniel 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 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 Mon, 25 Sep 2006 11:11:36 +0200 + +larpe (0.0.2-1) unstable; urgency=low + + * Initial package. + + -- Damien Laniel Fri, 08 Sep 2006 16:00:00 +0200 + diff --git a/larpe/tags/release-1.0/debian/compat b/larpe/tags/release-1.0/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/larpe/tags/release-1.0/debian/compat @@ -0,0 +1 @@ +5 diff --git a/larpe/tags/release-1.0/debian/config b/larpe/tags/release-1.0/debian/config new file mode 100755 index 0000000..9543f2d --- /dev/null +++ b/larpe/tags/release-1.0/debian/config @@ -0,0 +1,24 @@ +#!/bin/sh -e + +# Source debconf library. +. /usr/share/debconf/confmodule + +# Hostname +#db_input high larpe/hostname || true +#db_go + +# Administrator email address +#db_input medium larpe/admin_email || true +#db_go + +# Enable this vhost +#db_input high larpe/enable_vhost || true +#db_go + +# Administrator login +#db_input high larpe/admin_username || true +#db_go + +# Administrator password +#db_input high larpe/admin_password || true +#db_go diff --git a/larpe/tags/release-1.0/debian/control b/larpe/tags/release-1.0/debian/control new file mode 100644 index 0000000..20f26b5 --- /dev/null +++ b/larpe/tags/release-1.0/debian/control @@ -0,0 +1,17 @@ +Source: larpe +Section: web +Priority: optional +Maintainer: Damien Laniel +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. + . diff --git a/larpe/tags/release-1.0/debian/copyright b/larpe/tags/release-1.0/debian/copyright new file mode 100644 index 0000000..c81e494 --- /dev/null +++ b/larpe/tags/release-1.0/debian/copyright @@ -0,0 +1,27 @@ +This package was debianized by Damien Laniel on +Fri, 08 Sep 2006 16:00:00 +0200. + +Upstream Author: Damien Laniel + +Copyright (c) 2005 Entr'ouvert; +copyright (c) 2003-2005 dotclear for some graphics. + +License is GNU GPL v2 or later plus OpenSSL exception clause. + +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. + +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. + +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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +On Debian GNU/Linux systems, the complete text of the GNU General Public +License can be found in `/usr/share/common-licenses/GPL'. + diff --git a/larpe/tags/release-1.0/debian/dirs b/larpe/tags/release-1.0/debian/dirs new file mode 100644 index 0000000..cc80198 --- /dev/null +++ b/larpe/tags/release-1.0/debian/dirs @@ -0,0 +1,4 @@ +etc/apache2/sites-available +etc/larpe +usr/sbin +var/lib/larpe diff --git a/larpe/tags/release-1.0/debian/docs b/larpe/tags/release-1.0/debian/docs new file mode 100644 index 0000000..55bc0a6 --- /dev/null +++ b/larpe/tags/release-1.0/debian/docs @@ -0,0 +1,2 @@ +README +AUTHORS diff --git a/larpe/tags/release-1.0/debian/init b/larpe/tags/release-1.0/debian/init new file mode 100644 index 0000000..edca4da --- /dev/null +++ b/larpe/tags/release-1.0/debian/init @@ -0,0 +1,82 @@ +#! /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 + +# Gracefully exit if the package has been removed. +test -x $DAEMON || exit 0 + +# Source function library +. /lib/lsb/init-functions + +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 + + +# 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 +} + +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 $? + ;; + + 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|force-reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/larpe/tags/release-1.0/debian/larpe-reload-apache2-script b/larpe/tags/release-1.0/debian/larpe-reload-apache2-script new file mode 100755 index 0000000..5431d0d --- /dev/null +++ b/larpe/tags/release-1.0/debian/larpe-reload-apache2-script @@ -0,0 +1,3 @@ +#!/bin/sh + +/etc/init.d/apache2 reload diff --git a/larpe/tags/release-1.0/debian/postinst b/larpe/tags/release-1.0/debian/postinst new file mode 100644 index 0000000..6aa61f2 --- /dev/null +++ b/larpe/tags/release-1.0/debian/postinst @@ -0,0 +1,66 @@ +#! /bin/sh +# postinst script for larpe +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package +# +# quoting from the policy: +# Any necessary prompting should almost always be confined to the +# post-installation script, and should be protected with a conditional +# so that unnecessary prompting doesn't happen if a package's +# installation fails and the `postinst' is called with `abort-upgrade', +# `abort-remove' or `abort-deconfigure'. + +PACKAGE=larpe +VERSION=2.4 +LIB="/usr/lib/python$VERSION" +DIRLIST="/usr/share/pycentral/larpe/site-packages/larpe/" + +case "$1" in + configure|abort-upgrade|abort-remove|abort-deconfigure) + for i in $DIRLIST ; do + /usr/bin/python$VERSION -O $LIB/compileall.py -q $i + /usr/bin/python$VERSION $LIB/compileall.py -q $i + done + + # Load Apache 2 modules + for module in "proxy" "rewrite" "headers" "proxy_http"; do + a2enmod ${module} > /dev/null || true + done + + # Restart Apache 2 + set +e + if [ -x /usr/sbin/invoke-rc.d ]; then + invoke-rc.d apache2 restart || true + else + /etc/init.d/apache2 restart || true + fi + set -e + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + + + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/larpe/tags/release-1.0/debian/prerm b/larpe/tags/release-1.0/debian/prerm new file mode 100644 index 0000000..525d726 --- /dev/null +++ b/larpe/tags/release-1.0/debian/prerm @@ -0,0 +1,41 @@ +#! /bin/sh +# prerm script for larpe +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +PACKAGE=larpe + +case "$1" in + remove|upgrade|deconfigure) + dpkg --listfiles $PACKAGE | + awk '$0~/\.py$/ {print $0"c\n" $0"o"}' | + xargs rm -f >&2 + ;; + failed-upgrade) + ;; + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/larpe/tags/release-1.0/debian/pycompat b/larpe/tags/release-1.0/debian/pycompat new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/larpe/tags/release-1.0/debian/pycompat @@ -0,0 +1 @@ +2 diff --git a/larpe/tags/release-1.0/debian/rules b/larpe/tags/release-1.0/debian/rules new file mode 100755 index 0000000..2ff8134 --- /dev/null +++ b/larpe/tags/release-1.0/debian/rules @@ -0,0 +1,73 @@ +#!/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 + +LARPE_USER = www-data +LARPE_GROUP = www-data + +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 + # Apache Vhost + dh_install conf/apache2-vhost-larpe etc/apache2/sites-available + # Apache reload script + dh_install debian/larpe-reload-apache2-script usr/sbin + + # Give files ownership to Larpe user and group + chown -R $(LARPE_USER):$(LARPE_GROUP) $(CURDIR)/debian/larpe/usr/share/larpe/ + chown -R $(LARPE_USER):$(LARPE_GROUP) $(CURDIR)/debian/larpe/var/lib/larpe/ + chgrp $(LARPE_GROUP) $(CURDIR)/debian/larpe/usr/sbin/larpe-reload-apache2 + +# 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_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 diff --git a/larpe/tags/release-1.0/debian/templates b/larpe/tags/release-1.0/debian/templates new file mode 100644 index 0000000..9a6cc43 --- /dev/null +++ b/larpe/tags/release-1.0/debian/templates @@ -0,0 +1,37 @@ +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 diff --git a/larpe/tags/release-1.0/doc/Makefile b/larpe/tags/release-1.0/doc/Makefile new file mode 100644 index 0000000..9a27348 --- /dev/null +++ b/larpe/tags/release-1.0/doc/Makefile @@ -0,0 +1,8 @@ +all: + $(MAKE) -C en + +clean: + $(MAKE) -C en clean + +.PHONY: clean + diff --git a/larpe/tags/release-1.0/doc/en/Makefile b/larpe/tags/release-1.0/doc/en/Makefile new file mode 100644 index 0000000..295ee43 --- /dev/null +++ b/larpe/tags/release-1.0/doc/en/Makefile @@ -0,0 +1,35 @@ +RST2HTML = rst2html +RST2LATEX = ../scripts/rst2latex.py +PDFLATEX = pdflatex +RM = rm -f + +all: larpe-admin.pdf larpe-admin.html + +%.html: %.rst + $(RST2HTML) --stylesheet=default.css --link-stylesheet --language=en $? > $@ + +figures-no-alpha-stamp: + -$(RM) -r figures-no-alpha/ + mkdir figures-no-alpha/ + for F in figures/*.png; do \ + ../scripts/removealpha.sh $$F figures-no-alpha/`basename $$F`; \ + done + touch figures-no-alpha-stamp + +%.tex: %.rst #figures-no-alpha-stamp + cat $? | sed -e 's/figures\//figures-no-alpha\//' \ + -e 's/ ::$$/ : ::/g' \ + -e 's/.. section-numbering:://' | $(RST2LATEX) --language=en > $@ + +%.pdf: %.tex custom.tex + $(PDFLATEX) $? + logfile=`echo "$@" |sed -r "s/(.*)....$$/\\1/"`.log; while [ -f "$$logfile" -a -n "`grep "Rerun to get cross-references right" $$logfile`" ]; do $(PDFLATEX) $< ; done + +clean: + -$(RM) *.aux *.toc *.log *.out + -$(RM) larpe-admin.pdf + -$(RM) larpe-admin.tex + -$(RM) larpe-admin.html + -$(RM) -r figures-no-alpha figures-no-alpha-stamp + +.PHONY: all clean diff --git a/larpe/tags/release-1.0/doc/en/custom.tex b/larpe/tags/release-1.0/doc/en/custom.tex new file mode 100644 index 0000000..cf42eb7 --- /dev/null +++ b/larpe/tags/release-1.0/doc/en/custom.tex @@ -0,0 +1,45 @@ +\usepackage{float,fancyhdr,lscape,sectsty,colortbl,color,lastpage,setspace} +\usepackage[perpage,bottom]{footmisc} +\usepackage[hang]{caption2} +\usepackage{marvosym} + +\usepackage{float,url,listings,tocbibind,fancyhdr,calc,placeins} + +\usepackage{palatino} +\usepackage[Glenn]{fncychap} + +\pagestyle{fancy} +\fancyhead{} +\fancyfoot{} +\fancyhead[L]{Authentic} +\fancyhead[R]{Administrator Guide} +\fancyfoot[C]{Page \thepage} +\addtolength{\headheight}{1.6pt} + +\setlength\parindent{0pt} +\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex} +\setlength\abovecaptionskip{0.1ex} + +\makeatletter +\renewcommand{\maketitle}{\begin{titlepage}% + \let\footnotesize\small + \let\footnoterule\relax + \parindent \z@ + \reset@font + \null\vfil + \begin{flushleft} + \huge \@title + \end{flushleft} + \par + \hrule height 1pt + \par + \begin{flushright} + \LARGE \@author \par + \end{flushright} + \vskip 60\p@ + \vfil\null + \end{titlepage}% + \setcounter{footnote}{0}% +} +\makeatother + diff --git a/larpe/tags/release-1.0/doc/en/default.css b/larpe/tags/release-1.0/doc/en/default.css new file mode 100644 index 0000000..d198a16 --- /dev/null +++ b/larpe/tags/release-1.0/doc/en/default.css @@ -0,0 +1,143 @@ +body { + font-family: sans-serif; +} + + +h1 a, h2 a, h3 a, h4 a { + text-decoration: inherit; + color: inherit; +} + +pre.literal-block { + background: #eee; + border: 1px inset black; + padding: 2px; + margin: auto 10px; + overflow: auto; +} + +h1.title { + text-align: center; + background: #eef; + border: 1px solid #aaf; + letter-spacing: 1px; +} + +div.section { + margin-bottom: 2em; +} + +div.section h1 { + padding: 0 15px; + background: #eef; + border: 1px solid #aaf; +} + +div.section h2 { + padding: 0 15px; + background: #eef; + border: 1px solid #aaf; +} + +div.document { + margin-top: 1em; + border-top: 1px solid #aaf; + border-bottom: 1px solid #aaf; +} + +div.section p, +div.section ul { + text-align: justify; +} + +div.contents { + float: right; + border: 1px solid black; + margin: 1em; + background: #eef; + max-width: 33%; +} + +div#building-liberty-services-with-lasso div#table-of-contents { + max-width: inherit; + float: none; + background: white url(lasso.png) bottom right no-repeat; +} + +div.contents ul { + padding-left: 1em; + list-style: none; +} + +div.contents li { + padding-bottom: 2px; +} + +div.contents p { + background: #ddf; + text-align: center; + border-bottom: 1px solid black; + margin: 0; +} + +th.docinfo-name { + text-align: right; + padding-right: 0.5em; +} + +dd { + margin-bottom: 1ex; +} + +table.table { + margin: 1ex 0; + border-spacing: 0px; +} + + +table.table th { + padding: 0px 1ex; + background: #eef; + font-weight: normal; +} + + +table.table td { + padding: 0 0.5ex; +} + +div.note, div.warning { + padding: 0.3ex; + padding-left: 60px; + min-height: 50px; + margin: 1ex 1em; +} + +div.note { + background: #ffa url(note.png) top left no-repeat; + border: 1px solid #fd8; +} + +div.warning { + background: #ffd url(warning.png) top left no-repeat; +} + +p.admonition-title { + font-weight: bold; + display: inline; + display: none; + padding-right: 1em; +} + +div.figure { + margin: 0 auto; + width: 70%; + min-width: 800px; + text-align: center; +} + +div.figure p.caption { + font-style: italic; + margin: 1ex 0 2em 0; + text-align: center; +} diff --git a/larpe/tags/release-1.0/doc/en/fncychap.sty b/larpe/tags/release-1.0/doc/en/fncychap.sty new file mode 100644 index 0000000..9c4ed8d --- /dev/null +++ b/larpe/tags/release-1.0/doc/en/fncychap.sty @@ -0,0 +1,490 @@ +%%% Copyright Ulf A. Lindgren +%%% +%%% Note Premission is granted to modify this file under +%%% the condition that it is saved using another +%%% file and package name. +%%% +%%% Revision 1.1 (1997) +%%% +%%% Jan. 8th Modified package name base date option +%%% Jan. 22th Modified FmN and FmTi for error in book.cls +%%% \MakeUppercase{#}->{\MakeUppercase#} +%%% Apr. 6th Modified Lenny option to prevent undesired +%%% skip of line. +%%% Nov. 8th Fixed \@chapapp for AMS +%%% +%%% Revision 1.2 (1998) +%%% +%%% Feb. 11th Fixed appendix problem related to Bjarne +%%% Aug. 11th Fixed problem related to 11pt and 12pt +%%% suggested by Tomas Lundberg. THANKS! +%%% +%%% Revision 1.3 (2004) +%%% Sep. 20th problem with frontmatter, mainmatter and +%%% backmatter, pointed out by Lapo Mori +%%% +%%% Revision 1.31 (2004) +%%% Sep. 21th problem with the Rejne definition streched text +%%% caused ugly gaps in the vrule aligned with the title +%%% text. Kindly pointed out to me by Hendri Adriaens +%%% +%%% Revision 1.32 (2005) +%%% Jun. 23th compatibility problem with the KOMA class 'scrbook.cls' +%%% a remedy is a redefinition of '\@schapter' in +%%% line with that used in KOMA. The problem was pointed +%%% out to me by Mikkel Holm Olsen +%%% +%%% Revision 1.33 (2005) +%%% Aug. 9th misspelled ``TWELV'' corrected, the error was pointed +%%% out to me by George Pearson +%%% + + +%%% Last modified Aug. 9th 2005 + +\NeedsTeXFormat{LaTeX2e}[1995/12/01] +\ProvidesPackage{fncychap} + [2004/09/21 v1.33 + LaTeX package (Revised chapters)] + +%%%% DEFINITION OF Chapapp variables +\newcommand{\CNV}{\huge\bfseries} +\newcommand{\ChNameVar}[1]{\renewcommand{\CNV}{#1}} + + +%%%% DEFINITION OF TheChapter variables +\newcommand{\CNoV}{\huge\bfseries} +\newcommand{\ChNumVar}[1]{\renewcommand{\CNoV}{#1}} + +\newif\ifUCN +\UCNfalse +\newif\ifLCN +\LCNfalse +\def\ChNameLowerCase{\LCNtrue\UCNfalse} +\def\ChNameUpperCase{\UCNtrue\LCNfalse} +\def\ChNameAsIs{\UCNfalse\LCNfalse} + +%%%%% Fix for AMSBook 971008 + +\@ifundefined{@chapapp}{\let\@chapapp\chaptername}{} + + +%%%%% Fix for Bjarne and appendix 980211 + +\newif\ifinapp +\inappfalse +\renewcommand\appendix{\par + \setcounter{chapter}{0}% + \setcounter{section}{0}% + \inapptrue% + \renewcommand\@chapapp{\appendixname}% + \renewcommand\thechapter{\@Alph\c@chapter}} + +%%%%% Fix for frontmatter, mainmatter, and backmatter 040920 + +\@ifundefined{@mainmatter}{\newif\if@mainmatter \@mainmattertrue}{} + +%%%%% + + + +\newcommand{\FmN}[1]{% +\ifUCN + {\MakeUppercase#1}\LCNfalse +\else + \ifLCN + {\MakeLowercase#1}\UCNfalse + \else #1 + \fi +\fi} + + +%%%% DEFINITION OF Title variables +\newcommand{\CTV}{\Huge\bfseries} +\newcommand{\ChTitleVar}[1]{\renewcommand{\CTV}{#1}} + +%%%% DEFINITION OF the basic rule width +\newlength{\RW} +\setlength{\RW}{1pt} +\newcommand{\ChRuleWidth}[1]{\setlength{\RW}{#1}} + +\newif\ifUCT +\UCTfalse +\newif\ifLCT +\LCTfalse +\def\ChTitleLowerCase{\LCTtrue\UCTfalse} +\def\ChTitleUpperCase{\UCTtrue\LCTfalse} +\def\ChTitleAsIs{\UCTfalse\LCTfalse} +\newcommand{\FmTi}[1]{% +\ifUCT + {\MakeUppercase#1}\LCTfalse +\else + \ifLCT + {\MakeLowercase#1}\UCTfalse + \else {#1} + \fi +\fi} + + + +\newlength{\mylen} +\newlength{\myhi} +\newlength{\px} +\newlength{\py} +\newlength{\pyy} +\newlength{\pxx} + + +\def\mghrulefill#1{\leavevmode\leaders\hrule\@height #1\hfill\kern\z@} + +\newcommand{\DOCH}{% + \CNV\FmN{\@chapapp}\space \CNoV\thechapter + \par\nobreak + \vskip 20\p@ + } +\newcommand{\DOTI}[1]{% + \CTV\FmTi{#1}\par\nobreak + \vskip 40\p@ + } +\newcommand{\DOTIS}[1]{% + \CTV\FmTi{#1}\par\nobreak + \vskip 40\p@ + } + +%%%%%% SONNY DEF + +\DeclareOption{Sonny}{% + \ChNameVar{\Large\sf} + \ChNumVar{\Huge} + \ChTitleVar{\Large\sf} + \ChRuleWidth{0.5pt} + \ChNameUpperCase + \renewcommand{\DOCH}{% + \raggedleft + \CNV\FmN{\@chapapp}\space \CNoV\thechapter + \par\nobreak + \vskip 40\p@} + \renewcommand{\DOTI}[1]{% + \CTV\raggedleft\mghrulefill{\RW}\par\nobreak + \vskip 5\p@ + \CTV\FmTi{#1}\par\nobreak + \mghrulefill{\RW}\par\nobreak + \vskip 40\p@} + \renewcommand{\DOTIS}[1]{% + \CTV\raggedleft\mghrulefill{\RW}\par\nobreak + \vskip 5\p@ + \CTV\FmTi{#1}\par\nobreak + \mghrulefill{\RW}\par\nobreak + \vskip 40\p@} +} + +%%%%%% LENNY DEF + +\DeclareOption{Lenny}{% + + \ChNameVar{\fontsize{14}{16}\usefont{OT1}{phv}{m}{n}\selectfont} + \ChNumVar{\fontsize{60}{62}\usefont{OT1}{ptm}{m}{n}\selectfont} + \ChTitleVar{\Huge\bfseries\rm} + \ChRuleWidth{1pt} + \renewcommand{\DOCH}{% + \settowidth{\px}{\CNV\FmN{\@chapapp}} + \addtolength{\px}{2pt} + \settoheight{\py}{\CNV\FmN{\@chapapp}} + \addtolength{\py}{1pt} + + \settowidth{\mylen}{\CNV\FmN{\@chapapp}\space\CNoV\thechapter} + \addtolength{\mylen}{1pt} + \settowidth{\pxx}{\CNoV\thechapter} + \addtolength{\pxx}{-1pt} + + \settoheight{\pyy}{\CNoV\thechapter} + \addtolength{\pyy}{-2pt} + \setlength{\myhi}{\pyy} + \addtolength{\myhi}{-1\py} + \par + \parbox[b]{\textwidth}{% + \rule[\py]{\RW}{\myhi}% + \hskip -\RW% + \rule[\pyy]{\px}{\RW}% + \hskip -\px% + \raggedright% + \CNV\FmN{\@chapapp}\space\CNoV\thechapter% + \hskip1pt% + \mghrulefill{\RW}% + \rule{\RW}{\pyy}\par\nobreak% + \vskip -\baselineskip% + \vskip -\pyy% + \hskip \mylen% + \mghrulefill{\RW}\par\nobreak% + \vskip \pyy}% + \vskip 20\p@} + + + \renewcommand{\DOTI}[1]{% + \raggedright + \CTV\FmTi{#1}\par\nobreak + \vskip 40\p@} + + \renewcommand{\DOTIS}[1]{% + \raggedright + \CTV\FmTi{#1}\par\nobreak + \vskip 40\p@} + } + + +%%%%%%% GLENN DEF + + +\DeclareOption{Glenn}{% + \ChNameVar{\bfseries\Large\sf} + \ChNumVar{\Huge} + \ChTitleVar{\bfseries\Large\rm} + \ChRuleWidth{1pt} + \ChNameUpperCase + \ChTitleUpperCase + \renewcommand{\DOCH}{% + \settoheight{\myhi}{\CTV\FmTi{Test}} + \setlength{\py}{\baselineskip} + \addtolength{\py}{\RW} + \addtolength{\py}{\myhi} + \setlength{\pyy}{\py} + \addtolength{\pyy}{-1\RW} + + \raggedright + \CNV\FmN{\@chapapp}\space\CNoV\thechapter + \hskip 3pt\mghrulefill{\RW}\rule[-1\pyy]{2\RW}{\py}\par\nobreak} + + \renewcommand{\DOTI}[1]{% + \addtolength{\pyy}{-4pt} + \settoheight{\myhi}{\CTV\FmTi{#1}} + \addtolength{\myhi}{\py} + \addtolength{\myhi}{-1\RW} + \vskip -1\pyy + \rule{2\RW}{\myhi}\mghrulefill{\RW}\hskip 2pt + \raggedleft\CTV\FmTi{#1}\par\nobreak + \vskip 80\p@} + +\newlength{\backskip} + \renewcommand{\DOTIS}[1]{% +% \setlength{\py}{10pt} +% \setlength{\pyy}{\py} +% \addtolength{\pyy}{\RW} +% \setlength{\myhi}{\baselineskip} +% \addtolength{\myhi}{\pyy} +% \mghrulefill{\RW}\rule[-1\py]{2\RW}{\pyy}\par\nobreak +% \addtolength{}{} +%\vskip -1\baselineskip +% \rule{2\RW}{\myhi}\mghrulefill{\RW}\hskip 2pt +% \raggedleft\CTV\FmTi{#1}\par\nobreak +% \vskip 60\p@} +%% Fix suggested by Tomas Lundberg + \setlength{\py}{25pt} % eller vad man vill + \setlength{\pyy}{\py} + \setlength{\backskip}{\py} + \addtolength{\backskip}{2pt} + \addtolength{\pyy}{\RW} + \setlength{\myhi}{\baselineskip} + \addtolength{\myhi}{\pyy} + \mghrulefill{\RW}\rule[-1\py]{2\RW}{\pyy}\par\nobreak + \vskip -1\backskip + \rule{2\RW}{\myhi}\mghrulefill{\RW}\hskip 3pt % + \raggedleft\CTV\FmTi{#1}\par\nobreak + \vskip 40\p@} + } + +%%%%%%% CONNY DEF + +\DeclareOption{Conny}{% + \ChNameUpperCase + \ChTitleUpperCase + \ChNameVar{\centering\Huge\rm\bfseries} + \ChNumVar{\Huge} + \ChTitleVar{\centering\Huge\rm} + \ChRuleWidth{2pt} + + \renewcommand{\DOCH}{% + \mghrulefill{3\RW}\par\nobreak + \vskip -0.5\baselineskip + \mghrulefill{\RW}\par\nobreak + \CNV\FmN{\@chapapp}\space \CNoV\thechapter + \par\nobreak + \vskip -0.5\baselineskip + } + \renewcommand{\DOTI}[1]{% + \mghrulefill{\RW}\par\nobreak + \CTV\FmTi{#1}\par\nobreak + \vskip 60\p@ + } + \renewcommand{\DOTIS}[1]{% + \mghrulefill{\RW}\par\nobreak + \CTV\FmTi{#1}\par\nobreak + \vskip 60\p@ + } + } + +%%%%%%% REJNE DEF + +\DeclareOption{Rejne}{% + + \ChNameUpperCase + \ChTitleUpperCase + \ChNameVar{\centering\Large\rm} + \ChNumVar{\Huge} + \ChTitleVar{\centering\Huge\rm} + \ChRuleWidth{1pt} + \renewcommand{\DOCH}{% + \settoheight{\py}{\CNoV\thechapter} + \parskip=0pt plus 1pt % Set parskip to default, just in case v1.31 + \addtolength{\py}{-1pt} + \CNV\FmN{\@chapapp}\par\nobreak + \vskip 20\p@ + \setlength{\myhi}{2\baselineskip} + \setlength{\px}{\myhi} + \addtolength{\px}{-1\RW} + \rule[-1\px]{\RW}{\myhi}\mghrulefill{\RW}\hskip + 10pt\raisebox{-0.5\py}{\CNoV\thechapter}\hskip 10pt\mghrulefill{\RW}\rule[-1\px]{\RW}{\myhi}\par\nobreak + \vskip -3\p@% Added -2pt vskip to correct for streched text v1.31 + } + \renewcommand{\DOTI}[1]{% + \setlength{\mylen}{\textwidth} + \parskip=0pt plus 1pt % Set parskip to default, just in case v1.31 + \addtolength{\mylen}{-2\RW} + {\vrule width\RW}\parbox{\mylen}{\CTV\FmTi{#1}}{\vrule width\RW}\par\nobreak% + \vskip -3pt\rule{\RW}{2\baselineskip}\mghrulefill{\RW}\rule{\RW}{2\baselineskip}% + \vskip 60\p@% Added -2pt in vskip to correct for streched text v1.31 + } + \renewcommand{\DOTIS}[1]{% + \setlength{\py}{\fboxrule} + \setlength{\fboxrule}{\RW} + \setlength{\mylen}{\textwidth} + \addtolength{\mylen}{-2\RW} + \fbox{\parbox{\mylen}{\vskip 2\baselineskip\CTV\FmTi{#1}\par\nobreak\vskip \baselineskip}} + \setlength{\fboxrule}{\py} + \vskip 60\p@ + } + } + + +%%%%%%% BJARNE DEF + +\DeclareOption{Bjarne}{% + \ChNameUpperCase + \ChTitleUpperCase + \ChNameVar{\raggedleft\normalsize\rm} + \ChNumVar{\raggedleft \bfseries\Large} + \ChTitleVar{\raggedleft \Large\rm} + \ChRuleWidth{1pt} + + +%% Note thechapter -> c@chapter fix appendix bug +%% Fixed misspelled 12 + + \newcounter{AlphaCnt} + \newcounter{AlphaDecCnt} + \newcommand{\AlphaNo}{% + \ifcase\number\theAlphaCnt + \ifnum\c@chapter=0 + ZERO\else{}\fi + \or ONE\or TWO\or THREE\or FOUR\or FIVE + \or SIX\or SEVEN\or EIGHT\or NINE\or TEN + \or ELEVEN\or TWELVE\or THIRTEEN\or FOURTEEN\or FIFTEEN + \or SIXTEEN\or SEVENTEEN\or EIGHTEEN\or NINETEEN\fi +} + + \newcommand{\AlphaDecNo}{% + \setcounter{AlphaDecCnt}{0} + \@whilenum\number\theAlphaCnt>0\do + {\addtocounter{AlphaCnt}{-10} + \addtocounter{AlphaDecCnt}{1}} + \ifnum\number\theAlphaCnt=0 + \else + \addtocounter{AlphaDecCnt}{-1} + \addtocounter{AlphaCnt}{10} + \fi + + + \ifcase\number\theAlphaDecCnt\or TEN\or TWENTY\or THIRTY\or + FORTY\or FIFTY\or SIXTY\or SEVENTY\or EIGHTY\or NINETY\fi + } + \newcommand{\TheAlphaChapter}{% + + \ifinapp + \thechapter + \else + \setcounter{AlphaCnt}{\c@chapter} + \ifnum\c@chapter<20 + \AlphaNo + \else + \AlphaDecNo\AlphaNo + \fi + \fi + } + \renewcommand{\DOCH}{% + \mghrulefill{\RW}\par\nobreak + \CNV\FmN{\@chapapp}\par\nobreak + \CNoV\TheAlphaChapter\par\nobreak + \vskip -1\baselineskip\vskip 5pt\mghrulefill{\RW}\par\nobreak + \vskip 20\p@ + } + \renewcommand{\DOTI}[1]{% + \CTV\FmTi{#1}\par\nobreak + \vskip 40\p@ + } + \renewcommand{\DOTIS}[1]{% + \CTV\FmTi{#1}\par\nobreak + \vskip 40\p@ + } +} + +\DeclareOption*{% + \PackageWarning{fancychapter}{unknown style option} + } + +\ProcessOptions* \relax + +\def\@makechapterhead#1{% + \vspace*{50\p@}% + {\parindent \z@ \raggedright \normalfont + \ifnum \c@secnumdepth >\m@ne + \if@mainmatter%%%%% Fix for frontmatter, mainmatter, and backmatter 040920 + \DOCH + \fi + \fi + \interlinepenalty\@M + \DOTI{#1} + }} + + +%%% Begin: To avoid problem with scrbook.cls (fncychap version 1.32) + +%%OUT: +%\def\@schapter#1{\if@twocolumn +% \@topnewpage[\@makeschapterhead{#1}]% +% \else +% \@makeschapterhead{#1}% +% \@afterheading +% \fi} + +%%IN: +\def\@schapter#1{% +\if@twocolumn% + \@makeschapterhead{#1}% +\else% + \@makeschapterhead{#1}% + \@afterheading% +\fi} + +%%% End: To avoid problem with scrbook.cls (fncychap version 1.32) + +\def\@makeschapterhead#1{% + \vspace*{50\p@}% + {\parindent \z@ \raggedright + \normalfont + \interlinepenalty\@M + \DOTIS{#1} + \vskip 40\p@ + }} + +\endinput + + diff --git a/larpe/tags/release-1.0/doc/en/larpe-admin.rst b/larpe/tags/release-1.0/doc/en/larpe-admin.rst new file mode 100644 index 0000000..32ac1c9 --- /dev/null +++ b/larpe/tags/release-1.0/doc/en/larpe-admin.rst @@ -0,0 +1,202 @@ +===================================== +Larpe - Administrator Guide +===================================== + +:author: Damien Laniel +:contact: dlaniel@entrouvert.com +:copyright: Copyright © 2006 Entr'ouvert + +.. contents:: Table of contents + +Overview +======== + +Larpe is a Liberty Alliance Reverse Proxy. It allows any service provider +(that is a website) to use Liberty Alliance features (Identity federation, +Single Sign On and Single Logout) without changing the code of +the service provider itself. It uses the Lasso_ library +which is certified by the `Liberty Alliance`_ consortium. Lasso_ and Larpe +are released under the terms of the `GNU GPL license`_. + + +How to get and install Larpe +============================ + +Installation under Debian_ Sarge +++++++++++++++++++++++++++++++++ + +To work correctly Larpe relies on : + +* Apache2_ ; + +* Lasso_ (0.6.3) ; + +* Quixote_ (2.0) ; + +* SCGI_ ; + +* mod_python_ ; + +* libxml2 ; + +* mod_proxy_html. + +You will also need a Liberty Alliance Identity Provider, be it on the same server or not. +We recommend Authentic_ for that need. + +Package Installation +-------------------- + +You need to add the following line to your /etc/apt/sources.list; this will +give you access to the repository where Larpe is stored:: + + deb http://deb.entrouvert.org/ sarge main + +As root type:: + + apt-get update + apt-get install larpe + +And follow the debconf wizard to set it up. + +All the required packages are now installed and configured. + +You might need to change the "" in your apache2 configuration +(/etc/apache2/sites-available/apache2-vhost-larpe) depending on how you +previously configured apache. + +Don't forget to modify your /etc/hosts file if necessary. Larpe now works, the +administration interface is reachable at http://your_domain_name/admin. The username +and password are the ones you entered during the installation wizard. + +If you don't want to modify your sources.list file, you can manually dowload and +install the required packages with the dpkg -i command : + +* Larpe, Authentic and Lasso on http://deb.entrouvert.org/ ; + +* Quixote 2.0 on http://authentic.labs.libre-entreprise.org/. + +Installation with another Linux distribution +++++++++++++++++++++++++++++++++++++++++++++ + +We suppose Apache2_, SCGI_, mod_python_, libxml2 and mod_proxy_html are already installed. You need then to +download and install the following sources : + +* Lasso http://lasso.entrouvert.org ; + +* Quixote http://www.mems-exchange.org/software/Quixote/ ; + +* Authentic http://authentic.labs.libre-entreprise.org/ ; + +* Larpe http://labs.libre-entreprise.org/frs/?group_id=108. + +To install Larpe, uncompress the sources you have downloaded and launch the +setup.py script :: + + tar xzf larpe*.tar.gz + cd larpe* + python setup.py install + +You need then to configure Apache2_ correctly. You should use the provided apache2-vhost-larpe template and adapt to your configuration. + +Don't forget to modify your /etc/hosts file if necessary. Larpe now works, the +administration interface is reachable at http://your_domain_name/admin. + +Basic Larpe configuration +========================= + +Identity Provider configuration ++++++++++++++++++++++++++++++++ + +If you don't have a configured Identity Provider yet, please read Authentic +manual to set it up. Then you must have the metadata and public key of the Identity +Provider to begin with Larpe. + +Then in Larpe administration interface, click on "Settings", then "Identity Provider". +Fill in the metadata and public key that you've got from your Identity Provider then +click Submit. +Your Identity Provider is now configured in Larpe, you can then configure as many Service +Providers as you want. + +Service Provider Configuration +++++++++++++++++++++++++++++++ + +Service Provider configuration +------------------------------ + +Click on "Hosts" then "New Host". + +Fill in the following parameters : + +* Label : the name you want to give to your Service Provider ; + +* Original Site Address : the root URL of your Service Provider ; + +* Authentication Page : if the page which contains the authentication form for + your Service Provider is on a separate page, fill the url of this page here ; + +* Authentication Form Page : if you didn't fill the previous field and if the + authentication form if not on the first page of your Service Provider either, + fill the url of the page which contains the authentication form here ; + +* Logout Address : when you want Single Sign On and Identity Federation, you probably + want Single Logout too. If so, fill the logout url of your original site here ; + +* Reversed Host Name : the domain name where you want to access your Service Provider + through the reverse proxy. It can be the domain name of Larpe or not ; + +Then click "Submit". Wait a few seconds then go to http://reversed_host_name/reverse_directory/ +to check if it works. If not, wait a bit more and try again. If it really doesn't work, +please submit a bug report at http://labs.libre-entreprise.org/tracker/?func=add&group_id=108&atid=512 + +Service Provider Example: Linuxfr +--------------------------------- + +To help you setup your own Service Provider, we provide an example of a working Service Provider +to guide you. + +To setup Linuxfr, fill in the following parameters : + +* Label : Linuxfr ; + +* Original Site Address : http://linuxfr.org/ ; + +* Authentication Page : Nothing here ; + +* Authentication Form Page : http://linuxfr.org/pub/ ; + +* Logout Address : http://linuxfr.org/close_session.html ; + +* Reversed Host Name : linuxfr.reverse-proxy.example.com. + +With "reverse-proxy.example.com" being the hostname you've set up before for your reverse-proxy + +Don't forget to add this new hostname to your /etc/hosts as well. + +You can then go to the reversed Linuxfr at http://linuxfr.reverse-proxy.example.com/ + +Service Provider Liberty Alliance final setup +--------------------------------------------- + +Now that you can access your Service Provider, you need a final step to use Liberty Alliance +features. Click on "Hosts", the click on the "Edit" icon of the Service Provider you've +just configured. Save the Service Provider Metadata (for ID-FF 1.2) and the Public Key +(right click then "Save as"). Configure this Service Provider on your Identity Provider +with these two files. + +Licenses +======== + +Larpe, Authentic_, Candle_ and Lasso_ are released under the terms of the +`GNU GPL license`_. + +.. _Lasso: http://lasso.entrouvert.org/ +.. _`Liberty Alliance`: http://projectliberty.org/ +.. _`GNU GPL License`: http://www.gnu.org/copyleft/gpl.html +.. _Debian: http://www.debian.org/ +.. _Apache2: http://httpd.apache.org/ +.. _Quixote: http://www.mems-exchange.org/software/Quixote +.. _mod_python: http://www.modpython.org/ +.. _SCGI: http://www.mems-exchange.org/software/scgi/ +.. _Candle: http://candle.labs.libre-entreprise.org/ +.. _Authentic: http://www.entrouvert.com/fr/authentic/ diff --git a/larpe/tags/release-1.0/doc/scripts/removealpha.sh b/larpe/tags/release-1.0/doc/scripts/removealpha.sh new file mode 100755 index 0000000..f29ee67 --- /dev/null +++ b/larpe/tags/release-1.0/doc/scripts/removealpha.sh @@ -0,0 +1,5 @@ +#! /bin/sh + +size=$(identify $1 | cut -d ' ' -f 3) +composite $1 -size $(identify $1 | cut -d ' ' -f3) xc:white $2 + diff --git a/larpe/tags/release-1.0/doc/scripts/rst2latex.py b/larpe/tags/release-1.0/doc/scripts/rst2latex.py new file mode 100755 index 0000000..4036fbf --- /dev/null +++ b/larpe/tags/release-1.0/doc/scripts/rst2latex.py @@ -0,0 +1,29 @@ +#! /usr/bin/python + +"""A minimal reST frontend, to create appropriate LaTeX files.""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, Publisher + +def set_io(self, source_path=None, destination_path=None): + Publisher.set_io_orig(self, source_path, destination_path='/dev/null') + +Publisher.set_io_orig, Publisher.set_io = Publisher.set_io, set_io + +output = publish_cmdline(writer_name='latex', + settings_overrides = { + 'documentclass': 'report', + 'documentoptions': '11pt,a4paper,titlepage', + 'use_latex_toc': True, + 'use_latex_docinfo': True, + 'stylesheet': 'custom.tex'}) + +output = output.replace('\\includegraphics', + '\\includegraphics[width=.9\\textwidth,height=15cm,clip,keepaspectratio]') +output = output.replace('\\begin{figure}[htbp]', '\\begin{figure}[H]') +print output diff --git a/larpe/tags/release-1.0/exclude_from_dist b/larpe/tags/release-1.0/exclude_from_dist new file mode 100644 index 0000000..d6e841d --- /dev/null +++ b/larpe/tags/release-1.0/exclude_from_dist @@ -0,0 +1,12 @@ +.svn +*.pyc +*.pyo +*.pye +*.ptle +*.swp +debian.sarge +make_debian_package.sh +build +dist +tests +larpe/filter diff --git a/larpe/tags/release-1.0/fedora/larpe-reload-apache2-script b/larpe/tags/release-1.0/fedora/larpe-reload-apache2-script new file mode 100755 index 0000000..928b0b7 --- /dev/null +++ b/larpe/tags/release-1.0/fedora/larpe-reload-apache2-script @@ -0,0 +1,22 @@ +#!/bin/sh +# +# The command "/etc/init.d/httpd reload" on Fedora actually _restarts_ Apache +# We need to _reload_ it without closing existing connections + +APACHE2CTL=/usr/sbin/apachectl + +echo -n "Testing Apache config... " +if ! $APACHE2CTL configtest > /dev/null 2>&1; then + $APACHE2CTL configtest || true + echo "[FAILED]" + exit 1 +else + echo "[OK]" +fi +echo -n "Reloading Apache config... " +if $APACHE2CTL graceful $2 ; then + echo "[OK]" +else + echo "[FAILED]" +fi + diff --git a/larpe/tags/release-1.0/fedora/larpe.init b/larpe/tags/release-1.0/fedora/larpe.init new file mode 100755 index 0000000..a41681f --- /dev/null +++ b/larpe/tags/release-1.0/fedora/larpe.init @@ -0,0 +1,104 @@ +#! /bin/bash +# +# larpe Startup script for the Larpe reverse proxy +# +# description: Larpe is Liberty Alliance reverse proxy. It is used to add Liberty Alliance \ +# features to some sites without modifying the sites themselves. +# processname: larpe +# config: /etc/httpd/conf.d/larpe.conf +# config: /etc/larpe/apache2-vhost-larpe-common +# config: /etc/sysconfig/larpe +# pidfile: /var/run/larpe.pid +# + +### BEGIN INIT INFO +# Provides: larpe +# Required-Start: $local_fs $network +# Required-Stop: $local_fs $network +# Default-Start: +# Default-Stop: 0 1 2 3 4 5 6 +# Short-Description: Start Larpe Liberty Alliance reverse proxy +# Description: Start Larpe Liberty Alliance reverse proxy +### END INIT INFO + +prog=larpe +LARPECTL=/usr/sbin/larpectl +PIDFILE=/var/run/larpe.pid + +# Source function library. +. /etc/rc.d/init.d/functions + +# Source networking configuration. +. /etc/sysconfig/network + +# Check that networking is up. +[ ${NETWORKING} = "no" ] && exit 0 + +[ -x $LARPECTL ] || exit 5 + +# Read config file if it is present. +if [ -f /etc/sysconfig/larpe ]; then + . /etc/sysconfig/larpe +fi + +RETVAL=0 + +# +# Function that starts the daemon/service. +# +start() { + echo -n $"Starting $prog: " + $LARPECTL start & + RETVAL=$? + if [ $RETVAL -eq 0 ] + then + touch /var/lock/subsys/$prog + fi + echo + return $RETVAL +} + +# +# Function that stops the daemon/service. +# +stop() { + echo -n $"Stopping $prog: " + killproc $LARPECTL + RETVAL=$? + if [ $RETVAL -eq 0 ] + then + rm -rf /var/lock/subsys/$prog + fi + echo + return $RETVAL +} + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status $prog + RETVAL=$? + ;; + restart) + stop + start + ;; + condrestart) + if [ -f ${PIDFILE} ] ; then + stop + start + fi + ;; + *) + echo $"Usage: $prog {start|stop|restart|condrestart|status}" + exit 1 +esac + +exit $RETVAL + diff --git a/larpe/tags/release-1.0/fedora/larpe.spec b/larpe/tags/release-1.0/fedora/larpe.spec new file mode 100644 index 0000000..21932dc --- /dev/null +++ b/larpe/tags/release-1.0/fedora/larpe.spec @@ -0,0 +1,139 @@ +%{!?python_sitearch: %define python_sitearch %(%{__python} -c 'from distutils import sysconfig; print sysconfig.get_python_lib(1)')} +# eval to 2.3 if python isn't yet present, workaround for no python in fc4 minimal buildroot +%{!?python_version: %define python_version %(%{__python} -c 'import sys; print sys.version.split(" ")[0]' || echo "2.3")} +%define apacheconfdir %{_sysconfdir}/httpd/conf.d + +Summary: Liberty Alliance Reverse Proxy +Name: larpe +Version: 0.2.9 +Release: 2%{?dist} +License: GPL +Group: System Environment/Applications +Url: http://larpe.labs.libre-entreprise.org/ +Source0: http://labs.libre-entreprise.org/frs/download.php/591/%{name}-%{version}.tar.gz +Buildroot: %{_tmppath}/%{name}-%{version}-%(id -u -n) +BuildRequires: python >= 2.3, python-quixote >= 2.0 +BuildRequires: gettext +Requires: httpd >= 2.0, mod_scgi, mod_proxy_html +Requires: lasso-python >= 0.6.3, python-quixote >= 2.0, python-scgi +Requires: initscripts +Requires(post): /sbin/chkconfig +Requires(preun):/sbin/chkconfig +Requires(preun): /sbin/service + +%description +Larpe is a Liberty Alliance Reverse Proxy. It allows any service provider (that is a website) +to use Liberty Alliance features (Identity federation, Single Sign On and Single Logout) without +changing the code of the service provider itself. + +It uses the Lasso library which is certified by the Liberty Alliance consortium. + +It is a quixote application and is commonly runned inside Apache web server. + +%package doc +Summary: Documentation files for %{name} development. +Group: Documentation +BuildRequires: python-docutils, tetex-latex + +%description doc +This package contains development documentation for %{name}. + +%prep +%setup -q + +# Change Apache vhost path in Larpe config +sed -i s#"/var/log/apache2/larpe-access.log"#"logs/larpe_access_log combined\n TransferLog logs/larpe_access_log"# conf/apache2-vhost-larpe +sed -i s#"/var/log/apache2/larpe-error.log"#"logs/larpe_error_log"# conf/apache2-vhost-larpe +sed -i s#"APACHE_MAIN_VHOST.*$"#"APACHE_MAIN_VHOST='/etc/httpd/conf.d/larpe.conf'"# larpe/Defaults.py + +%build + +%install +rm -rf %{buildroot} + +# install generic files +make install prefix=%{_prefix} DESTDIR=%{buildroot} + +# install init script +install -d %{buildroot}/%{_initrddir} +install -p -m 0755 fedora/larpe.init %{buildroot}%{_initrddir}/larpe + +# apache configuration +mkdir -p %{buildroot}%{apacheconfdir} +install -p -m 644 conf/apache2-vhost-larpe %{buildroot}%{apacheconfdir}/larpe.conf + +# apache reload script +install -p -m 0755 fedora/larpe-reload-apache2-script %{buildroot}%{_sbindir}/ + +# install doc files +install -d -m 0755 %{buildroot}%{_datadir}/gtk-doc/html/larpe +make -C doc DESTDIR=%{buildroot}%{_datadir}/gtk-doc/html/larpe + +%clean +rm -fr %{buildroot} + +%post +/sbin/chkconfig --add %{name} + +# manual post-installation +cat <<_EOF_ +You must edit first %{apacheconfdir}/larpe.conf + +You must enable Larpe with "chkconfig larpe on ; service larpe start" + +You must also restart Apache with "service httpd restart"! +_EOF_ + +%preun +if [ $1 = 0 ]; then + /sbin/service %{name} stop > /dev/null 2>&1 + /sbin/chkconfig --del %{name} +fi + +%files +%defattr(-,root,root,755) +%config %{_initrddir}/larpe +%config(noreplace) %{apacheconfdir}/larpe.conf +%config(noreplace) %{_sysconfdir}/larpe/apache2-vhost-larpe-common +%{_sbindir}/larpectl +%{_sbindir}/larpe-reload-apache2 +%{_sbindir}/larpe-reload-apache2-script +%{python_sitearch}/%{name} +%{_datadir}/%{name} +%{_datadir}/locale/fr/LC_MESSAGES/larpe.mo +/var/lib/larpe +%defattr(644,root,root,755) +%doc AUTHORS COPYING NEWS README + +%files doc +%defattr(-,root,root) +%doc %{_datadir}/gtk-doc/html/%{name} + +%changelog +* Tue Mar 05 2009 Jean-Marc Liger 0.2.9-2 +- Added missing BuildRequires gettext +- Enabled larpe init script + +* Mon Jan 19 2009 Damien Laniel 0.2.9-1 +- Updated to 0.2.9 +- Use Larpe Makefile to install generic files +- Copy fedora specific files + +* Sat Jan 17 2009 Jean-Marc Liger 0.2.1-2 +- Added missing BuildRequires tetex-latex for doc subpackage +- Rebuilt on CentOS 4,5 + +* Wed Jan 14 2009 Jean-Marc Liger 0.2.1-1 +- Updated to 0.2.1 +- Added missing Requires lasso-python +- Added missing Requires python-scgi +- Rebuilt on CentOS 4,5 + +* Fri Mar 02 2007 Jean-Marc Liger 0.2.0-1 +- Updated to 0.2.0 +- Added BuildRequires python-quixote +- Built on Fedora Core 3 / RHEL 4 and Fedora Core 6 / RHEL 5 + +* Wed Jan 24 2007 Jean-Marc Liger 0.1.0-1 +- First 0.1.0 +- Built on Fedora Core 3 / RHEL 4 and Fedora Core 6 / RHEL 5 diff --git a/larpe/tags/release-1.0/larpe-reload-apache2.c b/larpe/tags/release-1.0/larpe-reload-apache2.c new file mode 100644 index 0000000..73f681a --- /dev/null +++ b/larpe/tags/release-1.0/larpe-reload-apache2.c @@ -0,0 +1,173 @@ +/* + Template for a setuid program that calls a script. + + The script should be in an unwritable directory and should itself + be unwritable. In fact all parent directories up to the root + should be unwritable. The script must not be setuid, that's what + this program is for. + + This is a template program. You need to fill in the name of the + script that must be executed. This is done by changing the + definition of FULL_PATH below. + + There are also some rules that should be adhered to when writing + the script itself. + + The first and most important rule is to never, ever trust that the + user of the program will behave properly. Program defensively. + Check your arguments for reasonableness. If the user is allowed to + create files, check the names of the files. If the program depends + on argv[0] for the action it should perform, check it. + + Assuming the script is a Bourne shell script, the first line of the + script should be + #!/bin/sh - + The - is important, don't omit it. If you're using esh, the first + line should be + #!/usr/local/bin/esh -f + and for ksh, the first line should be + #!/usr/local/bin/ksh -p + The script should then set the variable IFS to the string + consisting of , , and . After this (*not* + before!), the PATH variable should be set to a reasonable value and + exported. Do not expect the PATH to have a reasonable value, so do + not trust the old value of PATH. You should then set the umask of + the program by calling + umask 077 # or 022 if you want the files to be readable + If you plan to change directories, you should either unset CDPATH + or set it to a good value. Setting CDPATH to just ``.'' (dot) is a + good idea. + If, for some reason, you want to use csh, the first line should be + #!/bin/csh -fb + You should then set the path variable to something reasonable, + without trusting the inherited path. Here too, you should set the + umask using the command + umask 077 # or 022 if you want the files to be readable +*/ + +#include +#include +#include +#include +#include +#include + +/* CONFIGURATION SECTION */ + +#ifndef FULL_PATH/* so that this can be specified from the Makefile */ + #define FULL_PATH "/usr/sbin/larpe-reload-apache2-script" +#endif +#ifndef UMASK + #define UMASK 077 +#endif + +/* END OF CONFIGURATION SECTION */ + +#if defined(__STDC__) && defined(__sgi) +#define environ _environ +#endif + +/* don't change def_IFS */ +char def_IFS[] = "IFS= \t\n"; +/* you may want to change def_PATH, but you should really change it in */ +/* your script */ +#ifdef __sgi +char def_PATH[] = "PATH=/usr/bsd:/usr/bin:/bin:/usr/local/bin:/usr/sbin"; +#else +char def_PATH[] = "PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin"; +#endif +/* don't change def_CDPATH */ +char def_CDPATH[] = "CDPATH=."; +/* don't change def_ENV */ +char def_ENV[] = "ENV=:"; + +/* + This function changes all environment variables that start with LD_ + into variables that start with XD_. This is important since we + don't want the script that is executed to use any funny shared + libraries. + + The other changes to the environment are, strictly speaking, not + needed here. They can safely be done in the script. They are done + here because we don't trust the script writer (just like the script + writer shouldn't trust the user of the script). + If IFS is set in the environment, set it to space,tab,newline. + If CDPATH is set in the environment, set it to ``.''. + Set PATH to a reasonable default. +*/ +void +clean_environ(void) +{ + char **p; + extern char **environ; + + for (p = environ; *p; p++) { + if (strncmp(*p, "LD_", 3) == 0) + **p = 'X'; + else if (strncmp(*p, "_RLD", 4) == 0) + **p = 'X'; + else if (strncmp(*p, "PYTHON", 6) == 0) + **p = 'X'; + else if (strncmp(*p, "IFS=", 4) == 0) + *p = def_IFS; + else if (strncmp(*p, "CDPATH=", 7) == 0) + *p = def_CDPATH; + else if (strncmp(*p, "ENV=", 4) == 0) + *p = def_ENV; + } + putenv(def_PATH); +} + +int +main(int argc, char **argv) +{ + struct stat statb; + gid_t egid = getegid(); + uid_t euid = geteuid(); + + /* + Sanity check #1. + This check should be made compile-time, but that's not possible. + If you're sure that you specified a full path name for FULL_PATH, + you can omit this check. + */ + if (FULL_PATH[0] != '/') { + fprintf(stderr, "%s: %s is not a full path name\n", argv[0], + FULL_PATH); + fprintf(stderr, "You can only use this wrapper if you\n"); + fprintf(stderr, "compile it with an absolute path.\n"); + exit(1); + } + + /* + Sanity check #2. + Check that the owner of the script is equal to either the + effective uid or the super user. + */ + if (stat(FULL_PATH, &statb) < 0) { + perror("stat"); + exit(1); + } + if (statb.st_uid != 0 && statb.st_uid != euid) { + fprintf(stderr, "%s: %s has the wrong owner\n", argv[0], + FULL_PATH); + fprintf(stderr, "The script should be owned by root,\n"); + fprintf(stderr, "and shouldn't be writeable by anyone.\n"); + exit(1); + } + + if (setregid(egid, egid) < 0) + perror("setregid"); + if (setreuid(euid, euid) < 0) + perror("setreuid"); + + clean_environ(); + + umask(UMASK); + + while (**argv == '-')/* don't let argv[0] start with '-' */ + (*argv)++; + execv(FULL_PATH, argv); + fprintf(stderr, "%s: could not execute the script\n", argv[0]); + exit(1); +} diff --git a/larpe/tags/release-1.0/larpe/Defaults.py b/larpe/tags/release-1.0/larpe/Defaults.py new file mode 100644 index 0000000..b19e629 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/Defaults.py @@ -0,0 +1,10 @@ +'''Default global variables''' + +APP_DIR = '/var/lib/larpe' +DATA_DIR = '/usr/share/larpe' +ERROR_LOG = None #'/var/log/larpe.log' +WEB_ROOT = 'larpe' +APACHE_MAIN_VHOST = '/etc/apache2/sites-available/apache2-vhost-larpe' +APACHE_VHOST_COMMON = '/etc/larpe/apache2-vhost-larpe-common' +APACHE_RELOAD = '/usr/sbin/larpe-reload-apache2' +OUTPUT_FILTER_BASE = '/usr/share/larpe/output_filter_base.py' diff --git a/larpe/tags/release-1.0/larpe/__init__.py b/larpe/tags/release-1.0/larpe/__init__.py new file mode 100644 index 0000000..f89239d --- /dev/null +++ b/larpe/tags/release-1.0/larpe/__init__.py @@ -0,0 +1,16 @@ +'''Setup path and load Lasso library''' + +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 + diff --git a/larpe/tags/release-1.0/larpe/admin/__init__.py b/larpe/tags/release-1.0/larpe/admin/__init__.py new file mode 100644 index 0000000..53f1b4b --- /dev/null +++ b/larpe/tags/release-1.0/larpe/admin/__init__.py @@ -0,0 +1 @@ +from root import RootDirectory diff --git a/larpe/tags/release-1.0/larpe/admin/apache.py b/larpe/tags/release-1.0/larpe/admin/apache.py new file mode 100644 index 0000000..060b0f8 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/admin/apache.py @@ -0,0 +1,302 @@ +import os +import re +import urllib +import base64 + +from quixote import get_publisher, get_request + +from qommon import get_cfg + +from larpe.hosts import Host +from larpe.Defaults import APP_DIR, APACHE_MAIN_VHOST, APACHE_VHOST_COMMON, APACHE_RELOAD + +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 = '' + 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(APACHE_MAIN_VHOST, 'r') + file_content = main_vhost.read() + regexp = re.compile('') + vhost_ip = regexp.findall(file_content)[0] + except (IOError, IndexError): + 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_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_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(APACHE_RELOAD) + + +class Vhost(object): + 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_cfg('proxy', {}).get('enabled') and self.host.use_proxy == True: + return 'http://%(ip)s:%(port)s' % get_cfg('proxy', {}) + return None + proxy_url = property(get_proxy_url) + + def get_proxy_auth(self): + if self.get_proxy_url() and get_cfg('proxy', {}).get('user'): + credentials = base64.encodestring( + '%(user)s:%(password)s' % get_cfg('proxy', {}))[:-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('' % 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 %s\n' % APACHE_VHOST_COMMON) + # 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('\n\n') + + +def apache_escape_chars(url): + special_characters = ('\\', '.', '?', '*', '+', '^', '$', '|', '(', ')', '[', ']') + for char in special_characters: + url = url.replace(char, '\%s' % char) + return url + +class Location(object): + 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 output_filter in self.host.apache_output_filters: + output_filters.append(output_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' % 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\n') + diff --git a/larpe/tags/release-1.0/larpe/admin/fields_prefill.ptl b/larpe/tags/release-1.0/larpe/admin/fields_prefill.ptl new file mode 100644 index 0000000..c927388 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/admin/fields_prefill.ptl @@ -0,0 +1,130 @@ +from quixote import get_response, redirect +from quixote.directory import Directory + +from qommon.form import * +from qommon.admin.menu import html_top, command_icon + +from field_prefill import FieldPrefill + +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')) + '

%s

' % _('Edit') + + form.render() + + def delete [html] (self): + form = Form(enctype='multipart/form-data') + form.widgets.append(HtmlWidget('

%s

' % _( + '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')) + '

%s : %s

' % (_('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')) + """""" % _('New Field') + + '
    ' + + 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:]) + + '
  • ' + '%s' % field_prefill.name + '
    %s' % xpath + '

    ' + command_icon('%s/' % field_prefill.id, 'edit') + command_icon('%s/delete' % field_prefill.id, 'remove') + '

  • ' + '
' + + 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) + diff --git a/larpe/tags/release-1.0/larpe/admin/forms_prefill.ptl b/larpe/tags/release-1.0/larpe/admin/forms_prefill.ptl new file mode 100644 index 0000000..0f34eff --- /dev/null +++ b/larpe/tags/release-1.0/larpe/admin/forms_prefill.ptl @@ -0,0 +1,127 @@ +from quixote import get_response, redirect +from quixote.directory import Directory + +from qommon.form import * +from qommon.admin.menu import html_top, command_icon + +from form_prefill import FormPrefill +from fields_prefill import FieldsDirectory + +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') + + '

%s

' % _('Form prefilling configuration') + '
' + '
%s
%s
' % ( + _('Edit'), _('Configure this form')) + '
%s
%s
' % ( + _('Fields'), _('Configure the fields of this form')) + '
' + + 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')) + '

%s

' % _('Edit') + + form.render() + + def delete [html] (self): + form = Form(enctype='multipart/form-data') + form.widgets.append(HtmlWidget('

%s

' % _( + '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')) + '

%s : %s

' % (_('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')) + """""" % _('New Form') + + '
    ' + + for form_prefill in FormPrefill.select(lambda x: x.host_id == self.host.id): + if not form_prefill.name: + continue + '
  • ' + '%s' % form_prefill.name + url = form_prefill.url + '
    %s' % (url, url) + '

    ' + command_icon('%s/' % form_prefill.id, 'edit') + command_icon('%s/delete' % form_prefill.id, 'remove') + '

  • ' + '
' + + 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) + diff --git a/larpe/tags/release-1.0/larpe/admin/hosts.ptl b/larpe/tags/release-1.0/larpe/admin/hosts.ptl new file mode 100644 index 0000000..e9ca69b --- /dev/null +++ b/larpe/tags/release-1.0/larpe/admin/hosts.ptl @@ -0,0 +1,1336 @@ +import os +import urllib +import urlparse + +from quixote import redirect, get_request, get_response, get_publisher +from quixote.directory import Directory + +import lasso + +from qommon.admin.menu import html_top, command_icon +from qommon import get_cfg +from qommon.form import * +from qommon.misc import http_get_page, get_abs_path + +from larpe import site_authentication +from larpe import errors +from larpe import misc +from larpe.hosts import Host +from larpe.admin.liberty_utils import * +#from larpe.filter import filter_misc +from larpe.admin.apache import write_apache2_vhosts +from larpe.admin.forms_prefill import FormsDirectory +from larpe.Defaults import OUTPUT_FILTER_BASE + +def check_basic_configuration(form): + get_publisher().reload_cfg() + # Check reversed_hostname and reversed_directory + reversed_hostname = form.get_widget('reversed_hostname').parse() + reversed_directory = form.get_widget('reversed_directory').parse() + if reversed_hostname == get_publisher().cfg['proxy_hostname'] and not reversed_directory: + form.set_error('reversed_hostname', + _('You must either choose a different hostname from Larpe or specify a reversed directory')) + +def convert_label_to_name(label): + '''Build host name from host label''' + name = label.lower() + invalid_characters = [' ', "'"] + for char in invalid_characters: + name = name.replace(char, '_') + return name + +class DictWidget(Widget): + def render_content [html] (self): + self.render_br = False + if self.value['enabled'] is True: + htmltag('input', xml_end=True, type='checkbox', name=self.name + '_enabled', checked='checked') + else: + htmltag('input', xml_end=True, type='checkbox', name=self.name + '_enabled') + ' ' + self.name + '  ' + htmltag('input', xml_end=True, type='text', name=self.name, value=self.value['value'], size='35', **self.attrs) + + def _parse(self, request): + enabled = request.form.get(self.name + '_enabled') + value = request.form.get(self.name) + self.value = { 'enabled': enabled, 'value': value } + + +class ConfigurationAssistant(Directory): + _q_exports = ['start', 'check_new_address', 'modify_site_address_and_name', + 'authentication_and_logout_adresses', 'check_auto_detected_configuration', + 'credentials', 'send_authentication_request', 'check_authentication', + 'see_authentication_response', 'see_response_html_page', + 'see_bad_response_html_page', 'authentication_success_criteria', + 'modify_authentication_request', 'auth_request_post_parameters', + 'auth_request_http_headers', 'sso_init_link', 'metadatas', + 'check_full_configuration', 'advanced_options'] + + def __init__(self, host): + self.host = host + + def html_top [html] (self, title): + html_top('hosts', title) + '

%s

' % title + + def start [html] (self): + # Check the global domain name has been previously set + get_publisher().reload_cfg() + if not get_cfg('domain_names') or not get_cfg('domain_names')[0]: + get_response().breadcrumb.append(('start', _('Basic configuration'))) + self.html_top(_('Need domain name configuration')) + return htmltext(_('''Before configuring hosts, you must +setup a global domain name in +%(settings)s menu.''') % { 'settings': _('Settings') }) + + form = self.form_start() + + if form.get_widget('cancel').parse(): + return redirect('../..') + + connection_failure = None + if form.is_submitted() and not form.has_errors(): + try: + self.submit_start_form(form) + except Exception, e: + connection_failure = e + else: + if form.get_widget('terminate').parse(): + return redirect('..') + return redirect('check_new_address') + + get_response().breadcrumb.append(('start', _('Basic configuration'))) + self.html_top(_('Step 1 - Basic configuration')) + + if connection_failure: + '
%s
' % connection_failure + + form.render() + + def form_start(self): + form = Form(enctype='multipart/form-data') + form.add(UrlWidget, 'orig_site', title = _('Original site root address'), required = True, + size = 50, value = self.host.orig_site, + hint = _('If your site address is http://test.org/index.php, put http://test.org/ here')) + get_publisher().reload_cfg() + if get_cfg('proxy', {}).get('enabled'): + form.add(CheckboxWidget, 'use_proxy', title = _('Use a proxy'), + hint = _("Uncheck it if Larpe doesn't need to use the proxy to connect to this site"), + value = self.host.use_proxy) + else: + form.add(HtmlWidget, htmltext('

%s

' % \ + _('''If Larpe needs to use a proxy to connect to this site, you must first configure + it in global proxy parameters.'''))) + form.add_submit('cancel', _('Cancel')) + form.add_submit('submit', _('Next')) + form.add_submit('terminate', _('Terminate')) + return form + + def submit_start_form(self, form): + fields = ['orig_site'] + use_proxy = get_cfg('proxy', {}).get('enabled') + if use_proxy: + fields += ['use_proxy'] + for f in fields: + setattr(self.host, f, form.get_widget(f).parse()) + + # If no proxy is setup yet, set use_proxy to False for new hosts in case a proxy is later configured + if not use_proxy: + self.host.use_proxy = False + + # Remove what is after the last '/' + #self.host.orig_site = '/'.join(self.host.orig_site.split('/')[:-1]) + + html_page = self.get_data_after_redirects(self.host.orig_site) + + if not self.host.label: + # Look for html title in original site index page + regexp = re.compile("""(.*?)""", re.DOTALL | re.IGNORECASE) + title = regexp.findall(html_page) + if title: + self.host.label = title[0] + else: + self.host.label = 'Untitled' + # If another site already uses this site title, add trailings "_" until we find an available name + existing_label = True + while existing_label is True: + for any_host in Host.select(): + if any_host.id != self.host.id and self.host.label == any_host.label: + self.host.label += '_' + break + else: + existing_label = False + + # Fill host.name attribute + self.host.name = convert_label_to_name(self.host.label) + + if not self.host.scheme: + # Get tokens from orig site url + orig_scheme, rest = urllib.splittype(self.host.orig_site) + orig_host, rest = urllib.splithost(rest) + + get_publisher().reload_cfg() + # Set url scheme (HTTP or HTTPS) + # TODO: Handle the option "Both" + if get_cfg('sites_url_scheme'): + self.host.scheme = get_cfg('sites_url_scheme') + else: + self.host.scheme = orig_scheme + + if not self.host.reversed_hostname: + # Build a new domain name + short_name = orig_host.split('.')[0] + if short_name == 'www': + short_name = orig_host.split('.')[1] + self.host.reversed_hostname = '%s.%s' % (short_name, get_cfg('domain_names')[0]) + # If another site already uses this domain name, add some trailing "_" until we find an available name + existing_domain = True + while existing_domain is True: + for any_host in Host.select(): + if any_host.id != self.host.id and self.host.reversed_hostname == any_host.reversed_hostname: + self.host.reversed_hostname += '-' + break + else: + existing_domain = False + self.host.reversed_directory = None + + if not self.host.new_url: + # New url for this host + self.host.new_url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME']) + # FIXME: Check if the new domain name already exists + + # New url for this host + # self.host.new_url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME']) + # if self.host.reversed_directory is not None: + # self.host.new_url += '%s/' % self.host.reversed_directory + + self.host.store() + write_apache2_vhosts() + + # XXX: Should use the FancyURLopener class instead when it supports proxies + def get_data_after_redirects(self, start_url): + if not start_url: + return '' + status = 302 + location = None + while status // 100 == 3: + if location is None: + url = start_url + elif location.startswith('http'): + # Location is an absolute path + url = location + else: + # Location is a relative path + url = urlparse.urljoin(start_url, location) + response, status, data, auth_headers = http_get_page(url, use_proxy=self.host.use_proxy) + location = response.getheader('Location', None) + return data + + def create_dirs(self): + # Hack : sites must use the configuration which is stored in main Larpe directory, + # but they need to have a directory named with their hostname, which will contain the + # main domain name for Larpe so they know where is the main configuration + hostname_dir = get_abs_path(os.path.join('..', self.host.reversed_hostname)) + if not os.path.exists(hostname_dir): + os.mkdir(hostname_dir) + # Load the configuration from the main directory + get_publisher().reload_cfg() + # Write it in the site directory + get_publisher().write_cfg(hostname_dir) + + # Storage directories + if not self.host.reversed_directory: + reversed_dir = 'default' + else: + reversed_dir = self.host.reversed_directory + self.host.site_dir = \ + os.path.join(get_publisher().app_dir, 'sp', self.host.reversed_hostname, reversed_dir) + user_dir = os.path.join(self.host.site_dir, 'users') + token_dir = os.path.join(self.host.site_dir, 'tokens') + filter_dir = os.path.join(self.host.site_dir, 'filters') + for dir in (self.host.site_dir, user_dir, token_dir, filter_dir): + if not os.path.isdir(dir): + os.makedirs(dir) + + def generate_ssl_keys(self): + # Generate SSL keys + private_key_path = os.path.join(self.host.site_dir, 'private_key.pem') + public_key_path = os.path.join(self.host.site_dir, 'public_key.pem') + 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 + self.host.public_key = public_key_path + + def generate_metadatas(self): + metadata_cfg = {} + + # Organization name + self.host.organization_name = self.host.label + metadata_cfg['organization_name'] = self.host.organization_name + + # Base URL + base_url = '%s://%s%s/liberty/%s/liberty' % (self.host.scheme, + self.host.reversed_hostname, + get_request().environ['SCRIPT_NAME'], + self.host.name) + metadata_cfg['base_url'] = base_url + self.host.base_url = base_url + + if lasso.SAML2_SUPPORT: + saml2_base_url = '%s://%s%s/liberty/%s/saml' % (self.host.scheme, + self.host.reversed_hostname, + get_request().environ['SCRIPT_NAME'], + self.host.name) + metadata_cfg['saml2_base_url'] = saml2_base_url + self.host.saml2_base_url = saml2_base_url + + # Provider Id + 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 + + # Read public key + public_key = '' + if self.host.public_key is not None and os.path.exists(self.host.public_key): + metadata_cfg['signing_public_key'] = open(self.host.public_key).read() + + # Write metadatas + metadata_path = os.path.join(self.host.site_dir, 'metadata.xml') + open(metadata_path, 'w').write(get_metadata(metadata_cfg)) + self.host.metadata = metadata_path + + if lasso.SAML2_SUPPORT: + saml2_metadata_path = os.path.join(self.host.site_dir, 'saml2_metadata.xml') + open(saml2_metadata_path, 'w').write(get_saml2_metadata(metadata_cfg)) + self.host.saml2_metadata = saml2_metadata_path + + def check_new_address [html] (self): + form = Form(enctype='multipart/form-data') + form.add_submit('cancel', _('Previous')) + form.add_submit('submit', _('Next')) + form.add_submit('terminate', _('Terminate')) + + if form.get_widget('cancel').parse(): + return redirect('start') + + if form.is_submitted(): + self.create_dirs() + if self.host.private_key is None: + self.generate_ssl_keys() + self.generate_metadatas() + self.host.store() + + if form.get_widget('terminate').parse(): + return redirect('..') + return redirect('authentication_and_logout_adresses') + + get_response().breadcrumb.append(('check_new_address', _('Check site address and name'))) + self.html_top(_('Step 2 - Check the new site address works')) + + '

%s

' % _('DNS configuration') + + '

%s

' % _('''Before opening the following link, ensure you have configured your DNS +for this address. If you don't have a DNS server and you just want to test Larpe, add this +domain name in the file "/etc/hosts".''') + + '

%s

' % _('''Then you can open this link in a new window or tab and see if your site +is displayed. If it's ok, you can click the "%(next)s" button. Otherwise, click the "%(previous)s" +button and check your settings.''') % {'next': _('Next'), 'previous': _('Previous')} + + '

%s

' % _('Site adress and name') + + '

%s' % _('The new address of this site is ') + '%s
' % (self.host.new_url, self.host.new_url) + '%s

' % _('The name of this site is "%s".') % self.host.label + '

%s

' % htmltext(_('''You can also +modify the address or the name of this site''')) + + form.render() + + def modify_site_address_and_name [html] (self): + form = self.form_modify_site_address_and_name() + + if form.get_widget('cancel').parse(): + return redirect('check_new_address') + + if form.is_submitted() and not form.has_errors(): + label = form.get_widget('label').parse() + name = convert_label_to_name(label) + for any_host in Host.select(): + if any_host.id != self.host.id and name == any_host.name: + form.set_error('label', _('An host with the same name already exists')) + break + + if form.is_submitted() and not form.has_errors(): + self.submit_modify_site_address_and_name_form(form) + return redirect('check_new_address') + + get_response().breadcrumb.append(('modify_site_address_and_name', _('Modify site address and name'))) + self.html_top(_('Modify site address and name')) + + form.render() + + def form_modify_site_address_and_name(self): + form = Form(enctype='multipart/form-data') + form.add(UrlWidget, 'new_url', title = _('Address'), required = True, + size = 50, value = self.host.new_url) + form.add(StringWidget, 'label', title = _('Name'), required = True, + size = 50, value = self.host.label) + form.add_submit('submit', _('Submit')) + form.add_submit('cancel', _('Cancel')) + return form + + def submit_modify_site_address_and_name_form(self, form): + fields = ['new_url', 'label'] + for f in fields: + setattr(self.host, f, form.get_widget(f).parse()) + + # Split url to retrieve components + tokens = urlparse.urlparse(self.host.new_url) + self.host.scheme = tokens[0] + self.host.reversed_hostname = tokens[1] + self.host.reversed_directory = tokens[2] + if self.host.reversed_directory.startswith('/'): + self.host.reversed_directory = self.host.reversed_directory[1:] + + # Fill host.name attribute + self.host.name = convert_label_to_name(self.host.label) + + self.host.store() + write_apache2_vhosts() + + def authentication_and_logout_adresses [html] (self): + form = self.form_authentication_and_logout_adresses() + + if form.get_widget('cancel').parse(): + return redirect('check_new_address') + + if form.is_submitted() and not form.has_errors(): + self.submit_authentication_and_logout_adresses_form(form) + if form.get_widget('terminate').parse(): + return redirect('..') + return redirect('check_auto_detected_configuration') + + get_response().breadcrumb.append(('authentication_and_logout_adresses', _('Authentication and logout'))) + self.html_top(_('Step 3 - Configure authentication and logout pages')) + + form.render() + + def form_authentication_and_logout_adresses(self): + form = Form(enctype='multipart/form-data') + form.add(ValidUrlWidget, 'auth_url', title = _('Authentication form page address'), + hint = _('Address of a page on the site which contains the authentication form'), + required = True, size = 50, value = self.host.auth_url) + form.add(ValidUrlWidget, 'logout_url', title = _('Logout address'), required = False, + hint = _('Address of the logout link on the site'), + size = 50, value = self.host.logout_url) + form.add_submit('cancel', _('Previous')) + form.add_submit('submit', _('Next')) + form.add_submit('terminate', _('Terminate')) + return form + + def submit_authentication_and_logout_adresses_form(self, form): + fields = ['auth_url', 'logout_url'] + for f in fields: + setattr(self.host, f, form.get_widget(f).parse()) + self.host.auth_form_url = self.host.auth_url + + if not self.host.http_headers: + self.host.http_headers = { + 'Content-Type': { 'enabled': True, 'value': 'application/x-www-form-urlencoded', 'immutable': False }, + 'X-Forwarded-For': { 'enabled': True, 'value': _('(computed automatically)'), 'immutable': True }, + 'X-Forwarded-Host': { 'enabled': True, 'value': self.host.reversed_hostname, 'immutable': False }, + } + + self.auto_detect_configuration() + self.host.store() + write_apache2_vhosts() + + def check_auto_detected_configuration [html] (self): + form = Form(enctype='multipart/form-data') + form.add_submit('cancel', _('Previous')) + form.add_submit('submit', _('Next')) + form.add_submit('terminate', _('Terminate')) + + if form.get_widget('cancel').parse(): + return redirect('authentication_and_logout_adresses') + + if form.is_submitted(): + if form.get_widget('terminate').parse(): + return redirect('..') + return redirect('credentials') + + get_response().breadcrumb.append(('check_auto_detected_configuration', _('Auto detected configuration'))) + self.html_top(_('Step 4 - Check automatically detected configuration for the authentication form')) + + host_attrs = ( + ('auth_check_url', _('Address where the authentication form must be sent')), + ('login_field_name', _('Name of the login field')), + ('password_field_name', _('Name of the password field')), + ) + + html_fields = '' + success = True + for attr, name in host_attrs: + color = 'black' + if attr in ('auth_check_url', 'login_field_name', 'password_field_name') and \ + not getattr(self.host, str(attr)): + color = 'red' + success = False + html_fields += '
%s
' % (color, name) + html_fields += '
%s
' % \ + (color, getattr(self.host, str(attr))) + if getattr(self.host, str(attr)) == '': + html_fields += '
' + + '

' + if success: + htmltext(_('''\ +The following authentication form parameters have been detected. If they look right, you can go to the next step. +If you think they are wrong, go back and check your settings then try again. +''')) + else: + htmltext(_('''\ +The following authentication form parameters in red haven't been correctly detected. Go back and check +your settings then try again. +''')) + '

' + + html_fields + form.render() + + def credentials [html] (self): + form = self.form_credentials() + + if form.get_widget('cancel').parse(): + return redirect('check_auto_detected_configuration') + + if form.is_submitted() and not form.has_errors(): + self.submit_credentials_form(form) + if form.get_widget('terminate').parse(): + return redirect('..') + return redirect('send_authentication_request') + + get_response().breadcrumb.append(('credentials', _('Credentials'))) + self.html_top(_('Step 5 - Fill in a valid username/password for this site')) + + form.render() + + def form_credentials(self): + form = Form(enctype='multipart/form-data') + form.add(StringWidget, 'username', title = _('Username'), required = True, + size = 30, value = self.host.valid_username) + form.add(PasswordWidget, 'password', title = _('Password'), required = True, + size = 30, value = self.host.valid_password) + 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) + form.add_submit('cancel', _('Previous')) + form.add_submit('submit', _('Next')) + form.add_submit('terminate', _('Terminate')) + return form + + def submit_credentials_form(self, form): + self.host.valid_username = form.get_widget('username').parse() + self.host.valid_password = form.get_widget('password').parse() + self.host.valid_select = {} + for name, values in self.host.select_fields.iteritems(): + if form.get_widget(name): + self.host.valid_select[name] = form.get_widget(name).parse() + + self.host.store() + + def send_authentication_request(self): + site_auth = site_authentication.get_site_authentication(self.host) + + # Request with good credentials + self.host.auth_request_status, self.host.auth_request_data = site_auth.local_auth_check_dispatch( + self.host.valid_username, self.host.valid_password, self.host.valid_select) + self.host.auth_request_success, self.host.auth_request_return_content = \ + site_auth.check_auth(self.host.auth_request_status, self.host.auth_request_data) + + # Request with bad credentials + self.host.auth_bad_request_status, self.host.auth_bad_request_data = site_auth.local_auth_check_dispatch( + 'this_is_a_bad_login', 'this_is_a_bad_password', {}) + self.host.auth_bad_request_success, self.host.auth_bad_request_return_content = \ + site_auth.check_auth(self.host.auth_bad_request_status, self.host.auth_bad_request_data) + + self.host.store() + + return redirect('check_authentication') + + def check_authentication [html] (self): + form = Form(enctype='multipart/form-data') + form.add_submit('cancel', _('Previous')) + form.add_submit('submit', _('Next')) + form.add_submit('terminate', _('Terminate')) + + if form.get_widget('cancel').parse(): + return redirect('credentials') + + if form.is_submitted(): + if form.get_widget('terminate').parse(): + return redirect('..') + return redirect('sso_init_link') + + get_response().breadcrumb.append(('check_authentication', _('Check authentication'))) + self.html_top(_('Step 6 - Check the authentication process')) + + if self.host.auth_request_success: + good_cred_status = 'Success [OK]' + else: + good_cred_status = 'Failed [KO]Results of authentication requests :

\n' + '
    \n' + '\t
  • With good credentials : %s
  • ' % good_cred_status + '\t
  • With bad credentials : %s
  • ' % bad_cred_status + '
\n' + + if self.host.auth_request_success and not self.host.auth_bad_request_success : + '

%s

\n' % _('Authentication succeeded ! You can go to the next step.') + else: + '

%s

\n' % _('Authentication has failed. To resolve this problem, you can :') + '
    \n' + '\t
  • %s
  • \n' % \ + _('Try authentication again') + '\t
  • %s
  • \n' % \ + _('See the response of the authentication requests') + '\t
  • %s
  • \n' % \ + _('Modify the parameters of the authentication requests') + '\t
  • %s
  • \n' % \ + _('Change the way Larpe detects the authentication is successful or not') + '\t
  • %s
  • \n' % _('Go back and change your username and/or password') + '
\n' + + form.render() + + def see_authentication_response [html] (self): + get_response().breadcrumb.append(('see_authentication_response', _('Authentication response'))) + self.html_top(_('Authentication response')) + + '

%s

' % _('Response of the request with good credentials') + + '
%s
' % \ + str(_('HTTP status code')) + '
%s (%s)
' % \ + (self.host.auth_request_status, status_reasons[self.host.auth_request_status]) + + '
' + '
%s
' % _('See HTML page') + '
' + + '

%s

' % _('Response of the request with bad credentials') + + '
%s
' % \ + str(_('HTTP status code')) + '
%s (%s)
' % \ + (self.host.auth_bad_request_status, status_reasons[self.host.auth_bad_request_status]) + + '
' + '
%s
' % _('See HTML page') + '
' + + '

' % _('Back') + + def see_response_html_page (self): + return self.host.auth_request_data + + def see_bad_response_html_page (self): + return self.host.auth_bad_request_data + + def authentication_success_criteria [html] (self): + form = self.form_authentication_success_criteria() + + if form.get_widget('cancel').parse(): + return redirect('check_authentication') + + if form.is_submitted() and not form.has_errors(): + self.submit_authentication_success_criteria_form(form) + return redirect('check_authentication') + + get_response().breadcrumb.append(('authentication_success_criteria', _('Authentication success criteria'))) + self.html_top(_('Authentication success criteria')) + + form.render() + + def form_authentication_success_criteria(self): + form = Form(enctype='multipart/form-data') + form.add(RadiobuttonsWidget, 'auth_system', title = _('Authentication system of the original site'), + options=[ + ('password', _('Check the existence of a password field'), 'password'), + ('match_text', _('Match some text to detect an authentication failure'), 'match_text'), + ], + sort=False, + delim=htmltext('
'), + value = self.host.auth_system) + form.add(RegexStringWidget, 'auth_match_text', title = _('Text to match in case of authentication failure'), + required = False, size = 50, value = self.host.auth_match_text) + form.add_submit('submit', _('Submit')) + form.add_submit('cancel', _('Cancel')) + return form + + def submit_authentication_success_criteria_form(self, form): + for f in ('auth_system', 'auth_match_text'): + value = form.get_widget(f).parse() + setattr(self.host, f, value) + + self.host.store() + + def modify_authentication_request [html] (self): + get_response().breadcrumb.append(('modify_authentication_request', _('Authentication request'))) + self.html_top(_('Modify the parameters of the authentication requests')) + + '
' + '
%s
%s
' % ( + _('Modify POST parameters'), _('Configure the form attributes that will be sent within the authentication POST requests')) + '
%s
%s
' % ( + _('Modify HTTP headers'), _('Configure the HTTP headers of the authentication requests made by Larpe')) + '
' + '

' % _('Back') + + def auth_request_post_parameters [html] (self): + form = self.form_auth_request_post_parameters() + + if form.get_widget('cancel').parse(): + return redirect('modify_authentication_request') + + if form.is_submitted() and not form.has_errors(): + self.submit_auth_request_post_parameters_form(form) + return redirect('modify_authentication_request') + + get_response().breadcrumb.append(('auth_request_post_parameters', _('POST parameters'))) + self.html_top(_('Configure POST parameters')) + + '

%s

' % _('''Here are the detected form fields that will be sent as parameters of the +authentication POST request. You can desactivate some or all of them, or change their value.''') + + form.render() + + def form_auth_request_post_parameters(self): + form = Form(enctype='multipart/form-data') + for name, value in self.host.post_parameters.iteritems(): + if value['immutable']: + form.add(DictWidget, name, value, disabled = 'disabled') + else: + form.add(DictWidget, name, value) + form.add_submit('submit', _('Submit')) + form.add_submit('cancel', _('Cancel')) + return form + + def submit_auth_request_post_parameters_form(self, form): + for name, old_value in self.host.post_parameters.iteritems(): + value = form.get_widget(name).parse() + if value['enabled'] == 'on': + old_value['enabled'] = True + else: + old_value['enabled'] = False + if old_value['immutable'] is False: + old_value['value'] = value['value'] + self.host.post_parameters[name] = old_value + self.host.store() + + def auth_request_http_headers [html] (self): + form = self.form_auth_request_http_headers() + + if form.get_widget('cancel').parse(): + return redirect('modify_authentication_request') + + if form.is_submitted() and not form.has_errors(): + self.submit_auth_request_http_headers_form(form) + return redirect('modify_authentication_request') + + get_response().breadcrumb.append(('auth_request_http_headers', _('HTTP headers'))) + self.html_top(_('Configure HTTP headers')) + + '

%s

' % _('''Here are the HTTP headers that will be sent within the authentication +POST request. You can desactivate some or all of them, or change their value.''') + + form.render() + + def form_auth_request_http_headers(self): + form = Form(enctype='multipart/form-data') + for name, value in self.host.http_headers.iteritems(): + if value['immutable']: + form.add(DictWidget, name, value, disabled = 'disabled') + else: + form.add(DictWidget, name, value) + form.add(HtmlWidget, htmltext('

%s

' % \ + _('The headers "Host", "Accept-Encoding" and "Content-Length" will also automatically be sent.'))) + if get_cfg('proxy', {}).get('enabled') and self.host.use_proxy: + form.add(HtmlWidget, htmltext('

%s

' % \ + _('''As Larpe uses a proxy for this site, the headers "Proxy-Authorization", +"Proxy-Connection" and "Keep-Alive" will be sent as well.'''))) + form.add_submit('submit', _('Submit')) + form.add_submit('cancel', _('Cancel')) + return form + + def submit_auth_request_http_headers_form(self, form): + for name, old_value in self.host.http_headers.iteritems(): + value = form.get_widget(name).parse() + if value['enabled'] == 'on': + old_value['enabled'] = True + else: + old_value['enabled'] = False + if old_value['immutable'] is False: + old_value['value'] = value['value'] + self.host.http_headers[name] = old_value + self.host.store() + + def generate_apache_filter(self): + # Set Python filter path for Apache configuration + python_path = os.path.join(self.host.site_dir, 'filters') + if python_path not in self.host.apache_python_paths: + self.host.apache_python_paths.append(python_path) + + # Write Python filter + python_file = open(os.path.join(self.host.site_dir, 'filters', 'output_replace_form.py'), 'w') + python_file.write(open(OUTPUT_FILTER_BASE).read()) + python_file.write('''\ +def filter_page(filter, page): + current_form = re.compile('
]*?action="%(auth_form_action)s".*?>.*?
', re.DOTALL) + return current_form.sub('
', page) +''' % { 'auth_form_action': self.host.auth_form_action, 'name': self.host.name }) + python_file.close() + + # Set Python filter for Apache configuration + if not 'output_replace_form' in self.host.apache_output_python_filters: + self.host.apache_output_python_filters.append('output_replace_form') + + def sso_init_link [html] (self): + form = self.form_sso_init_link() + + if form.get_widget('cancel').parse(): + return redirect('check_authentication') + + if form.is_submitted() and not form.has_errors(): + self.submit_sso_init_link_form(form) + if form.get_widget('terminate').parse(): + return redirect('..') + return redirect('metadatas') + + get_response().breadcrumb.append(('sso_init_link', _('SSO initiation'))) + self.html_top(_('Step 7 - Configure how a Single Sign On can be initiated')) + + '

%s\n' % _('Most sites use one of the following 2 ways to allow users to initialise an authentication :') + '\t

    \n' + '\t\t
  1. %s
  2. \n' % \ + _('''The site has a single authentication page. It redirects users to this page when +they click a "Login" button or try to access a page which require users to be authenticated.''') + '\t\t
  3. %s
  4. \n' % \ + _('''The site includes an authentication form in most or all of his pages. Users can +authenticate on any of these pages, and don't need to be redirected to a separate authentication page.''') + '\t
\n' + '

\n' + + '

%s

' % _('Select the way your site works :') + + form.render() + + def form_sso_init_link(self): + form = Form(enctype='multipart/form-data') + form.add(RadiobuttonsWidget, 'auth_form_places', + options=[ + ('form_once', _('The site has a single authentication page'), 'form_once'), + ('form_everywhere', _('The site includes an authentication form in most or all pages'), 'form_everywhere'), + ], + sort=False, required = True, delim=htmltext('
'), value = self.host.auth_form_places) + form.add_submit('cancel', _('Previous')) + form.add_submit('submit', _('Next')) + form.add_submit('terminate', _('Terminate')) + return form + + def submit_sso_init_link_form(self, form): + fields = [ 'auth_form_places', ] + for f in fields: + setattr(self.host, f, form.get_widget(f).parse()) + self.host.auth_form_url = self.host.auth_url + + if self.host.auth_form_places == 'form_everywhere' and self.host.auth_form_action: + self.generate_apache_filter() + else: + if 'output_replace_form' in self.host.apache_output_python_filters: + self.host.apache_output_python_filters.remove('output_replace_form') + + self.host.store() + write_apache2_vhosts() + + def metadatas [html] (self): + form = Form(enctype='multipart/form-data') + form.add_submit('cancel', _('Previous')) + form.add_submit('submit', _('Next')) + form.add_submit('terminate', _('Terminate')) + + if form.get_widget('cancel').parse(): + return redirect('sso_init_link') + + if form.is_submitted(): + if form.get_widget('terminate').parse(): + return redirect('..') + return redirect('advanced_options') + + get_response().breadcrumb.append(('metadatas', _('Metadatas'))) + self.html_top(_('Step 8 - Metadatas of %(site_name)s' % {'site_name': self.host.name})) + + '

%s

' % \ + _('''Download the metadatas and the public key for this site and +upload them on your identity provider in order to use Liberty Alliance features.''') + + '
' + if hasattr(self.host, str('base_url')): + if lasso.SAML2_SUPPORT: + saml2_metadata_url = '%s/metadata.xml' % self.host.saml2_base_url + '
%s
%s
' % ( + saml2_metadata_url, + _('SAML 2.0 Metadata'), + _('Download SAML 2.0 metadata file')) + metadata_url = '%s/metadata.xml' % self.host.base_url + '
%s
%s
' % ( + metadata_url, + _('ID-FF 1.2 Metadata'), + _('Download ID-FF 1.2 metadata file')) + else: + '

%s

' % _('No metadata has been generated for this host.') + + if hasattr(self.host, str('base_url')) and self.host.public_key and os.path.exists(self.host.public_key): + public_key_url = '%s/public_key' % self.host.base_url + '
%s
%s
' % ( + public_key_url, + _('Public key'), + _('Download SSL Public Key file')) + else: + '

%s

' % _('No public key has been generated for this host.') + '
' + + form.render() + + def advanced_options [html] (self): + form = self.form_advanced_options() + + if form.get_widget('cancel').parse(): + return redirect('metadatas') + + if not form.is_submitted() or form.has_errors(): + get_response().breadcrumb.append(('advanced_options', _('Advanced options'))) + self.html_top(_('Step 9 - Advanced options')) + + '

%s

' % _('Configure advanced options to setup the last details of your site.') + '

%s

' % _('''If you don't know what to configure here, just click %(next)s and +come here later if needed.''') % {'next': _('Next')} + + form.render() + else: + self.submit_advanced_options_form(form) + if form.get_widget('terminate').parse(): + return redirect('..') + return redirect('check_full_configuration') + + def form_advanced_options(self): + form = Form(enctype='multipart/form-data') + form.add(CheckboxWidget, 'redirect_root_to_login', + title=_('Redirect the root URL of the site to the login page.'), + value = self.host.redirect_root_to_login) + form.add(UrlOrAbsPathWidget, 'return_url', title = _('Return address'), + hint = _('Where the user will be redirected after a successful authentication'), + required = False, size = 50, value = self.host.return_url) + form.add(UrlOrAbsPathWidget, 'root_url', title = _('Error address'), + hint = _('Where the user will be redirected after a disconnection or an error'), + required = False, size = 50, value = self.host.root_url) + form.add(UrlOrAbsPathWidget, 'initiate_sso_url', title = _('URL which must initiate the SSO'), + hint = _('''Address which must initiate the SSO. If empty, defaults to the previously +specified "%s"''') % _('Authentication form page address'), + required = False, size = 50, value = self.host.initiate_sso_url) + form.add(CheckboxWidget, 'proxy-html', title = _('Apache HTML proxy'), + hint = _('''Converts urls in the HTML pages according to the host new domain name. +Disabled by default because it makes some sites not work correctly.'''), + value = 'proxy-html' in self.host.apache_output_filters) + + form.add_submit('cancel', _('Previous')) + form.add_submit('submit', _('Next')) + form.add_submit('terminate', _('Terminate')) + return form + + def submit_advanced_options_form(self, form): + old_redirect_root_to_login = self.host.redirect_root_to_login + + for f in ('redirect_root_to_login', 'return_url', 'root_url', 'initiate_sso_url'): + value = form.get_widget(f).parse() + setattr(self.host, f, value) + + f = 'proxy-html' + value = form.get_widget(f).parse() + if value is True and f not in self.host.apache_output_filters: + self.host.apache_output_filters.append(f) + if value is False and f in self.host.apache_output_filters: + self.host.apache_output_filters.remove(f) + + self.host.store() + + if self.host.initiate_sso_url or self.host.redirect_root_to_login is not old_redirect_root_to_login: + write_apache2_vhosts() + + def check_full_configuration [html] (self): + form = Form(enctype='multipart/form-data') + form.add_submit('cancel', _('Previous')) + form.add_submit('submit', _('Finish')) + + if form.get_widget('cancel').parse(): + return redirect('advanced_options') + + if form.is_submitted(): + return redirect('../..') + + get_response().breadcrumb.append(('check_full_configuration', _('Check everything works'))) + self.html_top(_('Step 10 - Check everything works')) + + '

%s

' % \ + _('''Now you can fully test your site, start from the home page, initiate a +Single Sign On, federate your identities and do a Single Logout.''') + + '

%s' % _('The address of your site is : ') + '%s' % (self.host.new_url, self.host.new_url) + '

' + + '

%s

' % \ + _('''If everything works, click the "%(finish)s" button, otherwise you can go +back and check your settings.''') % { 'finish': _('Finish') } + + form.render() + + def auto_detect_configuration(self): + # Reset previous detected values + self.host.auth_form = None + self.host.auth_check_url = None + self.host.login_field_name = None + self.host.password_field_name = None + if not self.host.post_parameters: + self.host.post_parameters = {} + + self.parse_page(self.host.auth_form_url) + + def parse_page(self, page_url): + # Get the authentication page + try: + response, status, page, auth_header = http_get_page(page_url, use_proxy=self.host.use_proxy) + except Exception, msg: + print msg + return + + if page is None: + return + #raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to get page'), self.host.auth_form_url)) + + # Default authentication mode + self.host.auth_mode = 'form' + + self.host.site_authentication_plugin = site_authentication.guess_site_authentication_class(page) + self.parse_frames(page) + self.parse_forms(page) + if self.host.auth_form is not None: + self.parse_form_action() + input_fields = self.parse_input_fields() + self.parse_login_field(input_fields) + self.parse_password_field() + self.parse_select_fields() + self.parse_other_fields() + + def parse_frames(self, page): + '''If there are frames, parse them recursively''' + regexp = re.compile("""]*?>""", re.DOTALL | re.IGNORECASE) + found_frames = regexp.findall(page) + if found_frames: + for frame_url in found_frames: + if frame_url.startswith('http'): + frame_full_url = frame_url + else: + page_url_tokens = page_url.split('/') + page_url_tokens[-1] = frame_url + frame_full_url = '/'.join(page_url_tokens) + self.parse_page(frame_full_url) + + def parse_forms(self, page): + '''Search for an authentication form''' + # Get all forms + regexp = re.compile("""""", 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("""]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE) + if regexp.search(found_form) is not None: + self.host.auth_form = found_form + break + + def parse_form_action(self): + '''Get the action url of the form''' + regexp = re.compile("""].*?>""", re.DOTALL | re.IGNORECASE) + self.host.auth_form_action = regexp.findall(self.host.auth_form)[0] + # FIXME: Find a Python module which unescapes html entities + self.host.auth_check_url = self.host.auth_form_action.replace('&', '&') + if not self.host.auth_check_url.startswith('http'): + if self.host.auth_check_url.startswith('/'): + if self.host.orig_site.startswith('https'): + orig_site_root = 'https://%s' % urllib.splithost(self.host.orig_site[6:])[0] + else: + orig_site_root = 'http://%s' % urllib.splithost(self.host.orig_site[5:])[0] + self.host.auth_check_url = orig_site_root + self.host.auth_check_url + else: + auth_form_url_tokens = self.host.auth_form_url.split('/') + auth_form_url_tokens[-1] = self.host.auth_check_url + self.host.auth_check_url = '/'.join(auth_form_url_tokens) + + def parse_input_fields(self): + '''Get all input fields''' + regexp = re.compile("""]*?>""", re.DOTALL | re.IGNORECASE) + return regexp.findall(self.host.auth_form) + + def parse_login_field(self, input_fields): + '''Get login field name''' + try: + regexp = re.compile("""]*?type=["']?text["']?[^>]*?>""", re.DOTALL | re.IGNORECASE) + text_fields = regexp.findall(self.host.auth_form) + login_field = '' + if text_fields: + login_field = text_fields[0] + else: + for field in input_fields: + if re.search("""type=["']?""", field, re.DOTALL | re.IGNORECASE) is None: + login_field = field + break + regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE) + self.host.login_field_name = regexp.findall(login_field)[0] + if not self.host.post_parameters.has_key(self.host.login_field_name): + self.host.post_parameters[self.host.login_field_name] = \ + { 'enabled': True, 'value': _('(filled by users)'), 'immutable': True } + self.host.store() + except IndexError, e: + self.host.login_field_name = None + print 'Error handling login field : %s' % e + + def parse_password_field(self): + '''Get password field name''' + try: + regexp = re.compile("""]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE) + password_field = regexp.findall(self.host.auth_form)[0] + regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE) + self.host.password_field_name = regexp.findall(password_field)[0] + if not self.host.post_parameters.has_key(self.host.password_field_name): + self.host.post_parameters[self.host.password_field_name] = \ + { 'enabled': True, 'value': _('(filled by users)'), 'immutable': True } + except IndexError, e: + self.host.password_field_name = None + print 'Error handling password field : %s' % e + + def parse_select_fields(self): + '''Add select fields to host attributes''' + # First added for Imuse (Rennes) + regexp = re.compile("""""", re.DOTALL | re.IGNORECASE) + self.host.select_fields = {} + for field in regexp.findall(self.host.auth_form): + try: + regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE) + name = regexp.findall(field)[0] + regexp = re.compile("""]*?>.*?""", re.DOTALL | re.IGNORECASE) + options = regexp.findall(field) + values = [] + for option in options: + regexp = re.compile("""]*?>(.*?)""", re.DOTALL | re.IGNORECASE) + option_label = regexp.findall(option) + regexp = re.compile("""value=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE) + option_value = regexp.findall(option) + if option_label: + if not option_value: + option_value = option_label + values.append((option_value[0], option_label[0])) + else: + print >> sys.stderr, 'W: Could not parse select options' + self.host.select_fields[name] = values + if not self.host.post_parameters.has_key(name): + self.host.post_parameters[name] = \ + { 'enabled': True, 'value': _('(filled by users)'), 'immutable': True } + except IndexError, e: + continue + + def parse_other_fields(self): + '''Get the default value of all other fields''' + self.host.other_fields = {} + + # Get hidden fields + regexp = re.compile("""]*?type=["']?hidden["']?[^>]*?>""", re.DOTALL | re.IGNORECASE) + other_fields = regexp.findall(self.host.auth_form) + + # Only get first submit field + regexp = re.compile("""]*?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 + + +class HostPage(Directory): + _q_exports = ['', 'delete'] + + def __init__(self, host_id): + self.host = Host.get(host_id) + get_response().breadcrumb.append((host_id + '/', self.host.label)) + + def _q_lookup(self, component): + if component == 'configuration_assistant': + return ConfigurationAssistant(self.host) + elif component == 'forms_prefill': + return FormsDirectory(self.host) + + def _q_index [html] (self): + get_publisher().reload_cfg() + html_top('hosts', title = self.host.label) + + '

%s

' % _('Configuration assistant') + + '
' + + '
%s
%s
' % ( + _('Address of the original site'), _('Configure the root address of the site')) + + '
%s
%s
' % ( + _('New address and name'), _('Configure the new address and name of this site')) + + '
%s
%s
' % ( + _('Authentication and logout addresses'), _('Configure the authentication and logout addresses of the original site')) + + '
%s
%s
' % ( + _('Check auto detected configuration'), _('Check the automatically detected configuration is right')) + + '
%s
%s
' % ( + _('Credentials'), _('Configure some valid credentials to authenticate on the original site')) + + '
%s
%s
' % ( + _('Retry authentication'), _('Retry sending an authentication request to the site to check if your new parameters work well')) + + '
%s
%s
' % ( + _('Check authentication response'), _('Check the response from the latest authentication request')) + + '
%s
%s
' % ( + _('Configure authentication success criteria'), _('Specify how Larpe knows if the authentication has succeeded or not')) + + '
%s
%s
' % ( + _('Modify authentication request'), _('Modify POST fields or HTTP headers of the authentication request')) + + '
%s
%s
' % ( + _('Configure how a Single Sign On can be initiated'), _('Configure how a Single Sign On can be initiated')) + + '
%s
%s
' % ( + _('Metadatas and key'), _('Download SAML 2.0 or ID-FF metadatas and SSL public key')) + + '
%s
%s
' % ( + _('Adavanced options'), _('Configure advanced options to setup the last details of your site')) + + '
' + + '

%s

' % _('Form prefilling with ID-WSF') + + '
' + '
%s
%s
' % ( + _('Forms'), _('Configure the forms to prefill')) + '
' + + def delete [html] (self): + form = Form(enctype='multipart/form-data') + form.widgets.append(HtmlWidget('

%s

' % _( + 'You are about to irrevocably delete this host.'))) + 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('hosts', title = _('Delete Host')) + '

%s : %s

' % (_('Delete Host'), self.host.label) + form.render() + else: + self.host.remove_self() + write_apache2_vhosts() + return redirect('..') + + +class HostsDirectory(Directory): + _q_exports = ['', 'new'] + + def _q_index [html] (self): + get_response().breadcrumb.append(('hosts/', _('Hosts'))) + html_top('hosts', title = _('Hosts')) + """""" % _('New Host') + + '
    ' + + for host in Host.select(lambda x: x.name != 'larpe', order_by = 'label'): + if not host.name: + continue + if not hasattr(host, str('scheme')): + host.scheme = str('http') + '
  • ' + '%s' % host.label + if hasattr(host, str('new_url')) and host.new_url: + url = host.new_url + else: + # Compat with older Larpe versions + url = '%s://%s%s/' % (host.scheme, host.reversed_hostname, get_request().environ['SCRIPT_NAME']) + if host.reversed_directory is not None: + url += '%s/' % host.reversed_directory + '
    %s' % (url, url) + '

    ' + command_icon('%s/' % host.id, 'edit') + command_icon('%s/delete' % host.id, 'remove') + '

  • ' + '
' + + def new [html] (self): + if not os.path.isdir(os.path.join(get_publisher().app_dir, str('idp'))): + html_top('hosts', title = _('New Host')) + html = '

%s

' % _('New Host') + html += 'You must ' % misc.get_root_url() + html += 'configure an Identity Provider first

' + html += '' % _('Back') + return html + + get_response().breadcrumb.append(('hosts/', _('Hosts'))) + get_response().breadcrumb.append(('new', _('New')) ) + host = Host() + host.store() + return redirect('%s/configuration_assistant/start' % host.id) + + def _q_lookup(self, component): + get_response().breadcrumb.append(('hosts/', _('Hosts'))) + return HostPage(component) + diff --git a/larpe/tags/release-1.0/larpe/admin/liberty_utils.py b/larpe/tags/release-1.0/larpe/admin/liberty_utils.py new file mode 100644 index 0000000..de324c4 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/admin/liberty_utils.py @@ -0,0 +1,129 @@ +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 = """\ + +""" % cfg + + sp_head = """ + """ + + 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 = """ + + + %s + + """ % cfg['signing_public_key'] + elif 'KEY' in cfg['signing_public_key']: + signing_public_key = """ + + + %s + + """ % cfg['signing_public_key'] + + sp_body = """ + %(base_url)s/assertionConsumer + + %(base_url)s/soapEndpoint + + %(base_url)s/singleLogout + %(base_url)s/singleLogoutReturn + http://projectliberty.org/profiles/slo-idp-http + http://projectliberty.org/profiles/slo-sp-soap + http://projectliberty.org/profiles/slo-sp-http + + %(base_url)s/federationTermination + %(base_url)s/federationTerminationReturn + http://projectliberty.org/profiles/fedterm-idp-soap + http://projectliberty.org/profiles/fedterm-idp-http + http://projectliberty.org/profiles/fedterm-sp-soap + http://projectliberty.org/profiles/fedterm-sp-http + + true + + """ % cfg + + orga = '' + if cfg.get('organization_name'): + orga = """ + + %s + """ % unicode(cfg['organization_name'], 'iso-8859-1').encode('utf-8') + + epilogue = """ +""" + + return '\n'.join([prologue, sp_head, signing_public_key, sp_body, orga, epilogue]) + + + +def get_saml2_metadata(cfg): + prologue = """\ + +""" % cfg + + sp_head = """ + """ + + 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 = """ + + + %s + + """ % cfg['signing_public_key'] + elif 'KEY' in cfg['signing_public_key']: + signing_public_key = """ + + + %s + + """ % cfg['signing_public_key'] + + sp_body = """ + + + + + """ % cfg + + orga = '' + if cfg.get('organization_name'): + orga = """ + + %s + """ % unicode(cfg['organization_name'], 'iso-8859-1').encode('utf-8') + + epilogue = """ +""" + + return '\n'.join([prologue, sp_head, signing_public_key, sp_body, orga, epilogue]) + diff --git a/larpe/tags/release-1.0/larpe/admin/root.ptl b/larpe/tags/release-1.0/larpe/admin/root.ptl new file mode 100644 index 0000000..8a65592 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/admin/root.ptl @@ -0,0 +1,90 @@ +import os + +import lasso + +from quixote import get_session, get_session_manager, get_publisher, get_request, get_response +from quixote.directory import Directory, AccessControlled + +from qommon.admin.menu import html_top +from qommon.admin import logger + +from larpe import errors +from larpe import misc + +import hosts +import users +import settings + +def gpl [html] (): + """

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.

+ +

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.

+ +

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.

+ """ + + +class RootDirectory(AccessControlled, Directory): + _q_exports = ['', 'hosts', 'users', 'settings', 'logger'] + + hosts = hosts.HostsDirectory() + users = users.UsersDirectory() + settings = settings.SettingsDirectory() + logger = logger.LoggerDirectory() + + menu_items = [ + ('hosts/', N_('Hosts')), + ('users/', N_('Users')), + ('settings/', N_('Settings')), + ('logger/', N_('Logs')), + ('/', N_('Liberty Alliance Reverse Proxy'))] + + 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() + + if misc.get_current_protocol() == lasso.PROTOCOL_SAML_2_0: + user = get_session().get_user(host.saml2_provider_id) + else: + 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() + diff --git a/larpe/tags/release-1.0/larpe/admin/settings.ptl b/larpe/tags/release-1.0/larpe/admin/settings.ptl new file mode 100644 index 0000000..a1a36f1 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/admin/settings.ptl @@ -0,0 +1,361 @@ +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 qommon.form import * +from qommon.misc import get_abs_path +from qommon.admin.cfg import cfg_submit +from qommon.admin.menu import html_top, error_page +from qommon.admin.emails import EmailsDirectory as QommonEmailsDirectory +from qommon.admin.settings import SettingsDirectory as QommonSettingsDirectory + +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')) + "

%s

" % _('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('settings', _('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('settings', _('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(QommonEmailsDirectory): + def _q_index [html] (self): + # Don't use custom emails + html_top('settings', title = _('Emails')) + '

%s

' % _('Emails') + + '
    ' + '
  • %s
  • ' % _('General Options') + '
' + + '

' + '%s' % _('Back') + '

' + + +class SettingsDirectory(QommonSettingsDirectory): + _q_exports = ['', 'liberty_sp', 'liberty_idp', 'domain_names', 'apache2_configuration_generation', + 'proxy', 'language', 'emails', 'debug_options' ] + + liberty_idp = LibertyIDPDir() + emails = EmailsDirectory() + + def _q_index [html] (self): + get_publisher().reload_cfg() + html_top('settings', title = _('Settings')) + + if lasso.SAML2_SUPPORT: + '

%s

' % _('Liberty Alliance & SAML 2.0 Service Provider') + else: + '

%s

' % _('Liberty Alliance Service Provider') + '
%s
%s
' % ( + _('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 + '
%s
%s
' % ( + metadata_url, + _('SAML 2.0 Metadata'), + _('Download SAML 2.0 metadata file for Larpe')) + + if self.host.metadata is not None: + metadata_url = '%s/metadata.xml' % self.host.base_url + '
%s
%s
' % ( + metadata_url, + _('ID-FF 1.2 Metadata'), + _('Download ID-FF 1.2 metadata file for Larpe')) + + if self.host.public_key is not None: + public_key_url = '%s/public_key' % self.host.base_url + '
%s
%s
' % ( + public_key_url, + _('Public key'), + _('Download SSL Public Key file')) + + if lasso.SAML2_SUPPORT: + '

%s

' % _('Liberty Alliance & SAML 2.0 Identity Provider') + else: + '

%s

' % _('Liberty Alliance Identity Provider') + + '
' + + '
%s
%s
' % ( + _('Identity Provider'), _('Configure an identity provider')) + + if get_publisher().cfg.has_key('idp'): + '
%s
%s
' % ( + _('Identity Provider metadatas'), _('See current identity provider metadatas')) + + '
' + + '

%s

' % _('Global parameters for the sites') + + '
' + '
%s
%s
' % ( + _('Domain name'), _('Configure the base domain name for the sites')) + '
%s
%s
' % ( + _('Apache 2 configuration generation'), _('Customise Apache 2 configuration generation')) + '
%s
%s
' % ( + _('Proxy'), _('Connect to the sites through a web proxy')) + '
' + + '

%s

' % _('Customisation') + + '
' + '
%s
%s
' % ( + _('Language'), _('Configure site language')) + '
%s
%s
' % ( + _('Emails'), _('Configure email settings')) + '
' + + '

%s

' % _('Misc') + + '
' + '
%s
%s
' % ( + _('Debug Options'), _('Configure options useful for debugging')) + '
' + + + 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')) + '

%s

' % _('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')) + '

%s

' % _('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')) + '

%s

' % _('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() diff --git a/larpe/tags/release-1.0/larpe/admin/users.ptl b/larpe/tags/release-1.0/larpe/admin/users.ptl new file mode 100644 index 0000000..6f07b87 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/admin/users.ptl @@ -0,0 +1,275 @@ +import random + +import lasso + +from quixote import get_request, get_session, redirect, get_publisher +from quixote.directory import Directory + +from qommon.admin.menu import html_top, error_page, command_icon +from qommon.form import * +from qommon import emails + +from larpe import errors +from larpe import misc +from larpe.users import User +from larpe.hosts import Host + +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, "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)) + '

%s - %s

' % (_('User'), self.user.name) + '
' + '
%s
' % _('Name') + '
%s
' % self.user.name + if self.user.email: + '
%s
' % _('Email') + '
%s
' % 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: +# '

%s

' % _('Liberty Alliance Details') +# '
    ' +# 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) +# '
  • ' +# _('Account federated with %s') % label +# '
    ' +# if federation.localNameIdentifier: +# _("local: ") + federation.localNameIdentifier.content +# if federation.remoteNameIdentifier: +# _("remote: ") + federation.remoteNameIdentifier.content +# '
  • ' +# '
' + +# # XXX: only display this in debug mode: +# '

%s

' % _('Lasso Identity Dump') +# '
%s
' % self.user.lasso_dump + '
' + + def debug [html] (self): + get_response().breadcrumb.append( ('debug', _('Debug')) ) + html_top('users', 'Debug') + "

Debug - %s

" % self.user.name + "
"
+        self.user.lasso_dump
+        "
" + + 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')) + '

%s

' % _('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('

%s

' % _( + "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')) + '

%s %s

' % (_('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')) + '

%s

' % _('Identification Token') + '

%s

' % _('You are about to generate a token than can be used to federate the account.') + '

%s

' % _('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: + '

%s

' % _('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() + + '

' + _('Identification Token for %s') % self.user.name + ' : %s

' % 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.') + '

' % _('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')): + '

%s

' % _('Liberty support must be setup before creating users.') + else: + """""" % _('New User') + + debug_cfg = get_publisher().cfg.get('debug', {}) + + users = User.select(lambda x: x.name is not None, order_by = 'name') + + '
    ' + for user in users: + '
  • ' + '%s' % user.name + if user.email: + '

    ' + user.email + '

    ' + + '

    ' + 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') + '

  • ' + '
' + + 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')) + '

%s

' % _('New User') + form.render() + else: + user_ui.submit_form(form) + if first_user: + session = get_session() + if hasattr(session, str('lasso_dump')): + user_ui.user.name_identifiers = [ session.name_identifier ] + user_ui.user.lasso_dumps = [ session.lasso_anonymous_identity_dump ] + user_ui.user.store() + if misc.get_current_protocol() == lasso.PROTOCOL_SAML_2_0: + get_session().set_user(user_ui.user.id, host.saml2_provider_id) + else: + 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() diff --git a/larpe/tags/release-1.0/larpe/ctl/__init__.py b/larpe/tags/release-1.0/larpe/ctl/__init__.py new file mode 100644 index 0000000..35a5923 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/ctl/__init__.py @@ -0,0 +1,2 @@ +from start import start + diff --git a/larpe/tags/release-1.0/larpe/ctl/start.py b/larpe/tags/release-1.0/larpe/ctl/start.py new file mode 100644 index 0000000..de41324 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/ctl/start.py @@ -0,0 +1,34 @@ +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, err: + if err[0] == 98: + print >> sys.stderr, 'address already in use' + sys.exit(1) + raise + except KeyboardInterrupt: + sys.exit(1) + diff --git a/larpe/tags/release-1.0/larpe/errors.ptl b/larpe/tags/release-1.0/larpe/errors.ptl new file mode 100644 index 0000000..dff4ee3 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/errors.ptl @@ -0,0 +1,14 @@ +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() + query = request.get_query() + session.after_url = request.get_url() + if query: + session.after_url += '?' + query + login_url = '%s/liberty/larpe/login' % request.environ['SCRIPT_NAME'] + redirect(login_url) diff --git a/larpe/tags/release-1.0/larpe/federations.py b/larpe/tags/release-1.0/larpe/federations.py new file mode 100644 index 0000000..e76328f --- /dev/null +++ b/larpe/tags/release-1.0/larpe/federations.py @@ -0,0 +1,35 @@ +'''Federation object. Configuration variables and utilities''' + +from qommon.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=None): + select = select or {} + 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 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) diff --git a/larpe/tags/release-1.0/larpe/field_prefill.py b/larpe/tags/release-1.0/larpe/field_prefill.py new file mode 100644 index 0000000..389d887 --- /dev/null +++ b/larpe/tags/release-1.0/larpe/field_prefill.py @@ -0,0 +1,13 @@ +from qommon.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 = {} diff --git a/larpe/tags/release-1.0/larpe/filter/larpe-filter.py b/larpe/tags/release-1.0/larpe/filter/larpe-filter.py new file mode 100755 index 0000000..2d9973b --- /dev/null +++ b/larpe/tags/release-1.0/larpe/filter/larpe-filter.py @@ -0,0 +1,164 @@ +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, """ +
+ +
""" % host_name) + except: + pass + return page + +def find_auth_form(page): + regexp = re.compile("""""", re.DOTALL | re.IGNORECASE) + found_forms = regexp.findall(page) + + for found_form in found_forms: + regexp = re.compile("""]*?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('
.*?
'), re.DOTALL) + return str_to_replace.sub(str(r"""
+ +
""" + ), 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 - token ]'), page) + if filter.req.uri == '/dot/ecrire/redacteur.php': + str_to_replace = re.compile(str('(