Browse Source

LDAP: Groups and permission managment (for groups in local db)

Bastien Sevajol 9 years ago
parent
commit
b6972e3b8f

+ 1 - 0
tracim/test.ini View File

@@ -33,6 +33,7 @@ ldap_bind_pass = toor
33 33
 ldap_ldap_naming_attribute = uid
34 34
 ldap_user_attributes = mail=email
35 35
 ldap_tls = False
36
+ldap_group_enabled = False
36 37
 use = config:development.ini
37 38
 
38 39
 # Add additional test specific configuration options as necessary.

+ 48 - 26
tracim/tracim/lib/auth/ldap.py View File

@@ -1,12 +1,13 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 from tg.configuration.auth import TGAuthMetadata
3
-from who_ldap import LDAPAttributesPlugin, LDAPGroupsPlugin
3
+from who_ldap import LDAPAttributesPlugin as BaseLDAPAttributesPlugin
4
+from who_ldap import LDAPGroupsPlugin as BaseLDAPGroupsPlugin
4 5
 from who_ldap import LDAPSearchAuthenticatorPlugin as BaseLDAPSearchAuthenticatorPlugin
5 6
 
6 7
 from tracim.lib.auth.base import Auth
7 8
 from tracim.lib.helpers import ini_conf_to_bool
8 9
 from tracim.lib.user import UserApi
9
-from tracim.model import auth, DBSession, User
10
+from tracim.model import DBSession, User
10 11
 
11 12
 
12 13
 class LDAPAuth(Auth):
@@ -21,7 +22,7 @@ class LDAPAuth(Auth):
21 22
         super().__init__(config)
22 23
         self.ldap_auth = self._get_ldap_auth()
23 24
         self.ldap_user_provider = self._get_ldap_user_provider()
24
-        if self._config.get('ldap_group_enabled', False):
25
+        if ini_conf_to_bool(self._config.get('ldap_group_enabled', False)):
25 26
             self.ldap_groups_provider = self._get_ldap_groups_provider()
26 27
 
27 28
     def wrap_config(self):
@@ -30,7 +31,7 @@ class LDAPAuth(Auth):
30 31
         self._config['sa_auth'].authenticators = [('ldapauth', self.ldap_auth)]
31 32
 
32 33
         mdproviders = [('ldapuser', self.ldap_user_provider)]
33
-        if self._config.get('ldap_group_enabled', False):
34
+        if ini_conf_to_bool(self._config.get('ldap_group_enabled', False)):
34 35
             mdproviders.append(('ldapgroups', self.ldap_groups_provider))
35 36
         self._config['sa_auth'].mdproviders = mdproviders
36 37
 
@@ -105,32 +106,53 @@ class LDAPSearchAuthenticatorPlugin(BaseLDAPSearchAuthenticatorPlugin):
105 106
 
106 107
 class LDAPApplicationAuthMetadata(TGAuthMetadata):
107 108
 
108
-    # map from LDAP group names to TurboGears group names
109
-    group_map = {'operators': 'managers'}
110
-
111
-    # set of permissions for all mapped groups
112
-    permissions_for_groups = {'managers': {'manage'}}
113
-
114
-    def __init__(self, sa_auth):
115
-        self.sa_auth = sa_auth
109
+    def __init__(self, config):
110
+        self.sa_auth = config.get('sa_auth')
111
+        self._config = config
116 112
 
117 113
     def get_user(self, identity, userid):
118
-        user = identity.get('user')
119
-        if user:
120
-            name = '{email}'.format(**user).strip()
121
-            user.update(user_name=userid, display_name=name)
122
-        return user
114
+        return identity.get('user')
123 115
 
124 116
     def get_groups(self, identity, userid):
125
-        get_group = self.group_map.get
126
-        return [get_group(g, g) for g in identity.get('groups', [])]
117
+        if not ini_conf_to_bool(self._config.get('ldap_group_enabled')):
118
+
119
+            # TODO - B.S. - 20160212: récupérer identity['user'].groups directement produit
120
+            # Parent instance XXX is not bound to a Session. Voir avec Damien.
121
+            user = DBSession.query(User).filter(User.email == identity['user'].email).one()
122
+            return [g.group_name for g in user.groups]
127 123
 
128
-    def get_permissions_for_group(self, group):
129
-        return self.permissions_for_groups.get(group, set())
124
+            return [g.group_name for g in identity['user'].groups]
125
+        else:
126
+            raise NotImplementedError()
130 127
 
