Sysadmin issues

54 views
Skip to first unread message

mobopro

unread,
Jun 5, 2010, 5:53:44 PM6/5/10
to django-auth-ldap
I've just installed a Django project with the django-auth-ldap
backend. The client's sysadmin has to maintain a very large LDAP and
isn't happy with the added maintenance burden in a couple of cases:

1. When users are deleted, they aren't deleted automatically from the
Django database. (Nor should they be, of course.) But can they be
marked as inactive automatically?

2. When usernames change (as they do in this facility when people
marry, etc.), the backend simply creates a new record instead of
updating the old record. This is tricky of course, since user records
on the Django side are keyed by username, not UID number. Can there be
an option to key from the UID number to update the Django record
properly?

Peter Sagerson

unread,
Jun 5, 2010, 6:46:30 PM6/5/10
to django-a...@googlegroups.com
Interesting questions. I think both of these are solvable.

> 1. When users are deleted, they aren't deleted automatically from the
> Django database. (Nor should they be, of course.) But can they be
> marked as inactive automatically?

Presumably you mean that when an LDAP user is deleted, the very next Django request should notice this and deactivate the user. I'm not sure if there's a way to integrate this into django-auth-ldap such that it doesn't feel like a hack, but I think there are a few ways to do this already. Of course, if you're using AUTH_LDAP_FIND_GROUP_PERMS and not AUTH_LDAP_CACHE_GROUPS, then a user's permissions will be checked on every request already. If all of your views are restricted by permissions, then it should be taken care of.

If you are granting access to users simply by virtue of being authenticated and active, then you will need something else. One simple solution would be to just do it in a little piece of middleware along these lines:

try:
dn = request.user.ldap_user.dn
except AttributeError:
pass
else:
con = ldap.initialize(<server uri>)
con.simple_bind_s(<user>, <pass>)
results = con.search_s(dn, ldap.SCOPE_BASE, '(objectClass=*)')
if len(results) == 0:
user.is_active = False
user.save()

Note that if you're not using AUTH_LDAP_USER_DN_TEMPLATE, accessing ldap_user.dn will search for the user every time and this can be shortened considerably:

assert getattr(settings, 'AUTH_LDAP_USER_DN_TEMPLATE') is None

try:
if request.user.ldap_user.dn is None:
request.user.is_active = False
request.user.save()
except AttributeError:
pass

One way or another, you'll obviously be hitting the LDAP server for every request. Pushing changes would be more efficient, but if that's not an option, this ought to do the trick.


> 2. When usernames change (as they do in this facility when people
> marry, etc.), the backend simply creates a new record instead of
> updating the old record. This is tricky of course, since user records
> on the Django side are keyed by username, not UID number. Can there be
> an option to key from the UID number to update the Django record
> properly?

It's true, the current policy is to map LDAP users to Django users strictly by (potentially modified) username. In principle, I don't think this has to be the case. The LDAPBackend class already has a couple of subclass hooks for converting from LDAP usernames to Django-friendly usernames and back. We could add a lower-level hook that simply takes the username along with all of a user's LDAP attributes and returns a Django user. The default implementation would just call User.objects.get_or_create(username=username), but custom implementations could do anything.

In your case, for instance, you'd want to start by using AUTH_LDAP_PROFILE_ATTR_MAP to map some stable identifier from the LDAP record into your profile model, thus establishing a permanent association. Then in your custom implementation of LDAPBackend.get_or_create_user, you could get the identifier from the LDAP attributes, find the associated User object, update the username if necessary, and return it.

Would that take care of it?

Dylan Kohler

unread,
Jun 10, 2010, 2:47:00 PM6/10/10
to django-a...@googlegroups.com
What a great and thorough reply. I think you're onto the solution I need.

Now all I have to do is get up to speed enough to understand how to implement it. :-) Questions below...


On Jun 5, 2010, at 3:46 PM, Peter Sagerson wrote:

> Interesting questions. I think both of these are solvable.
>
>> 1. When users are deleted, they aren't deleted automatically from the
>> Django database. (Nor should they be, of course.) But can they be
>> marked as inactive automatically?
>
> Presumably you mean that when an LDAP user is deleted, the very next Django request should notice this and deactivate the user. I'm not sure if there's a way to integrate this into django-auth-ldap such that it doesn't feel like a hack, but I think there are a few ways to do this already. Of course, if you're using AUTH_LDAP_FIND_GROUP_PERMS and not AUTH_LDAP_CACHE_GROUPS, then a user's permissions will be checked on every request already. If all of your views are restricted by permissions, then it should be taken care of.

I do have those flags set that way. However, some of my views are available without any special permissions -- a user needs to simply be authenicated and active. If requiring some "placebo" permission force the check, I could do that, too, but yes, it sounds like a hack.

