Thursday, January 22, 2009

How to Debug

I once worked with a wonderfully talented programmer named Cuong Tran, who has an almost uncanny ability to determine the cause of bugs, and then fix those bugs. He once explained to me how to debug code: "When you're trying to solve a problem, strip away everything and make it absolutely as simple as possible, until you have something that works. Then add stuff back, bit by bit, until you see the problem." Well, obviously. And yet I keep having to learn this lesson over and over again. Most recently, the teacher was another wonderfully talented programmer (Brian Ericson), and the classroom was Redmine.

Redmine is a lightweight collaboration tool based on Rails. It includes a wiki, forums, bug tracking and more, and yet still manages to feel small and simple and easy to configure and use. At least, it felt that way, right up until I tried to get LDAP authentication happening. Following the instructions on the Redmine wiki, I was having no success, and the log file was giving precious little information, even with debug output. Why wasn't the damn thing working??

Fortunately, Redmine has two helpful personality traits: (1) It comes with its own source code, and (2) the source code is Ruby, which means we could very easily add our own debug output - or even modify behavior - to learn what was happening. And this enabled us (and by "us" I mean "Brian Ericson") to strip away everything until we had something that worked.

After some poking around, it became apparent that the trouble was in auth_source_ldap.rb. Inside this class was simply the Net::LDAP class - shrink-wrapped Ruby. So just forget about Redmine for a minute - could we simply call the search method on Net::LDAP, and get something in response? We (and by "we" I mean "I") had almost given up when we (and by "we" I mean "Brian") discovered that yes, we could - but not with the sort of configuration information that I had provided.

The Redmine wiki implied that one simply needed to provide a username and password to connect to the LDAP server (assuming the LDAP server doesn't allow anonymous access, which mine does not). However, anyone familiar with LDAP - a club which did not include me, until yesterday - knows that you need to provide some context. Specifically, you need to chant the proper LDAP incantations to tell the LDAP server where in the directory hierarchy it should search for that user. Thus, instead of this:









NameOur LDAP
Hostldap.our-company.com
Port389
Accountkurtc
Password********
Base DNou=People, ou=Root, dc=our-compnay, dc=com


...Redmine requires this:









NameOur LDAP
Hostldap.our-company.com
Port389
Accountuid=kurtc, ou=People, ou=Root, dc=our-company, dc=com
Password ********
Base DNou=People, ou=Root, dc=our-compnay, dc=com


It all makes perfect sense, when you ask the right questions in the right order: In Ruby, what's the standard way of searching for user information in an LDAP server? How is Redmine using this code? And are we passing in the expected arguments?

Once we simplified the problem enough to ask the right questions, the problem was obvious: we (and by "we" I mean "I") wasn't passing in the right "Account" string, which needed to be the proper LDAP incantation, as opposed to a simple username.

So there it is - how to debug. Just strip away everything and make it absolutely as simple as possible, until you have something that works. Then add stuff back, bit by bit, until you see the problem. The details are left as an exercise for you, dear reader.