From f8f8d3f1f55aa394b2ce5aace69e26fe54583795 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 13 Nov 2013 11:35:39 +0100 Subject: [PATCH] finish packages amue-authentic2 and amue-haproxy --- Makefile | 31 ++- amue.conf | 15 ++ amue/__init__.py | 0 amue/settings.py | 401 ++++++++++++++++++++++++++++++++ amue/wsgi.py | 24 ++ db.conf | 7 + debian/amue-authentic2.conffile | 1 + debian/amue-authentic2.config | 13 ++ debian/amue-authentic2.dirs | 2 + debian/amue-authentic2.install | 6 + debian/amue-authentic2.postinst | 54 +++++ debian/amue-authentic2.postrm | 22 ++ debian/amue-haproxy.install | 3 + debian/amue-haproxy.postinst | 22 ++ debian/control | 9 +- debian/rules | 2 +- debian/source/format | 1 + gunicorn-cfg.py | 11 + haproxy.cfg | 49 ++++ idp.amue.fr.nginx | 28 +++ ldap.sh | 6 + manage.sh | 7 + reload.sh | 6 + run.sh | 5 + setup.py | 129 ++++++++++ stud-amue-idp-test.conf | 2 + supervisor.conf | 7 + syncdb.sh | 6 + wildcard-amue.fr.cert | 29 +++ 29 files changed, 892 insertions(+), 6 deletions(-) create mode 100644 amue/__init__.py create mode 100644 amue/settings.py create mode 100644 amue/wsgi.py create mode 100644 db.conf create mode 100644 debian/amue-authentic2.conffile create mode 100644 debian/amue-authentic2.config create mode 100644 debian/amue-authentic2.dirs create mode 100644 debian/amue-authentic2.install create mode 100644 debian/amue-authentic2.postinst create mode 100755 debian/amue-authentic2.postrm create mode 100644 debian/amue-haproxy.install create mode 100644 debian/amue-haproxy.postinst create mode 100644 debian/source/format create mode 100644 gunicorn-cfg.py create mode 100644 haproxy.cfg create mode 100644 idp.amue.fr.nginx create mode 100755 ldap.sh create mode 100755 manage.sh create mode 100755 reload.sh create mode 100755 run.sh create mode 100755 setup.py create mode 100644 stud-amue-idp-test.conf create mode 100644 supervisor.conf create mode 100755 syncdb.sh create mode 100644 wildcard-amue.fr.cert diff --git a/Makefile b/Makefile index 87e4389..3c9dbba 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,30 @@ all: true install: - install -d $(DESTDIR)/etc/authentic2/config.d/ - install -m 644 amue.conf $(DESTDIR)/etc/authentic2/config.d/ - - + install -d $(DESTDIR)/etc/amue-authentic2 + install -m 644 amue.conf $(DESTDIR)/etc/amue-authentic2/ + install -m 644 gunicorn-cfg.py $(DESTDIR)/etc/amue-authentic2/ + install -d $(DESTDIR)/usr/share/dbconfig-common/scripts/amue-authentic2/install + install -T -m 755 syncdb.sh $(DESTDIR)/usr/share/dbconfig-common/scripts/amue-authentic2/install/pgsql + install -d $(DESTDIR)/usr/share/amue-authentic2/templates + install db.conf $(DESTDIR)/usr/share/amue-authentic2/templates/ + install -d $(DESTDIR)/usr/share/pyshared/ + cp -R amue $(DESTDIR)/usr/share/pyshared/ + install -d $(DESTDIR)/usr/lib/amue-authentic2 + install -m 755 run.sh $(DESTDIR)/usr/lib/amue-authentic2 + install -m 755 manage.sh $(DESTDIR)/usr/lib/amue-authentic2 + install -m 755 reload.sh $(DESTDIR)/usr/lib/amue-authentic2 + # supervisor + install -d $(DESTDIR)/etc/supervisor/conf.d/ + install -T -m 644 supervisor.conf $(DESTDIR)/etc/supervisor/conf.d/amue-authentic2.conf + # stud + install -d $(DESTDIR)/etc/stud/ + install -T -m 644 stud-amue-idp-test.conf $(DESTDIR)/etc/stud/idp.conf + install -d $(DESTDIR)/etc/ssl/private/ + install -T -m 644 wildcard-amue.fr.cert $(DESTDIR)/etc/ssl/private/idp.pem + # haproxy + install -d $(DESTDIR)/etc/haproxy/ + install -T -m 644 haproxy.cfg $(DESTDIR)/etc/haproxy/haproxy-amue.cfg + # nginx + install -d $(DESTDIR)/etc/nginx/sites-available/ + install -T -m 644 idp.amue.fr.nginx $(DESTDIR)/etc/nginx/sites-available/idp.amue.fr diff --git a/amue.conf b/amue.conf index e69de29..b2dbad0 100644 --- a/amue.conf +++ b/amue.conf @@ -0,0 +1,15 @@ +# do not remove this line, it imports db configuration from dbconfig-common +. /etc/amue-authentic2/db.conf + +export A2_HOMEPAGE_URL=http://www.amue.fr/ +export CACHE_BACKEND='{"BACKEND": "django.core.cache.backends.memcached.MemcachedCache", "LOCATION": "127.0.0.1:11211"}' +export USE_X_FORWARDER_HOST=1 +export ALLOWED_HOSTS="idp-test.amue.fr:idp.amue.fr" +export LDAP_AUTH_SETTINGS='[{ + "url": "ldap://ldap.amue.fr", + "basedn": "OU=AMUE,DC=wan,DC=amue,DC=fr", + "binddn": "ldap.read@amue.fr", + "bindpw": "", + "user_filter": "sAMAccountName=%s", + "active_directory": true + }]' diff --git a/amue/__init__.py b/amue/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/amue/settings.py b/amue/settings.py new file mode 100644 index 0000000..3b1cabb --- /dev/null +++ b/amue/settings.py @@ -0,0 +1,401 @@ +# Django settings for authentic project. +import os +from django.core.exceptions import ImproperlyConfigured +import json + +gettext_noop = lambda s: s + +DEBUG = 'DEBUG' in os.environ +DEBUG_PROPAGATE_EXCEPTIONS = 'DEBUG_PROPAGATE_EXCEPTIONS' in os.environ +USE_DEBUG_TOOLBAR = 'USE_DEBUG_TOOLBAR' in os.environ +TEMPLATE_DEBUG = DEBUG + +PROJECT_PATH = os.path.join(os.path.dirname(__file__), '..') +PROJECT_NAME = 'amue-authentic2' + +ADMINS = () +if 'ADMINS' in os.environ: + ADMINS = filter(None, os.environ.get('ADMINS').split(':')) + ADMINS = [ admin.split(';') for admin in ADMINS ] + for admin in ADMINS: + assert len(admin) == 2, 'ADMINS setting must be a colon separated list of name and emails separated by a semi-colon' + assert '@' in admin[1], 'ADMINS setting pairs second value must be emails' + +MANAGERS = ADMINS + + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(PROJECT_PATH, PROJECT_NAME + '.db'), + } +} + +for key in os.environ: + if key.startswith('DATABASE_'): + prefix, db_key = key.split('_', 1) + DATABASES['default'][db_key] = os.environ[key] + +# Hey Entr'ouvert is in France !! +TIME_ZONE = 'Europe/Paris' +LANGUAGE_CODE = 'fr' +SITE_ID = 1 +USE_I18N = True + +LANGUAGES = ( + ('en', gettext_noop('English')), + ('fr', gettext_noop('French')), +) +USE_L10N = True + +# Static files + +STATIC_ROOT = os.environ.get('STATIC_ROOT', '/var/lib/%s/static' % PROJECT_NAME) +STATIC_URL = os.environ.get('STATIC_URL', '/static/') +if 'STATICFILES_DIRS' in os.environ: + STATICFILES_DIRS = os.environ['STATICFILES_DIRS'].split(':') +else: + STATICFILES_DIRS = ('/var/lib/%s/extra-static/' % PROJECT_NAME,) + +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +) + +TEMPLATE_CONTEXT_PROCESSORS = ( + 'django.contrib.auth.context_processors.auth', + 'django.core.context_processors.debug', + 'django.core.context_processors.i18n', + 'django.core.context_processors.media', + 'django.core.context_processors.request', + 'django.contrib.messages.context_processors.messages', + 'django.core.context_processors.static', + 'authentic2.context_processors.federations_processor', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.middleware.http.ConditionalGetMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.transaction.TransactionMiddleware', + 'authentic2.idp.middleware.DebugMiddleware' +) + +ROOT_URLCONF = 'authentic2.urls' + +if os.environ.get('TEMPLATE_DIRS'): + TEMPLATE_DIRS = os.environ['TEMPLATE_DIRS'].split(':') +else: + TEMPLATE_DIRS = ('/var/lib/%s/templates' % PROJECT_NAME,) + + +INSTALLED_APPS = ( + 'authentic2', + 'authentic2.nonce', + 'authentic2.saml', + 'authentic2.idp', + 'authentic2.idp.saml', + 'authentic2.auth2_auth', + 'authentic2.attribute_aggregator', + 'authentic2.disco_service', + 'admin_tools', + 'admin_tools.theming', + 'admin_tools.menu', + 'admin_tools.dashboard', + 'django.contrib.staticfiles', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.admin', + 'django.contrib.sites', + 'registration', + 'south', +) + +MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage' + + + +# Registration settings +ACCOUNT_ACTIVATION_DAYS = int(os.environ.get('ACCOUNT_ACTIVATION_DAYS', 3)) +PASSWORD_RESET_TIMEOUT_DAYS = int(os.environ.get('PASSWORD_RESET_TIMEOUT_DAYS', 3)) +if 'AUTHENTIC2_ALLOW_ACCOUNT_DELETION' in os.environ: + AUTHENTIC2_ALLOW_ACCOUNT_DELETION = True + +# authentication +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', +) + +# sessions +SESSION_EXPIRE_AT_BROWSER_CLOSE = 'SESSION_EXPIRE_AT_BROWSER_CLOSE' in os.environ +SESSION_COOKIE_AGE = int(os.environ.get('SESSION_COOKIE_AGE', 36000)) # one day of work +SESSION_COOKIE_NAME = os.environ.get('SESSION_COOKIE_NAME', 'sessionid') +SESSION_COOKIE_PATH = os.environ.get('SESSION_COOKIE_PATH', '/') +SESSION_COOKIE_SECURE = 'SESSION_COOKIE_SECURE' in os.environ + +# email settings +EMAIL_HOST = os.environ.get('EMAIL_HOST', 'localhost') +EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER', '') +EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD', '') +EMAIL_PORT = int(os.environ.get('EMAIL_PORT', 25)) +EMAIL_SUBJECT_PREFIX = os.environ.get('EMAIL_SUBJECT_PREFIX', '[Authentic]') +EMAIL_USE_TLS = 'EMAIL_USE_TLS' in os.environ +SERVER_EMAIL = os.environ.get('SERVER_EMAIL', 'root@localhost') +DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'webmaster@localhost') + +# web & network settings +if 'ALLOWED_HOSTS' in os.environ: + ALLOWED_HOSTS = os.environ['ALLOWED_HOSTS'].split(':') +USE_X_FORWARDED_HOST = 'USE_X_FORWARDED_HOST' in os.environ +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') + +LOGIN_REDIRECT_URL = os.environ.get('LOGIN_REDIRECT_URL', '/') +LOGIN_URL = os.environ.get('LOGIN_URL', '/login') +LOGOUT_URL = os.environ.get('LOGOUT_URL', '/accounts/logout') + +if 'INTERNAL_IPS' in os.environ: + INTERNAL_IPS = os.environ['INTERNAL_IPS'].split(':') +else: + INTERNAL_IPS = ('127.0.0.1',) + +# misc +SECRET_KEY = os.environ.get('SECRET_KEY', '0!=(1kc6kri-ui+tmj@mr+*0bvj!(p*r0duu2n=)7@!p=pvf9n') +DEBUG_TOOLBAR_CONFIG = {'INTERCEPT_REDIRECTS': False} + +# Authentic2 settings + +DISCO_SERVICE = 'DISCO_SERVICE' in os.environ +DISCO_USE_OF_METADATA = 'DISCO_USE_OF_METADATA' in os.environ + +DISCO_SERVICE_NAME = os.environ.get('DISCO_SERVICE_NAME', "http://www.identity-hub.com/disco_service/disco") +DISCO_RETURN_ID_PARAM = "entityID" +SHOW_DISCO_IN_MD = 'SHOW_DISCO_IN_MD' in os.environ +USE_DISCO_SERVICE = 'USE_DISCO_SERVICE' in os.environ + +########################### +# Authentication settings +########################### + +# Only RSA private keys are currently supported +AUTH_FRONTENDS = ( 'authentic2.auth2_auth.backend.LoginPasswordBackend',) +SSLAUTH_CREATE_USER = 'SSLAUTH_CREATE_USER' in os.environ +AUTHENTICATION_EVENT_EXPIRATION = int(os.environ.get('AUTHENTICATION_EVENT_EXPIRATION', 3600*24*7)) + +############################# +# Identity Provider settings +############################# + +# List of IdP backends, mainly used to show available services in the homepage +# of user, and to handle SLO for each protocols +IDP_BACKENDS = [ ] + +# You MUST changes these keys, they are just for testing ! +LOCAL_METADATA_CACHE_TIMEOUT = int(os.environ.get('LOCAL_METADATA_CACHE_TIMEOUT', 600)) +SAML_SIGNATURE_PUBLIC_KEY = os.environ.get('SAML_SIGNATURE_PUBLIC_KEY', '''-----BEGIN CERTIFICATE----- +MIIDIzCCAgugAwIBAgIJANUBoick1pDpMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV +BAoTCkVudHJvdXZlcnQwHhcNMTAxMjE0MTUzMzAyWhcNMTEwMTEzMTUzMzAyWjAV +MRMwEQYDVQQKEwpFbnRyb3V2ZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZn9Kqm4Cp +06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrUH8QT8NGh +ABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59xihSqsoFr +kmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9Hri8JRdXi +VT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziazZfvvw/VG +Tm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABo3YwdDAdBgNVHQ4EFgQUeF8ePnu0 +fcAK50iBQDgAhHkOu8kwRQYDVR0jBD4wPIAUeF8ePnu0fcAK50iBQDgAhHkOu8mh +GaQXMBUxEzARBgNVBAoTCkVudHJvdXZlcnSCCQDVAaInJNaQ6TAMBgNVHRMEBTAD +AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAy8l3GhUtpPHx0FxzbRHVaaUSgMwYKGPhE +IdGhqekKUJIx8et4xpEMFBl5XQjBNq/mp5vO3SPb2h2PVSks7xWnG3cvEkqJSOeo +fEEhkqnM45b2MH1S5uxp4i8UilPG6kmQiXU2rEUBdRk9xnRWos7epVivTSIv1Ncp +lG6l41SXp6YgIb2ToT+rOKdIGIQuGDlzeR88fDxWEU0vEujZv/v1PE1YOV0xKjTT +JumlBc6IViKhJeo1wiBBrVRIIkKKevHKQzteK8pWm9CYWculxT26TZ4VWzGbo06j +o2zbumirrLLqnt1gmBDvDvlOwC/zAAyL4chbz66eQHTiIYZZvYgy +-----END CERTIFICATE-----''') + +SAML_SIGNATURE_PRIVATE_KEY = os.environ.get('SAML_SIGNATURE_PRIVATE_KEY', '''-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZ +n9Kqm4Cp06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrU +H8QT8NGhABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59x +ihSqsoFrkmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9H +ri8JRdXiVT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziaz +Zfvvw/VGTm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABAoIBAQCj8t2iKXya10HG +V6Saaeih8aftoLBV38VwFqqjPU0+iKqDpk2JSXBhjI6s7uFIsaTNJpR2Ga1qvns1 +hJQEDMQSLhJvXfBgSkHylRWCpJentr4E3D7mnw5pRsd61Ev9U+uHcdv/WHP4K5hM +xsdiwXNXD/RYd1Q1+6bKrCuvnNJVmWe0/RV+r3T8Ni5xdMVFbRWt/VEoE620XX6c +a9TQPiA5i/LRVyie+js7Yv+hVjGOlArtuLs6ECQsivfPrqKLOBRWcofKdcf+4N2e +3cieUqwzC15C31vcMliD9Hax9c1iuTt9Q3Xzo20fOSazAnQ5YBEExyTtrFBwbfQu +ku6hp81pAoGBAN6bc6iJtk5ipYpsaY4ZlbqdjjG9KEXB6G1MExPU7SHXOhOF0cDH +/pgMsv9hF2my863MowsOj3OryVhdQhwA6RrV263LRh+JU8NyHV71BwAIfI0BuVfj +6r24KudwtUcvMr9pJIrJyMAMaw5ZyNoX7YqFpS6fcisSJYdSBSoxzrzVAoGBANu6 +xVeMqGavA/EHSOQP3ipDZ3mnWbkDUDxpNhgJG8Q6lZiwKwLoSceJ8z0PNY3VetGA +RbqtqBGfR2mcxHyzeqVBpLnXZC4vs/Vy7lrzTiHDRZk2SG5EkHMSKFA53jN6S/nJ +JWpYZC8lG8w4OHaUfDHFWbptxdGYCgY4//sjeiuXAoGBANuhurJ99R5PnA8AOgEW +4zD1hLc0b4ir8fvshCIcAj9SUB20+afgayRv2ye3Dted1WkUL4WYPxccVhLWKITi +rRtqB03o8m3pG3kJnUr0LIzu0px5J/o8iH3ZOJOTE3iBa+uI/KHmxygc2H+XPGFa +HGeAxuJCNO2kAN0Losbnz5dlAoGAVsCn94gGWPxSjxA0PC7zpTYVnZdwOjbPr/pO +LDE0cEY9GBq98JjrwEd77KibmVMm+Z4uaaT0jXiYhl8pyJ5IFwUS13juCbo1z/u/ +ldMoDvZ8/R/MexTA/1204u/mBecMJiO/jPw3GdIJ5phv2omHe1MSuSNsDfN8Sbap +gmsgaiMCgYB/nrTk89Fp7050VKCNnIt1mHAcO9cBwDV8qrJ5O3rIVmrg1T6vn0aY +wRiVcNacaP+BivkrMjr4BlsUM6yH4MOBsNhLURiiCL+tLJV7U0DWlCse/doWij4U +TKX6tp6oI+7MIJE6ySZ0cBqOiydAkBePZhu57j6ToBkTa0dbHjn1WA== +-----END RSA PRIVATE KEY-----''') + +# Whether to autoload SAML 2.0 identity providers and services metadata +# Only https URLS are accepted. +# Can be none, sp, idp or both +SAML_METADATA_AUTOLOAD = os.environ.get('SAML_METADATA_AUTOLOAD', 'none') + +PUSH_PROFILE_UPDATES = 'PUSH_PROFILE_UPDATES' in os.environ + +################################## +# LDAP Configuration +################################## +if 'LDAP_AUTH_SETTINGS' in os.environ: + try: + LDAP_AUTH_SETTINGS = json.loads(os.environ['LDAP_AUTH_SETTINGS']) + except Exception, e: + raise ImproperlyConfigured('LDAP_AUTH_SETTINGS is not a JSON document', e) +else: + LDAP_AUTH_SETTINGS = [] +################################## +# Cache configuration +################################## +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + }, +} +if 'CACHE_BACKEND' in os.environ: + CACHES['default'] = json.loads(os.environ['CACHE_BACKEND']) + + +# Logging settings + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'filters': { + 'cleaning': { + '()': 'authentic2.utils.CleanLogMessage', + }, + }, + 'formatters': { + 'verbose': { + 'format': '[%(asctime)s] %(levelname)s %(name)s: %(message)s', + 'datefmt': '%Y-%m-%d %a %H:%M:%S' + }, + }, + 'handlers': { + 'null': { + 'level':'DEBUG', + 'class':'django.utils.log.NullHandler', + }, + 'console': { + 'level':'DEBUG', + 'class':'logging.StreamHandler', + 'formatter': 'verbose', + 'filters': ['cleaning'], + }, + 'syslog': { + 'address': '/dev/log', + 'level':'INFO', + 'class':'logging.handlers.SysLogHandler', + 'filters': ['cleaning'], + }, + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler', + 'include_html': True, + 'filters': ['cleaning'], + } + }, + 'loggers': { + # disable default handlers + 'django.request': { + 'handlers': [], + 'propagate': True, + }, + '': { + 'handlers': ['mail_admins', 'syslog'] + (['console'] if DEBUG else []), + 'level': 'DEBUG' if DEBUG else 'INFO', + } + }, +} + +# add sentry handler if environment contains SENTRY_DSN +if 'SENTRY_DSN' in os.environ: + try: + import raven + except ImportError: + raise ImproperlyConfigured('SENTRY_DSN environment variable is set but raven is not installed.') + SENTRY_DSN = os.environ['SENTRY_DSN'] + LOGGING['handlers']['sentry'] = { + 'level': 'ERROR', + 'class': 'raven.handlers.logging.SentryHandler', + 'dsn': SENTRY_DSN, + } + LOGGING['loggers']['']['handlers'].append('sentry') + +SOUTH_TESTS_MIGRATE = False + +# Admin tools +ADMIN_TOOLS_INDEX_DASHBOARD = 'authentic2.dashboard.CustomIndexDashboard' +ADMIN_TOOLS_APP_INDEX_DASHBOARD = 'authentic2.dashboard.CustomAppIndexDashboard' +ADMIN_TOOLS_MENU = 'authentic2.menu.CustomMenu' + +AUTH_SAML2 = 'AUTH_SAML2' in os.environ +AUTH_OPENID = 'AUTH_OPENID' in os.environ +AUTH_SSL = 'AUTH_SSL' in os.environ +IDP_SAML2 = 'IDP_SAML2' in os.environ +IDP_OPENID = 'IDP_OPENID' in os.environ +IDP_CAS = 'IDP_CAS' in os.environ + +try: + from local_settings import * +except ImportError, e: + if 'local_settings' in e.args[0]: + pass + +if USE_DEBUG_TOOLBAR: + try: + import debug_toolbar + MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',) + INSTALLED_APPS += ('debug_toolbar',) + except ImportError: + print "Debug toolbar missing, not loaded" + +if AUTH_SAML2: + INSTALLED_APPS += ('authentic2.authsaml2',) + AUTHENTICATION_BACKENDS += ( + 'authentic2.authsaml2.backends.AuthSAML2PersistentBackend', + 'authentic2.authsaml2.backends.AuthSAML2TransientBackend') + AUTH_FRONTENDS += ('authentic2.authsaml2.frontend.AuthSAML2Frontend',) + IDP_BACKENDS += ('authentic2.authsaml2.backends.AuthSAML2Backend',) + DISPLAY_MESSAGE_ERROR_PAGE = True + +if AUTH_OPENID: + INSTALLED_APPS += ('authentic2.auth2_auth.auth2_openid', 'django_authopenid',) + AUTH_FRONTENDS += ('authentic2.auth2_auth.auth2_openid.backend.OpenIDFrontend',) + +if AUTH_SSL: + AUTHENTICATION_BACKENDS += ('authentic2.auth2_auth.auth2_ssl.backend.SSLBackend',) + AUTH_FRONTENDS += ('authentic2.auth2_auth.auth2_ssl.frontend.SSLFrontend',) + INSTALLED_APPS += ('authentic2.auth2_auth.auth2_ssl',) + +if IDP_SAML2: + IDP_BACKENDS += ('authentic2.idp.saml.backend.SamlBackend',) + +if IDP_OPENID: + INSTALLED_APPS += ('authentic2.idp.idp_openid',) + TEMPLATE_CONTEXT_PROCESSORS += ('authentic2.idp.idp_openid.context_processors.openid_meta',) + +if IDP_CAS: + INSTALLED_APPS += ('authentic2.idp.idp_cas',) + +if LDAP_AUTH_SETTINGS: + AUTHENTICATION_BACKENDS += ('authentic2.backends.LDAPBackend',) diff --git a/amue/wsgi.py b/amue/wsgi.py new file mode 100644 index 0000000..33cc5f3 --- /dev/null +++ b/amue/wsgi.py @@ -0,0 +1,24 @@ +""" +WSGI config for a authentic2 project. + +This module contains the WSGI application used by Django's development server +and any production WSGI deployments. It should expose a module-level variable +named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover +this application via the ``WSGI_APPLICATION`` setting. + +Usually you will have the standard Django WSGI application here, but it also +might make sense to replace the whole Django WSGI application with a custom one +that later delegates to the Django one. For example, you could introduce WSGI +middleware here, or combine a Django application with an application of another +framework. + +""" +import os + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "amue.settings") + +# This application object is used by any WSGI server configured to use this +# file. This includes Django's development server, if the WSGI_APPLICATION +# setting points here. +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() diff --git a/db.conf b/db.conf new file mode 100644 index 0000000..27708ab --- /dev/null +++ b/db.conf @@ -0,0 +1,7 @@ +#!/bin/sh + +export DATABASE_ENGINE='django.db.backends.postgresql_psycopg2' +export DATABASE_NAME='_DBC_DBNAME_' +export DATABASE_USER='_DBC_DBUSER_' +export DATABASE_PASSWORD='_DBC_DBPASS_' +export DATABASE_HOST='localhost' diff --git a/debian/amue-authentic2.conffile b/debian/amue-authentic2.conffile new file mode 100644 index 0000000..1866a4c --- /dev/null +++ b/debian/amue-authentic2.conffile @@ -0,0 +1 @@ +/etc/amue-authentic2/amue.conf diff --git a/debian/amue-authentic2.config b/debian/amue-authentic2.config new file mode 100644 index 0000000..f4a60e3 --- /dev/null +++ b/debian/amue-authentic2.config @@ -0,0 +1,13 @@ +#!/bin/sh +# config maintainer script for foo-pgsql + +# source debconf stuff +. /usr/share/debconf/confmodule +# source dbconfig-common shell library, and call the hook function +if [ -f /usr/share/dbconfig-common/dpkg/config.pgsql ]; then + . /usr/share/dbconfig-common/dpkg/config.pgsql + dbc_pgsql_createdb_encoding="UTF8" + dbc_go amue-authentic2 $@ +fi + +#DEBHELPER# diff --git a/debian/amue-authentic2.dirs b/debian/amue-authentic2.dirs new file mode 100644 index 0000000..25e2868 --- /dev/null +++ b/debian/amue-authentic2.dirs @@ -0,0 +1,2 @@ +/var/lib/amue-authentic2/extra-static +/var/lib/amue-authentic2/templates diff --git a/debian/amue-authentic2.install b/debian/amue-authentic2.install new file mode 100644 index 0000000..7aaa3ba --- /dev/null +++ b/debian/amue-authentic2.install @@ -0,0 +1,6 @@ +/etc/amue-authentic2/* +/usr/share/dbconfig-common/scripts/amue-authentic2/install/* +/usr/share/amue-authentic2/templates/* +/usr/share/pyshared/amue/* +/usr/lib/amue-authentic2/* +/etc/supervisor/conf.d/* diff --git a/debian/amue-authentic2.postinst b/debian/amue-authentic2.postinst new file mode 100644 index 0000000..751c7e4 --- /dev/null +++ b/debian/amue-authentic2.postinst @@ -0,0 +1,54 @@ +#!/bin/sh +# postinst maintainer script for foo-pgsql + +USER=amue-authentic2 +GROUP=amue-authentic2 + +case "$1" in + configure) + if ! getent group $GROUP > /dev/null 2>&1; then + echo -n "Adding group $GROUP.." + addgroup --quiet --system $GROUP + echo "..done" + fi + if ! getent passwd $USER >/dev/null; then + echo Adding user $USER... + adduser --quiet --system --gecos "AMUE authentic2 system user" \ + --ingroup $GROUP \ + --no-create-home \ + --home /var/lib/amue-authentic2 --shell /usr/sbin/nologin $USER + fi + mkdir -p /var/log/amue-authentic2 + chown amue-authentic2 /var/log/amue-authentic2 + ;; +esac + +# source debconf stuff +. /usr/share/debconf/confmodule +# source dbconfig-common shell library, and call the hook function +if [ -f /usr/share/dbconfig-common/dpkg/postinst.pgsql ]; then + . /usr/share/dbconfig-common/dpkg/postinst.pgsql + dbc_generate_include=template:/etc/amue-authentic2/db.conf + dbc_generate_include_args="-o template_infile=/usr/share/amue-authentic2/templates/db.conf -U" + dbc_generate_include_owner="amue-authentic2" + dbc_generate_include_perms="640" + dbc_go amue-authentic2 $@ +fi + +case "$1" in + configure) + echo Updating static files... + /usr/lib/amue-authentic2/manage.sh collectstatic --noinput + if [ -e /etc/nginx/sites-enabled/idp.amue.fr ]; then + echo Installed nginx virtualhost... + ln -s /etc/nginx/sites-availables/idp.amue.fr /etc/nginx/sites-enabled/ + invoke-rc.d nginx reload + fi + echo Restarting amue-authentic... + /usr/bin/supervisorctl update + /usr/bin/supervisorctl restart amue-authentic2 + ;; +esac + + +#DEBHELPER# diff --git a/debian/amue-authentic2.postrm b/debian/amue-authentic2.postrm new file mode 100755 index 0000000..df263ac --- /dev/null +++ b/debian/amue-authentic2.postrm @@ -0,0 +1,22 @@ +#!/bin/sh +# config maintainer script for foo-pgsql + +# source debconf stuff +. /usr/share/debconf/confmodule +# source dbconfig-common shell library, and call the hook function +if [ -f /usr/share/dbconfig-common/dpkg/postrm.pgsql ]; then + . /usr/share/dbconfig-common/dpkg/postrm.pgsql + dbc_go amue-authentic2 $@ +fi + +DBCONF=/etc/amue-authentic2/db.conf +if [ "$1" = "purge" ]; then + rm -f $DBCONF + if which ucf >/dev/null 2>&1; then + ucf --purge $DBCONF + fi +fi + +#DEBHELPER# + + diff --git a/debian/amue-haproxy.install b/debian/amue-haproxy.install new file mode 100644 index 0000000..0c70b2f --- /dev/null +++ b/debian/amue-haproxy.install @@ -0,0 +1,3 @@ +/etc/stud/idp.conf +/etc/ssl/private/idp.pem +/etc/haproxy/haproxy-amue.cfg diff --git a/debian/amue-haproxy.postinst b/debian/amue-haproxy.postinst new file mode 100644 index 0000000..421000e --- /dev/null +++ b/debian/amue-haproxy.postinst @@ -0,0 +1,22 @@ +#!/bin/sh +# postinst maintainer script for foo-pgsql + +case "$1" in + configure) + if ! readlink -q /etc/haproxy/haproxy.cfg; then + echo Using amue-haproxy configuration for haproxy \(symlink to /usr/share/amue-haproxy/\)... + ln -b -s /etc/haproxy/haproxy-amue.cfg /etc/haproxy/haproxy.cfg + fi + if grep -q ENABLED=0 /etc/default/haproxy; then + echo Activating HAProxy... + sed -i 's/ENABLED=0/ENABLED=1/' /etc/default/haproxy + fi + echo Restarting stud and haproxy... + invoke-rc.d stud stop + sleep 1 + invoke-rc.d stud start + invoke-rc.d haproxy restart + ;; +esac + +#DEBHELPER# diff --git a/debian/control b/debian/control index 2d8eb94..100f518 100644 --- a/debian/control +++ b/debian/control @@ -8,6 +8,13 @@ Homepage: https://dev.entrouvert.org/projects/amue/ Package: amue-authentic2 Architecture: all -Depends: authentic2 +Depends: ${misc:Depends}, python-authentic2, + dbconfig-common, nginx, gunicorn, supervisor +Recommends: postgresql-client Description: AMUE settings for the Authentic2 identity server Gather all needed settings for using Authentic2 at AMUE + +Package: amue-haproxy +Architecture: all +Depends: ${misc:Depends}, stud, haproxy +Description: AMUE settings for a proxy server diff --git a/debian/rules b/debian/rules index 7df73a8..b2eebb9 100755 --- a/debian/rules +++ b/debian/rules @@ -1,3 +1,3 @@ #!/usr/bin/make -f %: - dh $@ + dh --with python2 $@ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/gunicorn-cfg.py b/gunicorn-cfg.py new file mode 100644 index 0000000..c00c818 --- /dev/null +++ b/gunicorn-cfg.py @@ -0,0 +1,11 @@ +import multiprocessing + + +debug = True +bind = 'unix:/run/amue-authentic2.sock' +workers = multiprocessing.cpu_count() * 2 + 1 +worker_class = 'gevent' +timeout = 10 +user = 'amue-authentic2' +group = 'amue-authentic2' +proc_name = 'amue-authentic2' diff --git a/haproxy.cfg b/haproxy.cfg new file mode 100644 index 0000000..b2e9a7e --- /dev/null +++ b/haproxy.cfg @@ -0,0 +1,49 @@ +global + log /dev/log local0 + log /dev/log local1 notice + chroot /var/lib/haproxy + user haproxy + group haproxy + daemon + +defaults + log global + mode http + option httplog + contimeout 5000 + clitimeout 50000 + srvtimeout 50000 + errorfile 400 /etc/haproxy/errors/400.http + errorfile 403 /etc/haproxy/errors/403.http + errorfile 408 /etc/haproxy/errors/408.http + errorfile 500 /etc/haproxy/errors/500.http + errorfile 502 /etc/haproxy/errors/502.http + errorfile 503 /etc/haproxy/errors/503.http + errorfile 504 /etc/haproxy/errors/504.http + +frontend unsecured + bind *:80 + acl idp_test hdr(host) -i idp-test.amue.fr + acl idp hdr(host) -i idp.amue.fr + redirect location https://idp-test.amue.fr if idp_test + redirect location https://idp-test.amue.fr if idp + +frontend http_frontend + bind *:8100 + mode http + option httpclose + option forwardfor + reqadd X-Forwarded-Proto:\ https + acl idp_test hdr(host) -i idp-test.amue.fr + acl idp hdr(host) -i idp.amue.fr + use_backend idp_test_backend if idp_test + use_backend idp_backend if idp + +backend idp_test_backend + mode http + server s1 idp-test-backend.amue.fr:8000 check + +backend idp_backend + mode http + server s1 idp-paris-backend.amue.fr:8000 check + server s2 idp-montpellier-backend.amue.fr:8000 check diff --git a/idp.amue.fr.nginx b/idp.amue.fr.nginx new file mode 100644 index 0000000..8bea636 --- /dev/null +++ b/idp.amue.fr.nginx @@ -0,0 +1,28 @@ +server { + listen 8000; + server_name idp-test.amue.fr; + server_name idp.amue.fr; + + root html; + index index.html index.htm; + + location / { + proxy_pass http://unix:/run/amue-authentic2.sock:/; + + client_max_body_size 5k; + client_body_buffer_size 1m; + proxy_intercept_errors on; + proxy_buffering on; + proxy_buffer_size 128k; + proxy_buffers 256 16k; + proxy_busy_buffers_size 256k; + proxy_temp_file_write_size 256k; + proxy_max_temp_file_size 0; + proxy_read_timeout 300; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-Protocol https; + } + location /static { + alias /var/lib/amue-authentic2/static/; + } +} diff --git a/ldap.sh b/ldap.sh new file mode 100755 index 0000000..17411cb --- /dev/null +++ b/ldap.sh @@ -0,0 +1,6 @@ +URL='ldap://194.167.237.11' +LOGIN='ldap.read@amue.fr' +PASS='1DapR&@d#103' +BASEDN='DC=wan,DC=amue,DC=fr' + +ldapsearch -z 3 -H $URL -b "$BASEDN" -D $LOGIN -w "$PASS" "$@" diff --git a/manage.sh b/manage.sh new file mode 100755 index 0000000..1c1ca2f --- /dev/null +++ b/manage.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +. /etc/amue-authentic2/amue.conf + +export DJANGO_SETTINGS_MODULE=amue.settings + +django-admin $@ diff --git a/reload.sh b/reload.sh new file mode 100755 index 0000000..5e46a74 --- /dev/null +++ b/reload.sh @@ -0,0 +1,6 @@ +#!/bin/sh +PID="`sudo supervisorctl status | grep RUNNING | awk '{ print $4 }' | sed 's/,//'`" + +if [ "x$PID" != "x" ]; then + kill -HUP $PID +fi diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..0b7aa24 --- /dev/null +++ b/run.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +. /etc/amue-authentic2/amue.conf + +exec /usr/bin/gunicorn -c /etc/amue-authentic2/gunicorn-cfg.py amue.wsgi:application diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..d650bf7 --- /dev/null +++ b/setup.py @@ -0,0 +1,129 @@ +#! /usr/bin/env python + +import glob +import re +import sys +import os + +from setuptools import setup, find_packages +from setuptools.command.install_lib import install_lib as _install_lib +from distutils.command.build import build as _build +from distutils.command.sdist import sdist +from distutils.cmd import Command + +class compile_translations(Command): + description = 'compile message catalogs to MO files via django compilemessages' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + try: + from django.core.management.commands.compilemessages import \ + compile_messages + for path in []: + if not os.path.exists(os.path.join(path, 'locale')): + continue + curdir = os.getcwd() + os.chdir(os.path.realpath(path)) + compile_messages(stderr=sys.stderr) + os.chdir(curdir) + except ImportError: + print + sys.stderr.write('!!! Please install Django >= 1.4 to build translations') + print + print + +class build(_build): + sub_commands = [('compile_translations', None)] + _build.sub_commands + +class eo_sdist(sdist): + + def run(self): + print "creating VERSION file" + if os.path.exists('VERSION'): + os.remove('VERSION') + version = get_version() + version_file = open('VERSION', 'w') + version_file.write(version) + version_file.close() + sdist.run(self) + print "removing VERSION file" + if os.path.exists('VERSION'): + os.remove('VERSION') + +class install_lib(_install_lib): + def run(self): + self.run_command('compile_translations') + _install_lib.run(self) + +def get_version(): + + version = None + if os.path.exists('VERSION'): + version_file = open('VERSION', 'r') + version = version_file.read() + version_file.close() + return version + for d in glob.glob('*'): + if not os.path.isdir(d): + continue + module_file = os.path.join(d, '__init__.py') + if not os.path.exists(module_file): + continue + for v in re.findall("""__version__ *= *['"](.*)['"]""", + open(module_file).read()): + assert version is None + version = v + if version: + break + assert version is not None + if os.path.exists('.git'): + import subprocess + p = subprocess.Popen(['git','describe','--dirty','--match=v*'], + stdout=subprocess.PIPE) + result = p.communicate()[0] + assert p.returncode == 0, 'git returned non-zero' + new_version = result.split()[0][1:] + assert not new_version.endswith('-dirty'), 'git workdir is not clean' + assert new_version.split('-')[0] == version, '__version__ must match the last git annotated tag' + version = new_version.replace('-', '.') + return version + + +setup(name="amue-authentic2", + version=get_version(), + license="AGPLv3 or later", + description="AMUE Authentic2", + author="Entr'ouvert", + author_email="info@entrouvert.org", + maintainer="Benjamin Dauvergne", + maintainer_email="info@entrouvert.com", + include_package_data=True, + url='http://dev.entrouvert.org/projects/amue', + package_data={ + '': [ + 'templates/**.html', + 'templates/**.txt', + 'static/**.png', + 'static/**.gif', + 'static/**.css', + 'static/**.js', + 'locale/**.mo', + 'fixtures/*.json', + ] + }, + packages=find_packages(), + scripts=(), + install_requires=[ + ], + dependency_links = [ + ], + cmdclass={'build': build, 'install_lib': install_lib, + 'compile_translations': compile_translations, + 'sdist': eo_sdist}, +) diff --git a/stud-amue-idp-test.conf b/stud-amue-idp-test.conf new file mode 100644 index 0000000..8768c27 --- /dev/null +++ b/stud-amue-idp-test.conf @@ -0,0 +1,2 @@ +CERT=/etc/ssl/private/idp.pem +OPTIONS="-f *,443 -b 127.0.0.1,8100" diff --git a/supervisor.conf b/supervisor.conf new file mode 100644 index 0000000..bde5d83 --- /dev/null +++ b/supervisor.conf @@ -0,0 +1,7 @@ +[program:amue-authentic2] +command=/usr/lib/amue-authentic2/run.sh +autostart=true +autorestart=true +stdout_logfile=syslog +stderr_logfile=syslog + diff --git a/syncdb.sh b/syncdb.sh new file mode 100755 index 0000000..bd46748 --- /dev/null +++ b/syncdb.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +. /etc/amue-authentic2/amue.conf + +python /usr/lib/authentic2/manage.py syncdb --noinput +python /usr/lib/authentic2/manage.py migrate diff --git a/wildcard-amue.fr.cert b/wildcard-amue.fr.cert new file mode 100644 index 0000000..54a6153 --- /dev/null +++ b/wildcard-amue.fr.cert @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIICITCCAYoCCQCFgT08G4PMkTANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJG +UjETMBEGA1UECAwKU29tZS1TdGF0ZTEOMAwGA1UEBwwFUGFyaXMxDTALBgNVBAoM +BEFNVUUxEjAQBgNVBAMMCSouYW11ZS5mcjAeFw0xMzExMTIxMjIzNTlaFw0xMzEx +MTMxMjIzNTlaMFUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMQ4w +DAYDVQQHDAVQYXJpczENMAsGA1UECgwEQU1VRTESMBAGA1UEAwwJKi5hbXVlLmZy +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDS27JrsBeqJJ892r427vh1jcrz +1JO37rMxgQQAp3xwK9YS29uYMjYY3ABX3HnjwhAuloR1sngV+wgpvY+/rqxhLlTG +b3hTfrFaXpzvSQSmTTzo2DDPz/Gwt6I2+ahpl5HSmec/2u1pKnYRILrvOUgCO+O5 +LCuwuMC4vnX7JSxs8wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIt+GUR9VWYzf9kg +NlNa4CbVV6RSe1YUxE1G/S6i9z5JBrPLBemPxUidNjb28jVDFSyxHfkuseSECUet +qUVSe51rAIqwDmLyIxhEZTqOCoM9BZJQHsOEhQ/s5K/6cAJMNesBYWrk7pxcTkze +aTDb5E23mPDHjToJskTuzMRUWXa7 +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDS27JrsBeqJJ892r427vh1jcrz1JO37rMxgQQAp3xwK9YS29uY +MjYY3ABX3HnjwhAuloR1sngV+wgpvY+/rqxhLlTGb3hTfrFaXpzvSQSmTTzo2DDP +z/Gwt6I2+ahpl5HSmec/2u1pKnYRILrvOUgCO+O5LCuwuMC4vnX7JSxs8wIDAQAB +AoGBALvdDewwKgVnN5GOkKa05x0lRctUfIAF5hWXEw/aKV5vT/3hcJb7NYOUj6G4 +R8kKoAxCAqYyahd7X1yBDdAEOoOEDlqtZ11wNglKl3nHgN27UmEvaXtfxEhv4X8n +52e3+hLnQLulYJxg/ANNi7MKqiqYC5Og3NvR2sM/YwBSq++BAkEA+WpUHDO2/x1y +vI8xCZUvrzsVHV+sKm+2CeEFlj8/u9nkAeweyeqFpL7F2dXxHR3yJTRS5uAyV8vX +vgraadE6oQJBANhsx7uUUr0Qzqp+l/6oo1zY1N7e4FDHK+nxWrwqEy0yjHDN6gi5 +S3VnlgOMDMUB9Vomc9nyf3aB3WNoCKeXMxMCQBCEThWgBxpV5Oc/xEuSKZo9G2Ta +lRgqVa/JywjsH1hdUZAfBtrwQPFsAMYwOMto1ERKdsL7TdoqkZrwNQ6U4IECQQCw +/DRSJ7eJuaboMmJl9M6zbPaX07epF1fIFoHXAqlv+rhyv1G2FKGqvy0kdXEz4qgc +Mvnmr7Kg3Q6I7li1hKZVAkEArClTNqDZv3Net23nKlIUnFDw0HBB0a7GwtccxZfh +cTqt22hnt7QmUYV1q6RtjKS+J3b0nHufghesySiQh/8rCQ== +-----END RSA PRIVATE KEY-----