Projet

Général

Profil

« Précédent | Suivant » 

Révision e0ea574b

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

Fixes #710: manage storage backends and implement sql one

WARNING: this commit brake sql migrations

Voir les différences:

README.rst
52 52
 * sqlalchemy-migrate:: http://pypi.python.org/pypi/sqlalchemy-migrate
53 53

  
54 54

  
55
You can install all those dependencies quickly using pip::
56

  
57
   pip install poster SQLAlchemy Beaker Mako lxml gunicorn sqlalchemy-migrate xtraceback
58

  
59
or easy_install::
60

  
61
   easy_install poster SQLAlchemy Beaker Mako lxml gunicorn sqlalchemy-migrate xtraceback
62

  
63
or apt-get (Debian based distributions)::
55
Quick installation
56
------------------
64 57

  
65
    apt-get install gunicorn python-poster python-sqlalchemy python-beaker python-mako python-lxml python-setuptools
58
Install at least Python >=2.5 and pip in your system.
59
For example with Debian or a Debian based distribution::
66 60

  
67
It's recommanded to install the following modules
61
    sudo apt-get install python python-pip
68 62

  
69
 * PyCrypto >= 2.3:: http://pypi.python.org/pypi/pycrypto
70
 * Static >= 0.4:: http://pypi.python.org/pypi/static
63
Then install virtualenv ::
71 64

  
72
 You can install this Python modules with pip::
65
   pip install virtualenv
73 66

  
74
    pip install pycrypto static
67
Create your virtualenv activate it::
75 68

  
76
Quick installation
77
------------------
69
    virtualenv mandaye
70
    source mandaye/bin/activate
71
    pip install -U pip
78 72

  
79
Install at least Python >=2.5 and setuptools or distribute and enter this command in a shell::
73
Install mandaye::
80 74

  
75
    $ tar xfvz mandaye-VERSION.tar.gz
76
    $ cd mandaye-VERSION
77
    $ pip install -r requirements.txt
81 78
    $ python setup.py install
82 79

  
83 80
If you want to develop use this command line::
......
88 85
Quick Start
89 86
-----------
90 87

  
91
Configure MANDAYE_PATH/mandaye/config.py with your own preferences.
92
You must configure the database uri and the log file.
93

  
94
First create your database::
88
First step is to create a mandaye project::
95 89

  
96
  $ mandaye_admin.py --createdb
90
  $ mandaye_admin.py --newproject
97 91

  
98 92
Launch mandaye server::
99 93

  
mandaye/auth/authform.py
16 16
from urlparse import parse_qs
17 17

  
18 18
from mandaye import config, VERSION
19
from mandaye.db import sql_session
20 19
from mandaye.exceptions import MandayeException
21
from mandaye.models import Site, ExtUser, LocalUser
22 20
from mandaye.log import logger
23 21
from mandaye.http import HTTPResponse, HTTPHeader, HTTPRequest
24 22
from mandaye.response import _500, _302, _401
25 23
from mandaye.response import template_response
26 24
from mandaye.server import get_response
27 25

  
26
from mandaye.config.backend import ManagerIDPUser, ManagerSPUser,\
27
        ManagerServiceProvider
28

  
28 29
try:
29 30
    from Crypto.Cipher import AES
30 31
except ImportError:
31
    config.encrypt_ext_password = False
32
    config.encrypt_sp_password = False
32 33

  
33 34
class AuthForm(object):
34 35

  
......
72 73

  
73 74
    def _encrypt_pwd(self, post_values):
74 75
        """ This method allows you to encrypt a password
75
        To use this feature you muste set encrypt_ext_password to True
76
        To use this feature you muste set encrypt_sp_password to True
76 77
        in your configuration and set a secret in encrypt_secret
77 78
        post_values: containt the post values
78 79
        return None and modify post_values
......
98 99
    def _decrypt_pwd(self, post_values):
99 100
        """ This method allows you to dencrypt a password encrypt with
100 101
        _encrypt_pwd method. To use this feature you muste set
101
        encrypt_ext_password to True in your configuration and
102
        encrypt_sp_password to True in your configuration and
102 103
        set a secret in encrypt_secret
103 104
        post_values: containt the post values
104 105
        return None and modify post_values
......
120 121

  
121 122
    def _get_password(self, post_values):
122 123
        if self.form_values.has_key('password_field'):
123
            if config.encrypt_ext_password:
124
            if config.encrypt_sp_password:
124 125
                return self._encrypt_pwd(
125 126
                        post_values[self.form_values['password_field']]
126 127
                        )
......
182 183
        request = HTTPRequest(cookies, headers, "POST", params)
183 184
        return get_response(env, request, action, cj)
184 185

  
185
    def _save_association(self, env, local_login, post_values):
186
    def _save_association(self, env, unique_id, post_values):
186 187
        """ save an association in the database
187 188
        env: wsgi environment
188
        local_login: the Mandaye login
189
        unique_id: idp uinique id
189 190
        post_values: dict with the post values
190 191
        """
191
        ext_username = post_values[self.form_values['username_field']]
192
        if config.encrypt_ext_password:
192
        sp_login = post_values[self.form_values['username_field']]
193
        if config.encrypt_sp_password:
193 194
            self._encrypt_pwd(post_values)
194
        site = sql_session().query(Site).\
195
                filter_by(name=self.site_name).first()
196
        if not site:
197
            logger.info('Add %s site in the database' % self.site_name)
198
            site = Site(self.site_name)
199
            sql_session().add(site)
