[Python-modules-commits] [django-auth-ldap] 01/08: Import django-auth-ldap_1.2.12+dfsg.orig.tar.gz
Michael Fladischer
fladi at moszumanska.debian.org
Sun May 28 09:26:20 UTC 2017
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch master
in repository django-auth-ldap.
commit eb4338e4d7a8bd0af8b130f75a9d3ad09cca4612
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Wed May 24 10:45:58 2017 +0200
Import django-auth-ldap_1.2.12+dfsg.orig.tar.gz
---
CHANGES | 7 ++
PKG-INFO | 2 +-
django_auth_ldap.egg-info/PKG-INFO | 2 +-
django_auth_ldap/__init__.py | 2 +-
django_auth_ldap/backend.py | 63 +++++++++++++----
django_auth_ldap/config.py | 84 +++++++++++++++++++++++
django_auth_ldap/tests.py | 137 +++++++++++++++++++++++++++++++++++--
docs/source/conf.py | 2 +-
docs/source/groups.rst | 23 +++++++
docs/source/reference.rst | 59 +++++++++++++---
docs/source/users.rst | 11 +--
setup.py | 2 +-
test/.coveragerc | 1 +
13 files changed, 358 insertions(+), 37 deletions(-)
diff --git a/CHANGES b/CHANGES
index fe813c1..db447f4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,10 @@
+v1.2.12 - 2017-05-20 - Complex group queries
+--------------------------------------------
+
+- Support for complex group queries via
+ :class:`~django_auth_ldap.config.LDAPGroupQuery`.
+
+
v1.2.11 - 2017-04-22 - Testing and debugging cleanup
----------------------------------------------------
diff --git a/PKG-INFO b/PKG-INFO
index 9d03581..8f0b616 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-auth-ldap
-Version: 1.2.11
+Version: 1.2.12
Summary: Django LDAP authentication backend
Home-page: http://bitbucket.org/psagers/django-auth-ldap/
Author: Peter Sagerson
diff --git a/django_auth_ldap.egg-info/PKG-INFO b/django_auth_ldap.egg-info/PKG-INFO
index 9d03581..8f0b616 100644
--- a/django_auth_ldap.egg-info/PKG-INFO
+++ b/django_auth_ldap.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-auth-ldap
-Version: 1.2.11
+Version: 1.2.12
Summary: Django LDAP authentication backend
Home-page: http://bitbucket.org/psagers/django-auth-ldap/
Author: Peter Sagerson
diff --git a/django_auth_ldap/__init__.py b/django_auth_ldap/__init__.py
index a158771..4bdfabd 100644
--- a/django_auth_ldap/__init__.py
+++ b/django_auth_ldap/__init__.py
@@ -1,2 +1,2 @@
-version = (1, 2, 11)
+version = (1, 2, 12)
version_string = '.'.join(map(str, version))
diff --git a/django_auth_ldap/backend.py b/django_auth_ldap/backend.py
index 81ad4ae..4c6d698 100644
--- a/django_auth_ldap/backend.py
+++ b/django_auth_ldap/backend.py
@@ -45,11 +45,13 @@ information will be user_dn or user_info.
Additional classes can be found in the config module next to this one.
"""
+import copy
+from functools import reduce
import ldap
+import operator
+import pprint
import sys
import traceback
-import pprint
-import copy
from django.contrib.auth.models import User, Group, Permission
import django.conf
@@ -86,7 +88,7 @@ try:
except NameError:
basestring = str
-from django_auth_ldap.config import _LDAPConfig, LDAPSearch
+from django_auth_ldap.config import _LDAPConfig, LDAPGroupQuery, LDAPSearch
logger = _LDAPConfig.get_logger()
@@ -516,9 +518,11 @@ class _LDAPUser(object):
required_group_dn = self.settings.REQUIRE_GROUP
if required_group_dn is not None:
- is_member = self._get_groups().is_member_of(required_group_dn)
- if not is_member:
- raise self.AuthenticationFailed("user is not a member of AUTH_LDAP_REQUIRE_GROUP")
+ if not isinstance(required_group_dn, LDAPGroupQuery):
+ required_group_dn = LDAPGroupQuery(required_group_dn)
+ result = required_group_dn.resolve(self)
+ if not result:
+ raise self.AuthenticationFailed("user does not satisfy AUTH_LDAP_REQUIRE_GROUP")
return True
@@ -532,7 +536,7 @@ class _LDAPUser(object):
if denied_group_dn is not None:
is_member = self._get_groups().is_member_of(denied_group_dn)
if is_member:
- raise self.AuthenticationFailed("user is a member of AUTH_LDAP_DENY_GROUP")
+ raise self.AuthenticationFailed("user does not satisfy AUTH_LDAP_DENY_GROUP")
return True
@@ -601,9 +605,12 @@ class _LDAPUser(object):
def _populate_user_from_group_memberships(self):
for field, group_dns in self.settings.USER_FLAGS_BY_GROUP.items():
- if isinstance(group_dns, basestring):
- group_dns = [group_dns]
- value = any(self._get_groups().is_member_of(dn) for dn in group_dns)
+ try:
+ query = self._normalize_group_dns(group_dns)
+ except ValueError as e:
+ raise ImproperlyConfigured("{}: {}", self.settings._name('USER_FLAGS_BY_GROUP'), e)
+
+ value = query.resolve(self)
setattr(self._user, field, value)
def _should_populate_profile(self):
@@ -658,14 +665,37 @@ class _LDAPUser(object):
save_profile = False
for field, group_dns in self.settings.PROFILE_FLAGS_BY_GROUP.items():
- if isinstance(group_dns, basestring):
- group_dns = [group_dns]
- value = any(self._get_groups().is_member_of(dn) for dn in group_dns)
+ try:
+ query = self._normalize_group_dns(group_dns)
+ except ValueError as e:
+ raise ImproperlyConfigured("{}: {}", self.settings._name('PROFILE_FLAGS_BY_GROUP'), e)
+
+ value = query.resolve(self)
setattr(profile, field, value)
save_profile = True
return save_profile
+ def _normalize_group_dns(self, group_dns):
+ """
+ Converts one or more group DNs to an LDAPGroupQuery.
+
+ group_dns may be a string, a non-empty list or tuple of strings, or an
+ LDAPGroupQuery. The result will be an LDAPGroupQuery. A list or tuple
+ will be joined with the | operator.
+
+ """
+ if isinstance(group_dns, LDAPGroupQuery):
+ query = group_dns
+ elif isinstance(group_dns, basestring):
+ query = LDAPGroupQuery(group_dns)
+ elif isinstance(group_dns, (list, tuple)) and len(group_dns) > 0:
+ query = reduce(operator.or_, map(LDAPGroupQuery, group_dns))
+ else:
+ raise ValueError(group_dns)
+
+ return query
+
def _mirror_groups(self):
"""
Mirrors the user's LDAP groups in the Django database and updates the
@@ -875,6 +905,8 @@ class LDAPSettings(object):
instance will contain all of our settings as attributes, with default values
if they are not specified by the configuration.
"""
+ _prefix = 'AUTH_LDAP_'
+
defaults = {
'ALWAYS_UPDATE_USER': True,
'AUTHORIZE_ALL_USERS': False,
@@ -907,8 +939,13 @@ class LDAPSettings(object):
Loads our settings from django.conf.settings, applying defaults for any
that are omitted.
"""
+ self._prefix = prefix
+
defaults = dict(self.defaults, **defaults)
for name, default in defaults.items():
value = getattr(django.conf.settings, prefix + name, default)
setattr(self, name, value)
+
+ def _name(self, suffix):
+ return (self._prefix + suffix)
diff --git a/django_auth_ldap/config.py b/django_auth_ldap/config.py
index b57f052..58c612e 100644
--- a/django_auth_ldap/config.py
+++ b/django_auth_ldap/config.py
@@ -34,6 +34,7 @@ import ldap
import logging
import pprint
+from django.utils.tree import Node
try:
from django.utils.encoding import force_str
except ImportError: # Django < 1.5
@@ -635,3 +636,86 @@ class NestedOrganizationalRoleGroupType(NestedMemberDNGroupType):
"""
def __init__(self, name_attr='cn'):
super(NestedOrganizationalRoleGroupType, self).__init__('roleOccupant', name_attr)
+
+
+class LDAPGroupQuery(Node):
+ """
+ Represents a compound query for group membership.
+
+ This can be used to construct an arbitrarily complex group membership query
+ with AND, OR, and NOT logical operators. Construct primitive queries with a
+ group DN as the only argument. These queries can then be combined with the
+ ``&``, ``|``, and ``~`` operators.
+
+ :param str group_dn: The DN of a group to test for membership.
+
+ """
+ # Connection types
+ AND = 'AND'
+ OR = 'OR'
+ default = AND
+
+ _CONNECTORS = [AND, OR]
+
+ def __init__(self, *args, **kwargs):
+ super(LDAPGroupQuery, self).__init__(children=list(args) + list(kwargs.items()))
+
+ def __and__(self, other):
+ return self._combine(other, self.AND)
+
+ def __or__(self, other):
+ return self._combine(other, self.OR)
+
+ def __invert__(self):
+ obj = type(self)()
+ obj.add(self, self.AND)
+ obj.negate()
+
+ return obj
+
+ def _combine(self, other, conn):
+ if not isinstance(other, LDAPGroupQuery):
+ raise TypeError(other)
+ if conn not in self._CONNECTORS:
+ raise ValueError(conn)
+
+ obj = type(self)()
+ obj.connector = conn
+ obj.add(self, conn)
+ obj.add(other, conn)
+
+ return obj
+
+ def resolve(self, ldap_user, groups=None):
+ if groups is None:
+ groups = ldap_user._get_groups()
+
+ result = self.aggregator(self._resolve_children(ldap_user, groups))
+ if self.negated:
+ result = not result
+
+ return result
+
+ @property
+ def aggregator(self):
+ """
+ Returns a function for aggregating a sequence of sub-results.
+ """
+ if self.connector == self.AND:
+ aggregator = all
+ elif self.connector == self.OR:
+ aggregator = any
+ else:
+ raise ValueError(self.connector)
+
+ return aggregator
+
+ def _resolve_children(self, ldap_user, groups):
+ """
+ Generates the query result for each child.
+ """
+ for child in self.children:
+ if isinstance(child, LDAPGroupQuery):
+ yield child.resolve(ldap_user, groups)
+ else:
+ yield groups.is_member_of(child)
diff --git a/django_auth_ldap/tests.py b/django_auth_ldap/tests.py
index 61f2fcc..8661808 100644
--- a/django_auth_ldap/tests.py
+++ b/django_auth_ldap/tests.py
@@ -38,8 +38,9 @@ except ImportError:
import django
from django.conf import settings
-import django.db.models.signals
from django.contrib.auth.models import User, Permission, Group
+from django.core.exceptions import ImproperlyConfigured
+import django.db.models.signals
from django.test import TestCase
try:
@@ -60,6 +61,7 @@ except ImportError:
from django_auth_ldap.models import TestUser, TestProfile
from django_auth_ldap import backend
+from django_auth_ldap.config import LDAPGroupQuery
from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
from django_auth_ldap.config import PosixGroupType, MemberDNGroupType, NestedMemberDNGroupType, NISGroupType
from django_auth_ldap.config import GroupOfNamesType
@@ -164,6 +166,23 @@ class LDAPTest(TestCase):
"member": ["uid=bob,ou=people,o=test"]
})
+ # grouOfNames objects for LDAPGroupQuery testing
+ alice_gon = ("cn=alice_gon,ou=query_groups,o=test", {
+ "cn": ["alice_gon"],
+ "objectClass": ["groupOfNames"],
+ "member": ["uid=alice,ou=people,o=test"]
+ })
+ mutual_gon = ("cn=mutual_gon,ou=query_groups,o=test", {
+ "cn": ["mutual_gon"],
+ "objectClass": ["groupOfNames"],
+ "member": ["uid=alice,ou=people,o=test", "uid=bob,ou=people,o=test"]
+ })
+ bob_gon = ("cn=bob_gon,ou=query_groups,o=test", {
+ "cn": ["bob_gon"],
+ "objectClass": ["groupOfNames"],
+ "member": ["uid=bob,ou=people,o=test"]
+ })
+
# nisGroup objects
active_nis = ("cn=active_nis,ou=groups,o=test", {
"cn": ["active_nis"],
@@ -204,6 +223,7 @@ class LDAPTest(TestCase):
directory = dict([top, people, groups, moregroups, alice, bob, dressler,
nobody, active_px, staff_px, superuser_px, empty_gon,
active_gon, staff_gon, superuser_gon, other_gon,
+ alice_gon, mutual_gon, bob_gon,
active_nis, staff_nis, superuser_nis,
parent_gon, nested_gon, circular_gon])
@@ -224,7 +244,9 @@ class LDAPTest(TestCase):
cls.configure_logger()
cls.mockldap = mockldap.MockLdap(cls.directory)
- warnings.filterwarnings('ignore', message='.*?AUTH_PROFILE_MODULE', category=DeprecationWarning, module='django_auth_ldap')
+ warnings.filterwarnings(
+ 'ignore', message='.*?AUTH_PROFILE_MODULE', category=DeprecationWarning, module='django_auth_ldap'
+ )
@classmethod
def tearDownClass(cls):
@@ -753,6 +775,95 @@ class LDAPTest(TestCase):
'initialize', 'simple_bind_s', 'simple_bind_s', 'compare_s']
)
+ def test_simple_group_query(self):
+ self._init_settings(
+ USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+ GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'),
+ GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+ )
+ alice = self.backend.authenticate(username='alice', password='password')
+ query = LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test')
+ self.assertTrue(query.resolve(alice.ldap_user))
+
+ def test_negated_group_query(self):
+ self._init_settings(
+ USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+ GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'),
+ GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+ )
+ alice = self.backend.authenticate(username='alice', password='password')
+ query = ~LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test')
+ self.assertFalse(query.resolve(alice.ldap_user))
+
+ def test_or_group_query(self):
+ self._init_settings(
+ USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+ GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'),
+ GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+ )
+ alice = self.backend.authenticate(username='alice', password='password')
+ bob = self.backend.authenticate(username='bob', password='password')
+
+ query = (
+ LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') |
+ LDAPGroupQuery('cn=bob_gon,ou=query_groups,o=test')
+ )
+ self.assertTrue(query.resolve(alice.ldap_user))
+ self.assertTrue(query.resolve(bob.ldap_user))
+
+ def test_and_group_query(self):
+ self._init_settings(
+ USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+ GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'),
+ GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+ )
+ alice = self.backend.authenticate(username='alice', password='password')
+ bob = self.backend.authenticate(username='bob', password='password')
+
+ query = (
+ LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') &
+ LDAPGroupQuery('cn=mutual_gon,ou=query_groups,o=test')
+ )
+ self.assertTrue(query.resolve(alice.ldap_user))
+ self.assertFalse(query.resolve(bob.ldap_user))
+
+ def test_nested_group_query(self):
+ self._init_settings(
+ USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+ GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'),
+ GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+ )
+ alice = self.backend.authenticate(username='alice', password='password')
+ bob = self.backend.authenticate(username='bob', password='password')
+
+ query = (
+ (
+ LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') &
+ LDAPGroupQuery('cn=mutual_gon,ou=query_groups,o=test')
+ ) |
+ LDAPGroupQuery('cn=bob_gon,ou=query_groups,o=test')
+ )
+ self.assertTrue(query.resolve(alice.ldap_user))
+ self.assertTrue(query.resolve(bob.ldap_user))
+
+ def test_require_group_as_group_query(self):
+ query = (
+ LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') &
+ LDAPGroupQuery('cn=mutual_gon,ou=query_groups,o=test')
+ )
+ self._init_settings(
+ USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+ GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'),
+ GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+ REQUIRE_GROUP=query
+ )
+
+ alice = self.backend.authenticate(username='alice', password='password')
+ bob = self.backend.authenticate(username='bob', password='password')
+
+ self.assertTrue(alice is not None)
+ self.assertTrue(bob is None)
+
def test_group_union(self):
self._init_settings(
USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
@@ -816,7 +927,10 @@ class LDAPTest(TestCase):
)
alice = self.backend.authenticate(username='alice', password='password')
- self.assertEqual(alice.ldap_user.group_dns, set((g[0].lower() for g in [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon])))
+ self.assertEqual(
+ alice.ldap_user.group_dns,
+ set((g[0].lower() for g in [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon]))
+ )
def test_group_names(self):
self._init_settings(
@@ -834,7 +948,7 @@ class LDAPTest(TestCase):
GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
GROUP_TYPE=MemberDNGroupType(member_attr='member'),
USER_FLAGS_BY_GROUP={
- 'is_active': "cn=active_gon,ou=groups,o=test",
+ 'is_active': LDAPGroupQuery("cn=active_gon,ou=groups,o=test"),
'is_staff': ["cn=empty_gon,ou=groups,o=test",
"cn=staff_gon,ou=groups,o=test"],
'is_superuser': "cn=superuser_gon,ou=groups,o=test"
@@ -851,6 +965,21 @@ class LDAPTest(TestCase):
self.assertTrue(not bob.is_staff)
self.assertTrue(not bob.is_superuser)
+ def test_user_flags_misconfigured(self):
+ self._init_settings(
+ USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+ GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
+ GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+ USER_FLAGS_BY_GROUP={
+ 'is_active': LDAPGroupQuery("cn=active_gon,ou=groups,o=test"),
+ 'is_staff': [],
+ 'is_superuser': "cn=superuser_gon,ou=groups,o=test"
+ }
+ )
+
+ with self.assertRaises(ImproperlyConfigured):
+ self.backend.authenticate(username='alice', password='password')
+
def test_posix_membership(self):
self._init_settings(
USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
diff --git a/docs/source/conf.py b/docs/source/conf.py
index a4aad5d..e0ac649 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -59,7 +59,7 @@ copyright = u'2009, Peter Sagerson'
# The short X.Y version.
version = '1.1'
# The full version, including alpha/beta/rc tags.
-release = '1.2.11'
+release = '1.2.12'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/source/groups.rst b/docs/source/groups.rst
index 5ec0664..e84be4d 100644
--- a/docs/source/groups.rst
+++ b/docs/source/groups.rst
@@ -58,6 +58,8 @@ configuration must be of this type and part of the search results.
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
+.. _limiting-access:
+
Limiting Access
---------------
@@ -71,6 +73,27 @@ the reverse: if given, members of this group will be rejected.
AUTH_LDAP_REQUIRE_GROUP = "cn=enabled,ou=groups,dc=example,dc=com"
AUTH_LDAP_DENY_GROUP = "cn=disabled,ou=groups,dc=example,dc=com"
+However, these two settings alone may not be enough to satisfy your needs. In
+such cases, you can use the :class:`~django_auth_ldap.config.LDAPGroupQuery`
+object to perform more complex matches against a user's groups. For example:
+
+.. code-block:: python
+
+ from django_auth_ldap.config import LDAPGroupQuery
+
+ AUTH_LDAP_REQUIRE_GROUP = (
+ (
+ LDAPGroupQuery("cn=enabled,ou=groups,dc=example,dc=com") |
+ LDAPGroupQuery("cn=also_enabled,ou=groups,dc=example,dc=com")
+ ) &
+ ~LDAPGroupQuery("cn=disabled,ou=groups,dc=example,dc=com")
+ )
+
+It is important to note a couple features of the example above. First and foremost,
+this handles the case of both `AUTH_LDAP_REQUIRE_GROUP` and `AUTH_LDAP_DENY_GROUP`
+in one setting. Second, you can use three operators on these queries: ``&``, ``|``,
+and ``~``: ``and``, ``or``, and ``not``, respectively.
+
When groups are configured, you can always get the list of a user's groups from
``user.ldap_user.group_dns`` or ``user.ldap_user.group_names``. More advanced
uses of groups are covered in the next two sections.
diff --git a/docs/source/reference.rst b/docs/source/reference.rst
index 26db1ce..834f120 100644
--- a/docs/source/reference.rst
+++ b/docs/source/reference.rst
@@ -223,6 +223,10 @@ A mapping from boolean profile field names to distinguished names of LDAP
groups. The corresponding field in a user's profile is set to ``True`` or
``False`` according to whether the user is a member of the group.
+Values may be strings for simple group membership tests or
+:class:`~django_auth_ldap.config.LDAPGroupQuery` instances for more complex
+cases.
+
This is ignored in Django 1.7 and later.
@@ -234,7 +238,8 @@ AUTH_LDAP_REQUIRE_GROUP
Default: ``None``
The distinguished name of a group; authentication will fail for any user that
-does not belong to this group.
+does not belong to this group. This can also be an
+:class:`~django_auth_ldap.config.LDAPGroupQuery` instance.
.. setting:: AUTH_LDAP_SERVER_URI
@@ -312,6 +317,10 @@ A mapping from boolean :class:`~django.contrib.auth.models.User` field names to
distinguished names of LDAP groups. The corresponding field is set to ``True``
or ``False`` according to whether the user is a member of the group.
+Values may be strings for simple group membership tests or
+:class:`~django_auth_ldap.config.LDAPGroupQuery` instances for more complex
+cases.
+
.. setting:: AUTH_LDAP_USER_SEARCH
@@ -349,10 +358,12 @@ Configuration
.. method:: __init__(base_dn, scope, filterstr='(objectClass=*)')
- * ``base_dn``: The distinguished name of the search base.
- * ``scope``: One of ``ldap.SCOPE_*``.
- * ``filterstr``: An optional filter string (e.g. '(objectClass=person)').
- In order to be valid, ``filterstr`` must be enclosed in parentheses.
+ :param str base_dn: The distinguished name of the search base.
+ :param int scope: One of ``ldap.SCOPE_*``.
+ :param str filterstr: An optional filter string (e.g.
+ '(objectClass=person)'). In order to be valid, ``filterstr`` must be
+ enclosed in parentheses.
+
.. class:: LDAPSearchUnion
@@ -360,10 +371,12 @@ Configuration
.. method:: __init__(\*searches)
- * ``searches``: Zero or more LDAPSearch objects. The result of the
- overall search is the union (by DN) of the results of the underlying
- searches. The precedence of the underlying results and the ordering of
- the final results are both undefined.
+ :param searches: Zero or more LDAPSearch objects. The result of the
+ overall search is the union (by DN) of the results of the underlying
+ searches. The precedence of the underlying results and the ordering
+ of the final results are both undefined.
+ :type searches: :class:`LDAPSearch`
+
.. class:: LDAPGroupType
@@ -377,6 +390,7 @@ Configuration
first value of the cn attribute. You can specify a different attribute
with ``name_attr``.
+
.. class:: PosixGroupType
A concrete subclass of :class:`~django_auth_ldap.config.LDAPGroupType` that
@@ -385,6 +399,7 @@ Configuration
.. method:: __init__(name_attr='cn')
+
.. class:: NISGroupType
A concrete subclass of :class:`~django_auth_ldap.config.LDAPGroupType` that
@@ -392,6 +407,7 @@ Configuration
.. method:: __init__(name_attr='cn')
+
.. class:: MemberDNGroupType
A concrete subclass of
@@ -400,8 +416,9 @@ Configuration
.. method:: __init__(member_attr, name_attr='cn')
- * ``member_attr``: The attribute on the group object that contains a
- list of member DNs. 'member' and 'uniqueMember' are common examples.
+ :param str member_attr: The attribute on the group object that contains
+ a list of member DNs. 'member' and 'uniqueMember' are common
+ examples.
.. class:: NestedMemberDNGroupType
@@ -491,6 +508,26 @@ Configuration
.. method:: __init__(name_attr='cn')
+.. class:: LDAPGroupQuery
+
+ Represents a compound query for group membership.
+
+ This can be used to construct an arbitrarily complex group membership query
+ with AND, OR, and NOT logical operators. Construct primitive queries with a
+ group DN as the only argument. These queries can then be combined with the
+ ``&``, ``|``, and ``~`` operators.
+
+ This is used by certain settings, including
+ :setting:`AUTH_LDAP_REQUIRE_GROUP` and
+ :setting:`AUTH_LDAP_USER_FLAGS_BY_GROUP`. An example is shown in
+ :ref:`limiting-access`.
+
+ .. method:: __init__(group_dn)
+
+ :param str group_dn: The distinguished name of a group to test for
+ membership.
+
+
Backend
-------
diff --git a/docs/source/users.rst b/docs/source/users.rst
index 792244a..a493ce4 100644
--- a/docs/source/users.rst
+++ b/docs/source/users.rst
@@ -84,8 +84,10 @@ group membership::
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
"is_active": "cn=active,ou=groups,dc=example,dc=com",
- "is_staff": ["cn=staff,ou=groups,dc=example,dc=com",
- "cn=admin,ou=groups,dc=example,dc=com"],
+ "is_staff": (
+ LDAPGroupQuery("cn=staff,ou=groups,dc=example,dc=com") |
+ LDAPGroupQuery("cn=admin,ou=groups,dc=example,dc=com")
+ ),
"is_superuser": "cn=superuser,ou=groups,dc=example,dc=com"
}
@@ -93,8 +95,9 @@ group membership::
"is_awesome": ["cn=awesome,ou=groups,dc=example,dc=com"]
}
-If a list of groups is given, the flag will be set if the user is a member of
-any group.
+Values in these dictionaries may be simple DNs (as strings), lists or tuples of
+DNs, or :class:`~django_auth_ldap.config.LDAPGroupQuery` instances. Lists are
+converted to queries joined by ``|``.
.. note::
diff --git a/setup.py b/setup.py
index 7ce54de..c32174e 100644
--- a/setup.py
+++ b/setup.py
@@ -10,7 +10,7 @@ PY3 = (sys.version_info[0] == 3)
setup(
name="django-auth-ldap",
- version="1.2.11",
+ version="1.2.12",
description="Django LDAP authentication backend",
long_description=open('README').read(),
url="http://bitbucket.org/psagers/django-auth-ldap/",
diff --git a/test/.coveragerc b/test/.coveragerc
index b620ab8..2dea015 100644
--- a/test/.coveragerc
+++ b/test/.coveragerc
@@ -1,3 +1,4 @@
[run]
+data_file = ../.coverage
source = django_auth_ldap.config
django_auth_ldap.backend
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-auth-ldap.git
More information about the Python-modules-commits
mailing list