14

I'm looking for a guide about adding windows authentication support into a django app, particulary OSQA

I'm aware about http://code.google.com/p/python-ntlm/ And also saw this post: http://erny-rev.blogspot.com/2007/11/ntlm-authentication-in-django.html But I'm not a Django-dev, I just want to deploy OSQA in Windows enviroment (intranet, so I need to add windows authentication). So I'm looking for simple step-by-step description.

(I've managed to deploy a OSQA site on windows with SQL Server and it's working)

UPDATE:
I'd like to get not just auth against AD but SSO-like behavior in IE. As a user access my django-based site in IE it'd automaticaly authenticated with its domain account.

Community
  • 1
  • 1
Shrike
  • 9,218
  • 7
  • 68
  • 105
  • Actualy, no :( but I also asked here - http://meta.osqa.net/questions/4326/how-to-add-windows-authentication and now have noticed an answer with link to http://jira.osqa.net/browse/OSQA-425 – Shrike Sep 01 '10 at 19:13
  • I had seen that patch as well. Odd though as it won't solve what you and I would like and that is auto-authenticate. This patch seems to just add a new way to authenticate. –  Sep 01 '10 at 19:21

3 Answers3

11

You can do this using Apache, mod_auth_kerb and REMOTE_USER authentication with Django hosted as mod_wsgi.

Here is an example of some config we use:

WSGIDaemonProcess myapp user=myapp group=myapp processes=5 threads=1
WSGIProcessGroup myapp
WSGIScriptAlias /myapp /home/wolapp/code/wolapp.wsgi
<VirtualHost ...>
    <Location /myapp>
            AuthType                Kerberos
            AuthName                "Domain Login"
            KrbMethodNegotiate      On
            KrbMethodK5Passwd       On
            KrbAuthRealms           YOUR.DOMAIN
            Krb5Keytab              /etc/krb5.keytab
            KrbServiceName          HTTP/server.your.domain
            require                 valid-user
    </Location>
</VirtualHost>

You then need to setup this:

http://docs.djangoproject.com/en/dev/howto/auth-remote-user/

A couple of caveats to note:

  1. Opera fails completely in our testing; it can't handle the "Negotiate" header
  2. IE works fine if the machine is in the domain, but if it isn't, you get prompted for your password twice - the first time the machine uses "ITSNAME\username" which fails; the second time the bare "username"

Hope this helps.

Realist
  • 509
  • 2
  • 13
7

This might not be that elegant, but it works (import/put into views):

import base64


def get_msg_str(msg,start):
    msg_len, _, msg_off = struct.unpack("<HHH", msg[start:start + 6])
    return msg[msg_off:msg_off + msg_len].replace("\0", '')


def ntlm_auth(request):
    """Goes through ntlm stages...
    Return user_name, response.
    While response is not none, keep sending it.
    Then use the user.
    """
    username = None
    response = None

    auth = request.META.get('HTTP_AUTHORIZATION')
    if not auth:
        response = HttpResponse(status=401)
        response['WWW-Authenticate'] = "NTLM"
    elif auth[:4] == "NTLM":
        msg = base64.b64decode(auth[4:])
        #  print repr(msg)
        ntlm_fmt = "<8sb" #string, length 8, 4 - op
        NLTM_SIG = "NTLMSSP\0"
        signature, op = struct.unpack(ntlm_fmt, msg[:9])
        if signature != NLTM_SIG:
            print "error header not recognized"
        else:
            print "recognized"
            # print signature, op
            # print repr(msg)
            if op == 1:
                out_msg_fmt = ntlm_fmt + "2I4B2Q2H"
                out_msg = struct.pack(out_msg_fmt,
                    NLTM_SIG, #Signature
                    2, #Op
                    0, #target name len
                    0, #target len off
                    1, 2, 0x81, 1, #flags
                    0, #challenge
                    0, #context
                    0, #target info len
                    0x30, #target info offset
                )

                response = HttpResponse(status=401)
                response['WWW-Authenticate'] = "NTLM " + base64.b64encode(out_msg).strip()
            elif op == 3:
                username = get_msg_str(msg, 36)

    return username, response

Usage:

def my_view(request):
    username, response = ntlm_auth(request)
    if response:
        return response

I am sure this could be made more elegant as a decorator, and there may be other ways - but I've used it and it works.

Danny Staple
  • 7,101
  • 4
  • 43
  • 56
  • 2
    Where does the actual authentication happen here? This looks like it just accepts the browser response (which could be spoofed?). – Carl F. Mar 28 '13 at 12:08
  • This much is true - in our use case we actually aren't web facing, it is mostly so a small team can be lazier about typing in a username using IE SSO. However, I suspect there is further message data with more credential information to check against the Django user database. Feel free to improve my answer. – Danny Staple Mar 28 '13 at 16:27
  • I'm trying to use this example, but I can't quite get it to work. It sends the initial response under 'not auth', but nothing after that. Is there some detail I'm missing about making this work? – robbie Apr 21 '14 at 16:13
  • It never progresses to the second stage of this? Is the browser client sending back the packet with the NTLM auth in it? I suggest using wireshark to see what is going on... – Danny Staple Apr 22 '14 at 10:14
5

Generally, if you just want authentication against Active Directory, the approach most likely to succeed is to use LDAP authentication against the Active Directory LDAP service. The only trick is that unlike most LDAP servers, Active Directory needs to have an authenticated user (and password). Most folks end up establishing a 'ldap-query' user with and hardcode that user for the query configuration.

For examples, see http://djangosnippets.org/snippets/501/ and http://www.google.com/search?q=Django+LDAP+authentication

Craig Trader
  • 15,507
  • 6
  • 37
  • 55
  • I'd like to get not just auth against AD but SSO-like behavior in IE. As a user access my django-based site in IE it'd automaticaly authenticated with its domain account. – Shrike Aug 04 '10 at 10:54