Coder Social home page Coder Social logo

django-ldapdb's Introduction

django-ldapdb

https://secure.travis-ci.org/django-ldapdb/django-ldapdb.png?branch=master Latest Version Supported Python versions Wheel status License

django-ldapdb is an LDAP database backend for Django, allowing to manipulate LDAP entries through Django models.

It supports most of the same APIs as a Django model:

  • MyModel.objects.create()
  • MyModel.objects.filter(x=1, y__contains=2)
  • Full admin support and browsing

django-ldapdb supports every upstream-supported Django version, based on the Django support policy.

For the current version, the following versions are supported:

  • Django 2.2 (LTS), under Python 3.6 - 3.8 (Python 3.5 has reached its end of life);
  • Django 3.0, under Python 3.6 - 3.8;
  • Django 3.1, under Python 3.6 - 3.8.

Installing django-ldapdb

Linux

Use pip: pip install django-ldapdb

You might also need the usual LDAP packages from your distribution, usually named openldap or ldap-utils.

Windows

django-ldapdb depends on the python-ldap <https://pypi.python.org/pypi/python-ldap> project. Either follow its Windows installation guide, or install a pre-built version from https://www.lfd.uci.edu/~gohlke/pythonlibs/#python-ldap (choose the .whl file matching your Python/Windows combination, and install it with pip install python-ldap-3...whl).

You may then install django-ldapdb with

pip install django-ldapdb

Using django-ldapdb

Add the following to your settings.py:

DATABASES = {
    'ldap': {
        'ENGINE': 'ldapdb.backends.ldap',
        'NAME': 'ldap://ldap.nodomain.org/',
        'USER': 'cn=admin,dc=nodomain,dc=org',
        'PASSWORD': 'some_secret_password',
     },
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
     },
}
DATABASE_ROUTERS = ['ldapdb.router.Router']

If you want to access posixGroup entries in your application, you can add something like this to your models.py:

from ldapdb.models.fields import CharField, IntegerField, ListField
import ldapdb.models

class LdapGroup(ldapdb.models.Model):
    """
    Class for representing an LDAP group entry.
    """
    # LDAP meta-data
    base_dn = "ou=groups,dc=nodomain,dc=org"
    object_classes = ['posixGroup']

    # posixGroup attributes
    gid = IntegerField(db_column='gidNumber', unique=True)
    name = CharField(db_column='cn', max_length=200, primary_key=True)
    members = ListField(db_column='memberUid')

    def __str__(self):
        return self.name

    def __unicode__(self):
        return self.name

and add this to your admin.py:

from django.contrib import admin
from . import models

class LDAPGroupAdmin(admin.ModelAdmin):
    exclude = ['dn', 'objectClass']
    list_display = ['gid', 'name']

admin.site.register(models.LDAPGroup, LDAPGroupAdmin)
Important note:

You must declare an attribute to be used as the primary key. This attribute will play a special role, as it will be used to build the Relative Distinguished Name of the entry.

For instance in the example above, a group whose cn is foo will have the DN cn=foo,ou=groups,dc=nodomain,dc=org.

Supported fields

djanglo-ldapdb provides the following fields, all imported from ldapdb.models.fields:

Similar to Django:

  • IntegerField
  • FloatField
  • BooleanField
  • CharField
  • ImageField
  • DateTimeField
Specific to a LDAP server:
  • ListField (holds a list of text values)
  • TimestampField (Stores a datetime as a posix timestamp, typically for posixAccount)
Legacy:
  • DateField (Stores a date in an arbitrary format. A LDAP server has no notion of Date).

Tuning django-ldapdb

It is possible to adjust django-ldapdb's behavior by defining a few parameters in the DATABASE section:

PAGE_SIZE (default: 1000)
Define the maximum size of a results page to be returned by the server
QUERY_TIMEOUT (default: no limit)

Define the maximum time in seconds we'll wait to get a reply from the server (on a per-query basis).

Note

This setting applies on individual requests; if a high-level operation requires many queries (for instance a paginated search yielding thousands of entries), the timeout will be used on each individual request; the overall processing time might be much higher.

Developing with a LDAP server

When developing against a LDAP server, having access to a development LDAP server often proves useful.

django-ldapdb uses the volatildap project for this purpose:

  • A LDAP server is instantiated for each TestClass;
  • Its content is reset at the start of each test function;
  • It can be customized to embark any schemas required by the application;
  • Starting with volatildap 1.4.0, the volatildap server can be controlled remotely, avoiding the need to install a LDAP server on the host.

Applications using django-ldapdb may use the following code snippet when setting up their tests:

# This snippet is released in the Public Domain

from django.conf import settings
from django.test import TestCase

