Browse Source

ldap support, dev.

Bastien Sevajol 9 years ago
parent
commit
803fd4e630

+ 3 - 1
install/requirements.txt View File

@@ -13,7 +13,7 @@ WebOb==1.6.0a0
13 13
 WebTest==1.4.2
14 14
 alembic==0.8.4
15 15
 argparse==1.2.1
16
-backlash==0.0.6
16
+backlash==0.0.7
17 17
 beautifulsoup4==4.3.2
18 18
 cliff==1.8.0
19 19
 cmd2==0.6.7
@@ -47,3 +47,5 @@ zope.interface==4.1.3
47 47
 zope.sqlalchemy==0.7.6
48 48
 tgapp-resetpassword==0.1.3
49 49
 lxml
50
+python-ldap-test==0.2.0
51
+who-ldap==3.1.0

+ 22 - 0
tracim/migration/versions/b73e57760b36_add_user_field_for_imported_profiles.py View File

@@ -0,0 +1,22 @@
1
+"""Add User field for imported profiles
2
+
3
+Revision ID: b73e57760b36
4
+Revises: 43a323cc661
5
+Create Date: 2016-02-09 11:00:22.694054
6
+
7
+"""
8
+
9
+# revision identifiers, used by Alembic.
10
+revision = 'b73e57760b36'
11
+down_revision = '43a323cc661'
12
+
13
+import sqlalchemy as sa
14
+from alembic import op
15
+
16
+
17
+def upgrade():
18
+    op.add_column('users', sa.Column('imported_from', sa.Unicode(length=32), nullable=True))
19
+
20
+
21
+def downgrade():
22
+    op.drop_column('users', 'imported_from')

+ 59 - 0
tracim/s.py View File

@@ -0,0 +1,59 @@
1
+from time import sleep
2
+
3
+from ldap_test import LdapServer
4
+
5
+server = LdapServer({
6
+            'port': 3333,
7
+            'password': 'toor',
8
+
9
+            'bind_dn': 'cn=admin,dc=directory,dc=fsf,dc=org',
10
+            'base': {
11
+                'objectclass': ['dcObject', 'organization'],
12
+                'dn': 'dc=directory,dc=fsf,dc=org',
13
+                'attributes': {
14
+                    'o': 'Free Software Foundation',
15
+                    'dc': 'directory'
16
+                }
17
+            },
18
+
19
+            'entries': [
20
+                {
21
+                    'objectclass': ['organizationalRole'],
22
+                    'dn': 'cn=admin,dc=directory,dc=fsf,dc=org',
23
+                    'attributes': {
24
+                        'cn': 'admin'
25
+                    }
26
+                },
27
+                {
28
+                    'objectclass': ['organizationalUnit'],
29
+                    'dn': 'ou=people,dc=directory,dc=fsf,dc=org',
30
+                    'attributes': {
31
+                        'ou': 'people',
32
+                    }
33
+                },
34
+                {
35
+                    'objectclass': ['organizationalUnit'],
36
+                    'dn': 'ou=groups,dc=directory,dc=fsf,dc=org',
37
+                    'attributes': {
38
+                        'ou': 'groups',
39
+                    }
40
+                },
41
+                {
42
+                    'objectclass': ['account', 'top'],
43
+                    'dn': 'cn=richard-not-real-email@fsf.org,ou=people,dc=directory,dc=fsf,dc=org',
44
+                    'attributes': {
45
+                        'uid': 'richard-not-real-email@fsf.org',
46
+                        'userPassword': 'rms',
47
+                        'givenName': 'Richard',
48
+                        'sn': 'Stallman',
49
+                        'mail': 'richard-not-real-email@fsf.org'
50
+                    }
51
+                },
52
+            ]
53
+        })
54
+
55
+server.start()
56
+try:
57
+    sleep(999999999)
58
+except KeyboardInterrupt:
59
+    pass

+ 2 - 0
tracim/setup.py View File

@@ -39,6 +39,8 @@ install_requires=[
39 39
     "sqlalchemy",
40 40
     "alembic",
41 41
     "repoze.who",
42
+    "who-ldap==3.1.0",
43
+    "python-ldap-test==0.2.0",
42 44
     ]
43 45
 