> If you are granting access to users simply by virtue of being authenticated and active, then you will need something else. One simple solution would be to just do it in a little piece of middleware along these lines:
>
> try:
> dn = request.user.ldap_user.dn
> except AttributeError:
> pass
> else:
> con = ldap.initialize(<server uri>)
> con.simple_bind_s(<user>, <pass>)
> results = con.search_s(dn, ldap.SCOPE_BASE, '(objectClass=*)')
> if len(results) == 0:
> user.is_active = False
> user.save()
>
> Note that if you're not using AUTH_LDAP_USER_DN_TEMPLATE, accessing ldap_user.dn will search for the user every time and this can be shortened considerably:
>
> assert getattr(settings, 'AUTH_LDAP_USER_DN_TEMPLATE') is None
>
> try:
> if request.user.ldap_user.dn is None:
> request.user.is_active = False
> request.user.save()
> except AttributeError:
> pass
>
> One way or another, you'll obviously be hitting the LDAP server for every request. Pushing changes would be more efficient, but if that's not an option, this ought to do the trick.

Where should either of these middleware pieces be inserted? I am using AUTH_LDAP_USER_DN_TEMPLATE currently, but from the docs it looks like I don't need to. Either/or suits me.

I'll make the tradeoff clear to my sysadmin, but I suspect he'll choose the extra load on the LDAP server.

>> 2. When usernames change (as they do in this facility when people
>> marry, etc.), the backend simply creates a new record instead of
>> updating the old record. This is tricky of course, since user records
>> on the Django side are keyed by username, not UID number. Can there be
>> an option to key from the UID number to update the Django record
>> properly?
>
> It's true, the current policy is to map LDAP users to Django users strictly by (potentially modified) username. In principle, I don't think this has to be the case. The LDAPBackend class already has a couple of subclass hooks for converting from LDAP usernames to Django-friendly usernames and back. We could add a lower-level hook that simply takes the username along with all of a user's LDAP attributes and returns a Django user. The default implementation would just call User.objects.get_or_create(username=username), but custom implementations could do anything.
>
> In your case, for instance, you'd want to start by using AUTH_LDAP_PROFILE_ATTR_MAP to map some stable identifier from the LDAP record into your profile model, thus establishing a permanent association. Then in your custom implementation of LDAPBackend.get_or_create_user, you could get the identifier from the LDAP attributes, find the associated User object, update the username if necessary, and return it.
>
> Would that take care of it?

I believe it will! When you do add that hook, do let me know. Meanwhile, I'll start reading up on how to do those LDAP lookups and updates. I might be back. :-)

Thanks again, so much -- this is going to fit the bill perfectly.

Dylan

Peter Sagerson

unread,
Jun 10, 2010, 4:29:13 PM6/10/10
to django-a...@googlegroups.com
> Where should either of these middleware pieces be inserted? I am using AUTH_LDAP_USER_DN_TEMPLATE currently, but from the docs it looks like I don't need to. Either/or suits me.

If you want to add the middleware, you'll have to put it in a Python module and load it in your settings:

http://docs.djangoproject.com/en/1.2/topics/http/middleware/#topics-http-middleware
http://docs.djangoproject.com/en/1.2/topics/http/middleware/#writing-your-own-middleware

> I believe it will! When you do add that hook, do let me know. Meanwhile, I'll start reading up on how to do those LDAP lookups and updates. I might be back. :-)

I added the hook in 1.0.4 earlier this week, so have at.

http://packages.python.org/django-auth-ldap/#django_auth_ldap.backend.LDAPBackend.get_or_create_user

Dylan Kohler

unread,
Jun 10, 2010, 6:08:41 PM6/10/10
to django-a...@googlegroups.com
Fantastic. Thank you!

Di majo

unread,
May 12, 2024, 3:50:21 PM5/12/24
to django-auth-ldap
MT103/202 DIRECT WIRE TRANSFER
PAYPAL TRANSFER
CASHAPP TRANSFER
ZELLE TRANSFER
LOAN DEAL
TRANSFER WISE
WESTERN UNION TRANSFER
BITCOIN FLASHING
BANK ACCOUNT LOADING/FLASHING
IBAN TO IBAN TRANSFER
MONEYGRAM TRANSFER
IPIP/DTC
SLBC PROVIDER
CREDIT CARD TOP UP
DUMPS/ PINS
SEPA TRANSFER
WIRE TRANSFER
BITCOIN TOP UP
GLOBALPAY INC US
SKRILL USA
UNIONPAY RECEIVER

Thanks.


NOTE; ONLY SERIOUS / RELIABLE RECEIVERS CAN CONTACT.

DM ME ON WHATSAPP
+44 7529 555638

Reply all
Reply to author
Forward
0 new messages