131 128
     def get_permissions(self, identity, userid):
132
-        permissions = set()
133
-        get_permissions = self.get_permissions_for_group
134
-        for group in self.get_groups(identity, userid):
135
-            permissions |= get_permissions(group)
136
-        return permissions
129
+        if not ini_conf_to_bool(self._config.get('ldap_group_enabled')):
130
+
131
+            # TODO - B.S. - 20160212: récupérer identity['user'].groups directement produit
132
+            # Parent instance XXX is not bound to a Session. Voir avec Damien.
133
+            user = DBSession.query(User).filter(User.email == identity['user'].email).one()
134
+            return [p.permission_name for p in user.permissions]
135
+
136
+            return [p.permission_name for p in identity['user'].permissions]
137
+        else:
138
+            raise NotImplementedError()
139
+
140
+
141
+class LDAPGroupsPlugin(BaseLDAPGroupsPlugin):
142
+
143
+    def add_metadata(self, environ, identity):
144
+        super().add_metadata(environ, identity)
145
+        groups_names = identity[self.name]
146
+        raise NotImplementedError()  # Should sync groups etc ...
147
+
148
+
149
+class LDAPAttributesPlugin(BaseLDAPAttributesPlugin):
150
+
151
+    def __init__(self, *args, **kwargs):
152
+        super().__init__(*args, **kwargs)
153
+        self._user_api = UserApi(None)
154
+
155
+    def add_metadata(self, environ, identity):
156
+        super().add_metadata(environ, identity)
157
+        # TODO - B.S. - 20160212: identity contains now som information from LDAP what we can save in local database
158
+        identity[self.name] = self._user_api.get_one_by_email(identity.get('repoze.who.userid'))

+ 3 - 3
tracim/tracim/tests/functional/test_ldap_authentication.py View File

@@ -24,12 +24,12 @@ class TestAuthentication(LDAPTest, TracimTestController):
24 24
 
25 25
     def test_ldap_auth_fail_wrong_pass(self):
26 26
         # User is unknown in tracim database
