Saturday, March 29, 2008

git-cvsimport failure

I've been mirroring Project Wonder's CVS with github, and unfortunately git-cvsimport has been a real problem. If I try to run it directly on the repository, I get:

git-cvsimport: fatal: cvsps reported error 11

... which is really helpful. Well it turns out that you can run cvsps outside of git-cvsimport and get this all to work. Here's roughly what you need to do:

export CVSROOT=[your cvsroot]
cvsps -x --norc -u -A [your cvsmodule] > /tmp/cvsps.out
cd /path/to/your/git/repos
git-cvsimport -o git_cvs_head_branch_name -P /tmp/cvsps.out [your cvsmodule]
So in my case, I do:
export CVSROOT=/local/wonder/cvs
cvsps -x --norc -u -A Wonder > /tmp/cvsps.out
cd /local/wonder/git
git-cvsimport -o Wonder_HEAD_Branch -P /tmp/cvsps.out Wonder

The key here is that we run cvsps manually, saving its output, then run git-cvsimport with a -P passing it the name of the cvsps output. This allows us to manually ignore cvsps errors rather than have them kill git-cvsimport.

Note that by default gitcvs-import will use "master" as your head branch, which can cause problems with later merges. It's best to keep all CVS branches in their own git branch and don't touch them except with git-cvsimport.

VS 2008

Visual Studio 2008 has some really slick features ... Check out the Javascript interpreter stuff in particular. I plan to steal liberally from this for WOLips.

Handy shell things

stick these in your ~/.bash_profile:
alias grep='grep --color=auto'
function ps() {
 /bin/ps $* | sed -E $'s#[0-9]+#\e[31m&\e[0m#'
}
The grep one will make your grep matches change color, which makes it a lot easier to find in a line. The ps one makes the pid turn red, which is really useful if you have processes with really long commandlines (like a WO app):

LDAP EOF Adaptor

A word of warning if you're working with LDAP/JNDI and creating EOModels on them. LDAP supports multi-value attributes, which means you could have, for instance, multiple "uid"'s (Apple provides for multiple "short names", for instance). In your EOModel, you should be careful about how you define the types of these values. If there is one value, it will return a String. If there are multiple values, it will return an NSArray, so you should declare the value type to be "java.lang.Object" and check for what you're getting back when you call the method.

Apache Directory Studio

Apache Directory Studio -- actually "just worked" and is even sort of nice (I mean, for an Eclipse-based app). Totally shocked. So if you need an LDAP browser, check it out.

JAAS + Kerberos Auth

After digging around for quite a while (the docs on this TOTALLY suck), if you need to do Kerberos authentication from your app with JAAS + 1.5:

package your.app;

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

public class KerberosAuth {
 public static void main(String[] args) {
   String userName = "myuser@MYREALM.COM";
   char[] password = "mypassword".toCharArray();

   System.setProperty("java.security.auth.login.config", KerberosAuth.class.getResource("/kerberos.conf").toExternalForm());
   System.setProperty("java.security.krb5.realm", "MYREALM.COM");
   System.setProperty("java.security.krb5.kdc", "mykdc.MYREALM.COM");
   try {
     LoginContext lc = new LoginContext("primaryLoginContext", new UserNamePasswordCallbackHandler(userName, password));
     lc.login();
     System.out.println("KerberosAuth.main: " + lc.getSubject());
   }
   catch (LoginException le) {
     le.printStackTrace();
   }
 }

 public static class UserNamePasswordCallbackHandler implements CallbackHandler {
   private String _userName;
   private char[] _password;

   public UserNamePasswordCallbackHandler(String userName, char[] password) {
     _userName = userName;
     _password = password;
   }

   public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
     for (Callback callback : callbacks) {
       if (callback instanceof NameCallback && _userName != null) {
         ((NameCallback) callback).setName(_userName);
       }
       else if (callback instanceof PasswordCallback && _password != null) {
         ((PasswordCallback) callback).setPassword(_password);
       }
     }
   }
 }
}
put kerberos.conf (in this example) inside your Sources folder with the contents:
primaryLoginContext {
 com.sun.security.auth.module.Krb5LoginModule required client=true useTicketCache=false;
};

"java.security.auth.login.config" can map to a File or a URL. Annoyingly it appears that it cannot map to the actual contents of the file -- it has to be loaded from a URL, which seems completely stupid to me (as does just about all of the JAAS/GSSAPI api's, which were clearly written by "cryptography engineers" and not "normal humans").

I have not tried this against Active Directory, yet, just Open Directory ... I'll be trying Active Directory soon and post with info. One thing that's actually pretty damn slick is that if you need this in a Java client application (i.e. non-web-app) you can set "useTicketCache=true" and it will actually use your Kerberos info from the OS ticket cache, which means it actually does proper single sign-on. You can also combine this (which is what we plan to do) with SPNEGO using mod_krb5 on Apache. So you can have mod_krb5 do SPNEGO auth (and just read the user info from the remote user header), and use this as a fall-back if the user is not using a SPNEGO-compatible browser.