44 46
 setup(

+ 9 - 0
tracim/test.ini View File

@@ -23,4 +23,13 @@ use = config:development.ini
23 23
 use = main
24 24
 skip_authentication = True
25 25
 
26
+[app:ldap]
27
+sqlalchemy.url = postgresql://postgres:dummy@127.0.0.1:5432/tracim_test?client_encoding=utf8
28
+auth_type = 'ldap'
29
+ldap_url = 'ldaps://ad.my-company.org'
30
+ldap_base_dn = 'ou=users,dc=ad,dc=my-company,dc=com'
31
+ldap_bind_dn = 'cn=bind,cn=users,dc=ad,dc=my-company,dc=com'
32
+ldap_bind_pass = 'toor2'
33
+use = config:development.ini
34
+
26 35
 # Add additional test specific configuration options as necessary.

+ 12 - 51
tracim/tracim/config/app_cfg.py View File

@@ -25,6 +25,7 @@ from tg.i18n import lazy_ugettext as l_
25 25
 import tracim
26 26
 from tracim import model
27 27
 from tracim.lib import app_globals, helpers
28
+from tracim.lib.auth.wrapper import AuthConfigWrapper
28 29
 from tracim.lib.base import logger
29 30
 from tracim.model.data import ActionDescription
30 31
 from tracim.model.data import ContentType
@@ -72,60 +73,20 @@ base_config['flash.template'] = '''
72 73
 # YOU MUST CHANGE THIS VALUE IN PRODUCTION TO SECURE YOUR APP 
73 74
 base_config.sa_auth.cookie_secret = "3283411b-1904-4554-b0e1-883863b53080"
74 75
 
75
-base_config.auth_backend = 'sqlalchemy'
76
+base_config.auth_type = 'ldap'
76 77
 
77
-# what is the class you want to use to search for users in the database
78
-base_config.sa_auth.user_class = model.User
78
+# ldap_base_dn = 'ou=users,dc=ad,dc=snake-oil-company,dc=com'
79
+# ldap_bind_dn = 'cn=bind,cn=users,dc=ad,dc=snake-oil-company,dc=com'
79 80
 
80
-from tg.configuration.auth import TGAuthMetadata
81
+base_config.ldap_url = 'ldap://localhost:3333'
82
+base_config.ldap_base_dn = 'dc=directory,dc=fsf,dc=org'
83
+base_config.ldap_bind_dn = 'cn=admin,dc=directory,dc=fsf,dc=org'
84
+base_config.ldap_bind_pass = 'toor'
85
+base_config.ldap_ldap_naming_attribute = 'uid'
86
+base_config.ldap_user_attributes = 'mail=email'
87
+base_config.ldap_tls = False
81 88
 
82
-from sqlalchemy import and_
83
-#This tells to TurboGears how to retrieve the data for your user
84
-class ApplicationAuthMetadata(TGAuthMetadata):
85
-
86
-    def __init__(self, sa_auth):
87
-        self.sa_auth = sa_auth
88
-
89
-    def authenticate(self, environ, identity):
90
-        user = self.sa_auth.dbsession.query(self.sa_auth.user_class).filter(and_(
91
-            self.sa_auth.user_class.is_active==True,
92
-            self.sa_auth.user_class.email==identity['login']
93
-        )).first()
94
-
95
-        if user and user.validate_password(identity['password']):
96
-            return identity['login']
97
-    def get_user(self, identity, userid):
98
-        return self.sa_auth.dbsession.query(self.sa_auth.user_class).filter(and_(self.sa_auth.user_class.is_active==True, self.sa_auth.user_class.email==userid)).first()
99
-    def get_groups(self, identity, userid):
100
-        return [g.group_name for g in identity['user'].groups]
101
-    def get_permissions(self, identity, userid):
102
-        return [p.permission_name for p in identity['user'].permissions]
103
-
104
-base_config.sa_auth.dbsession = model.DBSession
105
-
106
-base_config.sa_auth.authmetadata = ApplicationAuthMetadata(base_config.sa_auth)
107
-
108
-# You can use a different repoze.who Authenticator if you want to
109
-# change the way users can login
110
-#base_config.sa_auth.authenticators = [('myauth', SomeAuthenticator()]
111
-
112
-# You can add more repoze.who metadata providers to fetch
113
-# user metadata.
114
-# Remember to set base_config.sa_auth.authmetadata to None
115
-# to disable authmetadata and use only your own metadata providers
116
-#base_config.sa_auth.mdproviders = [('myprovider', SomeMDProvider()]
117
-
118
-# override this if you would like to provide a different who plugin for
119
-# managing login and logout of your application
120
-base_config.sa_auth.form_plugin = None
121
-
122
-# You may optionally define a page where you want users to be redirected to
123
-# on login:
124
-base_config.sa_auth.post_login_url = '/post_login'
125
-
126
-# You may optionally define a page where you want users to be redirected to
127
-# on logout:
128
-base_config.sa_auth.post_logout_url = '/post_logout'
89
+AuthConfigWrapper.wrap(base_config)
129 90
 
130 91
 # INFO - This is the way to specialize the resetpassword email properties
131 92
 # plug(base_config, 'resetpassword', None, mail_subject=reset_password_email_subject)

+ 1 - 0
tracim/tracim/lib/auth/__init__.py View File

@@ -0,0 +1 @@
1
+from tracim.lib.auth.base import Auth

+ 22 - 0
tracim/tracim/lib/auth/base.py View File

@@ -0,0 +1,22 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+
4
+class Auth:
5
+
6
+    name = NotImplemented
7
+
8
+    def __init__(self, config):
9
+        self._config = config
10
+
11
+    def wrap_config(self):
12
+        # override this if you would like to provide a different who plugin for
13
+        # managing login and logout of your application
14
+        self._config.sa_auth.form_plugin = None
15
+
16
+        # You may optionally define a page where you want users to be redirected to
17
+        # on login:
18
+        self._config.sa_auth.post_login_url = '/post_login'
19
+
20
+        # You may optionally define a page where you want users to be redirected to
21
+        # on logout:
22
+        self._config.sa_auth.post_logout_url = '/post_logout'

+ 43 - 0
tracim/tracim/lib/auth/internal.py View File

@@ -0,0 +1,43 @@
1
+# -*- coding: utf-8 -*-
2
+from sqlalchemy import and_
3
+from tg.configuration.auth import TGAuthMetadata
4
+
5
+from tracim.lib.auth.base import Auth
6
+from tracim.model import DBSession, User
7
+
8
+
9
+class InternalAuth(Auth):
10
+
11
+    name = 'internal'
12
+
13
+    def wrap_config(self):
14
+        super().wrap_config()
15
+
16
+        self._config.sa_auth.user_class = User
17
+        self._config.auth_backend = 'sqlalchemy'
18
+        self._config.sa_auth.dbsession = DBSession
19
+        self._config.sa_auth.authmetadata = InternalApplicationAuthMetadata(self._config.sa_auth)
20
+
21
+
22
+class InternalApplicationAuthMetadata(TGAuthMetadata):
23
+    def __init__(self, sa_auth):
24
+        self.sa_auth = sa_auth
25
+
26
+    def authenticate(self, environ, identity):
27
+        user = self.sa_auth.dbsession.query(self.sa_auth.user_class).filter(and_(
28
+            self.sa_auth.user_class.is_active == True,
29
+            self.sa_auth.user_class.email == identity['login']
30
+        )).first()
31
+
32
+        if user and user.validate_password(identity['password']):
33
+            return identity['login']
34
+
35
+    def get_user(self, identity, userid):
36
+        return self.sa_auth.dbsession.query(self.sa_auth.user_class).filter(
37
+            and_(self.sa_auth.user_class.is_active == True, self.sa_auth.user_class.email == userid)).first()
38
+
39
+    def get_groups(self, identity, userid):
40
+        return [g.group_name for g in identity['user'].groups]
41
+
42
+    def get_permissions(self, identity, userid):
43
+        return [p.permission_name for p in identity['user'].permissions]

+ 135 - 0
tracim/tracim/lib/auth/ldap.py View File

@@ -0,0 +1,135 @@
1
+# -*- coding: utf-8 -*-
2
+from tg.configuration.auth import TGAuthMetadata
3
+from who_ldap import LDAPAttributesPlugin, LDAPGroupsPlugin
4
+from who_ldap import LDAPSearchAuthenticatorPlugin as BaseLDAPSearchAuthenticatorPlugin
5
+
6
+from tracim.lib.auth.base import Auth
7
+from tracim.lib.user import UserApi
8
+from tracim.model import auth, DBSession, User
9
+
10
+
11
+class LDAPAuth(Auth):
12
+    """
13
+    LDAP auth management.
14
+    TODO: Group connection
15
+
16
+    """
17
+    name = 'ldap'
18
+
19
+    def __init__(self, config):
20
+        super().__init__(config)
21
+        self.ldap_auth = self._get_ldap_auth()
22
+        self.ldap_user_provider = self._get_ldap_user_provider()
23
+        if self._config.get('ldap_group_enabled', False):
24
+            self.ldap_groups_provider = self._get_ldap_groups_provider()
25
+
26
+    def wrap_config(self):
27
+        super().wrap_config()
28
+        self._config.auth_backend = 'ldapauth'
29
+        self._config.sa_auth.authenticators = [('ldapauth', self.ldap_auth)]
30
+
31
+        mdproviders = [('ldapuser', self.ldap_user_provider)]
32
+        if self._config.get('ldap_group_enabled', False):
33
+            mdproviders.append(('ldapgroups', self.ldap_groups_provider))
34
+        self._config.sa_auth.mdproviders = mdproviders
35
+
36
+        self._config.sa_auth.authmetadata = LDAPApplicationAuthMetadata(self._config.sa_auth)
37
+
38
+    def _get_ldap_auth(self):
39
+        auth_plug = LDAPSearchAuthenticatorPlugin(
40
+            url=self._config.get('ldap_url'),
41
+            base_dn=self._config.get('ldap_base_dn'),
42
+            bind_dn=self._config.get('ldap_bind_dn'),
43
+            bind_pass=self._config.get('ldap_bind_pass'),
44
+            returned_id='login',
45
+            # the LDAP attribute that holds the user name:
46
+            naming_attribute=self._config.get('ldap_naming_attribute'),
47
+            start_tls=self._config.get('ldap_tls', False),
48
+        )
49
+        auth_plug.set_auth(self)
50
+        return auth_plug
51
+
52
+    def _get_ldap_user_provider(self):
53
+        return LDAPAttributesPlugin(
54
+            url=self._config.get('ldap_url'),
55
+            bind_dn=self._config.get('ldap_bind_dn'),
56
+            bind_pass=self._config.get('ldap_bind_pass'),
57
+            name='user',
58
+            # map from LDAP attributes to TurboGears user attributes:
59
+            attributes=self._config.get('ldap_user_attributes', 'mail=email'),
60
+            flatten=True,
61
+            start_tls=self._config.get('ldap_tls', False)
62
+        )
63
+
64
+    def _get_ldap_groups_provider(self):
65
+        return LDAPGroupsPlugin(
66
+            url=self._config.get('ldap_url'),
67
+            base_dn=self._config.get('ldap_base_dn'),
68
+            bind_dn=self._config.get('ldap_bind_dn'),
69
+            bind_pass=self._config.get('ldap_bind_pass'),
70
+            filterstr=self._config.get('ldap_group_filter', '(&(objectClass=group)(member=%(dn)s))'),
71
+            name='groups',
72
+            start_tls=self._config.get('ldap_tls', False)
73
+        )
74
+
75
+
76
+class LDAPSearchAuthenticatorPlugin(BaseLDAPSearchAuthenticatorPlugin):
77
+
78
+    def __init__(self, *args, **kwargs):
79
+        super().__init__(*args, **kwargs)
80
+        self._auth = None
81
+        self._user_api = UserApi(None)
82
+
83
+    def set_auth(self, auth):
84
+        self._auth = auth
85
+
86
+    def authenticate(self, environ, identity):
87
+        # Note: super().authenticate return None if already authenticated or not found
88
+        email = super().authenticate(environ, identity)
89
+        if email:
90
+            self._sync_ldap_user(email)
91
+        return email
92
+
93
+    def _sync_ldap_user(self, email):
94
+        if not self._user_api.user_with_email_exists(email):
95
+            user = User(email=email, imported_from=LDAPAuth.name)
96
+            DBSession.add(user)
97
+            import transaction
98
+            transaction.commit()
99
+
100
+            # TODO - B.S. - 20160208: Voir avec Damien, si je ne fait pas de transaction.commit()
101
+            # manuellement la donnée n'est pas en base.
102
+            # self._user_api.create_user(email=email, save_now=True)
103
+
104
+
105
+class LDAPApplicationAuthMetadata(TGAuthMetadata):
106
+
107
+    # map from LDAP group names to TurboGears group names
108
+    group_map = {'operators': 'managers'}
109
+
110
+    # set of permissions for all mapped groups
111
+    permissions_for_groups = {'managers': {'manage'}}
112
+
113
+    def __init__(self, sa_auth):
114
+        self.sa_auth = sa_auth
115
+
116
+    def get_user(self, identity, userid):
117
+        user = identity.get('user')
118
+        if user:
119
+            name = '{email}'.format(**user).strip()
120
+            user.update(user_name=userid, display_name=name)
121
+        return user
122
+
123
+    def get_groups(self, identity, userid):
124
+        get_group = self.group_map.get
125
+        return [get_group(g, g) for g in identity.get('groups', [])]
126
+
127
+    def get_permissions_for_group(self, group):
128
+        return self.permissions_for_groups.get(group, set())
129
+
130
+    def get_permissions(self, identity, userid):
131
+        permissions = set()
132
+        get_permissions = self.get_permissions_for_group
133
+        for group in self.get_groups(identity, userid):
134
+            permissions |= get_permissions(group)
135
+        return permissions

+ 26 - 0
tracim/tracim/lib/auth/wrapper.py View File

@@ -0,0 +1,26 @@
1
+# -*- coding: utf-8 -*-
2
+from tracim.lib.auth.internal import InternalAuth
3
+from tracim.lib.auth.ldap import LDAPAuth
4
+
5
+
6
+class AuthConfigWrapper:
7
+
8
+    # TODO: Dynamic load, like plugins ?
9
+    AUTH_WRAPPERS = (InternalAuth, LDAPAuth)
10
+    EXTERNAL_AUTHS = (LDAPAuth,)
11
+
12
+    @classmethod
13
+    def wrap(cls, config):
14
+        wrapper_class = cls._get_wrapper_class(config)
15
+        wrapper = wrapper_class(config)
16
+        wrapper.wrap_config()
17
+        return config
18
+
19
+    @classmethod
20
+    def _get_wrapper_class(cls, config):
21
+        for wrapper_class in cls.AUTH_WRAPPERS:
22
+            if wrapper_class.name is NotImplemented:
23
+                raise Exception("\"name\" attribute of %s is required" % str(wrapper_class))
24
+            if config.auth_type == wrapper_class.name:
25
+                return wrapper_class
26
+        raise Exception("No auth config wrapper found for \"%s\" auth_type config" % config.auth_type)

+ 1 - 0
tracim/tracim/model/auth.py View File

@@ -119,6 +119,7 @@ class User(DeclarativeBase):
119 119
     _password = Column('password', Unicode(128))
120 120
     created = Column(DateTime, default=datetime.now)
121 121
     is_active = Column(Boolean, default=True, nullable=False)
122
+    imported_from = Column(Unicode(32), nullable=True)
122 123
 
123 124
     @hybrid_property
124 125
     def email_address(self):

+ 56 - 0
tracim/tracim/tests/__init__.py View File

@@ -3,6 +3,9 @@
3 3
 
4 4
 from os import getcwd, path
5 5
 
6
+import ldap3
7
+from ldap_test import LdapServer
8
+from nose.tools import ok_
6 9
 from paste.deploy import loadapp
7 10
 from webtest import TestApp
8 11
 
@@ -23,6 +26,7 @@ from sqlalchemy.schema import Sequence
23 26
 from sqlalchemy.schema import Table
24 27
 
25 28
 import transaction
29
+from who_ldap import make_connection
26 30
 
27 31
 from tracim.lib.base import logger
28 32
 
@@ -191,3 +195,55 @@ class TestController(object):
191 195
         """Tear down test fixture for each functional test method."""
192 196
         # model.DBSession.remove()
193 197
         teardown_db()
198
+
199
+
200
+class TracimTestController(TestController):
201
+
202
+    def _connect_user(self, login, password):
203
+        # Going to the login form voluntarily:
204
+        resp = self.app.get('/', status=200)
205
+        form = resp.form
206
+        # Submitting the login form:
207
+        form['login'] = login
208
+        form['password'] = password
209
+        return form.submit(status=302)
210
+
211
+
212
+class LDAPTest:
213
+
214
+    """
215
+    Server fixtures, see https://github.com/zoldar/python-ldap-test
216
+    """
217
+    ldap_server_data = NotImplemented
218
+
219
+    def __init__(self, *args, **kwargs):
220
+        super().__init__(*args, **kwargs)
221
+        self._ldap_test_server = None
222
+        self._ldap_connection = None
223
+
224
+    def setUp(self):
225
+        super().setUp()
226
+        self._ldap_test_server = LdapServer(self.ldap_server_data)
227
+        self._ldap_test_server.start()
228
+        ldap3_server = ldap3.Server('localhost', port=self._ldap_test_server.config['port'])
229
+        self._ldap_connection = ldap3.Connection(
230
+            ldap3_server,
231
+            user=self._ldap_test_server.config['bind_dn'],
232
+            password=self._ldap_test_server.config['password'],
233
+            auto_bind=True
234
+        )
235
+
236
+    def tearDown(self):
237
+        super().tearDown()
238
+        self._ldap_test_server.stop()
239
+
240
+    def test_ldap_connectivity(self):
241
+        with make_connection(
242
+                'ldap://%s:%d' % ('localhost', self._ldap_test_server.config['port']),
243
+                self._ldap_test_server.config['bind_dn'],
244
+                'toor'
245
+        ) as conn:
246
+            if not conn.bind():
247
+                ok_(False, "Cannot establish connection with LDAP test server")
248
+            else:
249
+                ok_(True)

+ 80 - 0
tracim/tracim/tests/functional/test_ldap_authentication.py View File

@@ -0,0 +1,80 @@
1
+# -*- coding: utf-8 -*-
2
+"""
3
+Integration tests for the ldap authentication sub-system.
4
+"""
5
+from nose.tools import eq_
6
+from tg import config
7
+
8
+from tracim.model import DBSession, User
9
+from tracim.tests import LDAPTest, TracimTestController
10
+
11
+
12
+class TestAuthentication(LDAPTest, TracimTestController):
13
+    application_under_test = 'ldap'
14
+    ldap_server_data = {
15
+            'port': 3333,
16
+            'password': 'toor',
17
+
18
+            'bind_dn': 'cn=admin,dc=directory,dc=fsf,dc=org',
19
+            'base': {
20
+                'objectclass': ['dcObject', 'organization'],
21
+                'dn': 'dc=directory,dc=fsf,dc=org',
22
+                'attributes': {
23
+                    'o': 'Free Software Foundation',
24
+                    'dc': 'directory'
25
+                }
26
+            },
27
+
28
+            'entries': [
29
+                {
30
+                    'objectclass': ['organizationalRole'],
31
+                    'dn': 'cn=admin,dc=directory,dc=fsf,dc=org',
32
+                    'attributes': {
33
+                        'cn': 'admin'
34
+                    }
35
+                },
36
+                {
37
+                    'objectclass': ['organizationalUnit'],
38
+                    'dn': 'ou=people,dc=directory,dc=fsf,dc=org',
39
+                    'attributes': {
40
+                        'ou': 'people',
41
+                    }
42
+                },
43
+                {
44
+                    'objectclass': ['organizationalUnit'],
45
+                    'dn': 'ou=groups,dc=directory,dc=fsf,dc=org',
46
+                    'attributes': {
47
+                        'ou': 'groups',
48
+                    }
49
+                },
50
+                {
51
+                    'objectclass': ['account', 'top'],
52
+                    'dn': 'cn=richard-not-real-email@fsf.org,ou=people,dc=directory,dc=fsf,dc=org',
53
+                    'attributes': {
54
+                        'uid': 'richard-not-real-email@fsf.org',
55
+                        'userPassword': 'rms',
56
+                        'mail': 'richard-not-real-email@fsf.org'
57
+                    }
58
+                },
59
+            ]
60
+        }
61
+
62
+    def test_ldap_auth_fail(self):
63
+        # User is unknown in tracim database
64
+        eq_(0, DBSession.query(User).filter(User.email == 'unknown-user@fsf.org').count())
65
+
66
+        self._connect_user('unknown-user@fsf.org', 'no-pass')
67
+
68
+        # User is registered in tracim database
69
+        eq_(0, DBSession.query(User).filter(User.email == 'unknown-user@fsf.org').count())
70
+        DBSession.close()
71
+
72
+    def test_ldap_auth_sync(self):
73
+        # User is unknown in tracim database
74
+        eq_(0, DBSession.query(User).filter(User.email == 'richard-not-real-email@fsf.org').count())
75
+
76
+        self._connect_user('richard-not-real-email@fsf.org', 'rms')
77
+
78
+        # User is registered in tracim database
79
+        eq_(1, DBSession.query(User).filter(User.email == 'richard-not-real-email@fsf.org').count())
80
+        DBSession.close()

+ 30 - 29
tracim/tracim/websetup/bootstrap.py View File

@@ -13,35 +13,36 @@ def bootstrap(command, conf, vars):
13 13
     # <websetup.bootstrap.before.auth
14 14
     from sqlalchemy.exc import IntegrityError
15 15
     try:
16
-        u = model.User()
17
-        u.display_name = 'Global manager'
18
-        u.email = 'admin@admin.admin'
19
-        u.password = 'admin@admin.admin'
20
-        model.DBSession.add(u)
21
-
22
-        g1 = model.Group()
23
-        g1.group_id = 1
24
-        g1.group_name = 'users'
25
-        g1.display_name = 'Users'
26
-        g1.users.append(u)
27
-        model.DBSession.add(g1)
28
-
29
-        g2 = model.Group()
30
-        g2.group_id = 2
31
-        g2.group_name = 'managers'
32
-        g2.display_name = 'Global Managers'
33
-        g2.users.append(u)
34
-        model.DBSession.add(g2)
35
-
36
-        g3 = model.Group()
37
-        g3.group_id = 3
38
-        g3.group_name = 'administrators'
39
-        g3.display_name = 'Administrators'
40
-        g3.users.append(u)
41
-        model.DBSession.add(g3)
42
-
43
-        model.DBSession.flush()
44
-        transaction.commit()
16
+        # u = model.User()
17
+        # u.display_name = 'Global manager'
18
+        # u.email = 'admin@admin.admin'
19
+        # u.password = 'admin@admin.admin'
20
+        # model.DBSession.add(u)
21
+        #
22
+        # g1 = model.Group()
23
+        # g1.group_id = 1
24
+        # g1.group_name = 'users'
25
+        # g1.display_name = 'Users'
26
+        # g1.users.append(u)
27
+        # model.DBSession.add(g1)
28
+        #
29
+        # g2 = model.Group()
30
+        # g2.group_id = 2
31
+        # g2.group_name = 'managers'
32
+        # g2.display_name = 'Global Managers'
33
+        # g2.users.append(u)
34
+        # model.DBSession.add(g2)
35
+        #
36
+        # g3 = model.Group()
37
+        # g3.group_id = 3
38
+        # g3.group_name = 'administrators'
39
+        # g3.display_name = 'Administrators'
40
+        # g3.users.append(u)
41
+        # model.DBSession.add(g3)
42
+        #
43
+        # model.DBSession.flush()
44
+        # transaction.commit()
45
+        pass
45 46
 
46 47
     except IntegrityError:
47 48
         print('Warning, there was a problem adding your auth data, it may have already been added:')

+ 2 - 1
tracim/tracim/websetup/schema.py View File

@@ -174,7 +174,8 @@ CREATE TABLE users (
174 174
     display_name character varying(255),
175 175
     password character varying(128),
176 176
     created timestamp without time zone,
177
-    is_active boolean DEFAULT true NOT NULL
177
+    is_active boolean DEFAULT true NOT NULL,
178
+    imported_from character varying(32)
178 179
 );
179 180
 
180 181
 CREATE TABLE user_group (