27
-        eq_(0, DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').count())
27
+        eq_(0, DBSession.query(User).filter(User.email == 'richard-not-real-email@fsf.org').count())
28 28
 
29
-        self._connect_user('lawrence-not-real-email@fsf.org', 'wrong-pass')
29
+        self._connect_user('richard-not-real-email@fsf.org', 'wrong-pass')
30 30
 
31 31
         # User is registered in tracim database
32
-        eq_(0, DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').count())
32
+        eq_(0, DBSession.query(User).filter(User.email == 'richard-not-real-email@fsf.org').count())
33 33
 
34 34
     def test_ldap_auth_sync(self):
35 35
         # User is unknown in tracim database

+ 113 - 0
tracim/tracim/tests/library/test_ldap_without_ldap_groups.py View File

@@ -0,0 +1,113 @@
1
+# -*- coding: utf-8 -*-
2
+from nose.tools import eq_
3
+from tg import config
4
+
5
+from tracim.fixtures.ldap import ldap_test_server_fixtures
6
+from tracim.lib.auth.ldap import LDAPAuth
7
+from tracim.lib.helpers import ini_conf_to_bool
8
+from tracim.model import DBSession, User, Group
9
+from tracim.tests import LDAPTest, TestStandard
10
+
11
+
12
+class TestContentApi(LDAPTest, TestStandard):
13
+    """
14
+    This test load test.ini app:ldap config. LDAP groups management must be disabled in this config.
15
+    """
16
+    application_under_test = 'ldap'
17
+    ldap_server_data = ldap_test_server_fixtures
18
+
19
+    def _check_db_user(self, email, count=1):
20
+        eq_(count, DBSession.query(User).filter(User.email == email).count())
21
+
22
+    def test_authenticate_success(self):
23
+        """
24
+        LDAP Auth success
25
+        :return:
26
+        """
27
+        ldap_auth = LDAPAuth(config)
28
+        richard_identity = {'login': 'richard-not-real-email@fsf.org', 'password': 'rms'}
29
+
30
+        self._check_db_user('richard-not-real-email@fsf.org', 0)
31
+        auth_id = ldap_auth.ldap_auth.authenticate(environ={}, identity=richard_identity)
32
+
33
+        assert auth_id == 'richard-not-real-email@fsf.org'
34
+        self._check_db_user('richard-not-real-email@fsf.org', 1)
35
+
36
+    def test_authenticate_fail_wrong_pass(self):
37
+        """
38
+        LDAP Auth fail: wrong password
39
+        :return:
40
+        """
41
+        ldap_auth = LDAPAuth(config)
42
+        richard_identity = {'login': 'richard-not-real-email@fsf.org', 'password': 'wrong pass'}
43
+
44
+        self._check_db_user('richard-not-real-email@fsf.org', 0)
45
+        auth_id = ldap_auth.ldap_auth.authenticate(environ={}, identity=richard_identity)
46
+
47
+        assert auth_id is None
48
+        self._check_db_user('richard-not-real-email@fsf.org', 0)
49
+
50
+    def test_authenticate_fail_wrong_login(self):
51
+        """
52
+        LDAP Auth fail: wrong email
53
+        :return:
54
+        """
55
+        ldap_auth = LDAPAuth(config)
56
+        richard_identity = {'login': 'wrong-email@fsf.org', 'password': 'rms'}
57
+
58
+        self._check_db_user('wrong-email@fsf.org', 0)
59
+        auth_id = ldap_auth.ldap_auth.authenticate(environ={}, identity=richard_identity)
60
+
61
+        assert auth_id is None
62
+        self._check_db_user('wrong-email@fsf.org', 0)
63
+
64
+    def test_internal_groups(self):
65
+        """
66
+        LDAP don't manage groups here: We must retrieve internal groups of tested user
67
+        :return:
68
+        """
69
+        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').one()
70
+        managers = DBSession.query(Group).filter(Group.group_name == 'managers').one()
71
+        lawrence_identity = {'user': lawrence}
72
+
73
+        # Lawrence is in fixtures: he is in managers group
74
+        self._check_db_user('lawrence-not-real-email@fsf.org', 1)
75
+        assert lawrence in managers.users
76
+        assert False is ini_conf_to_bool(config.get('ldap_group_enabled', False))
77
+        assert ['managers'] == config.get('sa_auth').authmetadata.get_groups(
78
+            identity=lawrence_identity,
79
+            userid=lawrence.email
80
+        )
81
+
82
+        should_groups = ['managers']
83
+        are_groups = config.get('sa_auth').authmetadata.get_groups(
84
+            identity=lawrence_identity,
85
+            userid=lawrence.email
86
+        )
87
+        eq_(should_groups,
88
+            are_groups,
89
+            "Permissions should be %s, they are %s" % (should_groups, are_groups))
90
+
91
+    def test_internal_permissions(self):
92
+        """
93
+        LDAP don't manage groups here: We must retrieve internal groups permission of tested user
94
+        :return:
95
+        """
96
+        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').one()
97
+        managers = DBSession.query(Group).filter(Group.group_name == 'managers').one()
98
+        lawrence_identity = {'user': lawrence}
99
+
100
+        # Lawrence is in fixtures: he is in managers group
101
+        self._check_db_user('lawrence-not-real-email@fsf.org', 1)
102
+        assert lawrence in managers.users
103
+        assert False is ini_conf_to_bool(config.get('ldap_group_enabled', False))
104
+
105
+        should_permissions = []  # Actually there is no permission
106
+        are_permissions = config.get('sa_auth').authmetadata.get_permissions(
107
+            identity=lawrence_identity,
108
+            userid=lawrence.email
109
+        )
110
+        eq_(should_permissions,
111
+            are_permissions,
112
+            "Permissions should be %s, they are %s" % (should_permissions, are_permissions))
113
+

+ 9 - 0
tracim/tracim/websetup/bootstrap.py View File

@@ -40,6 +40,15 @@ def bootstrap(command, conf, vars):
40 40
         g3.users.append(u)
41 41
         model.DBSession.add(g3)
42 42
 
43
+        # TODO: - B.S. - 20160212: Following fixture is LDAP tests specific, should make an little fixture management
44
+        # for tests
45
+        lawrence = model.User()
46
+        lawrence.display_name = 'Lawrence Lessig'
47
+        lawrence.email = 'lawrence-not-real-email@fsf.org'
48
+        lawrence.password = 'foobarbaz'
49
+        model.DBSession.add(lawrence)
50
+        g2.users.append(lawrence)
51
+
43 52
         model.DBSession.flush()
44 53
         transaction.commit()
45 54
         pass