Setting up LDAP for Django authentication - django

I am working on a Django-based intranet app which needs users to authenticate against Active Directory. I've found django-auth-ldap, but I still have absolutely no idea what to do in order to setup a local LDAP server which I could develop against.
I installed AD LDS, but it needed a domain controller, and some SO answers that I read told me that I can't setup that on Windows 7. So I decided to try OpenLDAP instead, and it looks like it's working, but the tutorials I read weren't particularly clear on how the hell do I add data to it?
Would anyone please explain to me what steps do I need to take in order to add and successfully authenticate a Django user profile against a locally running LDAP service, be it OpenLDAP or Active Directory (I'll need to know how to successfully set up the later, if that's at all possible)?

You may have already solved this by now but just in case:
You need to setup your LDAP repository (done).
You need to create some user objects which can bind. This is accomplished by using an LDIF file or a similar method. A very useful tool for visualising your LDAP db is 'Apache Directory Studio'. An example of how an ldif may appear:
dn:cn=myuser,cn=localhost
changetype: modify
add: cn=myuser
userPassword: password01
It will vary depending on your schema. I highly recommend you read the Django docs for implementation specific to you: https://pythonhosted.org/django-auth-ldap/
This is a good example (from the docs):
import ldap
from django_auth_ldap.config import LDAPSearch
AUTH_LDAP_BIND_DN = ""
AUTH_LDAP_BIND_PASSWORD = ""
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
but assuming you had added a user via ldif to "cn=localhost" then thats the dn you'd use in the search field. As the docs note, you can search the whole directory if you prefer.
If its not clear what is happening:
Import Ldap Module
Import LDAPSEARCH function? from the django module
Set a blank bind DN (this is like a fully qualified username. e.g. uid=myuser,dc=com)
Set a blank bind password
Perform the search. Format like so: ("base",scope,searchfilter)
You can test using the standard ldap module in python:
import ldap
ld = ldap.initialize("ldaps://acme.com:636")
ld.bind_s("userDN","Password")
ld.search_s("cn=acme.com",ldap.SCOPE_SUBTREE,"uid=myuser")
Not really important, but "ldap.SCOPE_SUBTREE", "ldap.SCOPE_BASE" etc are just integers. So you can pass in 0, 1, 2 instead.
Good luck, even if you solved this long ago.

Related

How to get user name after authentication?

I built a website using Django and Apache. I have Apache LDAP authentication. How do I get the username after the user authenticate to the website? I want to get the username and represents it.
So you're using Apache LDAP Authentication. If you use mod_auth_ldap, see the docs here http://httpd.apache.org/docs/2.0/mod/mod_auth_ldap.html#frontpage. If you use
mod_authnz_ldap see the docs here https://httpd.apache.org/docs/2.4/mod/mod_authnz_ldap.html#exposed.
What the docs tell is that, when the user is authenticated Apache sets environment variable, that can be accessed from CGI script. Variable name varies depending on the version you use. Though Python uses WSGI, you should still try to get the variable as it's environment variable and should be accessible anyway.
In python to get access to environment variable:
import os
username=os.getenv["REMOTE_USER"] #will return variable value or None
if username:
pass
#process username here
See docs on this function here: https://docs.python.org/3.5/library/os.html#os.getenv
You can try to use this directly in your Python code where you expect the username. Or better use this code in wsgi.py in your Django project and if username is available add special header with its value, so that it will be available inside Django in request passed to Django views. But remember to strip that header before adding it, so if a malicious user forges the header it doesn't affect your app. For more information on this see https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/modwsgi/ and https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/apache-auth/.
Edit: Btw, there's a "How-to" for REMOTE_USER: https://docs.djangoproject.com/en/1.9/howto/auth-remote-user/
Edit: If you don't have any requirements for performing authentication with Apache, you might want to perform authentication in Django app directly, see: https://pythonhosted.org/django-auth-ldap/ and in example https://djangosnippets.org/snippets/901/.

How to configure CouchDB authentication in Docker?

