Révision de6e5967
Ajouté par Jérôme Schneider il y a presque 10 ans
mandaye/__init__.py | ||
---|---|---|
1 |
VERSION=0.4
|
|
1 |
__version__='0.8.0'
|
|
2 | 2 |
|
3 | 3 |
import os |
4 | 4 |
|
mandaye/auth/authform.py | ||
---|---|---|
15 | 15 |
from lxml.html import fromstring |
16 | 16 |
from urlparse import parse_qs |
17 | 17 |
|
18 |
from mandaye import config, VERSION
|
|
18 |
from mandaye import config, __version__
|
|
19 | 19 |
from mandaye.exceptions import MandayeException |
20 | 20 |
from mandaye.log import logger |
21 | 21 |
from mandaye.http import HTTPResponse, HTTPHeader, HTTPRequest |
... | ... | |
32 | 32 |
|
33 | 33 |
class AuthForm(object): |
34 | 34 |
|
35 |
def __init__(self, form_values, site_name):
|
|
35 |
def __init__(self, env, mapper):
|
|
36 | 36 |
""" |
37 |
form_values: dict example : |
|
38 |
{ |
|
39 |
'login_url': '/login', |
|
40 |
'post_url': '/login', |
|
41 |
'form_attrs': { 'name': 'form40', }, |
|
42 |
'username_field': 'user', |
|
43 |
'password_field': 'pass', |
|
44 |
'post_fields': ['birthdate', 'card_number'] |
|
45 |
} |
|
46 |
login_url, form_attrs, post_fields and username_field are obligatory |
|
47 |
site_name: str with the site name |
|
37 |
env: WSGI environment |
|
38 |
mapper: mapper's module like mandaye.mappers.linuxfr |
|
48 | 39 |
""" |
49 |
if not form_values.has_key('form_headers'): |
|
50 |
form_values['form_headers'] = { |
|
40 |
self.env = env |
|
41 |
self.urls = mapper.urls |
|
42 |
self.site_name = self.env["mandaye.config"]["site_name"] |
|
43 |
self.form_values = mapper.form_values |
|
44 |
if not self.form_values.has_key('form_headers'): |
|
45 |
self.form_values['form_headers'] = { |
|
51 | 46 |
'Content-Type': 'application/x-www-form-urlencoded', |
52 |
'User-Agent': 'Mozilla/5.0 Mandaye/%s' % VERSION
|
|
47 |
'User-Agent': 'Mozilla/5.0 Mandaye/%s' % __version__
|
|
53 | 48 |
} |
54 | 49 |
|
55 |
if not form_values.has_key('post_fields') or \ |
|
56 |
not form_values.has_key('username_field') or \ |
|
57 |
not form_values.has_key('login_url'): |
|
50 |
if not self.form_values.has_key('post_fields') or \
|
|
51 |
not self.form_values.has_key('username_field') or \
|
|
52 |
not self.form_values.has_key('login_url'):
|
|
58 | 53 |
logger.critical("Bad configuration: AuthForm form_values dict must have \ |
59 | 54 |
this keys: post_fields and username_field") |
60 | 55 |
raise MandayeException, 'AuthForm bad configuration' |
61 |
if not form_values.has_key('form_attrs') and \ |
|
62 |
not form_values.has_key('post_url'): |
|
56 |
if not self.form_values.has_key('form_attrs') and \
|
|
57 |
not self.form_values.has_key('post_url'):
|
|
63 | 58 |
logger.critical("Bad configuration: you must set form_attrs or post_url") |
64 |
if config.encrypt_secret and not form_values.has_key('password_field'): |
|
59 |
if config.encrypt_secret and not self.form_values.has_key('password_field'):
|
|
65 | 60 |
logger.critical("Bad configuration: AuthForm form_values dict must have a \ |
66 | 61 |
a password_field key if you want to encode a password.") |
67 | 62 |
raise MandayeException, 'AuthForm bad configuration' |
68 | 63 |
|
69 |
self.login_url = form_values.get('login_url') |
|
70 |
self.form_values = form_values |
|
64 |
self.login_url = self.form_values.get('login_url') |
|
71 | 65 |
if not self.form_values.has_key('post_fields'): |
72 | 66 |
self.form_values['post_fields'] = [] |
73 |
self.site_name = site_name |
|
67 |
|
|
68 |
def get_default_mapping(self): |
|
69 |
return [] |
|
74 | 70 |
|
75 | 71 |
def encrypt_pwd(self, password): |
76 | 72 |
""" This method allows you to encrypt a password |
... | ... | |
207 | 203 |
if request.msg: |
208 | 204 |
if not unique_id: |
209 | 205 |
logger.warning("Association failed: user isn't login on Mandaye") |
210 |
return _302(values.get('connection_url'))
|
|
206 |
return _302(self.urls.get('connection_url'))
|
|
211 | 207 |
if type(request.msg) == str: |
212 | 208 |
post = parse_qs(request.msg, request) |
213 | 209 |
else: |
... | ... | |
221 | 217 |
if not post.has_key(field): |
222 | 218 |
logger.info('Association auth failed: form not correctly filled') |
223 | 219 |
qs['type'] = 'badlogin' |
224 |
return _302(values.get('associate_url') + "?%s" % urllib.urlencode(qs))
|
|
220 |
return _302(self.urls.get('associate_url') + "?%s" % urllib.urlencode(qs))
|
|
225 | 221 |
post_values[field] = post[field][0] |
226 | 222 |
response = self.replay(env, post_values) |
227 | 223 |
if eval(condition): |
... | ... | |
232 | 228 |
return response |
233 | 229 |
logger.info('Auth failed: Bad password or login') |
234 | 230 |
qs['type'] = 'badlogin' |
235 |
return _302(values.get('associate_url') + "?%s" % urllib.urlencode(qs))
|
|
231 |
return _302(self.urls.get('associate_url') + "?%s" % urllib.urlencode(qs))
|
|
236 | 232 |
|
237 | 233 |
def _login_sp_user(self, sp_user, env, condition, values): |
238 | 234 |
""" Log in sp user |
... | ... | |
257 | 253 |
else: |
258 | 254 |
return response |
259 | 255 |
else: |
260 |
return _302(values.get('associate_url') + "?type=failed")
|
|
256 |
return _302(self.urls.get('associate_url') + "?type=failed")
|
|
261 | 257 |
|
262 | 258 |
def login(self, env, values, condition, request, response): |
263 | 259 |
""" Automatic login on a site with a form |
... | ... | |
282 | 278 |
sp_user = backend.ManagerSPUser.get_last_connected(idp_user, service_provider) |
283 | 279 |
if not sp_user: |
284 | 280 |
logger.debug('User %s is not associate' % env['beaker.session']['unique_id']) |
285 |
return _302(values.get('associate_url') + "?type=first")
|
|
281 |
return _302(self.urls.get('associate_url') + "?type=first")
|
|
286 | 282 |
return self._login_sp_user(sp_user, env, condition, values) |
287 | 283 |
|
288 | 284 |
def logout(self, env, values, request, response): |
... | ... | |
333 | 329 |
idp_user = backend.ManagerIDPUser.get(unique_id) |
334 | 330 |
sp_user = backend.ManagerSPUser.get_last_connected(idp_user, service_provider) |
335 | 331 |
if not sp_user: |
336 |
return _302(values.get('associate_url'))
|
|
332 |
return _302(self.urls.get('associate_url'))
|
|
337 | 333 |
return self._login_sp_user(sp_user, env, 'response.code==302', values) |
338 | 334 |
|
339 | 335 |
def disassociate(self, env, values, request, response): |
mandaye/auth/saml2.py | ||
---|---|---|
9 | 9 |
from mandaye import config, utils |
10 | 10 |
from mandaye.saml import saml2utils |
11 | 11 |
from mandaye.auth.authform import AuthForm |
12 |
from mandaye.exceptions import MandayeSamlException |
|
12 |
from mandaye.exceptions import MandayeSamlException, ImproperlyConfigured
|
|
13 | 13 |
from mandaye.response import _302, _401 |
14 | 14 |
from mandaye.log import logger |
15 | 15 |
from mandaye.http import HTTPResponse, HTTPHeader, HTTPRequest |
16 | 16 |
from mandaye.server import get_response |
17 | 17 |
|
18 |
""" |
|
19 |
Mandaye saml2 authentification support |
|
20 |
|
|
21 |
To use it you must set the following options into your |
|
22 |
virtual host : |
|
23 |
* saml2_idp_metadata: a link to your idp metadata |
|
24 |
* saml2_signature_public_key: a path to your public key |
|
25 |
* saml2_signature_private_key: a path to your private key |
|
26 |
|
|
27 |
Optional options : |
|
28 |
* saml2_sp_logout_url: the url to logout the service provider |
|
29 |
* saml2_sp_logout_method: GET or POST |
|
30 |
* saml2_authnresp_binding: only post is supported for now |
|
31 |
* saml2_authnreq_http_method: only http_redirect at the moment |
|
32 |
* saml2_name_identifier_format: only persistent at the moment |
|
33 |
""" |
|
34 |
|
|
35 |
END_POINTS_PATH = { |
|
36 |
'metadata': '/mandaye/metadata', |
|
37 |
'single_sign_on_post': '/mandaye/singleSignOnPost', |
|
38 |
'single_logout': '/mandaye/singleLogout', |
|
39 |
'single_logout_return': '/mandaye/singleLogoutReturn', |
|
40 |
} |
|
41 |
|
|
18 | 42 |
class SAML2Auth(AuthForm): |
19 | 43 |
""" SAML 2 authentification |
20 | 44 |
""" |
21 | 45 |
|
22 |
def __init__(self, form_values, site_name, saml2_config):
|
|
46 |
def __init__(self, env, mapper):
|
|
23 | 47 |
""" saml2_config: saml 2 config module |
48 |
env: WSGI environment |
|
49 |
mapper: mapper's module like mandaye.mappers.linuxfr |
|
24 | 50 |
""" |
25 |
self.config = saml2_config |
|
51 |
self.env = env |
|
52 |
for param in ('saml2_idp_metadata', |
|
53 |
'saml2_signature_public_key', |
|
54 |
'saml2_signature_private_key'): |
|
55 |
if not self.env['mandaye.config'].has_key(param): |
|
56 |
err = 'you must set %s option in vhost : %s' % \ |
|
57 |
(param, self.env['mandaye.vhost']) |
|
58 |
logger.error(err) |
|
59 |
raise ImproperlyConfigured, err |
|
60 |
public_key = self._get_file_content( |
|
61 |
self.env['mandaye.config']['saml2_signature_public_key'] |
|
62 |
) |
|
63 |
private_key = self._get_file_content( |
|
64 |
self.env['mandaye.config']['saml2_signature_private_key'] |
|
65 |
) |
|
66 |
self.config = { |
|
67 |
'saml2_idp_metadata': self.env['mandaye.config']['saml2_idp_metadata'], |
|
68 |
'saml2_signature_public_key': public_key, |
|
69 |
'saml2_signature_private_key': private_key, |
|
70 |
'saml2_sp_logout_url': None, |
|
71 |
'saml2_sp_logout_method': 'GET', |
|
72 |
'saml2_authnresp_binding': lasso.SAML2_METADATA_BINDING_POST, |
|
73 |
'saml2_authnreq_http_method': lasso.HTTP_METHOD_REDIRECT, |
|
74 |
'saml2_name_identifier_format': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT |
|
75 |
} |
|
76 |
|
|
26 | 77 |
self.metadata_map = ( |
27 | 78 |
('AssertionConsumerService', |
28 | 79 |
lasso.SAML2_METADATA_BINDING_POST , |
29 |
self.config.END_POINTS_PATH['single_sign_on_post']
|
|
80 |
END_POINTS_PATH['single_sign_on_post'] |
|
30 | 81 |
), |
31 | 82 |
('SingleLogoutService', |
32 | 83 |
lasso.SAML2_METADATA_BINDING_REDIRECT, |
33 |
self.config.END_POINTS_PATH['single_logout'],
|
|
34 |
self.config.END_POINTS_PATH['single_logout_return']),
|
|
84 |
END_POINTS_PATH['single_logout'], |
|
85 |
END_POINTS_PATH['single_logout_return']), |
|
35 | 86 |
) |
36 |
self.metadata_options = { 'key': self.config.SAML_SIGNATURE_PUBLIC_KEY } |
|
37 |
super(SAML2Auth, self).__init__(form_values, site_name) |
|
38 |
|
|
87 |
self.metadata_options = { 'key': public_key } |
|
88 |
super(SAML2Auth, self).__init__(env, mapper) |
|
89 |
|
|
90 |
def _get_file_content(self, path): |
|
91 |
if not os.path.isabs(path): |
|
92 |
path = os.path.join(config.config_root, |
|
93 |
path) |
|
94 |
if not os.path.exists(path): |
|
95 |
err = "%s: file %s doesn't exist" % \ |
|
96 |
(path, self.env['mandaye.vhost']) |
|
97 |
logger.error(err) |
|
98 |
raise ImproperlyConfigured, err |
|
99 |
with open(path, 'r') as f: |
|
100 |
content = f.read() |
|
101 |
return content |
|
39 | 102 |
|
40 | 103 |
def get_default_mapping(self): |
41 | 104 |
return [ |
42 | 105 |
{ |
43 |
'path': r'/mandaye/login$', |
|
44 |
'method': 'GET', |
|
45 |
'response': [{ |
|
46 |
'filter': self.login, |
|
47 |
'values': { |
|
48 |
'associate_url': '/mandaye/associate', |
|
49 |
}, |
|
50 |
'condition': 'response.code==302', |
|
51 |
},] |
|
52 |
}, |
|
53 |
{ |
|
54 |
'path': r'/mandaye/sso$', |
|
55 |
'method': 'GET', |
|
56 |
'response': [{ |
|
57 |
'filter': self.sso, |
|
58 |
}] |
|
59 |
}, |
|
60 |
{ |
|
61 |
'path': r'/mandaye/slo$', |
|
62 |
'method': 'GET', |
|
63 |
'response': [{ |
|
64 |
'filter': self.slo, |
|
65 |
}] |
|
66 |
}, |
|
67 |
{ |
|
68 |
'path': r'%s$' % self.config.END_POINTS_PATH['metadata'], |
|
106 |
'path': r'%s$' % END_POINTS_PATH['metadata'], |
|
69 | 107 |
'method': 'GET', |
70 | 108 |
'response': [{ |
71 | 109 |
'filter': self.metadata, |
72 | 110 |
}] |
73 | 111 |
}, |
74 | 112 |
{ |
75 |
'path': r'%s$' % self.config.END_POINTS_PATH['single_sign_on_post'],
|
|
113 |
'path': r'%s$' % END_POINTS_PATH['single_sign_on_post'], |
|
76 | 114 |
'method': 'POST', |
77 |
'response': [{ |
|
78 |
'filter': self.single_sign_on_post, |
|
79 |
'values': { |
|
80 |
'login_url': '/mandaye/login', |
|
81 |
} |
|
82 |
}] |
|
115 |
'response': [{'filter': self.single_sign_on_post}] |
|
83 | 116 |
}, |
84 | 117 |
{ |
85 |
'path': r'%s$' % self.config.END_POINTS_PATH['single_logout'],
|
|
118 |
'path': r'%s$' % END_POINTS_PATH['single_logout'], |
|
86 | 119 |
'method': 'GET', |
87 | 120 |
'response': [{ |
88 | 121 |
'filter': self.single_logout, |
89 | 122 |
}] |
90 | 123 |
}, |
91 | 124 |
{ |
92 |
'path': r'%s$' % self.config.END_POINTS_PATH['single_logout_return'],
|
|
125 |
'path': r'%s$' % END_POINTS_PATH['single_logout_return'], |
|
93 | 126 |
'method': 'GET', |
94 | 127 |
'response': [{ |
95 | 128 |
'filter': self.single_logout_return, |
... | ... | |
112 | 145 |
next_url = values['next_url'] |
113 | 146 |
|
114 | 147 |
req_cookies = request.cookies |
115 |
if not self.config.SP_LOGOUT_URL:
|
|
148 |
if not self.config['saml2_sp_logout_url']:
|
|
116 | 149 |
logger.warning('SP_LOGOUT_URL not set into saml2 configuration only removing cookies') |
117 | 150 |
for cookie in req_cookies.values(): |
118 | 151 |
cookie['expires'] = 'Thu, 01 Jan 1970 00:00:01 GMT' |
... | ... | |
121 | 154 |
return _302(next_url, req_cookies) |
122 | 155 |
else: |
123 | 156 |
return _302('/', req_cookies) |
124 |
if self.config.SP_LOGOUT_METHOD == 'POST':
|
|
157 |
if self.config['saml2_sp_logout_method'] == 'POST':
|
|
125 | 158 |
headers = HTTPHeader({'Content-Type': ['application/x-www-form-urlencoded']}) |
126 | 159 |
else: |
127 | 160 |
headers = HTTPHeader() |
128 |
request = HTTPRequest(req_cookies, headers, self.config.SP_LOGOUT_METHOD)
|
|
129 |
response = get_response(env, request, self.config.SP_LOGOUT_URL)
|
|
161 |
request = HTTPRequest(req_cookies, headers, self.config['saml2_sp_logout_method'])
|
|
162 |
response = get_response(env, request, self.config['saml2_sp_logout_url'])
|
|
130 | 163 |
if next_url: |
131 | 164 |
return _302(next_url, response.cookies) |
132 | 165 |
else: |
... | ... | |
134 | 167 |
|
135 | 168 |
def _get_idp_metadata_file_path(self): |
136 | 169 |
metadata_file_path = None |
137 |
if self.config.IDP_METADATA:
|
|
170 |
if self.config['saml2_idp_metadata']:
|
|
138 | 171 |
metadata_file_path = os.path.join(config.data_dir, |
139 |
self.config.IDP_METADATA.\
|
|
172 |
self.config['saml2_idp_metadata'].\
|
|
140 | 173 |
replace('://', '_').\ |
141 | 174 |
replace('/', '_') |
142 | 175 |
) |
143 | 176 |
if not os.path.isfile(metadata_file_path): |
144 | 177 |
try: |
145 |
response = urllib2.urlopen(self.config.IDP_METADATA)
|
|
178 |
response = urllib2.urlopen(self.config['saml2_idp_metadata'])
|
|
146 | 179 |
metadata = response.read() |
147 | 180 |
response.close() |
148 | 181 |
except Exception, e: |
149 | 182 |
logger.error("Unable to fetch metadata %s: %s" % \ |
150 |
(self.config.IDP_METADATA, str(e)))
|
|
183 |
(self.config['saml2_idp_metadata'], str(e)))
|
|
151 | 184 |
raise MandayeSamlException("Unable to find metadata: %s" % str(e)) |
152 |
metadata_file = open(metadata_file_path, 'a+')
|
|
185 |
metadata_file = open(metadata_file_path, 'w')
|
|
153 | 186 |
metadata_file.write(metadata) |
154 | 187 |
metadata_file.close() |
155 | 188 |
return metadata_file_path |
156 | 189 |
|
157 | 190 |
def _get_metadata(self, env): |
158 | 191 |
url_prefix = env['mandaye.scheme'] + '://' + env['HTTP_HOST'] |
159 |
metadata_path = self.config.END_POINTS_PATH['metadata']
|
|
192 |
metadata_path = END_POINTS_PATH['metadata'] |
|
160 | 193 |
single_sign_on_post_path = \ |
161 |
self.config.END_POINTS_PATH['single_sign_on_post']
|
|
194 |
END_POINTS_PATH['single_sign_on_post'] |
|
162 | 195 |
metagen = saml2utils.Saml2Metadata(url_prefix + metadata_path, |
163 | 196 |
url_prefix = url_prefix) |
164 | 197 |
metagen.add_sp_descriptor(self.metadata_map, self.metadata_options) |
... | ... | |
168 | 201 |
if env['beaker.session'].has_key('unique_id'): |
169 | 202 |
return _302('/') |
170 | 203 |
qs = parse_qs(env['QUERY_STRING']) |
171 |
target_idp = self.config.IDP_METADATA
|
|
204 |
target_idp = self.config['saml2_idp_metadata']
|
|
172 | 205 |
metadata_file_path = self._get_idp_metadata_file_path() |
173 | 206 |
if not metadata_file_path: |
174 | 207 |
raise MandayeSamlException("sso: unable to load provider") |
175 | 208 |
logger.debug('sso: target_idp is %s' % target_idp) |
176 |
logger.debug('sso: metadata url is %s' % self.config.IDP_METADATA)
|
|
209 |
logger.debug('sso: metadata url is %s' % self.config['saml2_idp_metadata'])
|
|
177 | 210 |
logger.debug('sso: mandaye metadata are %s' % self._get_metadata(env)) |
178 | 211 |
server = lasso.Server.newFromBuffers(self._get_metadata(env), |
179 |
self.config.SAML_SIGNATURE_PRIVATE_KEY)
|
|
212 |
self.config['saml2_signature_private_key'])
|
|
180 | 213 |
if not server: |
181 | 214 |
raise MandayeSamlException("sso: error creating server object.") |
182 | 215 |
logger.debug('sso: mandaye server object created') |
... | ... | |
184 | 217 |
login = lasso.Login(server) |
185 | 218 |
if not login: |
186 | 219 |
raise MandayeSamlException("sso: error creating login object.") |
187 |
http_method = self.config.AUTHNREQ_HTTP_METHOD
|
|
220 |
http_method = self.config['saml2_authnreq_http_method']
|
|
188 | 221 |
try: |
189 | 222 |
login.initAuthnRequest(target_idp, http_method) |
190 | 223 |
except lasso.Error, error: |
191 | 224 |
raise MandayeSamlException("sso: Error initiating request %s" % lasso.strError(error[0])) |
192 |
login.request.nameIDPolicy.format = self.config.SAML2_NAME_IDENTIFIER_FORMAT
|
|
193 |
login.request.nameIDPolicy.allowCreate = self.config.ALLOW_CREATE
|
|
194 |
login.request.nameIDPolicy.spNameQualifier = self.config.SP_NAME_QUALIFIER
|
|
195 |
login.request.protocolBinding = self.config.AUTHNRESP_BINDING
|
|
225 |
login.request.nameIDPolicy.format = self.config['saml2_name_identifier_format']
|
|
226 |
login.request.nameIDPolicy.allowCreate = True
|
|
227 |
login.request.nameIDPolicy.spNameQualifier = None
|
|
228 |
login.request.protocolBinding = self.config['saml2_authnresp_binding']
|
|
196 | 229 |
if qs.has_key('next_url'): |
197 | 230 |
login.msgRelayState = qs['next_url'][0] |
198 | 231 |
try: |
... | ... | |
213 | 246 |
if not metadata_file_path: |
214 | 247 |
raise MandayeSamlException("single_sign_on_post: Unable to load provider") |
215 | 248 |
server = lasso.Server.newFromBuffers(self._get_metadata(env), |
216 |
self.config.SAML_SIGNATURE_PRIVATE_KEY)
|
|
249 |
self.config['saml2_signature_private_key'])
|
|
217 | 250 |
if not server: |
218 | 251 |
raise MandayeSamlException("singleSignOnPost: error creating server object") |
219 | 252 |
server.addProvider(lasso.PROVIDER_ROLE_IDP, metadata_file_path) |
... | ... | |
230 | 263 |
raise MandayeSamlException("singleSignOnPost: Missing response") |
231 | 264 |
message = params[lasso.SAML2_FIELD_RESPONSE][0] |
232 | 265 |
logger.debug('sso: message posted %s' % message) |
233 |
|
|
234 |
try: |
|
235 |
login.processAuthnResponseMsg(message) |
|
236 |
except: |
|
237 |
raise MandayeSamlException("singleSignOnPost: Unable to proces authnresponse message") |
|
266 |
login.processAuthnResponseMsg(message) |
|
238 | 267 |
|
239 | 268 |
subject_confirmation = utils.get_absolute_uri(env) |
240 | 269 |
if not env['beaker.session'].has_key('request_id'): |
... | ... | |
268 | 297 |
next_url = values['next_url'] |
269 | 298 |
|
270 | 299 |
if next_url: |
271 |
return _302("%s?next_url=%s" % (values['login_url'], next_url))
|
|
300 |
return _302("%s?next_url=%s" % (self.urls.get('login_url'), next_url))
|
|
272 | 301 |
else: |
273 |
return _302(values['login_url'])
|
|
302 |
return _302(self.urls.get('login_url'))
|
|
274 | 303 |
|
275 | 304 |
def slo(self, env, values, request, response): |
276 | 305 |
""" |
277 | 306 |
Single Logout SP initiated by redirected |
278 | 307 |
""" |
279 | 308 |
logger.debug('slo: new slo request') |
280 |
target_idp = self.config.IDP_METADATA
|
|
309 |
target_idp = self.config['saml2_idp_metadata']
|
|
281 | 310 |
metadata_file_path = self._get_idp_metadata_file_path() |
282 | 311 |
if not metadata_file_path: |
283 | 312 |
raise MandayeSamlException("slo: Unable to load provider.") |
... | ... | |
285 | 314 |
logger.debug('slo: metadata file path %s' % metadata_file_path) |
286 | 315 |
|
287 | 316 |
server = lasso.Server.newFromBuffers(self._get_metadata(env), |
288 |
self.config.SAML_SIGNATURE_PRIVATE_KEY)
|
|
317 |
self.config['saml2_signature_private_key'])
|
|
289 | 318 |
if not server: |
290 | 319 |
raise MandayeSamlException("slo: Error creating server object") |
291 | 320 |
logger.debug('slo: mandaye server object created') |
... | ... | |
339 | 368 |
Redirect without query string") |
340 | 369 |
|
341 | 370 |
server = lasso.Server.newFromBuffers(self._get_metadata(env), |
342 |
self.config.SAML_SIGNATURE_PRIVATE_KEY)
|
|
371 |
self.config['saml2_signature_private_key'])
|
|
343 | 372 |
if not server: |
344 | 373 |
logger.warning('single_logout: Service provider not configured') |
345 | 374 |
return _401('single_logout: Service provider not configured') |
... | ... | |
378 | 407 |
""" |
379 | 408 |
metadata_file_path = self._get_idp_metadata_file_path() |
380 | 409 |
server = lasso.Server.newFromBuffers(self._get_metadata(env), |
381 |
self.config.SAML_SIGNATURE_PRIVATE_KEY)
|
|
410 |
self.config['saml2_signature_private_key'])
|
|
382 | 411 |
server.addProvider(lasso.PROVIDER_ROLE_IDP, metadata_file_path) |
383 | 412 |
if not server: |
384 | 413 |
logger.error('single_logout_return: Error creating server object.') |
mandaye/configs/saml2.py | ||
---|---|---|
1 |
|
|
2 |
import lasso |
|
3 |
|
|
4 |
# END_POINTS |
|
5 |
END_POINTS_PATH = { |
|
6 |
'metadata': '/mandaye/metadata', |
|
7 |
'single_sign_on_post': '/mandaye/singleSignOnPost', |
|
8 |
'single_logout': '/mandaye/singleLogout', |
|
9 |
'single_logout_return': '/mandaye/singleLogoutReturn', |
|
10 |
} |
|
11 |
|
|
12 |
# Now only support for a single IdP |
|
13 |
IDP_METADATA = ("http://www.identity-hub.net/idp/saml2/metadata") |
|
14 |
|
|
15 |
# SAML2 SP options |
|
16 |
AUTHNREQ_HTTP_METHOD = lasso.HTTP_METHOD_REDIRECT |
|
17 |
|
|
18 |
# Only persistent make sense |
|
19 |
SAML2_NAME_IDENTIFIER_FORMAT = lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT |
|
20 |
ALLOW_CREATE = True |
|
21 |
SP_NAME_QUALIFIER = None |
|
22 |
|
|
23 |
# Only POST is supported for now |
|
24 |
AUTHNRESP_BINDING = lasso.SAML2_METADATA_BINDING_POST |
|
25 |
|
|
26 |
## Configure local SP logout |
|
27 |
# If None remove cookies to logout |
|
28 |
SP_LOGOUT_URL = None |
|
29 |
SP_LOGOUT_METHOD = 'GET' |
|
30 |
|
|
31 |
# FIXME: change this keys |
|
32 |
SAML_SIGNATURE_PUBLIC_KEY = '''-----BEGIN CERTIFICATE----- |
|
33 |
MIIDIzCCAgugAwIBAgIJANUBoick1pDpMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV |
|
34 |
BAoTCkVudHJvdXZlcnQwHhcNMTAxMjE0MTUzMzAyWhcNMTEwMTEzMTUzMzAyWjAV |
|
35 |
MRMwEQYDVQQKEwpFbnRyb3V2ZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB |
|
36 |
CgKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZn9Kqm4Cp |
|
37 |
06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrUH8QT8NGh |
|
38 |
ABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59xihSqsoFr |
|
39 |
kmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9Hri8JRdXi |
|
40 |
VT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziazZfvvw/VG |
|
41 |
Tm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABo3YwdDAdBgNVHQ4EFgQUeF8ePnu0 |
|
42 |
fcAK50iBQDgAhHkOu8kwRQYDVR0jBD4wPIAUeF8ePnu0fcAK50iBQDgAhHkOu8mh |
|
43 |
GaQXMBUxEzARBgNVBAoTCkVudHJvdXZlcnSCCQDVAaInJNaQ6TAMBgNVHRMEBTAD |
|
44 |
AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAy8l3GhUtpPHx0FxzbRHVaaUSgMwYKGPhE |
|
45 |
IdGhqekKUJIx8et4xpEMFBl5XQjBNq/mp5vO3SPb2h2PVSks7xWnG3cvEkqJSOeo |
|
46 |
fEEhkqnM45b2MH1S5uxp4i8UilPG6kmQiXU2rEUBdRk9xnRWos7epVivTSIv1Ncp |
|
47 |
lG6l41SXp6YgIb2ToT+rOKdIGIQuGDlzeR88fDxWEU0vEujZv/v1PE1YOV0xKjTT |
|
48 |
JumlBc6IViKhJeo1wiBBrVRIIkKKevHKQzteK8pWm9CYWculxT26TZ4VWzGbo06j |
|
49 |
o2zbumirrLLqnt1gmBDvDvlOwC/zAAyL4chbz66eQHTiIYZZvYgy |
|
50 |
-----END CERTIFICATE-----''' |
|
51 |
|
|
52 |
SAML_SIGNATURE_PRIVATE_KEY = '''-----BEGIN RSA PRIVATE KEY----- |
|
53 |
MIIEpAIBAAKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZ |
|
54 |
n9Kqm4Cp06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrU |
|
55 |
H8QT8NGhABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59x |
|
56 |
ihSqsoFrkmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9H |
|
57 |
ri8JRdXiVT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziaz |
|
58 |
Zfvvw/VGTm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABAoIBAQCj8t2iKXya10HG |
|
59 |
V6Saaeih8aftoLBV38VwFqqjPU0+iKqDpk2JSXBhjI6s7uFIsaTNJpR2Ga1qvns1 |
|
60 |
hJQEDMQSLhJvXfBgSkHylRWCpJentr4E3D7mnw5pRsd61Ev9U+uHcdv/WHP4K5hM |
|
61 |
xsdiwXNXD/RYd1Q1+6bKrCuvnNJVmWe0/RV+r3T8Ni5xdMVFbRWt/VEoE620XX6c |
|
62 |
a9TQPiA5i/LRVyie+js7Yv+hVjGOlArtuLs6ECQsivfPrqKLOBRWcofKdcf+4N2e |
|
63 |
3cieUqwzC15C31vcMliD9Hax9c1iuTt9Q3Xzo20fOSazAnQ5YBEExyTtrFBwbfQu |
|
64 |
ku6hp81pAoGBAN6bc6iJtk5ipYpsaY4ZlbqdjjG9KEXB6G1MExPU7SHXOhOF0cDH |
|
65 |
/pgMsv9hF2my863MowsOj3OryVhdQhwA6RrV263LRh+JU8NyHV71BwAIfI0BuVfj |
|
66 |
6r24KudwtUcvMr9pJIrJyMAMaw5ZyNoX7YqFpS6fcisSJYdSBSoxzrzVAoGBANu6 |
|
67 |
xVeMqGavA/EHSOQP3ipDZ3mnWbkDUDxpNhgJG8Q6lZiwKwLoSceJ8z0PNY3VetGA |
|
68 |
RbqtqBGfR2mcxHyzeqVBpLnXZC4vs/Vy7lrzTiHDRZk2SG5EkHMSKFA53jN6S/nJ |
|
69 |
JWpYZC8lG8w4OHaUfDHFWbptxdGYCgY4//sjeiuXAoGBANuhurJ99R5PnA8AOgEW |
|
70 |
4zD1hLc0b4ir8fvshCIcAj9SUB20+afgayRv2ye3Dted1WkUL4WYPxccVhLWKITi |
|
71 |
rRtqB03o8m3pG3kJnUr0LIzu0px5J/o8iH3ZOJOTE3iBa+uI/KHmxygc2H+XPGFa |
|
72 |
HGeAxuJCNO2kAN0Losbnz5dlAoGAVsCn94gGWPxSjxA0PC7zpTYVnZdwOjbPr/pO |
|
73 |
LDE0cEY9GBq98JjrwEd77KibmVMm+Z4uaaT0jXiYhl8pyJ5IFwUS13juCbo1z/u/ |
|
74 |
ldMoDvZ8/R/MexTA/1204u/mBecMJiO/jPw3GdIJ5phv2omHe1MSuSNsDfN8Sbap |
|
75 |
gmsgaiMCgYB/nrTk89Fp7050VKCNnIt1mHAcO9cBwDV8qrJ5O3rIVmrg1T6vn0aY |
|
76 |
wRiVcNacaP+BivkrMjr4BlsUM6yH4MOBsNhLURiiCL+tLJV7U0DWlCse/doWij4U |
|
77 |
TKX6tp6oI+7MIJE6ySZ0cBqOiydAkBePZhu57j6ToBkTa0dbHjn1WA== |
|
78 |
-----END RSA PRIVATE KEY-----''' |
|
79 |
|
mandaye/dispatcher.py | ||
---|---|---|
1 | 1 |
|
2 |
import copy |
|
2 | 3 |
import re |
3 | 4 |
|
4 | 5 |
from urlparse import urlparse |
... | ... | |
12 | 13 |
|
13 | 14 |
# TODO: add an external url mapping |
14 | 15 |
|
15 |
def import_mapping(path): |
|
16 |
if not path or not '.' in path: |
|
17 |
return path |
|
18 |
i = path.rfind('.') |
|
19 |
module, attr = path[:i], path[i+1:] |
|
20 |
|
|
16 |
def import_mapping(name): |
|
17 |
if not name: |
|
18 |
return dict() |
|
19 |
if not config.mappers.has_key(name): |
|
20 |
logger.error("mapper %s not found" % name) |
|
21 |
return dict() |
|
22 |
module = config.mappers[name] |
|
21 | 23 |
try: |
22 |
mod = import_module(module)
|
|
24 |
mapper = import_module(module)
|
|
23 | 25 |
except ImportError, e: |
24 |
raise ImproperlyConfigured('Error importing mapping %s: "%s"' % (path, e)) |
|
25 |
try: |
|
26 |
mapping = getattr(mod, attr) |
|
27 |
except AttributeError: |
|
28 |
raise ImproperlyConfigured('Module "%s" does not define a "%s" mapping' % (module, attr)) |
|
29 |
return mapping |
|
26 |
raise ImproperlyConfigured('Error importing mapping %s: "%s"' % (module, e)) |
|
27 |
return mapper |
|
30 | 28 |
|
31 | 29 |
|
32 | 30 |
class Dispatcher(object): |
... | ... | |
34 | 32 |
It allows you to launch the right filter on the reqest and the response |
35 | 33 |
""" |
36 | 34 |
|
37 |
def __init__(self, env, target_url, mapping=dict()):
|
|
35 |
def __init__(self, env, target_url, mapper_name=None):
|
|
38 | 36 |
""" env: wsgi environ |
39 | 37 |
target_url: the full url of your destination |
40 |
mapping: dict with the site mapping
|
|
38 |
mepper: python module with the mapper
|
|
41 | 39 |
""" |
42 | 40 |
self.target = urlparse(target_url) |
43 | 41 |
self.env = env |
44 | 42 |
self.env['target'] = self.target |
45 |
self.mapping = import_mapping(mapping) |
|
43 |
self.mapper_name = mapper_name |
|
44 |
self.mapper = import_mapping(mapper_name) |
|
46 | 45 |
self.filter = MandayeFilter(self.env, self.target) |
47 |
self.req_mapping = self._parse_mapping(self.mapping) |
|
46 |
auth_type = self.env['mandaye.config']['auth_type'] |
|
47 |
path = config.authentifications[auth_type] |
|
48 |
i = path.rfind('.') |
|
49 |
module, attr = path[:i], path[i+1:] |
|
50 |
module = import_module(module) |
|
51 |
Auth = getattr(module, attr) |
|
52 |
self.auth = Auth(env, self.mapper) |
|
53 |
mapping = copy.deepcopy(self.mapper.mapping) |
|
54 |
mapping.extend(self.auth.get_default_mapping()) |
|
55 |
self.req_mapping = self._parse_mapping(mapping) |
|
48 | 56 |
|
49 | 57 |
def __get_mappings_hooks(self, mapper, req_mapping): |
50 | 58 |
""" fill the request mapping with the right hooks |
... | ... | |
55 | 63 |
for hookname in req_mapping: |
56 | 64 |
if mapper.has_key(hookname): |
57 | 65 |
if isinstance(req_mapping[hookname], list): |
58 |
req_mapping[hookname].extend(mapper[hookname]) |
|
66 |
for entry in mapper[hookname]: |
|
67 |
if entry.has_key('auth'): |
|
68 |
entry['filter'] = getattr(self.auth, entry['auth']) |
|
69 |
req_mapping[hookname].append(entry) |
|
59 | 70 |
else: |
60 | 71 |
req_mapping[hookname] = mapper[hookname] |
61 | 72 |
return req_mapping |
... | ... | |
67 | 78 |
req_mapping = { |
68 | 79 |
'on_request': [], |
69 | 80 |
'on_response': [], |
70 |
'response': None,
|
|
81 |
'response': [],
|
|
71 | 82 |
'target': None, |
72 | 83 |
'redirect': None, |
73 | 84 |
'decompress': config.auto_decompress |
... | ... | |
75 | 86 |
|
76 | 87 |
if not mapping: |
77 | 88 |
return req_mapping |
78 |
for mapper in mapping:
|
|
79 |
if mapper.has_key('path'):
|
|
80 |
if isinstance(mapper['path'], str):
|
|
81 |
if re.match(mapper['path'], self.env['PATH_INFO']):
|
|
82 |
req_mapping = self.__get_mappings_hooks(mapper, req_mapping)
|
|
89 |
for entry in mapping:
|
|
90 |
if entry.has_key('path'):
|
|
91 |
if isinstance(entry['path'], str):
|
|
92 |
if re.match(entry['path'], self.env['PATH_INFO']):
|
|
93 |
req_mapping = self.__get_mappings_hooks(entry, req_mapping)
|
|
83 | 94 |
else: |
84 |
for path in mapper['path']:
|
|
95 |
for path in entry['path']:
|
|
85 | 96 |
if re.match(path, self.env['PATH_INFO']): |
86 |
req_mapping = self.__get_mappings_hooks(mapper, req_mapping)
|
|
97 |
req_mapping = self.__get_mappings_hooks(entry, req_mapping)
|
|
87 | 98 |
else: |
88 | 99 |
logger.warning('Config error: you need to specify paths in your mapping') |
89 | 100 |
return req_mapping |
mandaye/filters/default.py | ||
---|---|---|
41 | 41 |
if key == 'location': |
42 | 42 |
location = value[0].replace(self.target.geturl(), |
43 | 43 |
"%s://%s" % (self.env['mandaye.scheme'], self.env["HTTP_HOST"])) |
44 |
response.headers.addheader(key, location)
|
|
44 |
response.headers['location'] = [location]
|
|
45 | 45 |
for name in blacklist: |
46 | 46 |
if response.headers.has_key(name): |
47 | 47 |
del response.headers[name] |
mandaye/global_config.py | ||
---|---|---|
9 | 9 |
|
10 | 10 |
## SQL Backend config |
11 | 11 |
# Database configuration |
12 |
# rfc 1738 http://rfc.net/rfc1738.html |
|
12 |
# http://docs.sqlalchemy.org/en/rel_0_7/core/engines.html |
|
13 |
# rfc 1738 https://tools.ietf.org/html/rfc1738 |
|
14 |
# dialect+driver://username:password@host:port/database |
|
13 | 15 |
db_url = 'sqlite:///test.db' |
14 | 16 |
|
15 |
# Needed if ssl is activated |
|
16 |
ssl = False |
|
17 |
keyfile = '' |
|
18 |
certfile = '' |
|
19 | 17 |
|
20 | 18 |
# Log configuration |
21 | 19 |
import logging |
... | ... | |
34 | 32 |
|
35 | 33 |
# Template directory |
36 | 34 |
template_directory = os.path.join(_PROJECT_PATH, 'mandaye/templates') |
35 |
# Configuration directory |
|
36 |
config_root = os.path.join(_PROJECT_PATH, 'conf.d') |
|
37 | 37 |
# Static path |
38 | 38 |
static_url = '/mandaye/static' |
39 | 39 |
# Static folder |
... | ... | |
64 | 64 |
# Must be a 16, 24, or 32 bytes long |
65 | 65 |
encrypt_secret = '' |
66 | 66 |
|
67 |
hosts = {} |
|
68 |
|
|
69 | 67 |
alembic_cfg = os.path.join(_PROJECT_PATH, 'mandaye/alembic.ini') |
70 | 68 |
alembic_script_path = os.path.join(_PROJECT_PATH, 'mandaye/alembic') |
71 | 69 |
|
70 |
# supported authentification |
|
71 |
authentifications = { |
|
72 |
'saml2': 'mandaye.auth.SAML2Auth' |
|
73 |
} |
|
74 |
|
|
75 |
# supported mappers |
|
76 |
mappers = {} |
|
77 |
|
|
72 | 78 |
# beaker session configuration |
73 | 79 |
session_opts = { |
74 | 80 |
'session.type': 'file', |
... | ... | |
77 | 83 |
'session.data_dir': '/var/tmp/beaker' |
78 | 84 |
} |
79 | 85 |
|
86 |
# Needed if ssl is activated |
|
87 |
ssl = False |
|
88 |
keyfile = '' |
|
89 |
certfile = '' |
|
90 |
|
mandaye/http.py | ||
---|---|---|
33 | 33 |
def addheader(self, name, value): |
34 | 34 |
""" name: field name |
35 | 35 |
value: string with the field value """ |
36 |
if isinstance(value, str): |
|
37 |
self[name.lower()] = [value] |
|
38 |
else: |
|
39 |
raise TypeError('Header value must be a string') |
|
36 |
self[name.lower()] = [value] |
|
40 | 37 |
|
41 | 38 |
def delheader(self, name): |
42 | 39 |
""" name: field name |
mandaye/server.py | ||
---|---|---|
1 | 1 |
|
2 | 2 |
import Cookie |
3 |
import json |
|
3 | 4 |
import urllib2 |
4 | 5 |
import random |
5 | 6 |
import re |
6 | 7 |
import os |
7 |
import sys |
|
8 | 8 |
import time |
9 |
import traceback |
|
10 | 9 |
|
11 |
from beaker.middleware import SessionMiddleware |
|
12 |
from cgi import escape |
|
13 | 10 |
from md5 import md5 |
14 | 11 |
from urlparse import urlparse |
15 |
from static import Cling |
|
16 |
|
|
17 |
import mandaye |
|
18 | 12 |
|
19 | 13 |
from mandaye import config |
14 |
from mandaye.exceptions import ImproperlyConfigured |
|
20 | 15 |
from mandaye.dispatcher import Dispatcher |
21 | 16 |
from mandaye.log import logger, format_logging_handlers |
22 | 17 |
from mandaye.handlers.default import MandayeRedirectHandler, MandayeErrorHandler |
... | ... | |
84 | 79 |
self.env = None |
85 | 80 |
self.dispatcher = None |
86 | 81 |
|
87 |
|
|
88 | 82 |
def __call__(self, env, start_response): |
89 | 83 |
""" called by the WSGI server |
90 | 84 |
env: standard WSGI env |
... | ... | |
106 | 100 |
(self.env['REMOTE_ADDR'], self.env['REQUEST_METHOD'], |
107 | 101 |
self.env['wsgi.url_scheme'], self.env['HTTP_HOST'], |
108 | 102 |
self.env['PATH_INFO'])) |
109 |
if config.hosts.has_key(local_host): |
|
110 |
for mapper in config.hosts[local_host]: |
|
111 |
if re.match(mapper.get('path'), path_info): |
|
112 |
if mapper.get('force_ssl'): |
|
103 |
for conf in os.listdir(config.config_root): |
|
104 |
conf_file = os.path.join(config.config_root, conf) |
|
105 |
if os.path.isfile(conf_file): |
|
106 |
with open(conf_file, 'r') as f: |
|
107 |
conf = json.loads(f.read()) |
|
108 |
for param in ['site_name', 'location', 'target', 'server_name', 'mapper', 'auth_type']: |
|
109 |
if not conf.has_key(param): |
|
110 |
error = 'you must set %s option in vhost : %s' % \ |
|
111 |
(param, conf_file) |
|
112 |
logger.error(error) |
|
113 |
raise ImproperlyConfigured, error |
|
114 |
if not config.mappers.has_key(conf['mapper']): |
|
115 |
err = '%s: mapper %s not found' % \ |
|
116 |
(conf_file, conf['mapper']) |
|
117 |
logger.error(err) |
|
118 |
raise ImproperlyConfigured, err |
|
119 |
self.env['mandaye.auth_type'] = conf['mapper'] |
|
120 |
if not config.authentifications.has_key(conf['auth_type']): |
|
121 |
err = '%s: authentification %s not found' % \ |
|
122 |
(conf_file, conf['auth_type']) |
|
123 |
logger.error(err) |
|
124 |
raise ImproperlyConfigured, err |
|
125 |
if local_host in conf['server_name'] and \ |
|
126 |
re.match(re.compile(conf['location']), path_info): |
|
127 |
if conf.get('force_ssl'): |
|
113 | 128 |
self.env['mandaye.scheme'] = 'https' |
114 |
if mapper.has_key('target'): |
|
115 |
self.dispatcher = Dispatcher(self.env, mapper.get('target'), |
|
116 |
mapper.get('mapping')) |
|
117 |
elif mapper.has_key('static'): |
|
118 |
env['PATH_INFO'] = re.sub('^' + mapper.get('path'), '', |
|
119 |
env['PATH_INFO']) |
|
120 |
response = Cling(mapper.get('static')).__call__(env, start_response) |
|
129 |
self.env['mandaye.config'] = conf |
|
130 |
self.env['mandaye.vhost'] = conf_file |
|
131 |
self.dispatcher = Dispatcher(self.env, conf['target'], |
|
132 |
conf['mapper']) |
|
133 |
response = self.on_request(start_response) |
|
121 | 134 |
if not response: |
122 |
if self.dispatcher: |
|
123 |
response = self.on_request(start_response) |
|
124 |
else: |
|
125 |
response = self.on_response(start_response, _404(env['PATH_INFO'])) |
|
135 |
response = self.on_response(start_response, _404(env['PATH_INFO'])) |
|
126 | 136 |
sql_session.commit() |
127 | 137 |
except Exception, e: |
128 | 138 |
sql_session.rollback() |
... | ... | |
186 | 196 |
self.env['REQUEST_METHOD'] = request.req_method |
187 | 197 |
self.env['wsgi.input'] = request.msg |
188 | 198 |
self.dispatcher = Dispatcher(self.env, self.env['target'].geturl(), |
189 |
self.dispatcher.mapping)
|
|
199 |
self.dispatcher.mapper_name)
|
|
190 | 200 |
response = self.dispatcher.get_response(request) |
191 | 201 |
else: |
192 | 202 |
response = get_response(self.env, request, request.target) |
mandaye/skel/conf.d/certs/saml.crt | ||
---|---|---|
1 |
-----BEGIN PUBLIC KEY----- |
|
2 |
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxdbs+ZLkuz0DISpAKhHn |
|
3 |
WvNBSW4G0xmlUyZcjUWDQlJH7wC3yxhjioQ2oFpxqcuNf5ft/E1E5KUTqZhcKyX9 |
|
4 |
i7XCmhPoea/fmYH3Egxbucv7++sM+TyZpUWbA0TZHBYAjcUPR/1HTcEz3bl0SqB0 |
|
5 |
EdjhN5PpXPu1p4pGDPXc4aIkEpFU3mlK+TlV5SrivEqNS/SI14VA9g2WWdJk4+CK |
|
6 |
PgozCfeiFtaiu2zem4uQSmd5AG0f0Av4jzxgut22owFYi9PV+Yl0cWoMOUphAwsR |
|
7 |
RE4gckEqbhLYluAy+VglgzfT4YCXBQ6o23EH0Z0tW28KnIYEY4dQkLca9YRAKhHc |
|
8 |
ywIDAQAB |
|
9 |
-----END PUBLIC KEY----- |
mandaye/skel/conf.d/certs/saml.key | ||
---|---|---|
1 |
-----BEGIN RSA PRIVATE KEY----- |
|
2 |
MIIEogIBAAKCAQEAxdbs+ZLkuz0DISpAKhHnWvNBSW4G0xmlUyZcjUWDQlJH7wC3 |
|
3 |
yxhjioQ2oFpxqcuNf5ft/E1E5KUTqZhcKyX9i7XCmhPoea/fmYH3Egxbucv7++sM |
|
4 |
+TyZpUWbA0TZHBYAjcUPR/1HTcEz3bl0SqB0EdjhN5PpXPu1p4pGDPXc4aIkEpFU |
|
5 |
3mlK+TlV5SrivEqNS/SI14VA9g2WWdJk4+CKPgozCfeiFtaiu2zem4uQSmd5AG0f |
|
6 |
0Av4jzxgut22owFYi9PV+Yl0cWoMOUphAwsRRE4gckEqbhLYluAy+VglgzfT4YCX |
|
7 |
BQ6o23EH0Z0tW28KnIYEY4dQkLca9YRAKhHcywIDAQABAoIBAHS7XPXhW36zAD64 |
|
8 |
XEW2bKj4cOQvvG0ga7EFKITeqBUg0XrPFKMMD+eyHT0+QGSsSyAm9+/vc5/pWxGt |
|
9 |
aWy4LMMbiug4qOnsAOXljm+ixRh6qIK67Nu+ivW+fTlPjT8KKGd+B4c1hbX2MnE4 |
|
10 |
NMq3o+TH8BNH/eC0UDm715tcEmk6pUSBH3lq3CG7W1TyVjC3FGJcjBAj/X6J45lE |
|
11 |
skJHt9d67KG/MwmzuyoI+U9q2b3jSzoIGzzQQaOItGx3OefRjqWeUyDlUWobuFNV |
|
12 |
Lky+XjmOFJC0voQsUiV2mBSJejHmfuLjJfE+W/HrRc3YwftxCp+emaFshs56U4Ob |
|
13 |
UWu2F9kCgYEA7livJ1nYhHVyYueX6kWKTkBCzcwQO0agLsuYpspDjKGqgUOlFHXW |
|
14 |
9CS+DPi/r086iRYLwmGuaFAnNQJqS3ofjowj9/iZCGD/qe6jj9zMmokWDl1FALYe |
|
15 |
jT3Eg1HLfhe8hddA815yheL5uIVw3t34TTaQuokN86nkcv/bJ53SW4UCgYEA1H4v |
|
16 |
jk88pCNnADqmAnXNbuhPK+w6llre159vtStgKaJrcCZiTejFVpffpdp1b8hU21S2 |
|
17 |
lg/FgXHgvrdfwq+uZ+lRNJGyCX3mqe3uXWn6d42A/7tgmRDW4NXtxwelV8MTpwHw |
|
18 |
nS4hwmDyLyYMupyBlw5Iv7N3XmDBJu/tsEPMgA8CgYBP5MpRlnxNalD9dkQl80l5 |
|
19 |
EXFTKqQGOpZXGUgCIKqj6U0OJ26efSGglPBfyMH4McadTRaEAdpEfRmnWzfmNPl+ |
|
20 |
/trPtDUX6evJOoT5JDoxUuJhzkHjCykSjzHgEvrzOWGoO486BN6+omayw4giLKWe |
|
21 |
vDunS2mx07EQG1OK5AwvQQKBgCZY21YwQH5SkTz+WIUrIza3n8oKaIxHu91nvW4R |
|
22 |
dNouoHrtwmHS9wHoiIjSwsy4d2/ZetXb5MW2eluQlix5Ld08wtXc0SdbXCwgbxrW |
|
23 |
jEfU9omwE/+rhUuv76gyXglXgA1skTKcZ6U/f5U4paVrpwtOnZxS0+DpTxIqzFc5 |
|
24 |
9QbLAoGAeqLr0vm4SKnvtwK9F/Q784Rc8Ygq56vUcQIZ81yL4BsE0h6fuTHcSq+H |
|
25 |
NhO5mQFr+CcitGDE48/CRxfw1HYpk+KOtRzY+EdKGAKEu26sUSh7GNCw3TkOvPTo |
|
26 |
E/RgydWsPwjJBDp03z87cITfaoyqoIWLtEmUTeDY8m5dGu0EBzk= |
|
27 |
-----END RSA PRIVATE KEY----- |
mandaye/skel/conf.d/linuxfr_saml_example | ||
---|---|---|
1 |
{{ |
|
2 |
"site_name": "linuxfr", |
|
3 |
"server_name": ["linuxfrsaml.local:8000"], |
|
4 |
"location": "/", |
|
5 |
"target": "https://linuxfr.org", |
|
6 |
"mapper": "linuxfr", |
|
7 |
"auth_type": "saml2", |
|
8 |
"saml2_idp_metadata": "http://www.identity-hub.com/idp/saml2/metadata", |
|
9 |
"saml2_signature_public_key": "certs/saml.crt", |
|
10 |
"saml2_signature_private_key": "certs/saml.key" |
|
11 |
}} |
mandaye/skel/example.module/__init__.py | ||
---|---|---|
1 |
__version__="0.1" |
|
1 |
__version__="0.1.0" |
mandaye/skel/example.module/config.py | ||
---|---|---|
3 | 3 |
|
4 | 4 |
_PROJECT_PATH = os.path.join(os.path.dirname(__file__), '..') |
5 | 5 |
|
6 |
## Virtual hosts configuration |
|
7 |
hosts = {{}} |
|
8 |
|
|
9 | 6 |
## SQL Backend config |
10 | 7 |
# Database configuration |
11 | 8 |
# http://docs.sqlalchemy.org/en/rel_0_7/core/engines.html |
12 | 9 |
# rfc 1738 https://tools.ietf.org/html/rfc1738 |
13 | 10 |
# dialect+driver://username:password@host:port/database |
14 |
db_url = 'sqlite:///' + os.path.join(_PROJECT_PATH, 'test.db')
|
|
11 |
db_url = 'sqlite:///' + os.path.join(_PROJECT_PATH, '{project_name}.db')
|
|
15 | 12 |
|
16 | 13 |
## Log configuration |
17 | 14 |
debug = False |
... | ... | |
29 | 26 |
## PATH |
30 | 27 |
# Template directory |
31 | 28 |
template_directory = os.path.join(_PROJECT_PATH, '{project_name}/templates') |
29 |
# Configuration directory |
|
30 |
config_root = os.path.join(_PROJECT_PATH, 'conf.d') |
|
32 | 31 |
# Static url |
33 | 32 |
static_url = '/mandaye/static' |
34 | 33 |
# Static folder |
... | ... | |
38 | 37 |
|
39 | 38 |
# Email notification configuration |
40 | 39 |
email_notification = False |
41 |
email_prefix = '[Mandaye CAM]'
|
|
40 |
email_prefix = '[Mandaye {project_name}]'
|
|
42 | 41 |
smtp_host = 'localhost' |
43 | 42 |
smtp_port = 25 |
44 | 43 |
email_from = 'traceback@entrouvert.com' |
45 | 44 |
email_to = ['admin@localhost'] |
46 | 45 |
|
47 |
# platform : should be prod, recette or dev |
|
48 |
platform = "prod" |
|
49 |
|
|
50 | 46 |
# Use long traceback with xtraceback |
51 | 47 |
use_long_trace = True |
52 | 48 |
|
... | ... | |
60 | 56 |
# Must be a 16, 24, or 32 bytes long |
61 | 57 |
encrypt_secret = '' |
62 | 58 |
|
59 |
# Supported authentification |
|
60 |
authentifications = {{ |
|
61 |
'saml2': 'mandaye.auth.saml2.SAML2Auth' |
|
62 |
}} |
|
63 |
|
|
64 |
# sp mappers |
|
65 |
mappers = {{ |
|
66 |
'linuxfr': '{project_name}.mappers.linuxfr_example' |
|
67 |
}} |
|
68 |
|
|
63 | 69 |
# Beaker session configuration |
64 | 70 |
session_opts = {{ |
65 | 71 |
'session.type': 'file', |
... | ... | |
72 | 78 |
# Only mandaye.backends.sql at the moment |
73 | 79 |
storage_backend = "mandaye.backends.sql" |
74 | 80 |
|
75 |
# Needed if ssl is activated |
|
76 |
ssl = False |
|
77 |
keyfile = '' |
|
78 |
certfile = '' |
|
79 |
|
|
80 |
SAML_SIGNATURE_PUBLIC_KEY = '''-----BEGIN CERTIFICATE----- |
|
81 |
MIIDIzCCAgugAwIBAgIJANUBoick1pDpMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV |
|
82 |
BAoTCkVudHJvdXZlcnQwHhcNMTAxMjE0MTUzMzAyWhcNMTEwMTEzMTUzMzAyWjAV |
|
83 |
MRMwEQYDVQQKEwpFbnRyb3V2ZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB |
|
84 |
CgKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZn9Kqm4Cp |
|
85 |
06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrUH8QT8NGh |
|
86 |
ABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59xihSqsoFr |
|
87 |
kmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9Hri8JRdXi |
|
88 |
VT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziazZfvvw/VG |
|
89 |
Tm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABo3YwdDAdBgNVHQ4EFgQUeF8ePnu0 |
|
90 |
fcAK50iBQDgAhHkOu8kwRQYDVR0jBD4wPIAUeF8ePnu0fcAK50iBQDgAhHkOu8mh |
|
91 |
GaQXMBUxEzARBgNVBAoTCkVudHJvdXZlcnSCCQDVAaInJNaQ6TAMBgNVHRMEBTAD |
|
92 |
AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAy8l3GhUtpPHx0FxzbRHVaaUSgMwYKGPhE |
|
93 |
IdGhqekKUJIx8et4xpEMFBl5XQjBNq/mp5vO3SPb2h2PVSks7xWnG3cvEkqJSOeo |
|
94 |
fEEhkqnM45b2MH1S5uxp4i8UilPG6kmQiXU2rEUBdRk9xnRWos7epVivTSIv1Ncp |
|
95 |
lG6l41SXp6YgIb2ToT+rOKdIGIQuGDlzeR88fDxWEU0vEujZv/v1PE1YOV0xKjTT |
|
96 |
JumlBc6IViKhJeo1wiBBrVRIIkKKevHKQzteK8pWm9CYWculxT26TZ4VWzGbo06j |
|
97 |
o2zbumirrLLqnt1gmBDvDvlOwC/zAAyL4chbz66eQHTiIYZZvYgy |
|
98 |
-----END CERTIFICATE-----''' |
|
99 |
|
|
100 |
SAML_SIGNATURE_PRIVATE_KEY = '''-----BEGIN RSA PRIVATE KEY----- |
|
101 |
MIIEpAIBAAKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZ |
|
102 |
n9Kqm4Cp06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrU |
|
103 |
H8QT8NGhABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59x |
|
104 |
ihSqsoFrkmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9H |
|
105 |
ri8JRdXiVT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziaz |
|
106 |
Zfvvw/VGTm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABAoIBAQCj8t2iKXya10HG |
|
107 |
V6Saaeih8aftoLBV38VwFqqjPU0+iKqDpk2JSXBhjI6s7uFIsaTNJpR2Ga1qvns1 |
|
108 |
hJQEDMQSLhJvXfBgSkHylRWCpJentr4E3D7mnw5pRsd61Ev9U+uHcdv/WHP4K5hM |
|
109 |
xsdiwXNXD/RYd1Q1+6bKrCuvnNJVmWe0/RV+r3T8Ni5xdMVFbRWt/VEoE620XX6c |
|
110 |
a9TQPiA5i/LRVyie+js7Yv+hVjGOlArtuLs6ECQsivfPrqKLOBRWcofKdcf+4N2e |
|
111 |
3cieUqwzC15C31vcMliD9Hax9c1iuTt9Q3Xzo20fOSazAnQ5YBEExyTtrFBwbfQu |
|
112 |
ku6hp81pAoGBAN6bc6iJtk5ipYpsaY4ZlbqdjjG9KEXB6G1MExPU7SHXOhOF0cDH |
|
113 |
/pgMsv9hF2my863MowsOj3OryVhdQhwA6RrV263LRh+JU8NyHV71BwAIfI0BuVfj |
|
114 |
6r24KudwtUcvMr9pJIrJyMAMaw5ZyNoX7YqFpS6fcisSJYdSBSoxzrzVAoGBANu6 |
|
115 |
xVeMqGavA/EHSOQP3ipDZ3mnWbkDUDxpNhgJG8Q6lZiwKwLoSceJ8z0PNY3VetGA |
|
116 |
RbqtqBGfR2mcxHyzeqVBpLnXZC4vs/Vy7lrzTiHDRZk2SG5EkHMSKFA53jN6S/nJ |
|
117 |
JWpYZC8lG8w4OHaUfDHFWbptxdGYCgY4//sjeiuXAoGBANuhurJ99R5PnA8AOgEW |
|
118 |
4zD1hLc0b4ir8fvshCIcAj9SUB20+afgayRv2ye3Dted1WkUL4WYPxccVhLWKITi |
|
119 |
rRtqB03o8m3pG3kJnUr0LIzu0px5J/o8iH3ZOJOTE3iBa+uI/KHmxygc2H+XPGFa |
|
120 |
HGeAxuJCNO2kAN0Losbnz5dlAoGAVsCn94gGWPxSjxA0PC7zpTYVnZdwOjbPr/pO |
|
121 |
LDE0cEY9GBq98JjrwEd77KibmVMm+Z4uaaT0jXiYhl8pyJ5IFwUS13juCbo1z/u/ |
|
122 |
ldMoDvZ8/R/MexTA/1204u/mBecMJiO/jPw3GdIJ5phv2omHe1MSuSNsDfN8Sbap |
|
123 |
gmsgaiMCgYB/nrTk89Fp7050VKCNnIt1mHAcO9cBwDV8qrJ5O3rIVmrg1T6vn0aY |
|
124 |
wRiVcNacaP+BivkrMjr4BlsUM6yH4MOBsNhLURiiCL+tLJV7U0DWlCse/doWij4U |
|
125 |
TKX6tp6oI+7MIJE6ySZ0cBqOiydAkBePZhu57j6ToBkTa0dbHjn1WA== |
|
126 |
-----END RSA PRIVATE KEY-----''' |
|
127 |
|
|
128 | 81 |
# Import local config |
129 | 82 |
try: |
130 | 83 |
from ..{project_name}.local_config import * |
mandaye/skel/example.module/configs/linuxfr_saml_example.py | ||
---|---|---|
1 |
|
|
2 |
from {project_name}.auth.example import MyAuthSAML |
|
3 |
from {project_name}.filters.example import ReplayFilter |
|
4 |
|
|
5 |
from mandaye.configs import saml2 as saml2_config |
|
6 |
|
|
7 |
form_values = {{ |
|
8 |
'login_url': '/compte/connexion', |
|
9 |
'form_attrs': {{ 'id': 'new_account' }}, |
|
10 |
'post_fields': ['account[login]', 'account[password]'], |
|
11 |
'username_field': 'account[login]', |
|
12 |
'password_field': 'account[password]', |
|
13 |
}} |
|
14 |
|
|
15 |
auth = MyAuthSAML(form_values, 'linuxfr', saml2_config) |
|
16 |
|
|
17 |
linuxfr_mapping = [ |
|
18 |
{{ |
|
19 |
'path': r'/mandaye/associate$', |
|
20 |
'method': 'GET', |
|
21 |
'on_response': [{{ |
|
22 |
'filter': ReplayFilter.associate, |
|
23 |
'values': {{ |
|
24 |
'action': '/mandaye/associate', |
|
25 |
'template': 'associate.html', |
|
26 |
'sp_name': 'Linux FR', |
|
27 |
'login_name': form_values['username_field'], |
|
28 |
'password_name': form_values['password_field'], |
|
29 |
}}, |
|
30 |
}},] |
|
31 |
}}, |
|
32 |
{{ |
|
33 |
'path': r'/mandaye/associate$', |
|
34 |
'method': 'POST', |
|
35 |
'response': [ |
|
36 |
{{ |
|
37 |
'filter': auth.associate_submit, |
|
38 |
'values': {{ |
|
39 |
'connection_url': '/mandaye/sso', |
|
40 |
'associate_url': '/mandaye/associate', |
|
41 |
}}, |
|
42 |
'condition': "response.code==302" |
|
43 |
}}, |
|
44 |
] |
|
45 |
}}, |
|
46 |
] |
|
47 |
|
|
48 |
linuxfr_mapping.extend(auth.get_default_mapping()) |
|
49 |
|
mandaye/skel/example.module/mappers/linuxfr_example.py | ||
---|---|---|
1 |
|
|
2 |
""" |
|
3 |
You need to defined 3 variables : |
|
4 |
|
|
5 |
* form_values (defined the login form values): |
|
6 |
form_values = {{ |
|
7 |
'login_url': '/login', |
|
8 |
'post_url': '/login', |
|
9 |
'form_attrs': {{ 'name': 'form40', }}, |
|
10 |
'username_field': 'user', |
|
11 |
'password_field': 'pass', |
|
12 |
'post_fields': ['birthdate', 'card_number'] |
|
13 |
}} |
|
14 |
login_url, form_attrs, post_fields and username_field are obligatory |
|
15 |
* urls (a dictionnary with urls) : |
|
16 |
urls = {{ |
|
17 |
'associate_url': '/mandaye/associate', |
|
18 |
'connection_url': '/mandaye/sso', |
|
19 |
'login_url': '/mandaye/login' |
|
20 |
}} |
|
21 |
* mapping |
|
22 |
""" |
|
23 |
|
|
24 |
from {project_name}.filters.example import ReplayFilter |
|
25 |
|
|
26 |
form_values = {{ |
|
27 |
'login_url': '/compte/connexion', |
|
28 |
'form_attrs': {{ 'id': 'new_account' }}, |
|
29 |
'post_fields': ['account[login]', 'account[password]'], |
|
30 |
'username_field': 'account[login]', |
|
31 |
'password_field': 'account[password]', |
|
32 |
}} |
|
33 |
|
|
34 |
urls = {{ |
|
35 |
'associate_url': '/mandaye/associate', |
|
36 |
'connection_url': '/mandaye/sso', |
|
37 |
'login_url': '/mandaye/login' |
|
38 |
}} |
|
39 |
|
|
40 |
mapping = [ |
|
41 |
{{ |
|
42 |
'path': r'/mandaye/login$', |
|
43 |
'method': 'GET', |
|
44 |
'response': [{{ |
|
45 |
'auth': 'login', |
|
46 |
'condition': 'response.code==302', |
|
47 |
}},] |
|
48 |
}}, |
|
49 |
{{ |
|
50 |
'path': r'/mandaye/sso$', |
|
51 |
'method': 'GET', |
|
52 |
'response': [{{ |
|
53 |
'auth': 'sso', |
|
54 |
}}] |
|
55 |
}}, |
|
56 |
{{ |
|
57 |
'path': r'/mandaye/slo$', |
|
58 |
'method': 'GET', |
|
59 |
'response': [{{ |
|
60 |
'auth': 'slo', |
|
61 |
}}] |
|
62 |
}}, |
|
63 |
{{ |
|
64 |
'path': r'/mandaye/associate$', |
|
65 |
'method': 'GET', |
|
66 |
'on_response': [{{ |
|
67 |
'filter': ReplayFilter.associate, |
|
68 |
'values': {{ |
|
69 |
'action': urls['associate_url'], |
|
70 |
'template': 'associate.html', |
|
71 |
'sp_name': 'Linux FR', |
|
72 |
'login_name': form_values['username_field'], |
|
73 |
'password_name': form_values['password_field'], |
|
74 |
}}, |
|
75 |
}},] |
|
76 |
}}, |
|
77 |
{{ |
|
78 |
'path': r'/mandaye/associate$', |
|
79 |
'method': 'POST', |
|
80 |
'response': [ |
|
81 |
{{ |
|
82 |
'auth': 'associate_submit', |
|
83 |
'condition': "response.code==302" |
|
84 |
}}, |
|
85 |
] |
|
86 |
}}, |
|
87 |
] |
|
88 |
|
mandaye/skel/example.module/wsgi.py | ||
---|---|---|
5 | 5 |
|
6 | 6 |
from {project_name} import config |
7 | 7 |
from beaker.middleware import SessionMiddleware |
8 |
from whitenoise import WhiteNoise |
|
8 | 9 |
|
9 |
os.environ['MANDAYE_CONFIG_MODULE'] = '{project_name}.config' |
|
10 |
os.environ['MANDAYE_CONFIG_MODULE'] = 'test.config' |
|
11 |
|
|
12 |
from mandaye import config |
|
10 | 13 |
|
11 | 14 |
application = SessionMiddleware(MandayeApp(), config.session_opts) |
15 |
application_dev = WhiteNoise(application, root=config.static_root, prefix=config.static_url) |
|
12 | 16 |
|
13 | 17 |
|
mandaye/skel/requirements.txt | ||
---|---|---|
1 | 1 |
gunicorn>=0.17 |
2 |
mandaye>=0.7.1 |
|
2 |
mandaye>=0.8.0 |
|
3 |
whitenoise>=1.0 |
mandaye/skel/server.py | ||
---|---|---|
15 | 15 |
class MandayeWSGIApplication(WSGIApplication): |
16 | 16 |
|
17 | 17 |
def init(self, parser, opts, args): |
18 |
self.cfg.set("default_proc_name", "{project_name}.wsgi:application") |
|
19 |
self.app_uri = "{project_name}.wsgi:application" |
|
18 |
self.cfg.set("default_proc_name", "{project_name}.wsgi:application_dev")
|
|
19 |
self.app_uri = "{project_name}.wsgi:application_dev"
|
|
20 | 20 |
|
21 | 21 |
def main(): |
22 | 22 |
""" The ``gunicorn`` command line runner for launcing Gunicorn with |
mandaye/skel/setup.py | ||
---|---|---|
14 | 14 |
|
15 | 15 |
install_requires=[ |
16 | 16 |
'gunicorn>=0.17', |
17 |
'mandaye>=0.7.1', |
|
17 |
'mandaye>=0.8.0', |
|
18 |
'whitenoise>=1.0' |
|
18 | 19 |
] |
19 | 20 |
|
20 | 21 |
def get_version(): |
requirements.txt | ||
---|---|---|
6 | 6 |
sqlalchemy>=0.7,<0.8 |
7 | 7 |
alembic>=0.5.0 |
8 | 8 |
Mako>=0.4 |
9 |
static |
scripts/mandaye-admin.py | ||
---|---|---|
47 | 47 |
shutil.copytree(skel, project_name) |
48 | 48 |
for root, dirs, files in os.walk(project_name): |
49 | 49 |
if not "templates" in root and not "static" in root: |
50 |
print root |
|
51 | 50 |
for filename in files: |
52 | 51 |
file_path = os.path.join(root, filename) |
53 |
print file_path |
|
54 | 52 |
with open(file_path, "r") as f: |
55 | 53 |
content = f.read() |
56 | 54 |
with open(file_path, "w") as f: |
57 |
print content.format(project_name=project_name) |
|
58 | 55 |
f.write(content.format(project_name=project_name)) |
59 | 56 |
shutil.move(modue_example, module) |
60 | 57 |
|
setup.py | ||
---|---|---|
18 | 18 |
'sqlalchemy>=0.7.3', |
19 | 19 |
'lxml>=2.0', |
20 | 20 |
'xtraceback>=0.3', |
21 |
'static', |
|
22 | 21 |
] |
23 | 22 |
|
24 | 23 |
if version < '2.7': |
... | ... | |
37 | 36 |
version = result.split()[0][1:] |
38 | 37 |
return version.replace('-','.') |
39 | 38 |
import mandaye |
40 |
return mandaye.VERSION
|
|
39 |
return mandaye.__version__
|
|
41 | 40 |
|
42 | 41 |
setup(name="mandaye", |
43 | 42 |
version=get_version(), |
Formats disponibles : Unified diff
initial import for branch 0.8
This is a complete rewrite of the configuration system
Fixes #4666