200
        local_user = sql_session().query(LocalUser).\
201
                filter_by(login=local_login).first()
202
        if not local_user:
203
            logger.debug('Add user %s in the database' % local_login)
204
            local_user = LocalUser(login=local_login)
205
            sql_session().add(local_user)
206
        ext_user = sql_session().query(ExtUser).\
207
                join(LocalUser).\
208
                filter(LocalUser.login==local_login).\
209
                filter(ExtUser.login==ext_username).\
210
                first()
211
        if not ext_user:
212
            ext_user = ExtUser()
213
            sql_session().add(ext_user)
214
            logger.info('New association: %s with %s on site %s' % \
215
                    (ext_username, local_login, self.site_name))
216
        ext_user.login = ext_username
217
        ext_user.post_values = post_values
218
        ext_user.local_user = local_user
219
        ext_user.last_connection = datetime.now()
220
        ext_user.site = site
221
        sql_session().commit()
222
        env['beaker.session']['login'] = local_login
223
        env['beaker.session'][self.site_name] = ext_user.id
195
        service_provider = ManagerServiceProvider.get_or_create(self.site_name)
196
        idp_user = ManagerIDPUser.get_or_create(unique_id)
197
        sp_user = ManagerSPUser.get_or_create(sp_login, post_values,
198
                idp_user, service_provider)
199
        sp_user.login = sp_login
200
        sp_user.post_values = post_values
201
        sp_user.idp_user = idp_user
202
        sp_user.last_connection = datetime.now()
203
        sp_user.service_provider = service_provider
204
        ManagerSPUser.save()
205
        env['beaker.session']['unique_id'] = unique_id
206
        env['beaker.session'][self.site_name] = sp_user.id
224 207
        env['beaker.session'].save()
225 208

  
226 209
    def associate_submit(self, env, values, condition, request, response):
227 210
        """  Associate your login / password into your database
228 211
        """
229 212
        logger.debug("Trying to associate a user")
230
        login = env['beaker.session'].get('login')
213
        unique_id = env['beaker.session'].get('unique_id')
231 214
        if request.msg:
232
            if not login:
215
            if not unique_id:
233 216
                logger.warning("Association failed: user isn't login on Mandaye")
234 217
                return _302(values.get('connection_url'))
235 218
            post = parse_qs(request.msg.read(), request)
......
247 230
            response = self.replay(env, post_values)
248 231
            if eval(condition):
249 232
                logger.debug("Replay works: save the association")
250
                self._save_association(env, login, post_values)
233
                self._save_association(env, unique_id, post_values)
251 234
                if qs.has_key('next_url'):
252 235
                    return _302(qs['next_url'], response.cookies)
253 236
                return response
......
256 239
            qs['type'] = 'badlogin'
257 240
            return _302(values.get('associate_url') + "?%s" % urllib.urlencode(qs))
258 241

  
259
    def _login_ext_user(self, ext_user, env, condition, values):
260
        """ Log in an external user
242
    def _login_sp_user(self, sp_user, env, condition, values):
243
        """ Log in sp user
261 244
        """
262
        if not ext_user.login:
245
        if not sp_user.login:
263 246
            return _500(env['PATH_INFO'],
264 247
                    'Invalid values for AuthFormDispatcher.login')
265
        post_values = copy.deepcopy(ext_user.post_values)
266
        if config.encrypt_ext_password:
248
        post_values = copy.deepcopy(sp_user.post_values)
249
        if config.encrypt_sp_password:
267 250
            self._decrypt_pwd(post_values)
268 251
        response = self.replay(env, post_values)
269 252
        if condition and eval(condition):
270
            ext_user.last_connection = datetime.now()
271
            sql_session().commit()
272
            env['beaker.session'][self.site_name] = ext_user.id
253
            sp_user.last_connection = datetime.now()
254
            ManagerSPUser.save()
255
            env['beaker.session'][self.site_name] = sp_user.id
273 256
            env['beaker.session'].save()
274 257
            return response
275 258
        else:
......
279 262
        """ Automatic login on a site with a form
280 263
        """
281 264
        logger.debug('Trying to login on Mandaye')
282
        login = self.get_current_login(env)
283
        if not login:
265
        # Specific method to get current idp unique id
266
        unique_id = self.get_current_unique_id(env)
267
        if not unique_id:
284 268
            return _401('Access denied: invalid token')
285 269

  
286 270
        # FIXME: hack to force beaker to generate an id
287 271
        # somtimes beaker doesn't do it by himself
288 272
        env['beaker.session'].regenerate_id()
289 273

  
290
        env['beaker.session']['login'] = login
274
        env['beaker.session']['unique_id'] = unique_id
291 275
        env['beaker.session'].save()
292
        logger.debug('User %s successfully login' % env['beaker.session']['login'])
293 276

  
294
        ext_user = sql_session().query(ExtUser).\
295
                join(LocalUser).\
296
                join(Site).\
297
                filter(LocalUser.login==login).\
298
                filter(Site.name==self.site_name).\
299
                order_by(ExtUser.last_connection.desc()).\
300
                first()
301
        if not ext_user:
302
            logger.debug('User %s is not associate' % env['beaker.session']['login'])
277
        logger.debug('User %s successfully login' % env['beaker.session']['unique_id'])
278

  
279
        idp_user = ManagerIDPUser.get(unique_id)
280
        service_provider = ManagerServiceProvider.get(self.site_name)
281
        sp_user = ManagerSPUser.get_last_connected(idp_user, service_provider)
282
        if not sp_user:
283
            logger.debug('User %s is not associate' % env['beaker.session']['unique_id'])
303 284
            return _302(values.get('associate_url') + "?type=first")
304
        return self._login_ext_user(ext_user, env, condition, values)
285
        return self._login_sp_user(sp_user, env, condition, values)
305 286

  
306 287
    def logout(self, env, values, request, response):
307 288
        """ Destroy the Beaker session