I'm trying to build a Dockerized CouchDB to run in AWS that bootstraps authentication for my app. I've got a Dockerfile that installs CouchDB 1.6.1 and sets up the rest of the environment the way I need it. However, before I put it on AWS and potentially expose it to the wild, I want to put some authentication in place. The docs show this:
http://docs.couchdb.org/en/1.6.1/api/server/authn.html
which hardly explains the configuration properly or what is required for basic security. I've spent the afternoon reading SO questions, docs and blogs, all about how to do it, but there's no consistent story and I can't tell if what worked in 2009 will works now, or which parts are obsolete. I see a bunch of possible settings in the current ini files, but they don't match what I'm seeing in my web searches. I'm about to start trying various random suggestions I've gleaned from various readings, but thought I would ask before doing trial and error work.
Since I want it to run in AWS I need it to be able to start up without manual modifications. I need my Dockerfile to do the configuration, so using Futon isn't going to cut it. If I need to I can add a script to run on start to handle what can't be done there.
I believe that I need to set up an admin user, then define a role for users, provide a validation function that checks for the proper role, then create users that have that role. Then I can use the cookie authentication (over SSL) to restrict access to my app that provides the correct login and handles the session/cookie.
It looks like some of it can be done in the Dockerfile. Do I need to configure authentication_handlers, and an admin user in the ini file? And I'm guessing that the operations that modify the database will need to be done by some runtime script. Has anyone done this, or seen some example of it being done?
UPDATE:
Based on Kxepal's suggestion I now have it working. My Dockerfile is derived from klaemo's docker-couchdb, as mentioned below. The solution is to force the database to require authentication, but a fresh install starts out as Admin-Party. To stop that you have to create an admin user, which secures the system data but leaves other databases open. First, create an admin user in your Dockerfile:
RUN sed -e '/^\[admins\]$/a admin=openpassword\n' -i /usr/local/etc/couchdb/local.ini
(just following klaemo's sed pattern of using -e) and when CouchDB runs it will salt and hash this password and replace it in the local.ini file. I extract that password and replaced "openpassword" with this so that my Dockerfile didn't have the password in plain text. CouchDB can tell by the form of it not to hash it again.
The normal pattern to now secure the other databases is to create users/roles and use them in a validation function to deny access to the other databases. Since I am only interested in getting a secure system in place for testing I opted to defer this and just use the settings in local.ini to force everyone to be authenticated.
The Dockerfile now needs to set the require_valid_user flag:
RUN sed -e '/^\[couch_httpd_auth\]$/a require_valid_user = true\n' -i /usr/local/etc/couchdb/local.ini
And that requires uncommenting the WWW-Authenticate setting:
RUN sed -e 's/^;WWW-Authenticate/WWW-Authenticate/' -i /usr/local/etc/couchdb/local.ini
Which, since the setting shows Basic realm="administrator" means that the NSURLProtectionSpace in my iOS app needs to use #"administrator" as the realm.
After this I now have a Dockerfile that creates a CouchDB server that does not allow anonymous modification or reading.
This hasn't solved all of my configuration issues since I need to populate a database, but since I use a python script to do that and since I can pass credentials when I run that, I have solved most problems.
To setup auth configuration during image build, you need to check not API, but configuration for server admins. TL;DR just put [admin] section into local.ini file with your username and password in plain text - on start, CouchDB will replace password with it hash and CouchDB wouldn't be in Admin Party state.
P.S. Did you check docker-couchdb project?

Upgrading from Django 1.3 to 1.5

I'd like to upgrade an existing application to a more recent version of django. In 1.4 they changed the password hashing algorithm such that all my old passwords will no longer match when people try to login. Is there some way to upgrade but not require users to reset their passwords?
according to https://docs.djangoproject.com/en/dev/topics/auth/passwords/#auth-password-storage it will still check everything as usual.
if you are worried about storing everything as SHA1 by default, then put the hasher first in the list (this is not recommended though).
# settings.py
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
...
)
if you need to check for yourself, you can consider using virtualenv with the newer django==1.5 package and create a dummy project/app connected to the same database to try it out. If you have admin privileges and already use the admin interface, you can use that to login there.
I did the same upgrade last month and Django passwords still fully functional. The changes I made are basically in generic views( now all generic views are class based) , The logging in settings.py was changed and I have to put a ALLOWED_HOSTS list. For example: ALLOWED_HOSTS = ['.stackoverflow.com']
Particularly, I changed my urls calls cause I was using named urls without quotes in url tags and it's no longed supported by django. The right way is like this: {% url 'name_of_the_view' arg1 arg2%}
I suggest you to create another environment and try use django 1.5 just making this small changes.

