Projet

Général

Profil

0001-ldap_backend-groups-to-A2-roles-mapping-16523.patch

Paul Marillonnet, 06 décembre 2017 18:23

Télécharger (8,31 ko)

Voir les différences:

Subject: [PATCH] ldap_backend: groups to A2 roles mapping (#16523)

 src/authentic2/backends/ldap_backend.py | 75 ++++++++++++++++++++++++++++-----
 tests/test_ldap.py                      | 41 ++++++++++++++++++
 2 files changed, 106 insertions(+), 10 deletions(-)
src/authentic2/backends/ldap_backend.py
228 228
        'groupstaff': None,
229 229
        'groupactive': None,
230 230
        'group_mapping': (),
231
        'group_to_role_mapping': (),
231 232
        'replicas': True,
232 233
        'email_field': 'mail',
233 234
        'fname_field': 'givenName',
......
540 541
                elif dn not in group_dns and group in groups:
541 542
                    user.groups.remove(group)
542 543

  
544
    def populate_roles_by_mapping(self, user, dn, conn, block, role_dns):
545
        '''Assign role to user based on a mapping from group DNs'''
546
        group_to_role_mapping = block.get('group_to_role_mapping')
547
        if not group_to_role_mapping:
548
            return
549
        if not user.pk:
550
            user.save()
551
            user._changed = False
552
        roles = user.roles.all()
553
        for dn, role_names in group_to_role_mapping:
554
            for role_name in role_names:
555
                role, error = self.get_role(block, role_id=role_name)
556
                if role is None:
557
                    log.warning('error %s: couldn\'t retrieve role %r',
558
                            error, role_name)
559
                    continue
560
                # Add missing roles
561
                if dn in role_dns and role not in roles:
562
                    user.roles.add(role)
563
                # Remove extra roles
564
                elif dn not in role_dns and role in roles:
565
                    user.roles.remove(role)
566

  
543 567
    def get_ldap_group_dns(self, user, dn, conn, block, attributes):
544 568
        '''Retrieve group DNs from the LDAP by attributes (memberOf) or by
545 569
           filter.
......
549 573
        group_filter = block['group_filter']
550 574
        group_dns = set()
551 575
        if member_of_attribute:
552
            member_of_attribute = str(member_of_attribute)
553
            results = conn.search_s(dn, ldap.SCOPE_BASE, '', [member_of_attribute])
554
            group_dns.update(results[0][1].get(member_of_attribute, []))
576
            group_dns.update(attributes.get(member_of_attribute, []))
555 577
        if group_filter:
556 578
            group_filter = str(group_filter)
557 579
            params = attributes.copy()
......
572 594
        self.populate_admin_flags_by_group(user, block, group_dns)
573 595
        self.populate_groups_by_mapping(user, dn, conn, block, group_dns)
574 596

  
597
    def populate_user_roles(self, user, dn, conn, block, attributes):
598
        group_dns = self.get_ldap_group_dns(user, dn, conn, block, attributes)
599
        log.debug('roles for dn %r: %r', dn, group_dns)
600
        # TODO? Admin flags by roles?
601
        self.populate_roles_by_mapping(user, dn, conn, block, group_dns)
602

  
575 603
    def get_group_by_name(self, block, group_name, create=None):
576 604
        '''Obtain a Django group'''
577 605
        if create is None:
......
585 613
            except Group.DoesNotExist:
586 614
                return None
587 615

  
588
    def get_role_by_name(self, block, role_name):
616
    def get_role(self, block, role_id):
589 617
        '''Obtain a Django role'''
590
        try:
591
            return Role.objects.get(name=role_name)
592
        except Role.DoesNotExist:
593
            return None
618
        kwargs = {}
619
        slug = None
620
        if isinstance(role_id, basestring):
621
            slug = role_id
622
        elif isinstance(role_id, (tuple, list)):
623
            try:
624
                slug, ou__slug = role_id
625
                kwargs = {'ou__slug': ou__slug}
626
            except ValueError:
627
                try:
628
                    slug, ou__slug, service__slug = role_id
629
                    kwargs = {'ou__slug': ou__slug, 'service__slug': service__slug}
630
                except ValueError:
631
                    pass
632
        if slug:
633
            try:
634
                return Role.objects.get(slug=slug, **kwargs), None
635
            except Role.DoesNotExist:
636
                try:
637
                    return Role.objects.get(name=slug, **kwargs), None
638
                except Role.DoesNotExist:
639
                    error = ('role %r does not exist' % role_id)
640
            except Role.MultipleObjectsReturned:
641
                error = 'multiple objects returned, identifier is imprecise'
642
        else:
643
            error = 'invalid role identifier must be slug, (slug, ou__slug) or (slug, ou__slug, service__slug)'
644
        return None, error
594 645

  
595 646
    def populate_mandatory_groups(self, user, block):
596 647
        mandatory_groups = block.get('set_mandatory_groups')
......
617 668
            user._changed = False
618 669
        roles = user.roles.all()
619 670
        for role_name in mandatory_roles:
620
            role = self.get_role_by_name(block, role_name)
671
            role, error = self.get_role(block, role_id=role_name)
621 672
            if role is None:
673
                log.warning('error %s: couldn\'t retrieve role %r',
674
                        error, role_name)
622 675
                continue
623 676
            if role not in roles:
624 677
                user.roles.add(role)
......
641 694
        self.populate_mandatory_groups(user, block)
642 695
        self.populate_mandatory_roles(user, block)
643 696
        self.populate_user_groups(user, dn, conn, block, attributes)
697
        self.populate_user_roles(user, dn, conn, block, attributes)
644 698

  
645 699
    def populate_user_ou(self, user, dn, conn, block, attributes):
646 700
        '''Assign LDAP user to an ou, the default one if ou_slug setting is
......
671 725
    def get_ldap_attributes_names(cls, block):
672 726
        attributes = set()
673 727
        attributes.update(map(str, block['attributes']))
674
        for field in ('email_field', 'fname_field', 'lname_field'):
728
        for field in ('email_field', 'fname_field', 'lname_field',
729
                'member_of_attribute'):
675 730
            if block[field]:
676 731
                attributes.add(block[field])
677 732
        for external_id_tuple in block['external_id_tuples']:
tests/test_ldap.py
272 272

  
273 273

  
274 274
@pytest.mark.django_db
275
def test_group_to_role_mapping(slapd, settings, client):
276
    from authentic2.a2_rbac.models import Role
277

  
278
    Role.objects.get_or_create(name='Role1')
279
    settings.LDAP_AUTH_SETTINGS = [{
280
        'url': [slapd.ldap_url],
281
        'basedn': 'o=orga',
282
        'use_tls': False,
283
        'group_to_role_mapping': [
284
            ('cn=group1,o=orga', ['Role1']),
285
        ],
286
    }]
287
    response = client.post('/login/', {'login-password-submit': '1',
288
                                       'username': USERNAME,
289
                                       'password': PASS}, follow=True)
290
    assert response.context['user'].username == u'%s@ldap' % USERNAME
291
    assert response.context['user'].roles.count() == 1
292

  
293

  
294
@pytest.mark.django_db
295
def test_posix_group_to_role_mapping(slapd, settings, client):
296
    from authentic2.a2_rbac.models import Role
297

  
298
    Role.objects.get_or_create(name='Role2')
299
    settings.LDAP_AUTH_SETTINGS = [{
300
        'url': [slapd.ldap_url],
301
        'basedn': 'o=orga',
302
        'use_tls': False,
303
        'group_to_role_mapping': [
304
            ('cn=group2,o=orga', ['Role2']),
305
        ],
306
        'group_filter': '(&(memberUid={uid})(objectClass=posixGroup))',
307
    }]
308
    response = client.post('/login/', {'login-password-submit': '1',
309
                                       'username': USERNAME,
310
                                       'password': PASS}, follow=True)
311
    assert response.context['user'].username == u'%s@ldap' % USERNAME
312
    assert response.context['user'].roles.count() == 1
313

  
314

  
315
@pytest.mark.django_db
275 316
def test_group_su(slapd, settings, client):
276 317
    from django.contrib.auth.models import Group
277 318

  
278
-