import volatildap

class LdapEnabledTestCase(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.ldap = volatildap.LdapServer(
            # Load some initial data
            initial={'ou=people': {
                'ou': ['people'],
                'objectClass': ['organizationalUnit'],
            }},
            # Enable more LDAP schemas
            schemas=['core.schema', 'cosine.schema', 'inetorgperson.schema', 'nis.schema'],
        )
        # The volatildap server uses specific defaults, and listens on an arbitrary port.
        # Copy the server-side values to Django settings
        settings.DATABASES['ldap']['USER'] = cls.ldap.rootdn
        settings.DATABASES['ldap']['PASSWORD'] = cls.ldap.rootpw
        settings.DATABASES['ldap']['NAME'] = cls.ldap.uri

    def setUp(self):
        super().setUp()
        # Starting an already-started volatildap server performs a data reset
        self.ldap.start()

    @classmethod
    def tearDownClass(cls):
        # Free up resources on teardown.
        cls.ldap.stop()
        super().tearDownClass()

django-ldapdb's People

Contributors

bd808 avatar dzen avatar g1itch avatar ikreb7 avatar jlaine avatar mgorny avatar natureshadow avatar nikolaik avatar pdpotter avatar rbarrois avatar xaroth avatar xavfernandez avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-ldapdb's Issues

Cannot search on DN

Hi,
I can't filter on DNs (with dn__contains).

Ou.objects.using('ldap').filter(dn__contains=u'server')
[]

Ou.objects.using('ldap').all()
[<Ou: Users>, <Ou: Groups>, <Ou: Aliases>, <Ou: Externals>,[<Ou: Users>, <Ou: Groups>, <Ou: Aliases>, <Ou: Externals>,[<Ou: Users>, <Ou: Groups>, <Ou: Aliases>, <Ou: Externals>]

Ou.objects.using('ldap').all()[0].dn
u'ou=Users,domainName=test.server.local,o=domains,dc=server,dc=local'

Is there any other way I can achieve this ?

Honor the using argument for selecting a specific backend

The save function (models.base.Model.save) should pass the "using" argument when fetching the original instance from the database.

Line 123:
orig = self.class.objects.get(pk=self.saved_pk)

should be replaced by:
orig = self.class.objects.using(using).get(pk=self.saved_pk)

Only flat directory tree is supported

You won't be able to edit any entity deeper than the base_dn, because models/base.py:build_rdn builds up rdn concatenating only the primary key fields.

For example. Let base_dn be ou=Users,dc=example,dc=com.
Let's have a user with the following dn: cn=username,ou=Finance,ou=Users,dc=example,dc=com

The primary key column is cn, build_rdn will generate the following rdn: cn=username, thus the full dn will be cn=username,ou=Users,dc=example,dc=com without ou=Finance.

A possible solution of mine is to strip the base_dn and current primary key (cn=username) from the whole dn, and prefix the rest with the new one.

Bests,
Andor

save() got an unexpected keyword argument 'force_insert' with django-rest-framework?

It looks like there is a problem for me with django-rest-framework and django-ldapdb save() function.

In the save object force_insert=False is included, and things goes haywire :)
This was a fugly fix for me, any ideas what goest wrong and what I can do to not hardcode base.py?

models/base.py
65c65
<     def save(self, using=None):

---
>     def save(self, using=None, *args, **kvargs):

This is the installed packages..

pip freeze

Django==1.9.9
django-ldapdb==0.6.0
djangorestframework==3.4.6
pkg-resources==0.0.0
pyldap==2.4.25.1

And the Traceback

Traceback (most recent call last):
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/rest_framework/views.py", line 474, in dispatch
    response = self.handle_exception(exc)
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/rest_framework/views.py", line 434, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/rest_framework/views.py", line 471, in dispatch
    response = handler(request, *args, **kwargs)
  File "/home/falk/dev/ldapdbtest/d1xldap/views.py", line 34, in post
    serializer.save()
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/rest_framework/serializers.py", line 192, in save
    self.instance = self.create(validated_data)
  File "/home/falk/dev/ldapdbtest/d1xldap/serializers.py", line 12, in create
    return Device.objects.create(**validated_data)
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/django/db/models/manager.py", line 122, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/falk/env/ldapdbtest/lib/python3.5/site-packages/django/db/models/query.py", line 401, in create
    obj.save(force_insert=True, using=self.db)
TypeError: save() got an unexpected keyword argument 'force_insert'

Regards Falk

ldapdb/tests.py will break in django 1.9

The Constraint class from django's private API was deprecated in Django 1.7.
They replaced it with the public and documented Lookup class.
https://docs.djangoproject.com/en/1.8/howto/custom-lookups/

Constraint will be completely removed in Django 1.9, and ldapdb/tests.py uses it a lot in the WhereTestCase. So to maintain compatibility, the tests needs to be majorly rewritten to test without using it.

Since Django no longer supports any versions before 1.7, it should not be necessary for the new testing approach to be compatible with versions older than that.

Support global LDAP options

For several reasons, ldap needs to set options before any connection to an ldap server is made (for example: disabling ssl certficate checks).

Currently, only CONNECTION_OPTIONS can be set. A list of GLOBAL_OPTIONS would be fine, that would be set before the first connection is made.

Multi-Value-Fields in Admin

It would be great if there would be some support/documentation regarding how to handle multi-value-Fields in the django admin interface.

get_or_create() throws an exception

Hi. django-ldapdb==0.3.2, Django==1.6.5
AccessGroup is ldapdb.models.Model
Works:

g = AccessGroup(name='test', usernames=['uid=test,ou=people,dc=domain,dc=com'])
g.save()

Doesn't work:

g2 = AccessGroup.objects.get_or_create(name='test2', defaults={'usernames': 'uid=test,ou=people,dc=domain,dc=com', 'name': 'test2'})

Traceback (most recent call last):
  File "/home/atan/projects/virtenv/test/lib/python2.7/site-packages/django/db/models/manager.py", line 154, in get_or_create
    return self.get_queryset().get_or_create(**kwargs)
  File "/home/atan/projects/virtenv/test/lib/python2.7/site-packages/django/db/models/query.py", line 383, in get_or_create
    obj.save(force_insert=True, using=self.db)
TypeError: save() got an unexpected keyword argument 'force_insert'

Scoping schema is causing a Conflict

Hello,
I've been using django-ldapdb for a while now, and just recently we upgraded our project to django 1.7. After doing so, we discovered that we were getting the following error when we try to scope our lookups for our RADIUS config:
RuntimeError: Conflicting 'ldapradius_cn_192.168.28.0/24_cn_clients_cn_radius_dc_int_dc_switchtower_dc_com' models in application 'radius': <class 'radius.models.LdapRADIUS_cn_192.168.28.0/24_cn_clients_cn_radius_dc_int_dc_switchtower_dc_com

It appeared to be similar to this issue with django: https://code.djangoproject.com/ticket/22280

Let me know if there is any further information you need from me to get this figured out.

Keep up the good work by the way :)

testsuite fails all, trips over @ django/db/backends/__init__.py

PYTHONPATH=./:ldaptests python2.7 manage.py test --settings=settings

=========================================================
ERROR: test_update (examples.tests.UserTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib64/python2.7/site-packages/django/test/testcases.py", line 259, in __call__
    self._pre_setup()
  File "/usr/lib64/python2.7/site-packages/django/test/testcases.py", line 479, in _pre_setup
    self._fixture_setup()
  File "/usr/lib64/python2.7/site-packages/django/test/testcases.py", line 829, in _fixture_setup
    if not connections_support_transactions():
  File "/usr/lib64/python2.7/site-packages/django/test/testcases.py", line 816, in connections_support_transactions
    for conn in connections.all())
  File "/usr/lib64/python2.7/site-packages/django/test/testcases.py", line 816, in <genexpr>
    for conn in connections.all())
  File "/usr/lib64/python2.7/site-packages/django/utils/functional.py", line 43, in __get__
    res = instance.__dict__[self.func.__name__] = self.func(instance)
  File "/usr/lib64/python2.7/site-packages/django/db/backends/__init__.py", line 455, in supports_transactions
    self.connection.leave_transaction_management()
  File "/usr/lib64/python2.7/site-packages/django/db/backends/__init__.py", line 138, in leave_transaction_management
    "Transaction managed block ended with pending COMMIT/ROLLBACK")
TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK

----------------------------------------------------------------------
Ran 84 tests in 3.023s

FAILED (errors=355, skipped=1)
Destroying test database for alias 'default'...

This looks that this error is occuring for almost the whole testsuite.
Fix 1, likely fix the other 354, so I am refraining from spamming gist with 355 instances of mild variations og the above

Cant Connect to a Active Directory or a Ldap Server through a SSL or TLS connection

Hello Folks,

I Can't connect to a Active Directory server using the ldapdb module. Everytime I try, the following error occurs:

'info': '000004DC: LdapErr: DSID-0C0906E8, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v1db1', 'desc': 'Operations error'}.

After this, I tried to turn the TLS on through the CONNECTION_OPTIONS:

'ldap': {
    'ENGINE': 'ldapdb.backends.ldap',
    'NAME': 'ldaps://IP/',
    'USER': 'mydn',
    'PASSWORD': 'mypasswd',
    'CONNECTION_OPTIONS': {
            'TLS': True,
    }

}

But The same error ocurred.

Currently, all my django apps that relies on ldap to perform something, uses the following options for ldap (global, not connection specfic):

    ldap.set_option(ldap.OPT_REFERRALS, 0)
    ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, 0)

With this, I can access the Active Directory Normally.

If I put these options immediately after the ensure method(line 98, base.py module), all works fine and all queries works very well.