In django-allauth, how do I add the OpenId social app?

Just starting to learn django, and I wanted to incorporate the allauth app.
Been trying to figure this out all day and haven't found the answer in other questions.
Anytime I try to add one of the social logins, I can't even get a login screen and django complains:
get_login_url() keywords must be strings
when it tries to render the provider list. (I copied over base, index, and profile from the example that came with allauth)
I've read that before I use one of the logins, I need to add the social app in the admin interface. So in the admin interface, I want to try one of the simpler ones, so i chose OpenId. Since I don't have a facebook app id or anything yet, I figured with OpenId, I wouldn't need that.
I'm getting hung up on what to use for the Key and Secret to register the social app. I'm new to this stuff, but I thought that was more for OAuth. But if I don't include it, it flags the fields as red and demands them. Where do I find/generate a Key/Secret?
Also, to use OpenId, am I supposed to specify a site like Google or Yahoo, or is there just an "OpenId" site?
I'm still using manage.py runserver, if that makes any difference. But I thought I would still be able to get the page to "render."
What version of Python are you running? If you are using an old 2.6 version, then you may be running into the issue described here:
http://cuu508.wordpress.com/2011/01/27/keywords-must-be-strings/
Please let me know if that pinpoints your problem. If so, I'll check if I can make allauth play nice with your version...
Update: haven't had the time to test this myself yet, could you give this change a try?:
--- a/allauth/socialaccount/templatetags/socialaccount.py
+++ b/allauth/socialaccount/templatetags/socialaccount.py
## -13,7 +13,7 ## class ProviderLoginURLNode(template.Node):
def render(self, context):
provider_id = self.provider_id_var.resolve(context)
provider = providers.registry.by_id(provider_id)
- query = dict([(name, var.resolve(context)) for name, var
+ query = dict([(str(name), var.resolve(context)) for name, var
in self.params.iteritems()])
request = context['request']
if not query.has_key('next'):

Alternative Django Authenication

Need to integrate Django with an existing authentication system. That system has it's own database, API, login/logout,edit profile web pages and cookie.
(I may have to add a few additional profile fields stored/updated locally)
What's the proper approach to substitute the out-of-the-box authentication in Django?
The proper approach to substitute authentication from django's out-of-the-box to your own is to substitute your classes in the AUTHENTICATION_BACKENDS tuple in settings.py as described in http://docs.djangoproject.com/en/dev/topics/auth/#specifying-authentication-backends. This is incredibly useful for just the issue you're describing.
A good example of an authentication backend done this way is django-cas. This uses CAS to authenticate in a django application. You can use this as your template and just write hooks into your own authentication system identically.
HTH
I've created a custom authentication backend when I've had to do something similar to what you have to do. See: http://docs.djangoproject.com/en/dev/topics/auth/#writing-an-authentication-backend
In the authenticate function you call your api to authenticate the user, and then map them to a django.contrib.auth.model.User object on some primary key, like username for example. If the primary key is something other than username I usually create a mapping object, or put it into the profile object for the project.
This depends on how you want to handle the problem. If you don't need to keep the existing system running, your best bet is to import their data into the django project.
If the authentication system must stay in tact, you might have to write a wrapper for django.auth. I've done this in the past using SQLAlchemy http://www.sqlalchemy.org to integrate to the external database.
It might be helpful to take a look at the Django 1.2 multi-db support http://djangoadvent.com/1.2/multiple-database-support
In either case I'd try to get the user information into django.auth rather than to write your own authentication system.