//
// Don't forget to remove the HTML tags above and at the end of the file
import javax.naming.*;
import javax.naming.directory.*;
import java.io.*;

import java.util.Properties;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.w3c.dom.*;
// Sample code of LDAP authentication with MCIT custom error messages
// written by Dmitriy Kashchenko, IDM team
public class LDAPBind {
  // const
  private final static String ldapserver = "ldap://ldap.ent.med.umich.edu:636/";
  private final static String baseSearchContext = "dc=med,dc=umich,dc=edu";
  private final static String messageFile = "ldapautherrors.xml";
  private final static String NOT_FOUND_MESS = "(-601)";

  private Hashtable errors = null;
  // main Directory Context to use
  private DirContext ctx = null;
  private Properties env = new Properties();

  // the constructor
  public LDAPBind () {
    env.put (Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
    env.put (Context.PROVIDER_URL,ldapserver);
    // Specify SSL
    env.put(Context.SECURITY_PROTOCOL, "ssl");
    // DO NOT deref aliases
    // puts too much load on LDAP servers
    env.put("java.naming.ldap.derefAliases", "never");
  }

  private void parseFile (String fname) throws Exception {
    errors = new Hashtable();

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

    DocumentBuilder db = null;
    db = dbf.newDocumentBuilder();
    Document doc = db.parse(new InputSource (new FileReader(fname)));
      
    NodeList list = doc.getElementsByTagName("message");
    if (list != null) {

      for (int i=0; i < list.getLength(); i++) {
        Node node = list.item(i);

        if (node.hasAttributes()) {
          NamedNodeMap attrs = node.getAttributes();
          Node attr = attrs.getNamedItem("index");
          if (attr != null) {
            String index = attr.getNodeValue();
            for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
               if (child.getNodeType() == Node.TEXT_NODE) errors.put (index,child.getNodeValue());
            }
          }

        }
      }
    }
  }

  private String getErrorMessage (Exception ex) {
    String rc = ex.toString();
    for (Enumeration e = errors.keys(); e.hasMoreElements();) {
      String index = (String)e.nextElement();
      if (rc.indexOf(index) != -1) {
        rc = (String)errors.get(index);
        break;
      }
    }
    return rc;
  }

  // standard bind procedure - lookup for login id first, then bind
  // expects unencrypted password
  public boolean bind (String loginID, String password) throws Exception {
    ctx = new InitialDirContext(env);
    System.out.println("Anonymous bind successull.");
    SearchResult proxy = search(loginID);
    ctx.close();  // could clean up the connection to LDAP server
    if (proxy != null) {
      // Authenticate
      env.put(Context.SECURITY_AUTHENTICATION,"simple");
      env.put(Context.SECURITY_PRINCIPAL,proxy.getName()+","+baseSearchContext);
      env.put(Context.SECURITY_CREDENTIALS,password);
      
      ctx = new InitialDirContext(env);
      System.out.println("Bind successfull.");
      return true;
    } else {
      throw new Exception(NOT_FOUND_MESS);
    }
  }

  // releases resorces
  public void close () throws Exception {
    if (ctx != null) ctx.close();
  }

  // this example assumes that only one entry is found
  // which is supposed to be happening in unique name environment
  private SearchResult search (String uniqueName) throws Exception {
    SearchResult entry = null;
    // Create Search Constarains
    SearchControls constraints = new SearchControls ();
    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
    //set dereference aliases
    constraints.setDerefLinkFlag(false);
    // define return attributes
    String[] returnedAttributes = { "uid", "sn", "givenName", "groupMembership" };
    constraints.setReturningAttributes(returnedAttributes);
    // construct the LDAP filter
    String filter = "uid=" + uniqueName;

    NamingEnumeration nl = ctx.search(baseSearchContext,filter,constraints);
    if (nl.hasMore()) {
      entry = (SearchResult)nl.next();
    }
    return entry;
  }

  // example for a single value attribute
  private String getAttribute (SearchResult entry, String name) throws Exception {
    javax.naming.directory.Attributes attrs = entry.getAttributes ();
    Attribute attr = attrs.get (name);
    if (attr != null) return (String)attr.get();
    else return "false";
  }

  public static void main(String[] args) {

    if (args.length < 3) {
      System.err.println("Usage: username password accountname ...");
      System.err.println("       where accountname is the name of account to read the attributes from.");
      System.exit(255);
    }  
    // command line arguments:
    // 	  first - proxy login id;
    // 	  second - proxy password
    //    third  - account to read the attributes from
    String loginID = args[0];
    String password = args[1];
    String account = args[2];

    LDAPBind obj = new LDAPBind ();
    try {
      obj.parseFile (messageFile);
      if (obj.bind (loginID,password)) {
        SearchResult entry = obj.search(account);
        if (entry != null) {
	  System.out.println ("found: "+entry.getName());
          String attrName = "loginDisabled";
          String value = obj.getAttribute(entry,attrName);
    	  System.out.println(attrName+": ["+value+"]");
        } else {
	  System.out.println(account+" not found");
        }
        obj.close ();
      }
      else
        System.err.println("unable to bind as "+loginID);
    } catch (Exception e) {
      System.err.println(obj.getErrorMessage(e));
    }
  }
}
//