def ensure_connection(self):
    if self.connection is None:
        ldap.set_option(ldap.OPT_REFERRALS, 0)
        ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, 0)

So, I ask: How to pass arguments or get active directory integration to work on ldapdb?

Thanks in advance.

django 1.8

SntPx@9673bd4

LdapUser.objects.filter(username='a2222111', password='123123')

error LDAP does not support MULTI queries

AttributeError: 'Query' object has no attribute 'select_fields' with Django 1.6

Hi.

I've noticed a problem when upgrading from Django 1.5 to 1.6.

Now, I have this error :
AttributeError: 'Query' object has no attribute 'select_fields' with Django 1.6

on a line like :
self.ldapuser = LdapUser.objects.get(uid=dpm.nickname)

where LdapUser inherits from ldapdb.models.Model
and has :
uid = CharField(db_column='uid', validators=[], primary_key=True)

Thanks in advance. Best regards

I can't get exclude using icontains distinguishedName.

I tried to do the following:

q = LdapGroup.objects.exclude(distinguishedName__icontains='Fiscal')

Is this supported? For me, when I do this, "q" ends up with an empty queryset.
If I loop through the queryset and print the distinguishedName field, it properly shows the strings for distinguishedName, as contained in the LDAP database. My brute force solution, was to do the filtering, by:

q = LdapGroup.objects.all()
for z in q:
if 'Fiscal' in z.distinguishedName:
q = q.exclude(pk=z.pk)

I'm sure there is a more elegant way of doing this on one line. Nevertheless, it would be nice if exclude would work. :) Thanks.

ListField behaving bad with project mysql

I have a configuration where I am using a default mysql database and a certain ldap backend. I have performed minimal configuration and it works as expected, but the

./manage.py check

complains about:

File "/<...>/lib/python2.7/site-packages/django/db/backends/mysql/validation.py", line 21, in check_field
  if (field_type.startswith('varchar')  # Look for CharFields...

it seems that by default, MySQL is checking all fields and the ListField.db_type() is returning None. And, as MySQL module expects a thing like varchar(200) or something similar, there is a error.

The production site is up&running, but I cannot perform debug of the application, so I am looking for any workaround or fix. I am not sure if there is some way to avoid MySQL checking the LDAP models, or... don't know. I am a bit lost here.

existance of 'ldapdb.backends.ldap' database backend slows down django tests

I'm maintaining a django 1.8 project with ~500 of unittests, these tests do have an overall runtime of around 3 minutes. However, by just simply adding a new ldapdb.backends.ldap powered database the speed of the test suite decreases significantly to around 45 minutes.

Note: the only required change is to have a DATABASES setting like

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    },
    'ldap': {
        'ENGINE': 'ldapdb.backends.ldap',
        'NAME': 'ldap://localhost',
        'USER': 'admin',
        'PASSWORD': 'admin',
     }
}

there are no ldapdb models, no extra tests, nothing.

In order to demonstrate this behaviour I created a very basic django project [0], which includes a ldapdb backend database, and an additional settings file which simply removes this backend. Even for a handfull of tests, this effect is already noticeable. Also worth mentioning is that the more tests we are having the slower the test suite gets, so we are not talking about some initial overhead when initializing the tests.
The demo projects includes a docker-compose file, so reproducing this effect is as simple as installing docker and docker-compose and run

$ docker-compose build
[...]
$ docker-compose run web python manage.py test
Creating test database for alias 'default'...
....
----------------------------------------------------------------------
Ran 4 tests in 0.153s

OK
Destroying test database for alias 'default'...
$ docker-compose run web python manage.py test --settings mysite.fast_tests
Creating test database for alias 'default'...
....
----------------------------------------------------------------------
Ran 4 tests in 0.002s

OK
Destroying test database for alias 'default'...

[0] https://github.com/thekorn/django-ldapdb-slow-tests

'module' object has no attribute 'SCOPE_SUBTREE'

