Projet

Général

Profil

« Précédent | Suivant » 

Révision de6e5967

Ajouté par Jérôme Schneider il y a presque 10 ans

initial import for branch 0.8

This is a complete rewrite of the configuration system

Fixes #4666

Voir les différences:

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