/* * @(#)RealmDumpServlet.java 1.16 97/10/25 * * Copyright (c) 1995-1997 Sun Microsystems, Inc. All Rights Reserved. * * This software is the confidential and proprietary information of Sun * Microsystems, Inc. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Sun. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING * THIS SOFTWARE OR ITS DERIVATIVES. * * CopyrightVersion 1.0 */ import java.io.*; import java.security.Principal; import java.security.acl.Acl; import java.security.acl.Group; import java.util.*; import javax.servlet.http.*; import com.sun.server.realm.*; /** * This class dumps realm data, using either the application interface * (invoke a static main method) or an HTTP servlet interface. * * By default it dumps all the data in all of the realms. Options may * be used to control what data is dumped. There is no default realm, * though many sites are expected to want to use only a single realm. * *

Options supported by the application interface are: * * * * * * * * * * * * * * * * * * * * * * * * * * *
-html The output of this command is a single HTML document.
-realm realmName Dumps only the named realm.
-acls Dumps acls in the selected domain(s).
-groups Dumps groups in the selected domain(s).
-users Dumps users in the selected domain(s).
* * @version 1.16 * @author David Brownell */ public class RealmDumpServlet extends HttpServlet { // // Option flags settable from the command line or via HTML // forms ... initial values are specified by the language spec. // Keep these in alphabetical order; helps keep it in sync // with the documentation above. // private boolean useHTML; private String onlyRealm; private boolean doACLs; private boolean doGroups; private boolean doUsers; // // Statistics gathered during this run: // private int realmCount; private int userCount; private int groupCount; private int aclCount; /** * Standard command line entry point; the command line options * are processed, and output is generated on the command's standard * output stream. * * @param argv command line argument vector */ public static void main (String argv []) { RealmDumpServlet d = new RealmDumpServlet (); PrintWriter out = new PrintWriter (System.out, true); try { // d.mutateGroups (); d.doCommand (argv, out); out.flush (); } catch (Exception e) { e.printStackTrace (); } } ////// BEGIN DEBUGGING CODE private void mutateGroups () { try { Realm r = Realm.get ("adminRealm"); Vector principals = new Vector (); int seed = 0x7fffffff & new Random ().nextInt (); // // Collect all the principals in the realm. // for (Enumeration e = r.getUserNames (); e.hasMoreElements (); ) principals.addElement (r.getUser ((String) e.nextElement ())); for (Enumeration e = r.getGroupNames (); e.hasMoreElements (); ) principals.addElement (r.getGroup ((String) e.nextElement ())); // // Create a new group, and populate it with some of the // earlier members of the list of principals. // Group g = r.addGroup ("G-" + seed); for (int i = 0; i < principals.size () && i < 31; i++) { if ((seed & (0x01 << i)) != 0) g.addMember ((Principal) principals.elementAt (i)); } // // See if we can delete the last group in the list ... // the main reason we couldn't is if we happened to add // it to the group we just created. We want to know that // fails cleanly anyway, so this is "always" interesting. // Object o = principals.lastElement (); if (o instanceof Group) { g = (Group) o; r.removeGroup (g.toString ()); } } catch (Exception e) { e.printStackTrace (System.err); } } ////// END DEBUG CODE /** * General purpose entry to the realm dumping facility. * * @param argv command line argument vector * @param out where to emit the text. */ public void doCommand (String argv [], PrintWriter out) throws IOException { // // Parse the "command line" arguments. // for (int i = 0; i < argv.length; i++) { if (argv [i].equals ("-html")) useHTML = true; else if (argv [i].equals ("-acls")) doACLs = true; else if (argv [i].equals ("-groups")) doGroups = true; else if (argv [i].equals ("-users")) doUsers = true; else if (argv [i].equals ("-realm") && ((i + 1) < argv.length)) onlyRealm = argv [++i]; else throw new IllegalArgumentException (argv [i]); } emit (out, null, true); } /** * HTTP GET operations sent to this servlet producs a form which * may be used to control which realm is being looked at. */ // XXX note bug: state is servlet-global, could be changed by // a concurrent request. We really want to be able to associate // servlet instances with sessions, and have per-session state. public void doGet ( HttpServletRequest request, HttpServletResponse response ) throws IOException { useHTML = true; response.setContentType ("text/html"); emit (response.getWriter (), request.getServletPath (), false); } /** * POSTing to this servlet uses form data to provide a detailed * look at a single realm. */ // XXX note bug: state is servlet-global, could be changed by // a concurrent request. public void doPost ( HttpServletRequest request, HttpServletResponse response ) throws IOException { String temp; useHTML = true; response.setContentType ("text/html"); temp = request.getParameter ("showACLs"); if (temp != null && temp.equalsIgnoreCase ("true")) doACLs = true; temp = request.getParameter ("showGroups"); if (temp != null && temp.equalsIgnoreCase ("true")) doGroups = true; temp = request.getParameter ("showUsers"); if (temp != null && temp.equalsIgnoreCase ("true")) doUsers = true; temp = request.getParameter ("onlyRealm"); if (temp != null && !temp.equalsIgnoreCase ("")) onlyRealm = temp; emit (response.getWriter (), request.getServletPath (), true); } // emit HTML page, maybe with POST form, maybe with data private void emit ( PrintWriter out, String relativePostURL, boolean dumpAll ) throws IOException { // // Start emitting the output. // if (useHTML) { out.println (""); out.println ("REALM DUMP"); out.println (""); out.println ("

");
	}

	//
	// The servlet is driven by posting to a form which it
	// generates.  Emit the form if it's appropriate.
	//
	if (relativePostURL != null) {
	    Realm	r;

	    out.print ("
\n"); out.println (header (1, "Choose Realm Data")); out.println ("
"); out.println (""); out.println (""); out.println (""); out.println (""); out.println ("
"); out.println ("Show Users
"); out.println ("Show Groups
"); out.println ("Show ACLs
You may choose only one realm to look at,"); out.println ("or leave the selection empty to look at them all:"); out.println ("
"); out.println ("
"); out.println (""); out.println (""); out.println ("
"); out.println ("

"); } if (dumpAll) try { out.println (header (1, "Realm Dump")); out.println ("Produced: " + new Date ()); // // If we're dumping only one realm, that's a simple case. // if (onlyRealm != null) { Realm r = Realm.get (onlyRealm); dump (r, out); // // Enumerate all the realms, dumping each one. // } else { Vector realmNames = new Vector (); Realm r; for (Enumeration e = Realm.getRealmNames (); e.hasMoreElements (); realmNames.addElement (e.nextElement ())) continue; if (useHTML) { out.println ("
");
		}

		for (int i = 0; i < realmNames.size (); i++) {
		    r = Realm.get ((String) realmNames.elementAt (i));
		    if (useHTML) {
			out.println ("

"); out.println (anchor ("Realm", r)); out.println ("
");
		    }
		    dump (r, out);
		}
		if (useHTML)
		    out.println ("

");
	    }

	    out.println (header (2, "Statistics"));
	    out.println ("  Realms = " + realmCount);
	    out.println ("  Users = " + userCount);
	    out.println ("  Groups = " + groupCount);
	    out.println ("  ACLs = " + aclCount);

	} catch (Exception e) {
	    e.printStackTrace (out);
	    if (useHTML)
		out.println ("
"); return; } if (useHTML) out.println (""); } /******************/ /* HTML HEADERS */ /******************/ // // By default, everything is preformatted as if on a TTY style // display ... in HTML mode we escape briefly back to HTML so we // break the visual monotony of a single font, size, and style. // private String header (int level, String label) { StringBuffer buf = new StringBuffer (); if (useHTML) buf.append (""); else { for (int i = 1; i < level; i++) buf.append ("*"); if (level != 1) buf.append ("\t"); } buf.append (label); if (useHTML) buf.append ("
");

	return buf.toString ();
    }

    // emit a header, using an anchor if this is HTML style
    private String header (int level, String anchor, String label)
    {
	if (useHTML)
	    return header (level, anchor + label);
	else
	    return header (level, label);
    }

    // href to a realm or its usr/group/acl section
    private String href (String type, String realm, String label)
    {
	if (!useHTML)
	    return label;
	return "" + label + "";
    }

    // anchor for the realm/users/groups/acls section of a named realm
    private String anchor (String type, Realm r)
    {
	if (!useHTML)
	    return "";
	return "";
    }

    // anchor for a particular user/group/acl section of a named realm
    private String anchor (String type, Realm r, String value)
    {
	if (!useHTML)
	    return "";
	return "";
    }


    /******************/
    /* DUMP REALM     */
    /******************/

    private void dump (Realm r, PrintWriter out) throws Exception
    {
	boolean		doAll;

	if (doUsers || doGroups || doACLs)
	    doAll = false;
	else
	    doAll = true;

	out.println (header (2, "Dump of Realm = " + r));
	if (useHTML) {
	    out.println ("

You may click on links to: "); if (doAll || doUsers) out.println (href ("Users", r.getName (), "individual Users,")); if (doAll || doGroups) out.println (href ("Groups", r.getName (), "Groups of users,")); if (doAll || doACLs) out.println (href ("ACLs", r.getName (), "Access Control Lists")); out.println (" in this realm.

");
	}

	if (doAll || doUsers) {
	    out.println (header (3, anchor ("Users", r),
		"Individual Users"));
	    dumpUsers (r, out);
	}

	if (doAll || doGroups) {
	    out.println (header (3, anchor ("Groups", r),
		"Groups of Users (and other Groups)"));
	    dumpGroups (r, out);
	}

	if (doAll || doACLs) {
	    out.println (header (3, anchor ("ACLs", r),
		"Access Control Lists (ACLs)"));
	    dumpACLs (r, out);
	}
	realmCount++;
    }


    /******************/
    /* DUMP SETS      */
    /******************/

    // It'd be nice to sort each of these sets, supposing they
    // all fit neatly into memory at once.  Even Sun's NIS maps
    // can support that -- only 10K users.  Probably it'd be a
    // reasonable default.

    private void dumpUsers (Realm r, PrintWriter out) throws Exception
    {
	Enumeration	names = r.getUserNames ();
	User		u;

	while (names.hasMoreElements ()) {
	    u = r.getUser ((String) names.nextElement ());
	    dump (u, out);
	}
    }

    private void dumpGroups (Realm r, PrintWriter out) throws Exception
    {
	Enumeration	names = r.getGroupNames ();
	Group		g;

	while (names.hasMoreElements ()) {
	    g = r.getGroup ((String) names.nextElement ());
	    dump (g, out);
	}
    }

    private void dumpACLs (Realm r, PrintWriter out) throws Exception
    {
	Enumeration	names = r.getAclNames ();
	Acl		acl;

	while (names.hasMoreElements ()) {
	    acl = r.getAcl ((String) names.nextElement ());
	    dump (acl, out);
	}
    }


    /******************/
    /* DUMP INSTANCES */
    /******************/

    // We should support page-relative links (#tag) for this ...
    // "NAME=#Group-RealmName-GroupName" is tough when we can't
    // get the realm for a java.security.acl.Group or an ACL ...

    private void dump (User u, PrintWriter out) throws Exception
    {
	String		temp;

	out.print ("    User:  " + u);
	// if (u instanceof SharedPasswordUser)
	    // out.print (", password = " + (String) u.getAuthInfo ());

	if ((temp = (String) u.getAttribute ("fullname")) != null)
	    out.print (", fullname = \"" + temp + "\"");

	// XXX we also want a link to public_html ...

	if ((temp = (String) u.getAttribute ("home")) != null)
	    out.print (", home = " + temp);

	out.println ();

	userCount++;
    }

    private void dump (Principal p, PrintWriter out) throws Exception
    {
	if (p instanceof Group)
	    out.println ("      Group:      " + p);
	else
	    out.println ("      Principal:  " + p);
    }

    private void dump (Group g, PrintWriter out) throws Exception
    {
	out.println ("    Group:  " + g);

	for (Enumeration e = g.members (); e.hasMoreElements (); ) {
	    dump ((Principal) e.nextElement (), out);
	}

	// XXX prefer to dump members as links to user objects

	groupCount++;
    }

    private void dump (Acl acl, PrintWriter out) throws Exception
    {
	out.println ("    ACL:  " + acl.getName ());
	out.println ("          " + acl);

	// XXX dump owners
	// XXX dump each entry

	aclCount++;
    }
}