I get the following on runserver command:
Unhandled exception in thread started by <bound method Command.inner_run of <django.contrib.staticfiles.management.commands.runserver.Command object at 0xa66732c>>
Traceback (most recent call last):
File "/home/venvs/ldap/local/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 93, in inner_run
self.validate(display_num_errors=True)
File "/home/venvs/ldap/local/lib/python2.7/site-packages/django/core/management/base.py", line 280, in validate
num_errors = get_validation_errors(s, app)
File "/home/venvs/ldap/local/lib/python2.7/site-packages/django/core/management/validation.py", line 35, in get_validation_errors
for (app_name, error) in get_app_errors().items():
File "/home/venvs/ldap/local/lib/python2.7/site-packages/django/db/models/loading.py", line 166, in get_app_errors
self._populate()
File "/home/venvs/ldap/local/lib/python2.7/site-packages/django/db/models/loading.py", line 72, in _populate
self.load_app(app_name, True)
File "/home/venvs/ldap/local/lib/python2.7/site-packages/django/db/models/loading.py", line 96, in load_app
models = import_module('.models', app_name)
File "/home/venvs/ldap/local/lib/python2.7/site-packages/django/utils/importlib.py", line 35, in import_module
import(name)
File "/home/ldap/ldapdb/models/init.py", line 33, in
from ldapdb.models.base import Model # noqa
File "/home/ldap/ldapdb/models/base.py", line 46, in
class Model(django.db.models.base.Model):
File "/home/ldap/ldapdb/models/base.py", line 54, in Model
search_scope = ldap.SCOPE_SUBTREE
AttributeError: 'module' object has no attribute 'SCOPE_SUBTREE'

I tried to user python-ldap 2.3.13, 2.4.10, 2.4.15

Please package examples/tests to pypi

django-ldapdb-0.3.2 on pypi doesn't contain examples and tests. You can add it through MANIFEST.in:

diff --git a/MANIFEST.in b/MANIFEST.in
index b44eea6..e5e36c2 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,5 @@
 include AUTHORS
 include LICENSE
 include README.md
+recursive-include examples *
+include settings.py manage.py

ListField with multi-value default

If you define a ListField with a List (with multiple values) as default, ldapdb will encode the list as string. This will generate something like that:

['[', 'v', 'a', 'l', 'u', 'e', '1', ',', ' ', 'v', 'a', 'l', 'u', 'e', '2', ']']

This will usually result in an error "value provided more than once" which is probably covered in #57. But the result would be wrong anyway...

Field value used in model DN is not escaped

I'm getting a lot of LDAPErrors like

{'info': 'invalid DN', 'desc': 'Invalid DN syntax'}

because there are some bad characters in the values used for the models primary key.

Should the Model.build_rdn() run ldap.dn.escape_dn_chars() on the primary key field value?

Doesn't support pre_save signal

ldapdb doesn't support pre_save signal.

I've managed by adding this line at top of the save function in models/base.py :

signals.pre_save.send(sender=self.__class__, instance=self)

Django 1.6 : 'Query' object has no attribute 'select_fields'

I've justed tested with Django 1.6 and I got this kind of message.
Work well with 1.4.5

Regards,
Hug

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 71, in __repr__
    data = list(self[:REPR_OUTPUT_SIZE + 1])
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 96, in __iter__
    self._fetch_all()
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 854, in _fetch_all
    self._result_cache = list(self.iterator())
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 220, in iterator
    for row in compiler.results_iter():
  File "/usr/local/lib/python2.7/dist-packages/ldapdb/backends/ldap/compiler.py", line 131, in results_iter
    if self.query.select_fields:
AttributeError: 'Query' object has no attribute 'select_fields'

no such table: LdapManager_ldapuser on POST/Saving of ldap user or group

I'm running a setup with three database sources.

Opening The ldap group directory and opening a specific ldap entry works fine.
Since commit 6930a8d I only have the problem with adding new ldap entries. Saving existing now works.

The error message is the same.
OperationalError at /admin/LdapManager/ldapuser/add/
no such table: LdapManager_ldapuser

I'm using ldapdb form within the admin interface. Maybe that's something the issue in combination with multiple databases?

In short it tries to use the default database 'sqlite' to lookup the LdapManager_ldapuser table.
This is part of a self.full_clean() sanity check, where it checks if the value is unique.

Sorry, I can't pinpoint where this is going wrong. There is no ldap reference in the trace.

Any idea how to get around this?

Settings:

    DATABASES = {
     'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        ...
     },
     'serviceCatDb' : {
        "ENGINE" : 'ibm_db_django',
        ...
     },
     'ldap': {
        'ENGINE': 'ldapdb.backends.ldap',
        ...
      }
     }

Model:

class AccessGroup(ldapdb.models.Model):
'''
Class for representing ldap access group entry.
'''
class Meta:
    managed = False
    verbose_name = "AccessGroup"

base_dn = "ou=Groups,dc=sview,dc=domain,dc=nl"
object_classes = ['groupOfNames','top']

