Révision 19b45165
Ajouté par Jérôme Schneider il y a presque 10 ans
README | ||
---|---|---|
1 |
wcsinst |
|
2 |
======= |
|
3 |
|
|
4 |
wcsinst is a web application to deploy w.c.s. instances; it is released under |
|
5 |
the GNU AGPL Licence. |
|
6 |
|
|
7 |
|
|
8 |
Architecture |
|
9 |
------------ |
|
10 |
|
|
11 |
This project is composed of two Django applications; wcsinst provides a model |
|
12 |
and is expected to be installed as part of an administrative portal; wcsinstd |
|
13 |
provides the web API to deploy w.c.s. instances and should be installed on its |
|
14 |
own, on the host instances should be deployed. |
|
15 |
|
|
16 |
|
|
17 |
Configuration |
|
18 |
------------- |
|
19 |
|
|
20 |
If the WCSINSTD_URL environment variable is set, ./manage.py will run itself as |
|
21 |
a standalone wcsinst (admin) site. Go to /admin/. |
|
22 |
|
|
23 |
Example: WCSINSTD_URL=http://127.0.0.1:8001/ ./manage.py runserver |
|
24 |
|
|
25 |
The wcsinstd application expects a WCSINST_WCS_APP_DIR setting, that should |
|
26 |
point to the w.c.s. application directory (typically /var/lib/wcs/). |
|
27 |
|
|
28 |
Additionally it expects site skeletons to be made available from the |
|
29 |
media/skeletons directory; the default skeleton should go in a subdirectory |
|
30 |
named "default". |
|
31 |
|
|
32 |
A skeleton directory has the following files: |
|
33 |
|
|
34 |
- export.wcs: as produced from wcs, admin/export |
|
35 |
- idff-metadata-template: template file for ID-FF 1.2 metadata (optional) |
|
36 |
- saml2-metadata-template: template file for SAMLv2 metadata (optional) |
|
37 |
- private-key.pem & public-key.pem: pair of keys for ID-FF/SAML (optional) |
|
38 |
- idp-metadata.xml: metadata of the identity provider (optional) |
|
39 |
- site-options.cfg: template file for the w.c.s. site-options.cfg file |
|
40 |
- apache-vhost.conf: tempalte file for the Apache virtual host configuration |
|
41 |
|
|
42 |
The template files contains template strings (cf the Python documentation); |
|
43 |
there is a single variable made available, named domain (hence ${domain} to |
|
44 |
use it). |
|
45 |
|
|
46 |
The wcsinstd application should be able to run "sudo -n /etc/init.d/apache2 |
|
47 |
reload", to reload virtual hosts configuration. Virtual host configuration |
|
48 |
files are created in $media/vhosts.d |
debian/debian_settings.py | ||
---|---|---|
1 |
# Django settings for wcsinst project. |
|
2 |
|
|
3 |
import os |
|
4 |
import json |
|
5 |
import logging.handlers |
|
6 |
import wcsinst |
|
7 |
|
|
8 |
DEBUG = 'DEBUG' in os.environ |
|
9 |
TEMPLATE_DEBUG = DEBUG |
|
10 |
|
|
11 |
VAR_DIR = '/var/lib/wcsinstd' |
|
12 |
PROJECT_PATH = os.path.dirname(wcsinst.__file__) |
|
13 |
|
|
14 |
if 'ADMINS' in os.environ: |
|
15 |
ADMINS = [ x.split(';') for x in os.environ['ADMINS'].split(',') ] |
|
16 |
else: |
|
17 |
ADMINS = ( ('root', 'root@localhost') ) |
|
18 |
|
|
19 |
MANAGERS = ADMINS |
|
20 |
|
|
21 |
DATABASES = { |
|
22 |
'default': { |
|
23 |
'ENGINE': 'django.db.backends.sqlite3', |
|
24 |
'NAME': os.path.join(VAR_DIR, 'wcsinst.sqlite3'), |
|
25 |
} |
|
26 |
} |
|
27 |
|
|
28 |
# Hosts/domain names that are valid for this site; required if DEBUG is False |
|
29 |
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts |
|
30 |
if 'ALLOWED_HOSTS' in os.environ: |
|
31 |
ALLOWED_HOSTS = os.environ['ALLOWED_HOSTS'].split(',') |
|
32 |
else: |
|
33 |
ALLOWED_HOSTS = [ '*' ] |
|
34 |
|
|
35 |
# Local time zone for this installation. Choices can be found here: |
|
36 |
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name |
|
37 |
# although not all choices may be available on all operating systems. |
|
38 |
# In a Windows environment this must be set to your system time zone. |
|
39 |
TIME_ZONE = 'Europe/Brussels' |
|
40 |
|
|
41 |
# Language code for this installation. All choices can be found here: |
|
42 |
# http://www.i18nguy.com/unicode/language-identifiers.html |
|
43 |
LANGUAGE_CODE = 'fr-fr' |
|
44 |
|
|
45 |
SITE_ID = 1 |
|
46 |
|
|
47 |
# If you set this to False, Django will make some optimizations so as not |
|
48 |
# to load the internationalization machinery. |
|
49 |
USE_I18N = True |
|
50 |
|
|
51 |
# If you set this to False, Django will not format dates, numbers and |
|
52 |
# calendars according to the current locale. |
|
53 |
USE_L10N = True |
|
54 |
|
|
55 |
# If you set this to False, Django will not use timezone-aware datetimes. |
|
56 |
USE_TZ = True |
|
57 |
|
|
58 |
# Absolute filesystem path to the directory that will hold user-uploaded files. |
|
59 |
# Example: "/var/www/example.com/media/" |
|
60 |
MEDIA_ROOT = os.path.join(VAR_DIR, 'media') |
|
61 |
|
|
62 |
# URL that handles the media served from MEDIA_ROOT. Make sure to use a |
|
63 |
# trailing slash. |
|
64 |
# Examples: "http://example.com/media/", "http://media.example.com/" |
|
65 |
MEDIA_URL = '/media/' |
|
66 |
|
|
67 |
# Absolute path to the directory static files should be collected to. |
|
68 |
# Don't put anything in this directory yourself; store your static files |
|
69 |
# in apps' "static/" subdirectories and in STATICFILES_DIRS. |
|
70 |
# Example: "/var/www/example.com/static/" |
|
71 |
STATIC_ROOT = os.path.join(VAR_DIR, 'static') |
|
72 |
|
|
73 |
# URL prefix for static files. |
|
74 |
# Example: "http://example.com/static/", "http://static.example.com/" |
|
75 |
STATIC_URL = '/static/' |
|
76 |
|
|
77 |
# Additional locations of static files |
|
78 |
STATICFILES_DIRS = ( |
|
79 |
os.path.join(VAR_DIR, 'extra-static'), |
|
80 |
) |
|
81 |
|
|
82 |
# List of finder classes that know how to find static files in |
|
83 |
# various locations. |
|
84 |
STATICFILES_FINDERS = ( |
|
85 |
'django.contrib.staticfiles.finders.FileSystemFinder', |
|
86 |
'django.contrib.staticfiles.finders.AppDirectoriesFinder', |
|
87 |
# 'django.contrib.staticfiles.finders.DefaultStorageFinder', |
|
88 |
) |
|
89 |
|
|
90 |
# Make this unique, and don't share it with anybody. |
|
91 |
SECRET_KEY = os.environ.get('SECRET_KEY') or file('/etc/wcsinstd/secret').read() |
|
92 |
|
|
93 |
# List of callables that know how to import templates from various sources. |
|
94 |
TEMPLATE_LOADERS = ( |
|
95 |
'django.template.loaders.filesystem.Loader', |
|
96 |
'django.template.loaders.app_directories.Loader', |
|
97 |
# 'django.template.loaders.eggs.Loader', |
|
98 |
) |
|
99 |
|
|
100 |
MIDDLEWARE_CLASSES = ( |
|
101 |
'django.middleware.common.CommonMiddleware', |
|
102 |
'django.contrib.sessions.middleware.SessionMiddleware', |
|
103 |
'django.middleware.csrf.CsrfViewMiddleware', |
|
104 |
'django.contrib.auth.middleware.AuthenticationMiddleware', |
|
105 |
'django.contrib.messages.middleware.MessageMiddleware', |
|
106 |
# Uncomment the next line for simple clickjacking protection: |
|
107 |
# 'django.middleware.clickjacking.XFrameOptionsMiddleware', |
|
108 |
) |
|
109 |
|
|
110 |
ROOT_URLCONF = 'wcsinst.urls' |
|
111 |
|
|
112 |
# Python dotted path to the WSGI application used by Django's runserver. |
|
113 |
WSGI_APPLICATION = 'wcsinst.wsgi.application' |
|
114 |
|
|
115 |
TEMPLATE_DIRS = ( |
|
116 |
os.path.join(PROJECT_PATH, 'wcsinst', 'templates'), |
|
117 |
os.path.join(VAR_DIR, 'templates'), |
|
118 |
) |
|
119 |
|
|
120 |
INSTALLED_APPS = ( |
|
121 |
'south', |
|
122 |
'django.contrib.auth', |
|
123 |
'django.contrib.contenttypes', |
|
124 |
'django.contrib.sessions', |
|
125 |
'django.contrib.sites', |
|
126 |
'django.contrib.messages', |
|
127 |
'django.contrib.staticfiles', |
|
128 |
'django.contrib.admin', |
|
129 |
) |
|
130 |
|
|
131 |
# A sample logging configuration. The only tangible logging |
|
132 |
# performed by this configuration is to send an email to |
|
133 |
# the site admins on every HTTP 500 error when DEBUG=False. |
|
134 |
# See http://docs.djangoproject.com/en/dev/topics/logging for |
|
135 |
# more details on how to customize your logging configuration. |
|
136 |
LOGGING = { |
|
137 |
'version': 1, |
|
138 |
'disable_existing_loggers': True, |
|
139 |
'formatters': { |
|
140 |
'syslog': { |
|
141 |
'format': 'wcsinstd(pid=%(process)d) %(levelname)s %(name)s: %(message)s', |
|
142 |
}, |
|
143 |
}, |
|
144 |
'handlers': { |
|
145 |
'mail_admins': { |
|
146 |
'level': 'ERROR', |
|
147 |
'filters': [], |
|
148 |
'class': 'django.utils.log.AdminEmailHandler' |
|
149 |
}, |
|
150 |
'syslog': { |
|
151 |
'level': 'DEBUG', |
|
152 |
'class': 'entrouvert.logging.handlers.SysLogHandler', |
|
153 |
'formatter': 'syslog', |
|
154 |
'facility': logging.handlers.SysLogHandler.LOG_LOCAL0, |
|
155 |
'address': '/dev/log', |
|
156 |
'max_length': 999, |
|
157 |
}, |
|
158 |
}, |
|
159 |
'loggers': { |
|
160 |
'': { |
|
161 |
'handlers': ['mail_admins','syslog'], |
|
162 |
'level': 'DEBUG' if DEBUG else 'INFO', |
|
163 |
'propagate': True, |
|
164 |
}, |
|
165 |
} |
|
166 |
} |
|
167 |
|
|
168 |
INSTALLED_APPS += ('wcsinst.wcsinstd',) |
|
169 |
|
|
170 |
if 'SENTRY_DSN' in os.environ: |
|
171 |
INSTALLED_APPS += ('raven.contrib.django.raven_compat',) |
|
172 |
RAVEN_CONFIG = { |
|
173 |
'dsn': os.environ.get('SENTRY_DSN'), |
|
174 |
} |
|
175 |
|
|
176 |
for var in ('WCSINST_WCSCTL_SCRIPT', 'WCSINST_URL_TEMPLATE', 'WCSINST_WCS_APP_DIR'): |
|
177 |
if var in os.environ: |
|
178 |
globals()[var] = os.environ[var] |
debian/debian_wsgi.py | ||
---|---|---|
1 |
""" |
|
2 |
WSGI config for wcsinst project. |
|
3 |
|
|
4 |
This module contains the WSGI application used by Django's development server |
|
5 |
and any production WSGI deployments. It should expose a module-level variable |
|
6 |
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover |
|
7 |
this application via the ``WSGI_APPLICATION`` setting. |
|
8 |
|
|
9 |
Usually you will have the standard Django WSGI application here, but it also |
|
10 |
might make sense to replace the whole Django WSGI application with a custom one |
|
11 |
that later delegates to the Django one. For example, you could introduce WSGI |
|
12 |
middleware here, or combine a Django application with an application of another |
|
13 |
framework. |
|
14 |
|
|
15 |
""" |
|
16 |
import os |
|
17 |
|
|
18 |
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks |
|
19 |
# if running multiple sites in the same mod_wsgi process. To fix this, use |
|
20 |
# mod_wsgi daemon mode with each site in its own daemon process, or use |
|
21 |
# os.environ["DJANGO_SETTINGS_MODULE"] = "wcsinst.settings" |
|
22 |
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "debian_settings") |
|
23 |
|
|
24 |
# This application object is used by any WSGI server configured to use this |
|
25 |
# file. This includes Django's development server, if the WSGI_APPLICATION |
|
26 |
# setting points here. |
|
27 |
from django.core.wsgi import get_wsgi_application |
|
28 |
application = get_wsgi_application() |
|
29 |
|
|
30 |
# Apply WSGI middleware here. |
|
31 |
# from helloworld.wsgi import HelloWorldApplication |
|
32 |
# application = HelloWorldApplication(application) |
debian/wcsinstd.init | ||
---|---|---|
31 | 31 |
--workers=1 \ |
32 | 32 |
--worker-class=sync \ |
33 | 33 |
--timeout=60 \ |
34 |
debian_wsgi:application"
|
|
34 |
wcsinst.wsgi:application"
|
|
35 | 35 |
|
36 | 36 |
export PYTHONPATH=/usr/lib/$NAME |
37 | 37 |
|
jenkins.sh | ||
---|---|---|
1 |
#!/bin/sh |
|
2 |
pip install --upgrade pip |
|
3 |
pip install --upgrade pylint |
|
4 |
pip install --upgrade -v -r requirements.txt |
|
5 |
echo Nothing to test for now |
|
6 |
(pylint -f parseable --rcfile /var/lib/jenkins/pylint.django.rc wcsinst/ | tee pylint.out) || /bin/true |
manage.py | ||
---|---|---|
1 |
#!/usr/bin/env python |
|
2 |
import os |
|
3 |
import sys |
|
4 |
|
|
5 |
if __name__ == "__main__": |
|
6 |
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wcsinst.settings") |
|
7 |
|
|
8 |
from django.core.management import execute_from_command_line |
|
9 |
|
|
10 |
execute_from_command_line(sys.argv) |
requirements.txt | ||
---|---|---|
1 |
django < 1.6 |
|
2 |
south>=0.8,<0.9 |
|
3 |
http://pypi.python.org/packages/source/d/django-jsonresponse/django-jsonresponse-0.5.tar.gz |
setup.py | ||
---|---|---|
1 |
#! /usr/bin/python |
|
2 |
|
|
3 |
from setuptools import setup, find_packages |
|
4 |
import os |
|
5 |
import subprocess |
|
6 |
|
|
7 |
VERSION='0.1' |
|
8 |
|
|
9 |
def get_version(): |
|
10 |
if os.path.exists('.git'): |
|
11 |
p = subprocess.Popen(['git','describe','--dirty'], stdout=subprocess.PIPE) |
|
12 |
result = p.communicate()[0] |
|
13 |
return result.split()[0].replace('-','.') |
|
14 |
return VERSION |
|
15 |
|
|
16 |
setup(name='wcsinst', |
|
17 |
version=get_version(), |
|
18 |
license='AGPLv3', |
|
19 |
description='', |
|
20 |
url='https://dev.entrouvert.org/projects/wcsinst/', |
|
21 |
download_url='http://repos.entrouvert.org/wcsinst.git/', |
|
22 |
author="Entr'ouvert", |
|
23 |
author_email="info@entrouvert.com", |
|
24 |
packages=find_packages(os.path.dirname(__file__) or '.'), |
|
25 |
scripts=['manage.py'], |
|
26 |
include_package_data = True, |
|
27 |
install_requires=[ |
|
28 |
'django >= 1.5.1, < 1.6', |
|
29 |
], |
|
30 |
dependency_links = [ |
|
31 |
'http://pypi.python.org/packages/source/d/django-jsonresponse/django-jsonresponse-0.5.tar.gz', |
|
32 |
], |
|
33 |
) |
wcsinst/settings.py | ||
---|---|---|
1 |
# Django settings for wcsinst project. |
|
2 |
|
|
3 |
import os |
|
4 |
|
|
5 |
DEBUG = True |
|
6 |
TEMPLATE_DEBUG = DEBUG |
|
7 |
|
|
8 |
PROJECT_PATH = os.path.dirname(os.path.dirname(__file__)) |
|
9 |
|
|
10 |
ADMINS = ( |
|
11 |
# ('Your Name', 'your_email@example.com'), |
|
12 |
) |
|
13 |
|
|
14 |
MANAGERS = ADMINS |
|
15 |
|
|
16 |
DATABASES = { |
|
17 |
'default': { |
|
18 |
'ENGINE': 'django.db.backends.sqlite3', |
|
19 |
'NAME': os.path.join(PROJECT_PATH, 'wcsinst.sqlite3'), |
|
20 |
} |
|
21 |
} |
|
22 |
|
|
23 |
# Hosts/domain names that are valid for this site; required if DEBUG is False |
|
24 |
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts |
|
25 |
ALLOWED_HOSTS = [] |
|
26 |
|
|
27 |
# Local time zone for this installation. Choices can be found here: |
|
28 |
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name |
|
29 |
# although not all choices may be available on all operating systems. |
|
30 |
# In a Windows environment this must be set to your system time zone. |
|
31 |
TIME_ZONE = 'Europe/Brussels' |
|
32 |
|
|
33 |
# Language code for this installation. All choices can be found here: |
|
34 |
# http://www.i18nguy.com/unicode/language-identifiers.html |
|
35 |
LANGUAGE_CODE = 'fr-fr' |
|
36 |
|
|
37 |
SITE_ID = 1 |
|
38 |
|
|
39 |
# If you set this to False, Django will make some optimizations so as not |
|
40 |
# to load the internationalization machinery. |
|
41 |
USE_I18N = True |
|
42 |
|
|
43 |
# If you set this to False, Django will not format dates, numbers and |
|
44 |
# calendars according to the current locale. |
|
45 |
USE_L10N = True |
|
46 |
|
|
47 |
# If you set this to False, Django will not use timezone-aware datetimes. |
|
48 |
USE_TZ = True |
|
49 |
|
|
50 |
# Absolute filesystem path to the directory that will hold user-uploaded files. |
|
51 |
# Example: "/var/www/example.com/media/" |
|
52 |
MEDIA_ROOT = os.path.join(PROJECT_PATH, 'media') |
|
53 |
|
|
54 |
# URL that handles the media served from MEDIA_ROOT. Make sure to use a |
|
55 |
# trailing slash. |
|
56 |
# Examples: "http://example.com/media/", "http://media.example.com/" |
|
57 |
MEDIA_URL = '/media/' |
|
58 |
|
|
59 |
# Absolute path to the directory static files should be collected to. |
|
60 |
# Don't put anything in this directory yourself; store your static files |
|
61 |
# in apps' "static/" subdirectories and in STATICFILES_DIRS. |
|
62 |
# Example: "/var/www/example.com/static/" |
|
63 |
STATIC_ROOT = os.path.join(PROJECT_PATH, 'static') |
|
64 |
|
|
65 |
# URL prefix for static files. |
|
66 |
# Example: "http://example.com/static/", "http://static.example.com/" |
|
67 |
STATIC_URL = '/static/' |
|
68 |
|
|
69 |
# Additional locations of static files |
|
70 |
STATICFILES_DIRS = ( |
|
71 |
os.path.join(PROJECT_PATH, 'wcsinst', 'static'), |
|
72 |
) |
|
73 |
|
|
74 |
# List of finder classes that know how to find static files in |
|
75 |
# various locations. |
|
76 |
STATICFILES_FINDERS = ( |
|
77 |
'django.contrib.staticfiles.finders.FileSystemFinder', |
|
78 |
'django.contrib.staticfiles.finders.AppDirectoriesFinder', |
|
79 |
# 'django.contrib.staticfiles.finders.DefaultStorageFinder', |
|
80 |
) |
|
81 |
|
|
82 |
# Make this unique, and don't share it with anybody. |
|
83 |
SECRET_KEY = 'sw-v(^psaet3)44flti-zr!=u64mzfeaodkey(m=&^nz(=43!o' |
|
84 |
|
|
85 |
# List of callables that know how to import templates from various sources. |
|
86 |
TEMPLATE_LOADERS = ( |
|
87 |
'django.template.loaders.filesystem.Loader', |
|
88 |
'django.template.loaders.app_directories.Loader', |
|
89 |
# 'django.template.loaders.eggs.Loader', |
|
90 |
) |
|
91 |
|
|
92 |
MIDDLEWARE_CLASSES = ( |
|
93 |
'django.middleware.common.CommonMiddleware', |
|
94 |
'django.contrib.sessions.middleware.SessionMiddleware', |
|
95 |
'django.middleware.csrf.CsrfViewMiddleware', |
|
96 |
'django.contrib.auth.middleware.AuthenticationMiddleware', |
|
97 |
'django.contrib.messages.middleware.MessageMiddleware', |
|
98 |
# Uncomment the next line for simple clickjacking protection: |
|
99 |
# 'django.middleware.clickjacking.XFrameOptionsMiddleware', |
|
100 |
) |
|
101 |
|
|
102 |
ROOT_URLCONF = 'wcsinst.urls' |
|
103 |
|
|
104 |
# Python dotted path to the WSGI application used by Django's runserver. |
|
105 |
WSGI_APPLICATION = 'wcsinst.wsgi.application' |
|
106 |
|
|
107 |
TEMPLATE_DIRS = ( |
|
108 |
os.path.join(PROJECT_PATH, 'wcsinst', 'templates'), |
|
109 |
) |
|
110 |
|
|
111 |
INSTALLED_APPS = ( |
|
112 |
'south', |
|
113 |
'django.contrib.auth', |
|
114 |
'django.contrib.contenttypes', |
|
115 |
'django.contrib.sessions', |
|
116 |
'django.contrib.sites', |
|
117 |
'django.contrib.messages', |
|
118 |
'django.contrib.staticfiles', |
|
119 |
'django.contrib.admin', |
|
120 |
) |
|
121 |
|
|
122 |
# A sample logging configuration. The only tangible logging |
|
123 |
# performed by this configuration is to send an email to |
|
124 |
# the site admins on every HTTP 500 error when DEBUG=False. |
|
125 |
# See http://docs.djangoproject.com/en/dev/topics/logging for |
|
126 |
# more details on how to customize your logging configuration. |
|
127 |
LOGGING = { |
|
128 |
'version': 1, |
|
129 |
'disable_existing_loggers': False, |
|
130 |
'filters': { |
|
131 |
'require_debug_false': { |
|
132 |
'()': 'django.utils.log.RequireDebugFalse' |
|
133 |
} |
|
134 |
}, |
|
135 |
'handlers': { |
|
136 |
'mail_admins': { |
|
137 |
'level': 'ERROR', |
|
138 |
'filters': ['require_debug_false'], |
|
139 |
'class': 'django.utils.log.AdminEmailHandler' |
|
140 |
} |
|
141 |
}, |
|
142 |
'loggers': { |
|
143 |
'django.request': { |
|
144 |
'handlers': ['mail_admins'], |
|
145 |
'level': 'ERROR', |
|
146 |
'propagate': True, |
|
147 |
}, |
|
148 |
} |
|
149 |
} |
|
150 |
|
|
151 |
WCSINSTD_URL = os.environ.get('WCSINSTD_URL') |
|
152 |
|
|
153 |
if WCSINSTD_URL: |
|
154 |
INSTALLED_APPS += ('wcsinst.wcsinst',) |
|
155 |
else: |
|
156 |
INSTALLED_APPS += ('wcsinst.wcsinstd',) |
|
157 |
|
|
158 |
WCSINST_WCSCTL_SCRIPT = os.environ.get('WCSINST_WCSCTL_SCRIPT', 'wcsctl') |
|
159 |
|
|
160 |
try: |
|
161 |
from local_settings import * |
|
162 |
except ImportError: |
|
163 |
pass |
wcsinst/urls.py | ||
---|---|---|
1 |
from django.conf.urls import patterns, include, url |
|
2 |
from django.conf import settings |
|
3 |
|
|
4 |
from django.contrib import admin |
|
5 |
admin.autodiscover() |
|
6 |
|
|
7 |
urlpatterns = patterns('', |
|
8 |
url(r'^admin/', include(admin.site.urls)), |
|
9 |
) |
|
10 |
|
|
11 |
if 'wcsinst.wcsinstd' in settings.INSTALLED_APPS: |
|
12 |
urlpatterns += patterns('', |
|
13 |
(r'^wcsinstd/', include('wcsinst.wcsinstd.urls'))) |
wcsinst/wcsinst/admin.py | ||
---|---|---|
1 |
from django.contrib import admin |
|
2 |
from django.utils.translation import ugettext_lazy as _ |
|
3 |
|
|
4 |
from models import WcsInstance, ApiSecret, Variable |
|
5 |
|
|
6 |
class ApiSecretsInline(admin.TabularInline): |
|
7 |
model = ApiSecret |
|
8 |
verbose_name = _('API secret') |
|
9 |
verbose_name_plural = _('API secrets') |
|
10 |
|
|
11 |
class VariablesInline(admin.TabularInline): |
|
12 |
model = Variable |
|
13 |
verbose_name = _('variable') |
|
14 |
verbose_name_plural = _('variables') |
|
15 |
|
|
16 |
class WcsInstanceAdmin(admin.ModelAdmin): |
|
17 |
prepopulated_fields = {'domain': ('title',)} |
|
18 |
fieldsets = ( |
|
19 |
(None, {'fields': ('title', 'domain'),}), |
|
20 |
('site-options.cfg', |
|
21 |
{'fields': ('postgresql', ('saml2_use_role', 'saml2_role_prefix',), 'backoffice_feed_url' )} |
|
22 |
), |
|
23 |
('site-options.cfg au-quotidien', |
|
24 |
{'fields': ('drupal', 'ezldap', 'strongbox', 'clicrdv', 'domino' )} |
|
25 |
), |
|
26 |
) |
|
27 |
inlines = [VariablesInline, ApiSecretsInline] |
|
28 |
save_as = True |
|
29 |
|
|
30 |
|
|
31 |
admin.site.register(WcsInstance, WcsInstanceAdmin) |
wcsinst/wcsinst/migrations/0001_initial.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
import datetime |
|
3 |
from south.db import db |
|
4 |
from south.v2 import SchemaMigration |
|
5 |
from django.db import models |
|
6 |
|
|
7 |
|
|
8 |
class Migration(SchemaMigration): |
|
9 |
|
|
10 |
def forwards(self, orm): |
|
11 |
# Adding model 'WcsInstance' |
|
12 |
db.create_table(u'wcsinst_wcsinstance', ( |
|
13 |
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
14 |
('title', self.gf('django.db.models.fields.CharField')(max_length=50)), |
|
15 |
('domain', self.gf('django.db.models.fields.CharField')(max_length=100)), |
|
16 |
)) |
|
17 |
db.send_create_signal(u'wcsinst', ['WcsInstance']) |
|
18 |
|
|
19 |
|
|
20 |
def backwards(self, orm): |
|
21 |
# Deleting model 'WcsInstance' |
|
22 |
db.delete_table(u'wcsinst_wcsinstance') |
|
23 |
|
|
24 |
|
|
25 |
models = { |
|
26 |
u'wcsinst.wcsinstance': { |
|
27 |
'Meta': {'object_name': 'WcsInstance'}, |
|
28 |
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
|
29 |
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
30 |
'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
|
31 |
} |
|
32 |
} |
|
33 |
|
|
34 |
complete_apps = ['wcsinst'] |
wcsinst/wcsinst/migrations/0002_auto__add_variable__add_apisecret__add_field_wcsinstance_postgresql__a.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
import datetime |
|
3 |
from south.db import db |
|
4 |
from south.v2 import SchemaMigration |
|
5 |
from django.db import models |
|
6 |
|
|
7 |
|
|
8 |
class Migration(SchemaMigration): |
|
9 |
|
|
10 |
def forwards(self, orm): |
|
11 |
# Adding model 'Variable' |
|
12 |
db.create_table(u'wcsinst_variable', ( |
|
13 |
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
14 |
('key', self.gf('django.db.models.fields.CharField')(max_length=128)), |
|
15 |
('value', self.gf('django.db.models.fields.CharField')(max_length=1024, blank=True)), |
|
16 |
('wcs_instance', self.gf('django.db.models.fields.related.ForeignKey')(related_name='variables', to=orm['wcsinst.WcsInstance'])), |
|
17 |
)) |
|
18 |
db.send_create_signal(u'wcsinst', ['Variable']) |
|
19 |
|
|
20 |
# Adding model 'ApiSecret' |
|
21 |
db.create_table(u'wcsinst_apisecret', ( |
|
22 |
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
23 |
('key', self.gf('django.db.models.fields.CharField')(max_length=128)), |
|
24 |
('value', self.gf('django.db.models.fields.CharField')(max_length=1024, blank=True)), |
|
25 |
('wcs_instance', self.gf('django.db.models.fields.related.ForeignKey')(related_name='api_secrets', to=orm['wcsinst.WcsInstance'])), |
|
26 |
)) |
|
27 |
db.send_create_signal(u'wcsinst', ['ApiSecret']) |
|
28 |
|
|
29 |
# Adding field 'WcsInstance.postgresql' |
|
30 |
db.add_column(u'wcsinst_wcsinstance', 'postgresql', |
|
31 |
self.gf('django.db.models.fields.BooleanField')(default=False), |
|
32 |
keep_default=False) |
|
33 |
|
|
34 |
# Adding field 'WcsInstance.saml2_use_role' |
|
35 |
db.add_column(u'wcsinst_wcsinstance', 'saml2_use_role', |
|
36 |
self.gf('django.db.models.fields.BooleanField')(default=False), |
|
37 |
keep_default=False) |
|
38 |
|
|
39 |
# Adding field 'WcsInstance.saml2_role_prefix' |
|
40 |
db.add_column(u'wcsinst_wcsinstance', 'saml2_role_prefix', |
|
41 |
self.gf('django.db.models.fields.CharField')(default='', max_length=128, blank=True), |
|
42 |
keep_default=False) |
|
43 |
|
|
44 |
# Adding field 'WcsInstance.backoffice_feed_url' |
|
45 |
db.add_column(u'wcsinst_wcsinstance', 'backoffice_feed_url', |
|
46 |
self.gf('django.db.models.fields.URLField')(default='', max_length=128, blank=True), |
|
47 |
keep_default=False) |
|
48 |
|
|
49 |
# Adding field 'WcsInstance.drupal' |
|
50 |
db.add_column(u'wcsinst_wcsinstance', 'drupal', |
|
51 |
self.gf('django.db.models.fields.BooleanField')(default=False), |
|
52 |
keep_default=False) |
|
53 |
|
|
54 |
# Adding field 'WcsInstance.ezldap' |
|
55 |
db.add_column(u'wcsinst_wcsinstance', 'ezldap', |
|
56 |
self.gf('django.db.models.fields.BooleanField')(default=False), |
|
57 |
keep_default=False) |
|
58 |
|
|
59 |
# Adding field 'WcsInstance.strongbox' |
|
60 |
db.add_column(u'wcsinst_wcsinstance', 'strongbox', |
|
61 |
self.gf('django.db.models.fields.BooleanField')(default=False), |
|
62 |
keep_default=False) |
|
63 |
|
|
64 |
# Adding field 'WcsInstance.clicrdv' |
|
65 |
db.add_column(u'wcsinst_wcsinstance', 'clicrdv', |
|
66 |
self.gf('django.db.models.fields.BooleanField')(default=False), |
|
67 |
keep_default=False) |
|
68 |
|
|
69 |
# Adding field 'WcsInstance.domino' |
|
70 |
db.add_column(u'wcsinst_wcsinstance', 'domino', |
|
71 |
self.gf('django.db.models.fields.BooleanField')(default=False), |
|
72 |
keep_default=False) |
|
73 |
|
|
74 |
|
|
75 |
def backwards(self, orm): |
|
76 |
# Deleting model 'Variable' |
|
77 |
db.delete_table(u'wcsinst_variable') |
|
78 |
|
|
79 |
# Deleting model 'ApiSecret' |
|
80 |
db.delete_table(u'wcsinst_apisecret') |
|
81 |
|
|
82 |
# Deleting field 'WcsInstance.postgresql' |
|
83 |
db.delete_column(u'wcsinst_wcsinstance', 'postgresql') |
|
84 |
|
|
85 |
# Deleting field 'WcsInstance.saml2_use_role' |
|
86 |
db.delete_column(u'wcsinst_wcsinstance', 'saml2_use_role') |
|
87 |
|
|
88 |
# Deleting field 'WcsInstance.saml2_role_prefix' |
|
89 |
db.delete_column(u'wcsinst_wcsinstance', 'saml2_role_prefix') |
|
90 |
|
|
91 |
# Deleting field 'WcsInstance.backoffice_feed_url' |
|
92 |
db.delete_column(u'wcsinst_wcsinstance', 'backoffice_feed_url') |
|
93 |
|
|
94 |
# Deleting field 'WcsInstance.drupal' |
|
95 |
db.delete_column(u'wcsinst_wcsinstance', 'drupal') |
|
96 |
|
|
97 |
# Deleting field 'WcsInstance.ezldap' |
|
98 |
db.delete_column(u'wcsinst_wcsinstance', 'ezldap') |
|
99 |
|
|
100 |
# Deleting field 'WcsInstance.strongbox' |
|
101 |
db.delete_column(u'wcsinst_wcsinstance', 'strongbox') |
|
102 |
|
|
103 |
# Deleting field 'WcsInstance.clicrdv' |
|
104 |
db.delete_column(u'wcsinst_wcsinstance', 'clicrdv') |
|
105 |
|
|
106 |
# Deleting field 'WcsInstance.domino' |
|
107 |
db.delete_column(u'wcsinst_wcsinstance', 'domino') |
|
108 |
|
|
109 |
|
|
110 |
models = { |
|
111 |
u'wcsinst.apisecret': { |
|
112 |
'Meta': {'object_name': 'ApiSecret'}, |
|
113 |
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
114 |
'key': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
|
115 |
'value': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), |
|
116 |
'wcs_instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'api_secrets'", 'to': u"orm['wcsinst.WcsInstance']"}) |
|
117 |
}, |
|
118 |
u'wcsinst.variable': { |
|
119 |
'Meta': {'object_name': 'Variable'}, |
|
120 |
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
121 |
'key': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
|
122 |
'value': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), |
|
123 |
'wcs_instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variables'", 'to': u"orm['wcsinst.WcsInstance']"}) |
|
124 |
}, |
|
125 |
u'wcsinst.wcsinstance': { |
|
126 |
'Meta': {'object_name': 'WcsInstance'}, |
|
127 |
'backoffice_feed_url': ('django.db.models.fields.URLField', [], {'max_length': '128', 'blank': 'True'}), |
|
128 |
'clicrdv': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
129 |
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
|
130 |
'domino': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
131 |
'drupal': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
132 |
'ezldap': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
133 |
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
134 |
'postgresql': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
135 |
'saml2_role_prefix': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), |
|
136 |
'saml2_use_role': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
137 |
'strongbox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
138 |
'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
|
139 |
} |
|
140 |
} |
|
141 |
|
|
142 |
complete_apps = ['wcsinst'] |
wcsinst/wcsinst/models.py | ||
---|---|---|
1 |
import json |
|
2 |
import logging |
|
3 |
import urllib2 |
|
4 |
|
|
5 |
from django.conf import settings |
|
6 |
from django.db import models |
|
7 |
from django.utils.translation import ugettext_lazy as _ |
|
8 |
|
|
9 |
logger = logging.getLogger(__name__) |
|
10 |
|
|
11 |
class WcsInstance(models.Model): |
|
12 |
title = models.CharField(max_length=50) |
|
13 |
domain = models.CharField(max_length=100) |
|
14 |
|
|
15 |
# site-options.cfg options |
|
16 |
postgresql = models.BooleanField(verbose_name=_('postgresql'), |
|
17 |
blank=True) |
|
18 |
saml2_use_role = models.BooleanField(verbose_name=_('use saml2 roles'), |
|
19 |
blank=True) |
|
20 |
saml2_role_prefix = models.CharField(verbose_name=_('role prefix'), |
|
21 |
blank=True, max_length=128) |
|
22 |
backoffice_feed_url = models.URLField(verbose_name=_('backoffice feed url'), |
|
23 |
blank=True, max_length=128) |
|
24 |
drupal = models.BooleanField(verbose_name=_('drupal'), |
|
25 |
blank=True) |
|
26 |
ezldap = models.BooleanField(verbose_name=_('ezldap'), |
|
27 |
blank=True) |
|
28 |
strongbox = models.BooleanField(verbose_name=_('strongbox'), |
|
29 |
blank=True) |
|
30 |
clicrdv = models.BooleanField(verbose_name=_('clicrdv'), |
|
31 |
blank=True) |
|
32 |
domino = models.BooleanField(verbose_name=_('domino'), |
|
33 |
blank=True) |
|
34 |
|
|
35 |
|
|
36 |
def __unicode__(self): |
|
37 |
return '%s (%s)' % (self.title, self.domain) |
|
38 |
|
|
39 |
def site_options_cfg(self): |
|
40 |
d = { |
|
41 |
'postgresql': self.postgresql, |
|
42 |
'saml2_use_role': self.saml2_use_role, |
|
43 |
'saml2_role_prefix': self.saml2_role_prefix, |
|
44 |
'backoffice_feed_url': self.backoffice_feed_url, |
|
45 |
'drupal': self.drupal, |
|
46 |
'ezldap': self.ezldap, |
|
47 |
'strongbox': self.strongbox, |
|
48 |
'clicrdv': self.clicrdv, |
|
49 |
'domino': self.domino, |
|
50 |
} |
|
51 |
d['api_secrets'] = dict((kv.key, kv.value) for kv in self.api_secrets.all()) |
|
52 |
d['variables'] = dict((kv.key, kv.value) for kv in self.variables.all()) |
|
53 |
return d |
|
54 |
|
|
55 |
def to_json(self): |
|
56 |
return { |
|
57 |
'title': self.title, |
|
58 |
'domain': self.domain, |
|
59 |
'site_options_cfg': self.site_options_cfg(), |
|
60 |
} |
|
61 |
|
|
62 |
def save(self, *args, **kwargs): |
|
63 |
created = (self.id is None) |
|
64 |
super(WcsInstance, self).save(*args, **kwargs) |
|
65 |
# notify wcsinstd |
|
66 |
if not settings.WCSINSTD_URL: |
|
67 |
return |
|
68 |
if created: |
|
69 |
url = settings.WCSINSTD_URL + 'wcsinstd/create' |
|
70 |
else: |
|
71 |
url = settings.WCSINSTD_URL + 'wcsinstd/%s/' % self.domain |
|
72 |
post_data = json.dumps(self.to_json()) |
|
73 |
request = urllib2.Request(url) |
|
74 |
request.add_header('Accept', 'application/json') |
|
75 |
request.add_header('Content-Type', 'application/json;charset=UTF-8') |
|
76 |
request.add_data(post_data) |
|
77 |
try: |
|
78 |
p = urllib2.urlopen(request) |
|
79 |
except urllib2.HTTPError as e: |
|
80 |
logger.error('wcsinstd HTTP error (%s)', str(e)) |
|
81 |
print e.read() |
|
82 |
except urllib2.URLError as e: |
|
83 |
print e |
|
84 |
logger.error('wcsinstd URL error (%s)', str(e)) |
|
85 |
else: |
|
86 |
out_data = p.read() |
|
87 |
p.close() |
|
88 |
|
|
89 |
class Meta: |
|
90 |
verbose_name = _('wcs instance') |
|
91 |
verbose_name_plural = _('wcs instances') |
|
92 |
|
|
93 |
|
|
94 |
class KeyValue(models.Model): |
|
95 |
key = models.CharField(max_length=128) |
|
96 |
value = models.CharField(max_length=1024, blank=True) |
|
97 |
|
|
98 |
class Meta: |
|
99 |
abstract = True |
|
100 |
|
|
101 |
|
|
102 |
class Variable(KeyValue): |
|
103 |
wcs_instance = models.ForeignKey(WcsInstance, related_name='variables') |
|
104 |
|
|
105 |
class Meta: |
|
106 |
verbose_name = _('variable') |
|
107 |
verbose_name_plural = _('variables') |
|
108 |
|
|
109 |
|
|
110 |
class ApiSecret(KeyValue): |
|
111 |
wcs_instance = models.ForeignKey(WcsInstance, related_name='api_secrets') |
|
112 |
|
|
113 |
class Meta: |
|
114 |
verbose_name = _('api secret') |
|
115 |
verbose_name_plural = _('api secrets') |
|
116 |
|
|
117 |
|
wcsinst/wcsinst/tests.py | ||
---|---|---|
1 |
""" |
|
2 |
This file demonstrates writing tests using the unittest module. These will pass |
|
3 |
when you run "manage.py test". |
|
4 |
|
|
5 |
Replace this with more appropriate tests for your application. |
|
6 |
""" |
|
7 |
|
|
8 |
from django.test import TestCase |
|
9 |
|
|
10 |
|
|
11 |
class SimpleTest(TestCase): |
|
12 |
def test_basic_addition(self): |
|
13 |
""" |
|
14 |
Tests that 1 + 1 always equals 2. |
|
15 |
""" |
|
16 |
self.assertEqual(1 + 1, 2) |
wcsinst/wcsinst/views.py | ||
---|---|---|
1 |
# Create your views here. |
wcsinst/wcsinstd/app_settings.py | ||
---|---|---|
1 |
from django.conf import settings |
|
2 |
|
|
3 |
URL_TEMPLATE = getattr(settings, 'WCSINST_URL_TEMPLATE', 'https://%(domain)s') |
|
4 |
WCS_APP_DIR = getattr(settings, 'WCSINST_WCS_APP_DIR', None) |
|
5 |
WCSCTL_SCRIPT = getattr(settings, 'WCSINST_WCSCTL_SCRIPT', 'wcsctl') |
wcsinst/wcsinstd/deploy.py | ||
---|---|---|
1 |
import string |
|
2 |
import cPickle |
|
3 |
import os |
|
4 |
import zipfile |
|
5 |
import subprocess |
|
6 |
from urlparse import urlparse |
|
7 |
|
|
8 |
from cStringIO import StringIO |
|
9 |
import xml.etree.ElementTree as ET |
|
10 |
|
|
11 |
from django.conf import settings |
|
12 |
|
|
13 |
import psycopg2 |
|
14 |
|
|
15 |
from . import app_settings |
|
16 |
|
|
17 |
|
|
18 |
def get_provider_key(provider_id): |
|
19 |
return provider_id.replace('://', '-').replace('/', '-').replace('?', '-').replace(':', '-') |
|
20 |
|
|
21 |
|
|
22 |
class DeployInstance(object): |
|
23 |
skeleton = 'default' |
|
24 |
|
|
25 |
skel_dir = None |
|
26 |
collectivity_install_dir = None |
|
27 |
|
|
28 |
def __init__(self, domain, title, site_options_cfg): |
|
29 |
self.domain = domain |
|
30 |
self.title = title |
|
31 |
self.site_options_cfg = site_options_cfg |
|
32 |
|
|
33 |
def make(self): |
|
34 |
self.skel_dir = os.path.join(settings.MEDIA_ROOT, 'skeletons', self.skeleton) |
|
35 |
|
|
36 |
url_template = app_settings.URL_TEMPLATE |
|
37 |
self.url = str(url_template % {'domain': self.domain}) |
|
38 |
|
|
39 |
host, path = urlparse(self.url)[1:3] |
|
40 |
if path.endswith('/'): |
|
41 |
path = path[:-1] |
|
42 |
|
|
43 |
coldir = host |
|
44 |
if path: |
|
45 |
coldir += path.replace('/', '+') |
|
46 |
|
|
47 |
self.collectivity_install_dir = os.path.join(app_settings.WCS_APP_DIR, coldir) |
|
48 |
|
|
49 |
if os.path.exists(self.collectivity_install_dir): |
|
50 |
# site exists, let's update it |
|
51 |
pass |
|
52 |
anew = False |
|
53 |
else: |
|
54 |
anew = True |
|
55 |
os.mkdir(self.collectivity_install_dir) |
|
56 |
|
|
57 |
z = zipfile.ZipFile(os.path.join(self.skel_dir, 'export.wcs'), 'r') |
|
58 |
|
|
59 |
for f in z.namelist(): |
|
60 |
path = os.path.join(self.collectivity_install_dir, f) |
|
61 |
data = z.read(f) |
|
62 |
if not os.path.exists(os.path.dirname(path)): |
|
63 |
os.mkdir(os.path.dirname(path)) |
|
64 |
open(path, 'w').write(data) |
|
65 |
z.close() |
|
66 |
|
|
67 |
config_file = os.path.join(self.collectivity_install_dir, 'config.pck') |
|
68 |
if os.path.exists(config_file): |
|
69 |
wcs_cfg = cPickle.load(file(os.path.join(self.collectivity_install_dir, 'config.pck'))) |
|
70 |
else: |
|
71 |
wcs_cfg = {} |
|
72 |
|
|
73 |
has_sql = self.make_sql_config(wcs_cfg) |
|
74 |
self.make_sso_config(wcs_cfg) |
|
75 |
self.make_site_options() |
|
76 |
|
|
77 |
cPickle.dump(wcs_cfg, file(config_file, 'w')) |
|
78 |
|
|
79 |
if has_sql: |
|
80 |
self.make_sql_tables(wcs_cfg) |
|
81 |
|
|
82 |
self.make_apache_vhost() |
|
83 |
self.reload_apache() |
|
84 |
|
|
85 |
|
|
86 |
def make_sql_config(self, wcs_cfg): |
|
87 |
if not wcs_cfg.get('postgresql'): |
|
88 |
# this is not a site configured to use SQL |
|
89 |
return False |
|
90 |
|
|
91 |
database_name = wcs_cfg['postgresql'].get('database', 'wcs') |
|
92 |
domain_table_name = self.domain.replace('-', '_').replace('.', '_') |
|
93 |
if '_' in database_name: |
|
94 |
database_name = '%s_%s' % (database_name.split('_')[0], domain_table_name) |
|
95 |
else: |
|
96 |
database_name = '%s_%s' % (database_name, domain_table_name) |
|
97 |
|
|
98 |
postgresql_cfg = {} |
|
99 |
for k, v in wcs_cfg['postgresql'].items(): |
|
100 |
if v: |
|
101 |
postgresql_cfg[k] = v |
|
102 |
try: |
|
103 |
pgconn = psycopg2.connect(**postgresql_cfg) |
|
104 |
except psycopg2.Error: |
|
105 |
# XXX: log |
|
106 |
raise |
|
107 |
|
|
108 |
pgconn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) |
|
109 |
cur = pgconn.cursor() |
|
110 |
try: |
|
111 |
cur.execute('''CREATE DATABASE %s''' % database_name) |
|
112 |
except psycopg2.Error as e: |
|
113 |
print 'got psycopg2 error:', e |
|
114 |
cur.close() |
|
115 |
|
|
116 |
wcs_cfg['postgresql']['database'] = database_name |
|
117 |
|
|
118 |
return True |
|
119 |
|
|
120 |
def make_sql_tables(self, wcs_cfg): |
|
121 |
params = [] |
|
122 |
for param in ('database', 'user', 'password', 'host', 'port'): |
|
123 |
if wcs_cfg.get('postgresql').get(param): |
|
124 |
if param == 'database': |
|
125 |
params.append('--dbname') |
|
126 |
else: |
|
127 |
params.append('--' + param) |
|
128 |
params.append(wcs_cfg.get('postgresql').get(param)) |
|
129 |
os.system('%s convert-to-sql %s %s' % (app_settings.WCSCTL_SCRIPT, ' '.join(params), self.domain)) |
|
130 |
|
|
131 |
def make_sso_config(self, wcs_cfg): |
|
132 |
has_idff = False |
|
133 |
has_saml2 = False |
|
134 |
|
|
135 |
service_provider_configuration = {} |
|
136 |
|
|
137 |
if self.url.endswith('/'): |
|
138 |
url_stripped = self.url[:-1] |
|
139 |
else: |
|
140 |
url_stripped = self.url |
|
141 |
|
|
142 |
if os.path.exists(os.path.join(self.skel_dir, 'idff-metadata-template')): |
|
143 |
# there's a ID-FF metadata template, so we do the ID-FF stuff |
|
144 |
has_idff = True |
|
145 |
service_provider_configuration.update({ |
|
146 |
'base_url': '%s/liberty' % url_stripped, |
|
147 |
'metadata': 'metadata.xml', |
|
148 |
'providerid': '%s/liberty/metadata' % url_stripped, |
|
149 |
}) |
|
150 |
|
|
151 |
idff_metadata_template = file( |
|
152 |
os.path.join(self.skel_dir, 'idff-metadata-template')).read() |
|
153 |
file(os.path.join(self.collectivity_install_dir, 'metadata.xml'), 'w').write( |
|
154 |
string.Template(idff_metadata_template).substitute({'url': url_stripped})) |
|
155 |
|
|
156 |
if os.path.exists(os.path.join(self.skel_dir, 'saml2-metadata-template')): |
|
157 |
# there's a SAMLv2 metadata template, so we do the SAMLv2 stuff |
|
158 |
has_saml2 = True |
|
159 |
service_provider_configuration.update({ |
|
160 |
'saml2_base_url': '%s/saml' % url_stripped, |
|
161 |
'saml2_metadata': 'saml2-metadata.xml', |
|
162 |
'saml2_providerid': '%s/saml/metadata' % url_stripped |
|
163 |
}) |
|
164 |
|
|
165 |
saml2_metadata_template = file( |
|
166 |
os.path.join(self.skel_dir, 'saml2-metadata-template')).read() |
|
167 |
file(os.path.join(self.collectivity_install_dir, 'saml2-metadata.xml'), 'w').write( |
|
168 |
string.Template(saml2_metadata_template).substitute({'url': url_stripped})) |
|
169 |
|
|
170 |
if has_idff or has_saml2: |
|
171 |
idp_metadata = ET.parse(file(os.path.join(self.skel_dir, 'idp-metadata.xml'))) |
|
172 |
entity_id = idp_metadata.getroot().attrib['entityID'] |
|
173 |
idp_key = get_provider_key(entity_id) |
|
174 |
|
|
175 |
wcs_cfg['identification'] = {'methods': ['idp']} |
|
176 |
wcs_cfg['idp'] = { |
|
177 |
idp_key: { |
|
178 |
'metadata': 'provider-%s-metadata.xml' % idp_key, |
|
179 |
'metadata_url': entity_id, |
|
180 |
'publickey_url': None, |
|
181 |
'role': 2}} |
|
182 |
wcs_cfg['sp'] = { |
|
183 |
'common_domain': None, |
|
184 |
'common_domain_getter_url': None, |
|
185 |
'organization_name': self.title.encode('utf-8'), |
|
186 |
'privatekey': 'private-key.pem', |
|
187 |
'publickey': 'public-key.pem'} |
|
188 |
wcs_cfg['sp'].update(service_provider_configuration) |
|
189 |
|
|
190 |
file(os.path.join(self.collectivity_install_dir, 'provider-%s-metadata.xml' % idp_key), 'w').write( |
|
191 |
file(os.path.join(self.skel_dir, 'idp-metadata.xml')).read()) |
|
192 |
file(os.path.join(self.collectivity_install_dir, 'public-key.pem'), 'w').write( |
|
193 |
file(os.path.join(self.skel_dir, 'public-key.pem')).read()) |
|
194 |
file(os.path.join(self.collectivity_install_dir, 'private-key.pem'), 'w').write( |
|
195 |
file(os.path.join(self.skel_dir, 'private-key.pem')).read()) |
|
196 |
else: |
|
197 |
wcs_cfg['identification'] = {'methods': ['password']} |
|
198 |
|
|
199 |
|
|
200 |
def make_site_options(self): |
|
201 |
options_template_path = os.path.join(self.skel_dir, 'site-options.cfg') |
|
202 |
if not os.path.exists(options_template_path): |
|
203 |
return |
|
204 |
options_template = file(options_template_path).read() |
|
205 |
file(os.path.join(self.collectivity_install_dir, 'site-options.cfg'), 'w').write( |
|
206 |
string.Template(options_template).substitute({ |
|
207 |
'domain': self.domain, |
|
208 |
'options': self.site_options_cfg |
|
209 |
}) |
|
210 |
) |
|
211 |
|
|
212 |
|
|
213 |
def make_apache_vhost(self): |
|
214 |
apache_vhost_template_path = os.path.join(self.skel_dir, 'apache-vhost.conf') |
|
215 |
if not os.path.exists(apache_vhost_template_path): |
|
216 |
return |
|
217 |
apache_vhost_template = file(apache_vhost_template_path).read() |
|
218 |
apache_dir = os.path.join(settings.MEDIA_ROOT, 'vhosts.d') |
|
219 |
if not os.path.exists(apache_dir): |
|
220 |
os.mkdir(apache_dir) |
|
221 |
file(os.path.join(apache_dir, '%s.conf' % self.domain), 'w').write( |
|
222 |
string.Template(apache_vhost_template).substitute({'domain': self.domain})) |
|
223 |
|
|
224 |
|
|
225 |
def reload_apache(self): |
|
226 |
os.system('sudo -n /etc/init.d/apache2 reload') |
wcsinst/wcsinstd/models.py | ||
---|---|---|
1 |
from django.db import models |
|
2 |
|
|
3 |
# Create your models here. |
wcsinst/wcsinstd/urls.py | ||
---|---|---|
1 |
from django.conf.urls import patterns, url, include |
|
2 |
|
|
3 |
urlpatterns = patterns('wcsinst.wcsinstd.views', |
|
4 |
url(r'^create$', 'create'), |
|
5 |
url(r'^(?P<instance>[\w\.-]+)/$', 'update'), |
|
6 |
) |
|
7 |
|
wcsinst/wcsinstd/views.py | ||
---|---|---|
1 |
import json |
|
2 |
import threading |
|
3 |
|
|
4 |
from django.views.decorators.csrf import csrf_exempt |
|
5 |
from django.views.decorators.http import require_POST |
|
6 |
|
|
7 |
from jsonresponse import to_json |
|
8 |
|
|
9 |
from .deploy import DeployInstance |
|
10 |
|
|
11 |
|
|
12 |
@csrf_exempt |
|
13 |
@to_json('api') |
|
14 |
@require_POST |
|
15 |
def create(request): |
|
16 |
data = json.loads(request.body) |
|
17 |
deploy = DeployInstance(**data) |
|
18 |
threading.Thread(target=deploy.make).start() |
|
19 |
return {} |
|
20 |
|
|
21 |
|
|
22 |
@csrf_exempt |
|
23 |
@to_json('api') |
|
24 |
@require_POST |
|
25 |
def update(request, instance): |
|
26 |
print 'updating instance:', instance |
|
27 |
data = json.loads(request.body) |
|
28 |
if data.get('domain') != instance: |
|
29 |
raise Exception('domain mismatch') # -> should remove/add ? |
|
30 |
deploy = DeployInstance(**data) |
|
31 |
threading.Thread(target=deploy.make).start() |
|
32 |
return {} |
wcsinst/wsgi.py | ||
---|---|---|
1 |
""" |
|
2 |
WSGI config for wcsinst project. |
|
3 |
|
|
4 |
This module contains the WSGI application used by Django's development server |
|
5 |
and any production WSGI deployments. It should expose a module-level variable |
|
6 |
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover |
|
7 |
this application via the ``WSGI_APPLICATION`` setting. |
|
8 |
|
|
9 |
Usually you will have the standard Django WSGI application here, but it also |
|
10 |
might make sense to replace the whole Django WSGI application with a custom one |
|
11 |
that later delegates to the Django one. For example, you could introduce WSGI |
|
12 |
middleware here, or combine a Django application with an application of another |
|
13 |
framework. |
|
14 |
|
|
15 |
""" |
|
16 |
import os |
|
17 |
|
|
18 |
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks |
|
19 |
# if running multiple sites in the same mod_wsgi process. To fix this, use |
|
20 |
# mod_wsgi daemon mode with each site in its own daemon process, or use |
|
21 |
# os.environ["DJANGO_SETTINGS_MODULE"] = "wcsinst.settings" |
|
22 |
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wcsinst.settings") |
|
23 |
|
|
24 |
# This application object is used by any WSGI server configured to use this |
|
25 |
# file. This includes Django's development server, if the WSGI_APPLICATION |
|
26 |
# setting points here. |
|
27 |
from django.core.wsgi import get_wsgi_application |
|
28 |
application = get_wsgi_application() |
|
29 |
|
|
30 |
# Apply WSGI middleware here. |
|
31 |
# from helloworld.wsgi import HelloWorldApplication |
|
32 |
# application = HelloWorldApplication(application) |
Formats disponibles : Unified diff
cleanning debian branch