......
317 298
        This method must have a query string with a username parameter
318 299
        """
319 300
        # TODO: need to logout the first
320
        login = env['beaker.session']['login']
301
        unique_id = env['beaker.session']['unique_id']
321 302
        qs = parse_qs(env['QUERY_STRING'])
322 303
        if not login or not qs.has_key('id'):
323 304
            return _401('Access denied: beaker session invalid or not qs id')
324 305
        id = qs['id'][0]
325
        ext_user = sql_session().query(ExtUser).\
326
                join(LocalUser).\
327
                filter(LocalUser.login==login).\
328
                filter(ExtUser.id==id).\
329
                first()
330
        if not ext_user:
306
        service_provider = ManagerServiceProvider.get(self.site_name)
307
        idp_user = ManagerServiceProvider.get(unique_id)
308
        sp_user = ManagerSPUser.get_last_connected(idp_user, service_provider)
309
        if not sp_user:
331 310
            return _302(values.get('associate_url'))
332
        return self._login_ext_user(ext_user, env, 'response.code==302', values)
311
        return self._login_sp_user(sp_user, env, 'response.code==302', values)
333 312

  
334 313
    def disassociate(self, env, values, request, response):
335 314
        """ Multi accounts feature
......
345 324
        if not login or not qs.has_key('id'):
346 325
            return _401('Access denied: beaker session invalid or not id')
347 326
        id = qs['id'][0]
348
        ext_user = sql_session().query(ExtUser).\
349
                join(LocalUser).\
350
                filter(LocalUser.login==login).\
351
                filter(ExtUser.id==id).\
352
                first()
353
        if ext_user:
354
            logger.debug('Disassociate account %s' % ext_user.login)
355
            sql_session().delete(ext_user)
356
            sql_session().commit()
327
        sp_user = ManagerSPUser.get_by_id(id)
328
        if sp_user:
329
            ManagerSPUser.delete(sp_user)
357 330
            if qs.has_key('logout'):
358 331
                self.logout(env, values, request, response)
359 332
                return _302(values.get('next_url'))
mandaye/auth/vincennes.py
38 38
                res[keyvalue[0]] = keyvalue[1]
39 39
        return res
40 40

  
41
    def get_current_login(self, env):
42
        """ Return the current Vincennes pseudo
41
    def get_current_unique_id(self, env):
42
        """ Return the current Vincennes unique id