name = CharField(db_column='cn', max_length=200, primary_key=True)
members = ListField(db_column='member', default=[])
description = CharField(db_column='description',blank=True,null=True,max_length=200)

def __str__(self):
    return self.name

def __unicode__(self):
    return self.name

Admin:

class MultiDBModelAdmin(admin.ModelAdmin):
# A handy constant for the name of the alternate database.
using = 'ldap'

def save_model(self, request, obj, form, change):
    # Tell Django to save objects to the 'other' database.
    obj.save(using=self.using)

def delete_model(self, request, obj):
    # Tell Django to delete objects from the 'other' database
    obj.delete(using=self.using)

def get_queryset(self, request):
    # Tell Django to look for objects on the 'other' database.
    return super(MultiDBModelAdmin, self).get_queryset(request).using(self.using)

def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
    # Tell Django to populate ForeignKey widgets using a query
    # on the 'other' database.
    return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)

def formfield_for_manytomany(self, db_field, request=None, **kwargs):
    # Tell Django to populate ManyToMany widgets using a query
    # on the 'other' database.
    return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)


class AccessGroupAdmin(MultiDBModelAdmin):
    class Meta:
        model = AccessGroup

    form = LdapGroupForm    
    list_display = ('name','members','description')
    search_fields = ['name']
    exclude = ['dn']

Don't think the form is very relevant.

class LdapUserAdmin(MultiDBModelAdmin):
    class Meta:
        model = LdapUser

    list_display = ('userid', 'common_name', 'surname','display_name','mail')
    search_fields = ['userid','common_name','surname','display_name','mail']
    exclude = ['dn']

Full Trace:
Environment:

Request Method: POST
Request URL: http://localhost:8080/admin/LdapManager/ldapuser/add/

Django Version: 1.7
Python Version: 2.7.9
Installed Applications:
('django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'ServiceCatalogue',
 'LdapManager')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware')


Traceback:
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\core\handlers\base.py" in get_response
  111.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\contrib\admin\options.py" in wrapper
  567.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\utils\decorators.py" in _wrapped_view
  105.                     response = view_func(request, *args, **kwargs)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\views\decorators\cache.py" in _wrapped_view_func
  52.         response = view_func(request, *args, **kwargs)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\contrib\admin\sites.py" in inner
  204.             return view(request, *args, **kwargs)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\contrib\admin\options.py" in add_view
  1437.         return self.changeform_view(request, None, form_url, extra_context)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\utils\decorators.py" in _wrapper
  29.             return bound_func(*args, **kwargs)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\utils\decorators.py" in _wrapped_view
  105.                     response = view_func(request, *args, **kwargs)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\utils\decorators.py" in bound_func
  25.                 return func.__get__(self, type(self))(*args2, **kwargs2)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\transaction.py" in inner
  394.                 return func(*args, **kwargs)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\contrib\admin\options.py" in changeform_view
  1380.             if form.is_valid():
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\forms\forms.py" in is_valid
  162.         return self.is_bound and not bool(self.errors)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\forms\forms.py" in errors
  154.             self.full_clean()
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\forms\forms.py" in full_clean
  355.         self._post_clean()
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\forms\models.py" in _post_clean
  430.             self.validate_unique()
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\forms\models.py" in validate_unique
  439.             self.instance.validate_unique(exclude=exclude)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\models\base.py" in validate_unique
  799.         errors = self._perform_unique_checks(unique_checks)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\models\base.py" in _perform_unique_checks
  894.             if qs.exists():
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\models\query.py" in exists
  606.             return self.query.has_results(using=self.db)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\models\sql\query.py" in has_results
  445.         return compiler.has_results()
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\models\sql\compiler.py" in has_results
  757.         return bool(self.execute_sql(SINGLE))
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\models\sql\compiler.py" in execute_sql
  786.             cursor.execute(sql, params)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\backends\utils.py" in execute
  81.             return super(CursorDebugWrapper, self).execute(sql, params)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\backends\utils.py" in execute
  65.                 return self.cursor.execute(sql, params)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\utils.py" in __exit__
  94.                 six.reraise(dj_exc_type, dj_exc_value, traceback)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\backends\utils.py" in execute
  65.                 return self.cursor.execute(sql, params)
File "D:\Users\user\.virtualenv\BSM\lib\site-packages\django\db\backends\sqlite3\base.py" in execute
  485.         return Database.Cursor.execute(self, query, params)

Exception Type: OperationalError at /admin/LdapManager/ldapuser/add/
Exception Value: no such table: LdapManager_ldapuser

django-ldapdb .save() is failing?

