package org.springframework.ldap.odm.tools; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import javax.naming.AuthenticationException; import javax.naming.CommunicationException; import javax.naming.Context; import javax.naming.NameClassPair; import javax.naming.NameNotFoundException; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import java.io.PrintStream; import java.util.Hashtable; /** * A simple utility to list LDAP directory schema. * <p> * <code>SchemaViewer</code> takes the following flags: * <ul> * <li><code>-h,--help<</code> Print this help message</li> * <li><code>-l,--url <arg></code> Ldap url of directory to bind to (defaults to ldap://127.0.0.1:389)</li> * <li><code>-u,--username <arg></code> DN to bind with (defaults to "")</li> * <li><code>-p,--password <arg></code> Password to bind with (defaults to "")</li> * <li><code>-o,--objectclass <arg></code> Object class name or ? for all. Print object class schema</li> * <li><code>-a,--attribute <arg></code> Attribute name or ? for all. Print attribute schema</li> * <li><code>-s,--syntax <arg></code> Syntax or ? for all. Print syntax</li> * </ul> * * Only one of <code>-a</code>, <code>-o</code> and <code>-s</code> should be specified. * * @author Paul Harvey <paul.at.pauls-place.me.uk> * */ public final class SchemaViewer { private static final String DEFAULT_URL="ldap://127.0.0.1:389"; private enum Flag { URL("l", "url"), USERNAME("u", "username"), PASSWORD("p", "password"), OBJECTCLASS("o", "objectclass"), ATTRIBUTE("a", "attribute"), SYNTAX("s", "syntax"), HELP("h", "help"), ERROR("e", "error"); private String shortName; private String longName; private Flag(String shortName, String longName) { this.shortName = shortName; this.longName = longName; } public String getShort() { return shortName; } public String getLong() { return longName; } @Override public String toString() { return String.format("short=%1$s, long=%2$s", shortName, longName); } } private enum SchemaContext { OBJECTCLASS("ClassDefinition"), ATTRIBUTE("AttributeDefinition"), SYNTAX("SyntaxDefinition"); private String value; private SchemaContext(String value) { this.value = value; } public String getValue() { return value; } @Override public String toString() { return String.format("value=%1$s", value); } } private static final Options DEFAULT_OPTIONS = new Options(); static { DEFAULT_OPTIONS.addOption(Flag.URL.getShort(), Flag.URL.getLong(), true, "Ldap url (defaults to " + DEFAULT_URL + ")"); DEFAULT_OPTIONS.addOption(Flag.USERNAME.getShort(), Flag.USERNAME.getLong(), true, "DN to bind with (defaults to \"\")"); DEFAULT_OPTIONS.addOption(Flag.PASSWORD.getShort(), Flag.PASSWORD.getLong(), true, "Password to bind with defaults to \"\")"); DEFAULT_OPTIONS.addOption(Flag.OBJECTCLASS.getShort(), Flag.OBJECTCLASS.getLong(), true, "Object class name or ? for all. Print object class schema"); DEFAULT_OPTIONS.addOption(Flag.ATTRIBUTE.getShort(), Flag.ATTRIBUTE.getLong(), true, "Attribute name or ? for all. Print attribute schema"); DEFAULT_OPTIONS.addOption(Flag.SYNTAX.getShort(), Flag.SYNTAX.getLong(), true, "Syntax OID or ? for all. Print attribute syntax"); DEFAULT_OPTIONS.addOption(Flag.HELP.getShort(), Flag.HELP.getLong(), false, "Print this help message"); DEFAULT_OPTIONS.addOption(Flag.ERROR.getShort(), Flag.ERROR.getLong(), false, "Send output to standard error"); } /** * Not to be instantiated. */ private SchemaViewer() { } private static void printAttrs(Attributes attrs) throws NamingException { NamingEnumeration<? extends Attribute> attrsEnum = attrs.getAll(); while (attrsEnum.hasMore()) { Attribute currentAttr = attrsEnum.next(); outstream.print(String.format("%1$s:", currentAttr.getID())); NamingEnumeration<?> valuesEnum = currentAttr.getAll(); while (valuesEnum.hasMoreElements()) { outstream.print(String.format("%1$s ", valuesEnum.nextElement().toString())); } outstream.println(); } } private static void printObject(String contextName, String schemaName, DirContext schemaContext) throws NameNotFoundException, NamingException { DirContext oContext = (DirContext)schemaContext.lookup(contextName + "/" + schemaName); outstream.println("NAME:" + schemaName); printAttrs(oContext.getAttributes("")); } private static void printSchema(String contextName, DirContext schemaContext) throws NameNotFoundException, NamingException { outstream.println(); NamingEnumeration<NameClassPair> schemaList = schemaContext.list(contextName); while (schemaList.hasMore()) { NameClassPair ncp = schemaList.nextElement(); printObject(contextName, ncp.getName(), schemaContext); outstream.println(); } outstream.println(); } private static void print(String optionValue, String contextName, DirContext schemaContext) throws NameNotFoundException, NamingException { if (optionValue.equals(WILDCARD)) { printSchema(contextName, schemaContext); } else { printObject(contextName, optionValue, schemaContext); } } private static PrintStream outstream=System.out; private final static String WILDCARD = "?"; public static void main(String[] argv) { CommandLineParser parser = new PosixParser(); CommandLine cmd = null; try { cmd = parser.parse(DEFAULT_OPTIONS, argv); } catch (ParseException e) { System.out.println(e.getMessage()); System.exit(1); } if (cmd.hasOption(Flag.HELP.getShort())) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp(120, SchemaViewer.class.getSimpleName(), null, DEFAULT_OPTIONS, null, true); System.exit(0); } if (cmd.hasOption(Flag.ERROR.getShort())) { outstream=System.err; } String url = cmd.getOptionValue(Flag.URL.getShort(), DEFAULT_URL); String user = cmd.getOptionValue(Flag.USERNAME.getShort(), ""); String pass = cmd.getOptionValue(Flag.PASSWORD.getShort(), ""); Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.PROVIDER_URL, url); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); if (user != null) { env.put(Context.SECURITY_PRINCIPAL, user); } if (pass != null) { env.put(Context.SECURITY_CREDENTIALS, pass); if (user == null) { System.err.println("You must specify a user if you specify a password"); System.exit(1); } } try { DirContext context = new InitialDirContext(env); DirContext schemaContext = context.getSchema(""); if (cmd.hasOption(Flag.OBJECTCLASS.getShort())) { print(cmd.getOptionValue(Flag.OBJECTCLASS.getShort()), SchemaContext.OBJECTCLASS.getValue(), schemaContext); } if (cmd.hasOption(Flag.ATTRIBUTE.getShort())) { print(cmd.getOptionValue(Flag.ATTRIBUTE.getShort()), SchemaContext.ATTRIBUTE.getValue(), schemaContext); } if (cmd.hasOption(Flag.SYNTAX.getShort())) { print(cmd.getOptionValue(Flag.SYNTAX.getShort()), SchemaContext.SYNTAX.getValue(), schemaContext); } } catch (AuthenticationException e) { System.err.println(String.format("Failed to bind to ldap server at %1$s", url)); } catch (CommunicationException e) { System.err.println(String.format("Failed to contact ldap server at %1$s", url)); } catch (NameNotFoundException e) { System.err.println(String.format("Can't find object %1$s", e.getMessage())); } catch (NamingException e) { System.err.println(e.toString()); } } }