43 43
        """
44 44
        from mandaye import config
45 45
        # TODO: test time validity
......
97 97
        if not login:
98 98
            logger.debug('Auto login failed because the user is not connected on vincennes.fr')
99 99
            return _302(path, request.cookies)
100
        env['beaker.session']['login'] = login
100
        env['beaker.session']['unique_id'] = unique_id
101 101
        env['beaker.session'].save()
102 102
        ext_user = sql_session().query(ExtUser).\
103 103
                join(LocalUser).\
mandaye/backends/default.py
1

  
2
class DefaultManagerIDPUser:
3

  
4
    @staticmethod
5
    def get(unique_id, idp_id='default'):
6
        pass
7

  
8
    @staticmethod
9
    def create(unique_id, idp_id='default'):
10
        pass
11

  
12
    @staticmethod
13
    def get_or_create(unique_id, idp_id='default'):
14
        pass
15

  
16
    @staticmethod
17
    def delete(idp_user):
18
        pass
19

  
20
    @staticmethod
21
    def save(idp_user):
22
        pass
23

  
24
class DefaultManagerSPUser:
25

  
26
    @staticmethod
27
    def get(login, idp_user, service_provider):
28
        pass
29

  
30
    @staticmethod
31
    def get_by_id(id):
32
        pass
33

  
34
    @staticmethod
35
    def get_last_connected(idp_user, service_provider):
36
        pass
37

  
38
    @staticmethod
39
    def create(login, post_values, idp_user, service_provider):
40
        pass
41

  
42
    @staticmethod
43
    def get_or_create(login, post_values, idp_user, service_provider):
44
        pass
45

  
46
    @staticmethod
47
    def delete(sp_user):
48
        pass
49

  
50
    @staticmethod
51
    def save(sp_user):
52
        pass
53

  
54
class DefaultServiceProvider:
55

  
56
    @staticmethod
57
    def get(name):
58
        pass
59

  
60
    @staticmethod
61
    def create(name):
62
        pass
63

  
64
    @staticmethod
65
    def get_or_create(name):
66
        pass
67

  
68
    @staticmethod
69
    def delete(service_provider):
70
        pass
71

  
72
    @staticmethod
73
    def save(service_provider):
74
        pass
75

  
76

  
mandaye/backends/sql.py
1

  
2
from datetime import datetime
3

  
4
from mandaye.db import sql_session
5
from mandaye.models import IDPUser, SPUser, ServiceProvider
6

  
7
class ManagerIDPUserSQL:
8

  
9
    @staticmethod
10
    def get(unique_id, idp_id='default'):
11
        idp_user = sql_session().query(IDPUser).\
12
                filter_by(unique_id=unique_id,
13
                        idp_id='default')
14
        if len(idp_user) > 1:
15
            logger.critical('ManagerIDPUserSQL.get %s not unique' % unique_id)
16
            raise MandayeException(
17
                    'ManagerIDPUserSQL.get : %s is not unique' % unique_id)
18
        if idp_user:
19
            return idp_user.first()
20
        else:
21
            return None
22

  
23
    @staticmethod
24
    def create(unique_id, idp_id='default'):
25
        idp_user = IDPUser(
26
                    unique_id=unique_id,
27
                    idp_id=idp_id)
28
        sql_session().add(idp_user)
29
        return idp_user
30

  
31
    @staticmethod
32
    def get_or_create(unique_id, idp_id='default'):
33
        if ManagerIDPUserSQL.get(**kwargs):
34
            return user
35
        else:
36
            return ManagerIDPUserSQL.create(**kwargs)
37

  
38
    @staticmethod
39
    def delete(idp_user):
40
        sql_session().delete(idp_user)
41
        sql_session().commit()
42

  
43
    @staticmethod
44
    def save():
45
        sql_session().commit()
46

  
47
class ManagerSPUserSQL:
48

  
49
    @staticmethod
50
    def get(login, idp_user, service_provider):
51
        sp_user = sql_session().query(SPPUser).\
52
                join(IDPUser).\
53
                join(ServiceProvider).\
54
                filter_by(login=login,
55
                        idp_user=idp_user,
56
                        service_provider=service_provider)
57
        if sp_user:
58
            return sp_user.first()
59
        else:
60
            return None
61

  
62
    @staticmethod
63
    def get_by_id(id):
64
        return sql_session().query(SPUser).\
65
                filter(id==id).first()
66

  
67
    @staticmethod
68
    def get_last_connected(idp_user, service_provider):
69
        return sql_session().query(SPPUser).\
70
                join(IDPUser).\
71
                join(ServiceProvider).\
72
                filter(idp_user=idp_user).\
73
                filer(service_provider=service_provider).\
74
                order_by(SPUser.last_connection.desc()).\
75
                first()
76
    @staticmethod
77
    def get_sp_users(idp_unique_id, service_provider_name):
78
        return sql_session().query(SPUser).\
79
                    join(IDPUser).\
80
                    join(ServiceProvider).\
81
                    filter(IDPUser.unique_id==idp_unique_id).\
82
                    filter(ServiceProvider.name==service_provider_name).\
83
                    order_by(SPUser.last_connection.desc()).\
84
                    all()
85

  
86
    @staticmethod
87
    def create(login, post_values, idp_user, service_provider):
88
        sp_user = SPUser(
89
                    login=login,
90
                    post_values=post_values,
91
                    idp_id=idp_id,
92
                    service_provider = service_provider
93
                    )
94
        logger.info('New association: %s with %s on site %s' % \
95
                    (login, idp_user.unique_id, service_provider.name))
96
        sql_session().add(sp_user)
97
        sql_session().commit()
98
        return idp_user
99

  
100
    @staticmethod
101
    def get_or_create(login, post_values, idp_user, service_provider):
102
        sp_user = ManagerSPUserSQL.get(login, idp_user, service_provider)
103
        if sp_user:
104
            return sp_user
105
        else:
106
            return ManagerSPUserSQL.create(login, post_values,
107
                    idp_user, service_provider)
108
    @staticmethod
109
    def all():
110
        return sql_session().query(SPUser).all()
111

  
112
    @staticmethod
113
    def delete(sp_user):
114
        logger.debug('Disassociate account %s' % sp_user.login)
115
        sql_session().delete(sp_user)
116
        sql_session().commit()
117

  
118
    @staticmethod
119
    def save():
120
        sql_session().commit()
121

  
122
class ManagerServiceProviderSQL:
123

  
124
    @staticmethod
125
    def get(name):
126
        sp = sql_session().query(ServiceProvider).\
127
                filter_by(name=name)
128
        if sp:
129
            return sp.first()
130
        else:
131
            return None
132

  
133
    @staticmethod
134
    def create(name):
135
        logger.info('Add %s service provider into the database' % name)
136
        sp = ServiceProvider(name=name)
137
        sql_session().add(sp)
138
        sql_session().commit()
139
        return sp
140

  
141
    @staticmethod
142
    def get_or_create(name):
143
        sp = ManagerServiceProviderSQL.get(name)
144
        if sp:
145
            return sp
146
        else:
147
            return ManagerServiceProviderSQL.create(name)
148

  
149
    @staticmethod
150
    def delete(service_provider):
151
        logger.debug('Delete service provider %s' % service_provider.name)
152
        sql_session().delete(service_provider)
153
        sql_session().commit()
154

  
155
    @staticmethod
156
    def save():
157
        sql_session().commit()
158

  
159
ManagerServiceProvider = ManagerServiceProviderSQL
160
ManagerSPUser = ManagerSPUserSQL
161
ManagerServiceProvider = ManagerServiceProviderSQL
162

  
mandaye/config.py
1 1
import logging
2 2
from mandaye.exceptions import ImproperlyConfigured
3 3

  
4
# Database configuration
5
# rfc 1738 http://rfc.net/rfc1738.html
6
db_url = 'sqlite:///test.db'
7

  
8
# Default local backend
9
import mandaye.backends.sql
10
backend = mandaye.backends.sql
11

  
4 12
# Needed if ssl is activated
5 13
ssl = False
6 14
keyfile = ''
......
11 19
syslog = False
12 20
log_file = '/var/log/mandaye/mandaye.log'
13 21
log_level = logging.INFO
22

  
14 23
# Log rotation
15 24
# W[0-6] : weekly (0: Monday), D: day, ... (python doc)
16 25
log_when = 'W6'
......
24 33
# Static folder
25 34
static_root = 'mandaye/static'
26 35

  
27
# Database configuration
28
# rfc 1738 http://rfc.net/rfc1738.html
29
db_url = 'sqlite:///test.db'
30 36

  
31 37
# Email notification configuration
32 38
email_notification = False
......
42 48
# Decompress response only if you load a filter
43 49
auto_decompress = True
44 50

  
45
# Encrypt external passwords with a secret
51
# Encrypt service provider passwords with a secret
46 52
# You should install pycypto to use this feature
47
encrypt_ext_password = False
53
encrypt_sp_password = False
48 54
# Must be a 16, 24, or 32 bytes long
49 55
encrypt_secret = ''
50 56

  
mandaye/exceptions.py
9 9
class MandayeException(Exception):
10 10
    "Mandaye generic exception"
11 11
    pass
12

  
mandaye/filters/vincennes.py
6 6
from BeautifulSoup import BeautifulSoup
7 7
import lxml.html
8 8

  
9
from mandaye.db import sql_session
10 9
from mandaye.log import logger
11 10
from mandaye.models import Site, ExtUser, LocalUser
12 11
from mandaye.response import _302, _401
13 12
from mandaye.template import serve_template
14 13

  
14
from mandaye.config.backend import ManagerSPUser
15

  
15 16
def get_associate_form(env, values):
16 17
    """ Return association template content