I'm trying to use a django-ldapdb with my little app: ( https://github.com/JohnTheodore/django-ldap/tree/sshkeys/backend_django/sshkeys ). I have a view setup ( localhost:8000/sshkeys ) where it reads an ldap server and gives me the results from my user. Reading seems to work fine, it's writing or .save() that I'm failing on. The results of me trying to .save() are object not found, the log is here for that: https://gist.github.com/anonymous/3029037378b4116ef519

Why does it say object not found, since I just read the object and it does exist?

The ultimate goal is I want to be able to have people manage their own ssh keys in ldap, for passwordless ssh login through ldap.

ListField fails with duplicate values in iterable

Hi, when having a field like:

cn = ListField(db_column='cn')

trying to save eg. ['foo', 'bar', 'bar'] lead to a protocol error because duplicate value. Can't tell ATM if duplicates are allowed on a per-attribute basis, don't think anyway as it is really pointless in respect to RFC2251 : "the order of attribute values within the vals set is undefined and implementation-dependent, and MUST NOT be relied upon."
Maybe simply returning sets from ListField field class eg. from_ldap and get_db_prep_save ? not sure

thank you, ciao

Running tests with real LDAP db

Hi,
I am trying to build test running a real db without mock:

class PeopleTest(TestCase):
    def setUp(self):
        p = People(username='ahahh', sis_id='1123')
        p.save()
    def test_get_person(self):

        self.assertEqual(People.objects.get(username='ahahh'), People.objects.get(username='1123'))

How should I do to have a real save in the Ldap db?

Charset error

Hi,

When saving an instance to a ldapdb model I get a connection charset error. Is there anything special to do or is it a bug in django-ldapdb ?

File "/home/moimael/Developpement/octo_env/lib/python2.7/site-packages/django/forms/models.py", line 99, in save_instance
instance.save()
File "build/bdist.linux-x86_64/egg/ldapdb/models/base.py", line 109, in save
value = field.get_db_prep_save(value, connection=connection)
File "build/bdist.linux-x86_64/egg/ldapdb/models/fields.py", line 70, in get_db_prep_save
return [value.encode(connection.charset)]
AttributeError: 'DatabaseWrapper' object has no attribute 'charset'

exists() for a QuerySet doesn't work

I want to use exists().

Here an example:
Entry.objects.filter(user='username').exists()
But it returns False.

But it returns something without exists() like [<Entry: user01>]

Also if I use count(), it returns 1.
Entry.objects.filter(user='username').count()

Doesn't support django-ldapdb exists() or is there a bug?

can't change object_classes model list

changing Model's object_classes list raise an exception:

DoesNotExist: LdapUser matching query does not exist.

sample code:

def test_update_objectclass(self):
    u = LdapUser.objects.get(username='foouser')
    u.object_classes.append('person')
    u.save()

    # make sure DN gets updated if we change the pk
    u.object_classes.remove('person')
    u.save()
    self.assertEquals(u.object_classes, ['posixAccount', 'shadowAccount', 'inetOrgPerson'])

in fact this code fails at first save()

Use / option to use ReconnectLDAPObject to survive temporary LDAP issues?

When faced with temporary LDAP outage it'd be nice if ldapdb used or gave the option to use ReconnectLDAPObject which will automatically retry a connection.

Something like:

ldapdb/backends/ldap/base.py

def ensure_connection(self):
    if self.connection is None:
        #self.connection = ldap.initialize(self.settings_dict['NAME'])
        self.connection = ldap.ldapobject.ReconnectLDAPObject(
            self.settings_dict['NAME'],
            retry_max=10)
...

.. but with the retry settings configurable.

Is this something that might be accepted as a PR?

Publish support for Django 1.8 on PyPI

Hello. The version on PyPI and repo are both 0.3.2 but, the repo works with Django 1.8 while the one in PyPI not.

Since the commit to support 1.8 was done few months ago, I ask you to publish the changes to PyPI, if it is stable.

Thx!

Switch to pyldap with 'bytes_mode=False' likely to break many Python2 consumers

The changes in 46f4744 mean that all Python2 consumers must ensure that any and all string data passed to the library are unicode strings (u'...' or unicode('...')). Failure to do this conversion in the application prior to calling ldapdb will result in errors like: All provided fields *must* be text when bytes mode is off; got 'cn'.

This breaking change is probably worth documenting in the Changelog and README. Having read through pyldap/pyldap#1 I think I understand why this change is being made, but users should be warned/educated about the need to ensure that unicode is used.

Django 1.7

Just to give a headsup, Django 1.7 will have it's db stuff refractored by quite a bit.

As a result, django-ldapdb will not (yet) work with 1.7 alpha builds.

One of the offending commits (there are probably more): django/django@20bab2c

