/* IRISBuilderTask.java This class is intended to dump the Ganymede datastore to the ARL:UT IRIS system. Created: 27 September 2004 Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2014 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Web site: http://www.arlut.utexas.edu/gash2 Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package arlut.csd.ganymede.gasharl; import arlut.csd.ganymede.common.Invid; import arlut.csd.ganymede.server.DBLogEvent; import arlut.csd.ganymede.server.DBObject; import arlut.csd.ganymede.server.Ganymede; import arlut.csd.ganymede.server.GanymedeBuilderTask; import arlut.csd.ganymede.server.PasswordDBField; import arlut.csd.ganymede.server.ServiceNotFoundException; import arlut.csd.ganymede.server.ServiceFailedException; import arlut.csd.ganymede.common.SchemaConstants; import arlut.csd.Util.PathComplete; import arlut.csd.Util.SharedStringBuffer; import arlut.csd.Util.VectorUtils; import arlut.csd.Util.FileOps; import java.util.*; import java.text.*; import java.io.*; import java.rmi.*; /*------------------------------------------------------------------------------ class IRISBuilderTask ------------------------------------------------------------------------------*/ /** * * This class is intended to dump the Ganymede datastore to the ARL:UT * IRIS system. * * @author Jonathan Abbey jonabbey@arlut.utexas.edu * */ public class IRISBuilderTask extends GanymedeBuilderTask { static final boolean debug = false; private static String path = null; private static String dnsdomain = null; private static String buildScript = null; private static Runtime runtime = null; // --- private Date now = null; private SharedStringBuffer result = new SharedStringBuffer(); private Invid normalCategory = null; /* -- */ public IRISBuilderTask(Invid _taskObjInvid) { // set the taskDefObjInvid in GanymedeBuilderTask so // we can look up option strings taskDefObjInvid = _taskObjInvid; } /** * <p>This method is intended to be overridden by subclasses of * GanymedeBuilderTask.</p> * * <p>This method runs with a dumpLock obtained for the builder * task.</p> * * <p>Code run in builderPhase1() can call enumerateObjects() and * baseChanged(). Note that the Enumeration of objects returned by * enumerateObjects() is only valid and should only be consulted * while builderPhase1 is running.. as soon as builderPhase1 * returns, the dumpLock used to make the enumerateObjects() call * safe to use is relinquished, and any Enumerations obtained will * then be unsafe to depend on.</p> * * @return true if builderPhase1 made changes necessitating the * execution of builderPhase2. */ public boolean builderPhase1() { PrintWriter out, out2; boolean needBuild = false; /* -- */ Ganymede.debug("build: IRISBuilderTask writing files"); if (path == null) { path = System.getProperty("ganymede.builder.output"); if (path == null) { throw new RuntimeException("IRISBuilder not able to determine output directory."); } path = PathComplete.completePath(path); } now = null; if (baseChanged(SchemaConstants.UserBase) || /* User netgroups */ baseChanged((short) 270)) { if (debug) { Ganymede.debug("Need to build IRIS output"); } needBuild = true; out = null; try { out = openOutFile(path + "iris_sync.txt", "iris"); } catch (IOException ex) { throw new RuntimeException("IRISBuilderTask.builderPhase1(): couldn't open iris_sync.txt file: " + ex); } if (out != null) { try { for (DBObject user: getObjects(SchemaConstants.UserBase)) { if (user_in_netgroup(user, "IRIS-users")) { if (debug) { System.err.println("Writing out IRIS user " + user.getLabel()); } write_iris(out, user); } } } finally { out.close(); } } if (false) { try { out2 = openOutFile(path + "iris_sync_test.txt", "iris"); } catch (IOException ex) { throw new RuntimeException("IRISBuilderTask.builderPhase1(): couldn't open iris_sync_test.txt file: " + ex); } if (out2 != null) { try { for (DBObject user: getObjects(SchemaConstants.UserBase)) { if (user_in_maillist(user, "IRIS-test-users")) { if (debug) { System.err.println("Writing out IRIS test user " + user.getLabel()); } write_iris(out2, user); } } } finally { out2.close(); } } } } if (debug) { Ganymede.debug("IRISBuilderTask builderPhase1 completed"); } return needBuild; } /** * * This method runs after this task's dumpLock has been * relinquished. This method is intended to be used to finish off a * build process by running (probably external) code that does not * require direct access to the database. * * builderPhase2 is only run if builderPhase1 returns true. * */ public boolean builderPhase2() { File file; /* -- */ Ganymede.debug("build: IRISBuilderTask running build"); if (buildScript == null) { buildScript = System.getProperty("ganymede.builder.scriptlocation"); buildScript = PathComplete.completePath(buildScript); buildScript = buildScript + "irisbuilder"; } int resultCode = -999; // a resultCode of 0 is success file = new File(buildScript); boolean startedOk = false; if (file.exists()) { if (runtime == null) { runtime = Runtime.getRuntime(); } Process process = null; /* -- */ try { resultCode = FileOps.runProcess(buildScript); startedOk = true; } catch (IOException ex) { Ganymede.debug("Couldn't exec buildScript (" + buildScript + ") due to IOException: " + ex); } catch (InterruptedException ex) { Ganymede.debug("Failure during exec of buildScript (" + buildScript + "): " + ex); } } else { Ganymede.debug(buildScript + " doesn't exist, not running external IRIS build script"); } if (resultCode != 0) { String path = ""; try { path = file.getCanonicalPath(); } catch (IOException ex) { path = buildScript; } String message = "Error encountered running sync script \"" + path + "\" for the IRISBuilderTask." + "\n\nI got a result code of " + resultCode + " when I tried to run it."; DBLogEvent event = new DBLogEvent("externalerror", message, null, null, null, null); Ganymede.log.logSystemEvent(event); if (startedOk) { throw new ServiceFailedException("gashbuilder returned a failure code: " + resultCode); } else { if (!file.exists()) { throw new ServiceNotFoundException("Couldn't find " + path); } else { throw new ServiceNotFoundException("Couldn't run " + path); } } } if (debug) { Ganymede.debug("IRISBuilderTask builderPhase2 completed"); } return true; } /** * <p>Returns true if the user object is a member of the given * netgroup name, either through direct membership or through * transitive closure.</p> */ public boolean user_in_netgroup(DBObject user, String netgroupName) { Hashtable table = new Hashtable(); Vector<Invid> netgroups = (Vector<Invid>) user.getFieldValuesLocal(userSchema.NETGROUPS); for (Invid netgroupInvid: netgroups) { DBObject netgroup = getObject(netgroupInvid); if (netgroup_or_parent_equals(netgroup, netgroupName)) { return true; } } return false; } public boolean user_in_maillist(DBObject user, String mailListName) { if (user == null || mailListName == null) { return false; } Invid mailListInvid = findLabeledObject(mailListName, emailListSchema.BASE); if (mailListInvid == null) { return false; } return user_in_maillist(user, mailListInvid); } public boolean user_in_maillist(DBObject user, Invid mailListInvid) { DBObject mailListObject = getObject(mailListInvid); Vector<Invid> members = (Vector<Invid>) mailListObject.getFieldValuesLocal(emailListSchema.MEMBERS); for (Invid inv: members) { if (inv.equals(user.getInvid())) { return true; } else if (inv.getType() == emailListSchema.BASE && user_in_maillist(user, inv)) { return true; } } return false; } /** * <p>Recursive method to determine whether a given netgroup (or its containers) * matches netgroupName.</p> */ public boolean netgroup_or_parent_equals(DBObject netgroup, String netgroupName) { String name = (String) netgroup.getFieldValueLocal(userNetgroupSchema.NETGROUPNAME); if (name.equals(netgroupName)) { return true; } Vector<Invid> netgroups = (Vector<Invid>) netgroup.getFieldValuesLocal(userNetgroupSchema.OWNERNETGROUPS); for (Invid netgroupInvid: netgroups) { if (netgroup_or_parent_equals(getObject(netgroupInvid), netgroupName)) { return true; } } return false; } /** * <p>Write a line to the IRIS sync file</p> * * <p>The lines look like this:</p> * * <p>username|invid|badge number|md5Crypt|plaintext</p> * * <p>example:</p> * * <p>broccol|3:627|4297|$1$BoVVyEwQ$wbJYuEHeN/vdMeARLFhG0/|NotMyRealPass</p> */ private void write_iris(PrintWriter out, DBObject userObject) { String username, invidString, badge, md5Crypt, plaintext; /* -- */ username = (String) userObject.getFieldValueLocal(userSchema.USERNAME); invidString = userObject.getInvid().toString(); badge = (String) userObject.getFieldValueLocal(userSchema.BADGE); PasswordDBField passField = userObject.getPassField(userSchema.PASSWORD); if (passField == null) { md5Crypt = "*"; plaintext = "*"; } else { md5Crypt = passField.getMD5CryptText(); plaintext = passField.getPlainText(); } StringBuilder output = new StringBuilder(); output.append(escapeString(username)); output.append("|"); output.append(invidString); output.append("|"); output.append(escapeString(badge)); output.append("|"); output.append(escapeString(md5Crypt)); output.append("|"); output.append(escapeString(plaintext)); out.println(output.toString()); } /** * We can't have any | characters in passwords in the iris_sync.txt * file we generate, since we use | chars as field separators in * this file. Make sure that we backslash any such chars. */ private String escapeString(String in) { if (in == null) { return ""; } StringBuilder buffer = new StringBuilder(); char[] ary = in.toCharArray(); /* -- */ // do it for (int i = 0; i < ary.length; i++) { if (ary[i] == '|') { buffer.append("\\|"); } else if (ary[i] == '\\') { buffer.append("\\\\"); } else { buffer.append(ary[i]); } } return buffer.toString(); } }