17 18
    """
......
37 38
    """ Return the current Mandaye user """
38 39
    site_name = values.get('site_name')
39 40
    if env['beaker.session'].get(site_name):
40
        return sql_session().query(ExtUser).\
41
                get(env['beaker.session'].get(site_name))
41
        return ManagerSPUser.get_by_id(env['beaker.session'].get(site_name))
42 42
    else:
43 43
        return None
44 44

  
......
46 46
def get_multi_template(env, values, current_account):
47 47
    """ return the content of the multi account template
48 48
    """
49
    login = env['beaker.session'].get('login')
49
    unique_id = env['beaker.session'].get('unique_id')
50 50
    if login:
51
            ext_users = sql_session().query(ExtUser).\
52
                    join(LocalUser).\
53
                    join(Site).\
54
                    filter(LocalUser.login==login).\
55
                    filter(Site.name==values.get('site_name')).\
56
                    order_by(ExtUser.last_connection.desc()).\
57
                    all()
58 51
            accounts = {}
59
            for ext_user in ext_users:
60
                accounts[ext_user.id] = ext_user.login
52
            sp_users = ManagerSPUser.get_sp_users(unique_id,
53
                    values.get('site_name'))
54
            for sp_user in sp_users:
55
                accounts[sp_user.id] = sp_user.login
61 56
            if current_account:
62 57
                current_login = current_account.login
63 58
            else:
......
113 108
        """ Modify response html to support multi accounts
114 109
        """
115 110
        if response.msg and '<h2><div>Mon compte</div></h2>' in response.msg:
116
            if env['beaker.session'].get('login'):
111
            if env['beaker.session'].get('unique_id'):
117 112
                current_account = get_current_account(env, values)
118 113
                template = get_multi_template(env, values, current_account)
119 114
                if current_account:
......
177 172
        """
178 173
        if response.msg and\
179 174
                '<!-- Navigation -->' in response.msg:
180
            login = env['beaker.session'].get('login')
175
            login = env['beaker.session'].get('unique_id')
181 176
            current_account = get_current_account(env, values)
182 177
            if login and current_account:
183 178
                disassociate = serve_template('famille/disassociate.html',
......
225 220
        """
226 221
        if response.msg \
227 222
                and 'font-weight:bold;">Conservatoire de Vincennes' in response.msg:
228
            login = env['beaker.session'].get('login')
223
            login = env['beaker.session'].get('unique_id')
229 224
            site_name = values.get('site_name')
230 225
            current_account = get_current_account(env, values)
231 226
            if login and current_account:
mandaye/migration/versions/001_initial_schema.py
1
import collections
2
import json
1 3

  
2 4
from datetime import datetime
3 5

  
4
from sqlalchemy import *
5
from migrate import *
6
from sqlalchemy import Column, Integer, String, DateTime
7
from sqlalchemy import ForeignKey
8
from sqlalchemy.ext.declarative import declarative_base
9
from sqlalchemy.ext.mutable import Mutable
10
from sqlalchemy.orm import column_property, relationship, backref
11
from sqlalchemy.types import TypeDecorator, VARCHAR
12

  
13
Base = declarative_base()
14

  
15
class JSONEncodedDict(TypeDecorator):
16
    "Represents an immutable structure as a json-encoded string."
17

  
18
    impl = VARCHAR
19

  
20
    def process_bind_param(self, value, dialect):
21
        if value is not None:
22
            value = json.dumps(value)
23
        return value
24

  
25
    def process_result_value(self, value, dialect):
26
        if value is not None:
27
            value = json.loads(value)
28
        return value
29

  
30
class MutationDict(Mutable, dict):
31

  
32
    @classmethod
33
    def coerce(cls, key, value):
34
        """ Convert plain dictionaries to MutationDict. """
35
        if not isinstance(value, MutationDict):
36
            if isinstance(value, dict):
37
                return MutationDict(value)
38
            # this call will raise ValueError
39
            return Mutable.coerce(key, value)
40
        else:
41
            return value
42

  
43
    def __setitem__(self, key, value):
44
        """ Detect dictionary set events and emit change events. """
45
        dict.__setitem__(self, key, value)
46
        self.changed()
47

  
48
    def __delitem__(self, key):
49
        """ Detect dictionary del events and emit change events. """
50
        dict.__delitem__(self, key)
51
        self.changed()
52

  
53
MutationDict.associate_with(JSONEncodedDict)
54

  
55
class ServiceProvider(Base):
56

  
57
    __tablename__ = 'service_provider'
58

  
59
    id = Column(Integer, primary_key=True)
60
    name = Column(String(50), unique=True, nullable=False)
61

  
62
    def __init__(self, name):
63
        self.name = name
64

  
65
    def __repr__(self):
66
        return "<ServiceProvider('%s')>" % (self.name)
67

  
68
class IDPUser(Base):
69

  
70
    __tablename__ = 'idp_user'
71

  
72
    id = Column(Integer, primary_key=True)
73
    # Nameid, pseudo, email, ...
74
    unique_id = Column(String(150), nullable=False)
75
    # Entityid
76
    idp_id = Column(String(150), nullable=False)
77
    sp_users = relationship("SPUser", backref=backref('idp_user'))
78

  
79
    def __init__(self, unique_id=None, idp_id=None, sp_users=None):
80
        self.unique_id = unique_id
81
        self.idp_id = idp_id
82
        self.sp_users = sp_users
83

  
84
    def __repr__(self):
85
        return "<IDPUser %d '%s'>" % (self.id, self.unique_id)
86

  
87

  
88
class SPUser(Base):
89

  
90
    __tablename__ = 'sp_user'
91

  
92
    id = Column(Integer, primary_key=True)
93
    post_values = Column(JSONEncodedDict, nullable=False)
94
    creation_date = Column(DateTime, default=datetime.now(), nullable=False)
95
    last_connection = Column(DateTime, default=datetime.now())
96

  
97
    idp_user_id = Column(Integer, ForeignKey('idp_user.id'))
98
    service_provider_id = Column(Integer, ForeignKey('service_provider.id'),
99
            nullable=False)
100
    service_provider = relationship("ServiceProvider", backref=backref('users'))
101

  
102
    def __init__(self, post_values=None):
103
        self.post_values = post_values
104

  
105
    def __repr__(self):
106
        return "<SPUser '%d'>" % (self.id)
6 107

  
7 108

  
8 109
def upgrade(migrate_engine):
9
    meta = MetaData(bind=migrate_engine)
10
    ext_users = Table('ext_users', meta, autoload=True)
11
    creation_date = Column("creation_date", DateTime, default=datetime.now())
12
    creation_date.create(ext_users)
110
    ServiceProvider.__table__.create(migrate_engine)
111
    SPUser.__table__.create(migrate_engine)
112
    IDPUser.__table__.create(migrate_engine)
13 113

  
14 114
def downgrade(migrate_engine):
15
    meta = MetaData(bind=migrate_engine)
16
    ext_users = Table('ext_users', meta, autoload=True)
17
    ext_users.c.creation_date.drop()
115
    ServiceProvider.__table__.drop(migrate_engine)
116
    SPUser.__table__.drop(migrate_engine)
117
    IDPUser.__table__.drop(migrate_engine)
18 118

  
mandaye/models.py
1

  
1
""" This models is used if you are using form replay authentification
2
"""
2 3
import collections
3 4
import json
4 5

  
......
11 12
from sqlalchemy.orm import column_property, relationship, backref
12 13
from sqlalchemy.types import TypeDecorator, VARCHAR
13 14

  
15
Base = declarative_base()
14 16

  
15 17
class JSONEncodedDict(TypeDecorator):
16 18
    "Represents an immutable structure as a json-encoded string."
......
51 53
        self.changed()
52 54

  
53 55
MutationDict.associate_with(JSONEncodedDict)
54
Base = declarative_base()
55 56

  
56
class Site(Base):
57
    __tablename__ = 'sites'
57
class ServiceProvider(Base):
58

  
59
    __tablename__ = 'service_provider'
58 60

  
59 61
    id = Column(Integer, primary_key=True)
60 62
    name = Column(String(50), unique=True, nullable=False)
......
63 65
        self.name = name
64 66

  
65 67
    def __repr__(self):
66
        return "<Site('%s')>" % (self.name)
68
        return "<ServiceProvider('%s')>" % (self.name)
67 69

  
68
class LocalUser(Base):
69
    """ Mandaye's user
70
    """
71
    __tablename__ = 'local_users'
70
class IDPUser(Base):
72 71

  
73
    id = Column(Integer, primary_key=True)
74
    login = Column(String(150), nullable=False, unique=True)
75
    password = Column(String(25), nullable=True)
76
    firstname = Column(String(150), nullable=True)
77
    lastname = Column(String(150), nullable=True)
78
    fullname = column_property(firstname + " " + lastname)
72
    __tablename__ = 'idp_user'
79 73

  
80
    creation_date = Column(DateTime, default=datetime.now(), nullable=False)
81
    last_connection = Column(DateTime, default=datetime.now())
74
    id = Column(Integer, primary_key=True)
75
    # Nameid, pseudo, email, ...
76
    unique_id = Column(String(150), nullable=False)
77
    # Entityid
78
    idp_id = Column(String(150), nullable=False)
79
    sp_users = relationship("SPUser", backref=backref('idp_user'))
82 80

  
83
    def __init__(self, login=None, password=None, fullname=None):
84
        self.login = login
85
        self.password = password
86
        self.fullname = fullname
81
    def __init__(self, unique_id=None, idp_id=None, sp_users=None):
82
        self.unique_id = unique_id
83
        self.idp_id = idp_id
84
        self.sp_users = sp_users
87 85

  
88 86
    def __repr__(self):
89
        return "<LocalUser('%d %s')>" % (self.id, self.fullname)
87
        return "<IDPUser %d '%s'>" % (self.id, self.unique_id)
88

  
90 89

  
91
class ExtUser(Base):
92
    """ User of externals applications
93
    """
94
    __tablename__ = 'ext_users'
90
class SPUser(Base):
91

  
92
    __tablename__ = 'sp_user'
95 93

  
96 94
    id = Column(Integer, primary_key=True)
97 95
    login = Column(String(150), nullable=False)
98
    post_values = Column(JSONEncodedDict)
96
    post_values = Column(JSONEncodedDict, nullable=False)
99 97
    creation_date = Column(DateTime, default=datetime.now(), nullable=False)
100 98
    last_connection = Column(DateTime, default=datetime.now())
101 99

  
102
    local_user_id = Column(Integer, ForeignKey('local_users.id'), nullable=False)
103
    site_id = Column(Integer, ForeignKey('sites.id'), nullable=False)
104
    local_user = relationship("LocalUser", backref=backref('ext_users'))
105
    site = relationship("Site", backref=backref('users'))
100
    idp_user_id = Column(Integer, ForeignKey('idp_user.id'))
101
    service_provider_id = Column(Integer, ForeignKey('service_provider.id'),
102
            nullable=False)
103
    service_provider = relationship("ServiceProvider", backref=backref('users'))
106 104

  
107
    def __init__(self, login=None, post_values=None):
108
        self.login = login
105
    def __init__(self, post_values=None):
109 106
        self.post_values = post_values
110 107

  
111 108
    def __repr__(self):
112
        return "<ExtUser '%d'>" % (self.id)
113

  
109
        return "<SPUser '%d'>" % (self.id)
114 110

  
mandaye/response.py
76 76
        tb_str = _get_traceback()
77 77
        if email:
78 78
            try:
79
                email.sent('Mandaye internal server error',
79
                email.sent('Mandaye internal server error %s' % exception,
80 80
                        _get_text_error(tb_str, path, env=env))
81 81
            except Exception as detail:
82 82
                logger.warning('Sent mail failed with error: %s' % detail)
mandaye/templates/associate.html
1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3

  
4
<html xmlns="http://www.w3.org/1999/xhtml">
5

  
6
  <head>
7
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> 
8
    <meta http-equiv="Content-Language" content="fr-fr" /> 
9
    <title>1er connexion</title> 
10
  </head>
11

  
12
  <h3>1er connexion</h3>
13
  % if error_msg:
14
  <p>${error_msg}</p>
15
  % elif description:
16
  <p>${description}</p>
17
  % endif
18
  <form action="${action}" method="post" accept-charset="utf-8">
19
    <div>
20
      <label for="username">${username_label}</label>
21
      <input type="text" name="username" value="" id="username" />
22
    </div>
23
    <div>
24
      <label for="password">${password_label}</label>
25
      <input type="password" name="password" value="" id="password" />
26
    </div>
27
    <p><input type="submit" value="Enregistrer"></p>
28
  </form>
29

  
30
</html>
mandaye/templates/passord_replay/associate.html
1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3

  
4
<html xmlns="http://www.w3.org/1999/xhtml">
5

  
6
  <head>
7
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> 
8
    <meta http-equiv="Content-Language" content="fr-fr" /> 
9
    <title>1er connexion</title> 
10
  </head>
11

  
12
  <h3>1er connexion</h3>
13
  % if error_msg:
14
  <p>${error_msg}</p>
15
  % elif description:
16
  <p>${description}</p>
17
  % endif
18
  <form action="${action}" method="post" accept-charset="utf-8">
19
    <div>
20
      <label for="username">${username_label}</label>
21
      <input type="text" name="username" value="" id="username" />
22
    </div>
23
    <div>
24
      <label for="password">${password_label}</label>
25
      <input type="password" name="password" value="" id="password" />
26
    </div>
27
    <p><input type="submit" value="Enregistrer"></p>
28
  </form>
29

  
30
</html>
mandaye/templates/passord_replay/response.html
1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> 
2
<html>
3
  <head> 
4
    <title>${title}</title> 
5
  </head>
6
  <body> 
7
    <h1>${title}</h1> 
8
    <p>${body}</p>
9
  </body>
10
</html> 
mandaye/templates/response.html
1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> 
2
<html>
3
  <head> 
4
    <title>${title}</title> 
5
  </head>
6
  <body> 
7
    <h1>${title}</h1> 
8
    <p>${body}</p>
9
  </body>
10
</html> 
mandaye_admin.py
10 10

  
11 11
from mandaye import config
12 12
from mandaye.log import logger
13
from mandaye.models import ExtUser
14
from mandaye.db import sql_session
15

  
16 13

  
17 14
def get_cmd_options():
18
    usage = "usage: %prog --createdb|--cryptpwd"
15
    usage = "usage: %prog --newproject|--createdb|--cryptpwd"
19 16
    parser = OptionParser(usage=usage)
20 17
    parser.add_option("--createdb",
21 18
            dest="createdb",
......
29 26
            action="store_true",
30 27
            help="Crypt external password in Mandaye's database"
31 28
            )
29
    parser.add_option("--newproject",
30
            dest="cryptpwd",
31
            default=False,
32
            action="store_true",
33
            help="Crypt external password in Mandaye's database"
34
            )
32 35
    (options, args) = parser.parse_args()
33 36
    return options
34 37

  
......
56 59
        if config.db_url:
57 60
            import migrate.versioning.api
58 61
            import mandaye.migration
59
            from sqlalchemy import create_engine
60
            from mandaye.models import Base
61
            engine = create_engine(config.db_url)
62
            Base.metadata.create_all(engine)
63 62
            migrate.versioning.api.version_control(url=config.db_url,
64 63
                    repository=mandaye.migration.__path__[0])
65 64
            migrate.versioning.api.upgrade(url=config.db_url,
66 65
                    repository=mandaye.migration.__path__[0])
67 66
            logger.info("Database created")
68 67
    if options.cryptpwd:
69
        for user in sql_session().query(ExtUser).all():
68
        from mandaye.config.backend import ManagerSPUserSQL
69
        for user in ManagerSPUserSQL.all():
70 70
            user.password = encrypt_pwd(user.password)
71
        sql_session().commit()
71
        ManagerSPUserSQL.save()
72 72

  
73 73
if __name__ == "__main__":
74 74
    main()
75

  
mandaye_server.py
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3

  
4
""" Script to launch mandaye with gunicorn server
5
"""
6

  
7
import sys
8
import os
9

  
10
from mandaye.log import logger
11
from gunicorn.app.wsgiapp import WSGIApplication
12

  
13
class WSGIApplication(WSGIApplication):
14

  
15
    def init(self, parser, opts, args):
16
        self.cfg.set("default_proc_name", "mandaye.wsgi:application")
17
        self.app_uri = "mandaye.wsgi:application"
18

  
19
        sys.path.insert(0, os.getcwd())
20

  
21
def main():
22
    """ The ``gunicorn`` command line runner for launcing Gunicorn with
23
    generic WSGI applications.
24
    """
25
    logger.info('Mandaye start')
26
    WSGIApplication("%prog [OPTIONS]").run()
27

  
28
if __name__ == "__main__":
29
    main()
pip-requirements.txt
1
Beaker==1.5.4
2
Mako==0.4.2
3
MarkupSafe==0.15
4
SQLAlchemy==0.7.2
5
distribute==0.6.10
6
gevent==0.13.6
7
greenlet==0.3.1
8
importlib==1.0.2
9
poster==0.8.1
10
static==0.4
11
wsgiref==0.1.2
12
xtraceback==0.3
requirements.txt
1
beaker>=1.6
2
poster>=0.8
3
pycrypto>=2.0
4
lxml>=2.0
5
xtraceback>=0.3
6
sqlalchemy>=0.7,<0.8
7
static
server.py.default
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3

  
4
""" Script to launch mandaye with gunicorn server
5
"""
6

  
7
import sys
8
import os
9

  
10
from mandaye.log import logger
11
from gunicorn.app.wsgiapp import WSGIApplication
12

  
13
class WSGIApplication(WSGIApplication):
14

  
15
    def init(self, parser, opts, args):
16
        self.cfg.set("default_proc_name", "mandaye.wsgi:application")
17
        self.app_uri = "mandaye.wsgi:application"
18

  
19
        sys.path.insert(0, os.getcwd())
20

  
21
def main():
22
    """ The ``gunicorn`` command line runner for launcing Gunicorn with
23
    generic WSGI applications.
24
    """
25
    logger.info('Mandaye start')
26
    WSGIApplication("%prog [OPTIONS]").run()
27

  
28
if __name__ == "__main__":
29
    main()
setup.py
7 7
import mandaye
8 8

  
9 9
from setuptools import setup, find_packages
10
from sys import version
11

  
12
install_requires=[
13
          'beaker>=1.6',
14
          'gunicorn>=0.13',
15
          'mako>=0.3',
16
          'poster>=0.8',
17
          'pycrypto>=2.0',
18
          'sqlalchemy>=0.6',
19
          'sqlalchemy-migrate>=0.7.2',
20
          'lxml>=2.0',
21
          'xtraceback>=0.3',
22
          'static',
23
]
24

  
25
if version < '2.7':
26
    install_requires.append('importlib')
10 27

  
11 28
setup(name="mandaye",
12 29
      version=mandaye.VERSION,
......
17 34
      author_email="info@entrouvert.org",
18 35
      maintainer="Jerome Schneider",
19 36
      maintainer_email="jschneider@entrouvert.com",
20
      scripts=['mandaye_server.py', 'mandaye_admin.py', 'mandaye_migrate.py'],
37
      scripts=['mandaye_admin.py', 'mandaye_migrate.py'],
21 38
      packages=find_packages(),
22 39
      package_data={},
23
      install_requires=[
24
          'beaker>=1.6',
25
          'gunicorn>=0.13',
26
          'importlib',
27
          'mako>=0.3',
28
          'poster>=0.8',
29
          'pycrypto>=2.0',
30
          'sqlalchemy>=0.6',
31
          'sqlalchemy-migrate>=0.7.2',
32
          'lxml>=2.0',
33
          'xtraceback>=0.3',
34
          'static',
35
      ],
40
      install_requires=install_requires
36 41
)
42

  

Formats disponibles : Unified diff