Support simple LDAP filters for object_classes attr

Hello Folks,

I'm trying to create some classes that maps into my Active Directory Environment.

My models:

http://dpaste.com/1RMMN9G

In the case of Active Directory, the computer objects already uses the 'user' object class as well an 'computer' object class. In this case, if I try to query all users (LdapUser.objects.all) the query returns users AND computers.

In Fact, if i need to query just the users i need to perform a query such the following:

(&(objectClass=user)(!(objectClass=computer)))

A tried the following, but it wont work (returns Bad Search Filter):

object_classes = ['user', 'organizationalPerson', '&(!(computer))']

How can I achieve that behaviour with the object_classes attribute?

Thanks in advance.

Looking for new maintainers

Even though I have not needed LDAP for any of my projects for several years, I tried to continue to support newer Django versions and merge fixes contributed by users. It has now reached the point where I consider the project would best be served by someone else (ideally more than one person) taking over. If you are interested, please reply to this issue so we can arrange transfering code + issues.

Update pypi package

Hi,

I'd like to install django-ldapdb using pip (like every other django modules), but the release is still 0.1.0. Would it be possible to update the pypi package ?

Thanks !

IntegerField has invalid lookup: in

I'm starting to work with LDAP and using this app (thanks @jlaine!) I'm developing a django site to help me manage my LDAP db. Following the examples, I've created an LdapGroup model but instead of making the cn the pk, I chose the gidNumber. All works just fine but when deleting a group. I get an "TypeError: IntegerField has invalid lookup: in". I've found this IntegerField definition (/ldapdb/models/fields.py):

    def get_prep_lookup(self, lookup_type, value):
        "Perform preliminary non-db specific lookup checks and conversions"
        if lookup_type in ('exact', 'gte', 'lte'):
            return value
        raise TypeError("IntegerField has invalid lookup: %s" % lookup_type)

I've fixed the error simply by adding 'in' to that tuple. The question is: shouldn't be there by default? Or it has something to do with how LDAP works?

How I can extend LDAP models?

I have two databases: 1. on LDAP server 2. on my computer( sqlite3 )

I'm trying to extend LDAP model without interference in this database.

# LDAP_Profile_model

class LDAP_Profile(ldapdb.models.Model):

    ...

    username = CharField(db_column='sAMAccountName',unique=True)
    first_name = CharField(db_column='givenName')
    last_name = CharField(db_column='sn')
    full_name = CharField(db_column='cn')
    email = CharField(db_column='mail',unique=True,primary_key=True)

    def __unicode__(self):
        return u"%s (%s)" % (self.full_name,self.username)
# Custom_Profile

class Custom_Profile(models.Model):
    person = models.OneToOneField(LDAP_Profile)
    home_address = models.CharField(max_length=50)
    city = models.CharField(max_length=50)
    state = USStateField()
    zipcode = models.CharField(max_length=5)
    birth_date = models.DateField()

Support null fields

Hi,

I sometines need to save null or empty fields in ldap. I struggled a lot doing this by using python-ldapdb directly, but now i would like to replace these call by django-ldapdb to have a cleaner way to access ldap.

I modified the fields.py by adding :

elif lookup_type == 'isnull':
    return ['']

Python 3 Support

Do you plan to support Python 3 in the future?
This will be a really nice feature!

Make django.contrib.admin optionnal

Hi,

It seems that django-ldapdb needs django.contrib.admin to be activated.

We don't use it in our project, would it be possible to make it optional ?

Thanks !

How different is this from django_auth_ldap and what is the best solution

Hi All

We have an existing ldap implementation (using django_auth_ldap & APIs to execute raw ldap calls). We are in process of rewriting our LDAP part. I very much like this approach of manipulating with something similar to Django models. I have few questions though.

  • I can get basic get/filter/delete operation but how do we do permissions. Suppose I want to know all groups of a a certain user(mail=[email protected],dc=example,dc=com) what should be the best way. With django_auth_ldap this is a lot obvious.
  • There is no login/authenticate backend. How to integrate LdapBackend with this.
  • Should I use LdapUser model for ldap operation only? Is is a good practice to have foreign key in ldapdb.models? I don't see ForeignKey inside ldapdb.models.fields.
    This is mostly to draw line between LdapUser and django.contrib.auth.models.User. I was hoping if we can replace auth_user with LdapUser.

These above points leads me to a hybrid approach of using both django-ldapdb and django_auth_ldap. What do you guys think of it?

Create a good documentation for the project

Hello Folks,

I think that the project needs an updated documentation that shows some usefull examples (or provide links for existent examples).

If possible, please start it with some simple structure; I Think I can help in this area.

Thanks in advance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.