diff --git a/hsarback/src/de/hsadmin/cliClientConnector/ArgumentParser.java b/hsarback/src/de/hsadmin/cliClientConnector/ArgumentParser.java new file mode 100644 index 0000000..8f69ffb --- /dev/null +++ b/hsarback/src/de/hsadmin/cliClientConnector/ArgumentParser.java @@ -0,0 +1,357 @@ +package de.hsadmin.cliClientConnector; + +import java.lang.reflect.Method; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import de.hsadmin.cliClientConnector.CLIClientConnectorServlet.FunctionNotKnownException; +import de.hsadmin.cliClientConnector.CLIClientConnectorServlet.UnknownModuleException; +import de.hsadmin.core.model.Entity; +import de.hsadmin.core.model.ModuleInterface; + +/** + * Parses Arguments for the CLI Client Connector Servlet + * + * @author Christof Donat + * + */ +public class ArgumentParser { + /// I am working for this Servlet instance + private CLIClientConnectorServlet master; + private DateFormat df = new SimpleDateFormat( "yyyy-MM-dd"); + + public ArgumentParser(CLIClientConnectorServlet master) { + this.master = master; + } + + private String getUsageString() { + return " [ (-W name=value|--globalWhere:name=value) ...]\n"+ + " [ (-S name=value|--globalSet:name=value) ...]\n"+ + " [ (-D displayspec|--globalDisplay=displayspec) ...]\n"+ + " [ (-c module.function|--call=module.function)\n" + + " [ (-w name=value|--where:name=value) ...]\n"+ + " [ (-s name=value|--set:name=value) ...]\n"+ + " [ (-d displayspec|--display:displayspec) ...]\n"+ + " [ oids ...] ] ]\n"+ + "\n"+ + "(" + CLIClientConnectorServlet.version + ")\n"; + } + + private ArrayList getMethodList(Object o) { + Method[] meths = o.getClass().getMethods(); + ArrayList methodlist = new ArrayList(); + + for( int i = 0; i < meths.length; i++ ) { + Method m = meths[i]; + String n = m.getName(); + if( n.startsWith("get") && !n.equals("getNew") ) { + String fn = m.getName().substring(3).toLowerCase(); + if( fn.equals("class") ) continue; + if (m.getParameterTypes().length == 0) { + methodlist.add(m); + } + } + } + + return methodlist; + } + + private Hashtable getValues(Object o, ArrayList methodlist, ArrayListfieldNames, boolean deep, ArrayList foundObjects) { + Hashtable row = new Hashtable(); + int i, j; + + if( foundObjects == null ) foundObjects = new ArrayList(); + + for( i = 0; i < methodlist.size(); i++ ) { + Method m = methodlist.get(i); + try { + String name = fieldNames.get(i); + String type = m.getReturnType().getCanonicalName(); + String val = ""; + + Object value = null; + try { + value = m.invoke(o); + } catch( Exception e ) { + e.printStackTrace(); + } + + if( value == null ) + val = ""; + else if( type.equals("java.lang.String") ) + val = (String)value; + else if( type.equals("java.lang.Integer") ) + val = String.valueOf((Integer)value); + else if( type.equals("java.lang.Long") ) + val = String.valueOf((Long)value); + else if( type.equals("java.lang.Boolean") ) + val = String.valueOf((Boolean)value); + else if( type.equals("java.util.Date") ) { + val = df.format((Date)value); + } else if( type.equals("java.util.Set") ) { + val = ""; + } else try { + Entity v = (Entity)value; + val = v.createStringKey(); + if( deep && !foundObjects.contains(v) ) { + foundObjects.add(v); + ArrayList fieldNamesDeep = new ArrayList(); + ArrayList methodlistDeep = getMethodList(v); + for( j = 0; j < methodlistDeep.size(); j++ ) { + fieldNamesDeep.add(methodlistDeep.get(j).getName().substring(3).toLowerCase()); + } + Hashtable tmp = getValues(v,methodlistDeep,fieldNamesDeep,deep,foundObjects); + Enumeration keys = tmp.keys(); + while(keys.hasMoreElements()) { + try { + String k = (String)keys.nextElement(); + row.put(name+"."+k, tmp.get(k)); + } catch( Exception e ) { + e.printStackTrace(); + } + } + } + } catch(ClassCastException e) { + val = value.toString(); + } + if (val != null) row.put(name, val); + } catch (Exception e) { + e.printStackTrace(); + } + } + return row; + } + + private String formatObjectsWithoutDisplay(int j, ArrayList fieldNames, ArrayListcolumnWidths, ArrayList > rows) { + int i; + StringBuffer rval = new StringBuffer(); + + for( i = 0; i < fieldNames.size(); i++ ) { + StringBuffer name = new StringBuffer(fieldNames.get(i)); + int fieldwidth = columnWidths.get(i); + while( name.length() < fieldwidth ) + name.append(" "); + rval.append(name.toString()+((i < fieldNames.size()-1)?" | ":"")); + } + rval.append("\n"); + for( i = 0; i < j; i++ ) + rval.append("-"); + rval.append("\n"); + + for( j = 0; j < rows.size(); j++ ) { + for( i = 0; i < fieldNames.size(); i++ ) { + StringBuffer value = new StringBuffer(rows.get(j).get(fieldNames.get(i))); + int fieldwidth = columnWidths.get(i); + while( value.length() < fieldwidth ) + value.append(" "); + rval.append(value.toString()+((i < fieldNames.size()-1)?" | ":"")); + } + rval.append("\n"); + } + return rval.toString(); + } + + private String formatObjectsWithDisplay(ArrayList > rows, String displayDef) { + StringBuffer rval = new StringBuffer(); + for( int j = 0; j < rows.size(); j++) { + String rv = displayDef; + Enumeration fNames = rows.get(j).keys(); + while( fNames.hasMoreElements() ) { + String f = (String)fNames.nextElement(); + rv = rv.replaceAll("\\$\\{"+f+"\\}", rows.get(j).get(f)); + } + rv = rv.replaceAll("\\\\n", "\n"); + rv = rv.replaceAll("\\\\t", "\t"); + rv = rv.replaceAll("\\\\(.)?", "$1"); + rval.append(rv); + } + return rval.toString(); + } + + /** + * format Objects for the output as a Human readable table + * + * @param objects + * format these objects + * + * @return humanReadableString + * a string with the human readable representation of the objects + */ + public String formatObjects(List objects, String displayDef) { + if( objects.size() == 0 ) return ""; + if( objects.get(0) == null ) return ""; + ArrayList methodlist = getMethodList(objects.get(0)); + ArrayListcolumnWidths = new ArrayList(); + ArrayListfieldNames = new ArrayList(); + ArrayList > rows = + new ArrayList >(); + int i, j; + + for( i = 0; i < methodlist.size(); i++ ) { + Method m = methodlist.get(i); + String n = m.getName(); + String fn = n.substring(3).toLowerCase(); + fieldNames.add(fn); + columnWidths.add(n.length()+3); + } + + for( j = 0; j < objects.size(); j++ ) { + Object o = objects.get(j); + Hashtable row = getValues(o,methodlist,fieldNames,(displayDef != null), null); + for( i = 0; i < fieldNames.size(); i++ ) { + String val = row.get(fieldNames.get(i)); + if( val != null && i < columnWidths.size() && val.length()+3 > columnWidths.get(i) ) + columnWidths.set(i, val.length()+3); + } + rows.add(row); + } + + if( displayDef == null ) { + j = 0; + for( i = 0; i < columnWidths.size(); i++ ) { + j += columnWidths.get(i)+2; + } + j -= 2; + + return formatObjectsWithoutDisplay(j, fieldNames, columnWidths, rows); + } else + return formatObjectsWithDisplay(rows, displayDef); + } + + /** + * The main parser function. Parses the Parameters, uses the master to call + * the functions and returns a formatet output. + * + * @param arguments + * @return humanReadableObjectsString + */ + public String parse(List arguments, ModuleInterface module) { + String rval = ""; + + String currentModule = null; + String currentFunction = null; + Map currentWhere = new Hashtable(); + Map currentSet = new Hashtable(); + ArrayList currentOIDs = new ArrayList(); + + Map globalWhere = new Hashtable(); + Map globalSet = new Hashtable(); + String display = null; + String globalDisplay = null; + + for( int i = 0; i < arguments.size(); i++ ) { + String arg = arguments.get(i); + + if( arg.equals("-c") || arg.startsWith("--call:") ) { + // call + if( currentModule != null ) { + try { + // execute the last call now + rval += formatObjects(master.callModule( + currentModule, + currentFunction, + currentWhere, + currentSet, + currentOIDs, + module),(display==null)?globalDisplay:display); + } catch (FunctionNotKnownException e) { + rval += "Function unknown: "+currentModule+'.'+currentFunction+"\n"; + } catch (UnknownModuleException e) { + rval += "Module unknown: "+currentModule+"\n"; + } + } + // reset parameters for next call + currentWhere = new HashMap(); + currentWhere.putAll(globalWhere); + currentSet = new HashMap(); + currentSet.putAll(globalSet); + currentOIDs = new ArrayList(); + display = null; + + // set the new call + boolean isShortParam = arg.equals("-c"); + String calldef = isShortParam?arguments.get(i+1):arg.substring(7); + + if( calldef != null ) { + String[] split = calldef.split("[.]", 2); + currentModule = split[0]; + currentFunction = split[1]; + } + if( isShortParam ) i++; + } else if( arg.equals("-w") || arg.startsWith("--where:") ) { + // where + boolean isShortParam = arg.equals("-w"); + String wheredef = isShortParam?arguments.get(i+1):arg.substring(8); + if( wheredef != null ) { + String[] split = wheredef.split("[=]", 2); + currentWhere.put(split[0],split[1]); + } + if( isShortParam ) i++; + } else if( arg.equals("-W") || arg.startsWith("--globalWhere:") ) { + // global where + boolean isShortParam = arg.equals("-W"); + String gwheredef = isShortParam?arguments.get(i+1):arg.substring(14); + if( gwheredef != null ) { + String[] split = gwheredef.split("[=]", 2); + globalWhere.put(split[0],split[1]); + } + if( isShortParam ) i++; + } else if( arg.equals("-s") || arg.startsWith("--set:") ) { + // set + boolean isShortParam = arg.equals("-s"); + String setdef = isShortParam?arguments.get(i+1):arg.substring(6); + if( setdef != null ) { + String[] split = setdef.split("[=]", 2); + currentSet.put(split[0],split[1]); + } + if( isShortParam ) i++; + } else if( arg.equals("-S") || arg.startsWith("--globalSet:") ) { + // global set + boolean isShortParam = arg.equals("-S"); + String gsetdef = isShortParam?arguments.get(i+1):arg.substring(12); + if( gsetdef != null ) { + String[] split = gsetdef.split("[=]", 2); + globalSet.put(split[0],split[1]); + } + if( isShortParam ) i++; + } else if( arg.equals("-d") || arg.startsWith("--display:") ) { + // display + boolean isShortParam = arg.equals("-d"); + display = isShortParam?arguments.get(i+1):arg.substring(10); + if( isShortParam ) i++; + } else if( arg.equals("-D") || arg.startsWith("--globalDisplay:") ) { + // global display + boolean isShortParam = arg.equals("-D"); + globalDisplay = isShortParam?arguments.get(i+1):arg.substring(16); + if( isShortParam ) i++; + } else if( arg.equals("-h") || arg.equals("--help") ) { + return getUsageString(); + } else if( arg.startsWith("-") ) { + return "unknown option '"+arg+"'\n"+getUsageString(); + } else currentOIDs.add(arg); + } + if( currentModule != null ) { + try { + rval += formatObjects(master.callModule( + currentModule, + currentFunction, + currentWhere, + currentSet, + currentOIDs, + module),(display==null)?globalDisplay:display); + } catch (FunctionNotKnownException e) { + rval += "Function unknown: "+currentModule+'.'+currentFunction+"\n"; + } catch (UnknownModuleException e) { + rval += "Module unknown: "+currentModule+"\n"; + } + } + return rval; + } +} diff --git a/hsarback/src/de/hsadmin/cliClientConnector/Base64.java b/hsarback/src/de/hsadmin/cliClientConnector/Base64.java new file mode 100644 index 0000000..ea5e7fe --- /dev/null +++ b/hsarback/src/de/hsadmin/cliClientConnector/Base64.java @@ -0,0 +1,244 @@ +/* + * @(#)Base64.java 1.5 03/12/19 + * + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + +package de.hsadmin.cliClientConnector; + +/** + * Static methods for translating Base64 encoded strings to byte arrays + * and vice-versa. + * + * @author Josh Bloch + * @version 1.5, 12/19/03 + * @see Preferences + * @since 1.4 + */ +class Base64 { + /** + * Translates the specified byte array into a Base64 string as per + * Preferences.put(byte[]). + */ + static String byteArrayToBase64(byte[] a) { + return byteArrayToBase64(a, false); + } + + /** + * Translates the specified byte array into an "aternate representation" + * Base64 string. This non-standard variant uses an alphabet that does + * not contain the uppercase alphabetic characters, which makes it + * suitable for use in situations where case-folding occurs. + */ + static String byteArrayToAltBase64(byte[] a) { + return byteArrayToBase64(a, true); + } + + private static String byteArrayToBase64(byte[] a, boolean alternate) { + int aLen = a.length; + int numFullGroups = aLen/3; + int numBytesInPartialGroup = aLen - 3*numFullGroups; + int resultLen = 4*((aLen + 2)/3); + StringBuffer result = new StringBuffer(resultLen); + char[] intToAlpha = (alternate ? intToAltBase64 : intToBase64); + + // Translate all full groups from byte array elements to Base64 + int inCursor = 0; + for (int i=0; i> 2]); + result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); + result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]); + result.append(intToAlpha[byte2 & 0x3f]); + } + + // Translate partial group if present + if (numBytesInPartialGroup != 0) { + int byte0 = a[inCursor++] & 0xff; + result.append(intToAlpha[byte0 >> 2]); + if (numBytesInPartialGroup == 1) { + result.append(intToAlpha[(byte0 << 4) & 0x3f]); + result.append("=="); + } else { + // assert numBytesInPartialGroup == 2; + int byte1 = a[inCursor++] & 0xff; + result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); + result.append(intToAlpha[(byte1 << 2)&0x3f]); + result.append('='); + } + } + // assert inCursor == a.length; + // assert result.length() == resultLen; + return result.toString(); + } + + /** + * This array is a lookup table that translates 6-bit positive integer + * index values into their "Base64 Alphabet" equivalents as specified + * in Table 1 of RFC 2045. + */ + private static final char intToBase64[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + /** + * This array is a lookup table that translates 6-bit positive integer + * index values into their "Alternate Base64 Alphabet" equivalents. + * This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045. + * This alternate alphabet does not use the capital letters. It is + * designed for use in environments where "case folding" occurs. + */ + private static final char intToAltBase64[] = { + '!', '"', '#', '$', '%', '&', '\'', '(', ')', ',', '-', '.', ':', + ';', '<', '>', '@', '[', ']', '^', '`', '_', '{', '|', '}', '~', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '?' + }; + + /** + * Translates the specified Base64 string (as per Preferences.get(byte[])) + * into a byte array. + * + * @throw IllegalArgumentException if s is not a valid Base64 + * string. + */ + static byte[] base64ToByteArray(String s) { + return base64ToByteArray(s, false); + } + + /** + * Translates the specified "aternate representation" Base64 string + * into a byte array. + * + * @throw IllegalArgumentException or ArrayOutOfBoundsException + * if s is not a valid alternate representation + * Base64 string. + */ + static byte[] altBase64ToByteArray(String s) { + return base64ToByteArray(s, true); + } + + private static byte[] base64ToByteArray(String s, boolean alternate) { + byte[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt); + int sLen = s.length(); + int numGroups = sLen/4; + if (4*numGroups != sLen) + throw new IllegalArgumentException( + "String length must be a multiple of four."); + int missingBytesInLastGroup = 0; + int numFullGroups = numGroups; + if (sLen != 0) { + if (s.charAt(sLen-1) == '=') { + missingBytesInLastGroup++; + numFullGroups--; + } + if (s.charAt(sLen-2) == '=') + missingBytesInLastGroup++; + } + byte[] result = new byte[3*numGroups - missingBytesInLastGroup]; + + // Translate all full groups from base64 to byte array elements + int inCursor = 0, outCursor = 0; + for (int i=0; i> 4)); + result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); + result[outCursor++] = (byte) ((ch2 << 6) | ch3); + } + + // Translate partial group, if present + if (missingBytesInLastGroup != 0) { + int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt); + int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt); + result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); + + if (missingBytesInLastGroup == 1) { + int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt); + result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); + } + } + // assert inCursor == s.length()-missingBytesInLastGroup; + // assert outCursor == result.length; + return result; + } + + /** + * Translates the specified character, which is assumed to be in the + * "Base 64 Alphabet" into its equivalent 6-bit positive integer. + * + * @throw IllegalArgumentException or ArrayOutOfBoundsException if + * c is not in the Base64 Alphabet. + */ + private static int base64toInt(char c, byte[] alphaToInt) { + int result = alphaToInt[c]; + if (result < 0) + throw new IllegalArgumentException("Illegal character " + c); + return result; + } + + /** + * This array is a lookup table that translates unicode characters + * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) + * into their 6-bit positive integer equivalents. Characters that + * are not in the Base64 alphabet but fall within the bounds of the + * array are translated to -1. + */ + private static final byte base64ToInt[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + /** + * This array is the analogue of base64ToInt, but for the nonstandard + * variant that avoids the use of uppercase alphabetic characters. + */ + private static final byte altBase64ToInt[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, + 2, 3, 4, 5, 6, 7, 8, -1, 62, 9, 10, 11, -1 , 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 12, 13, 14, -1, 15, 63, 16, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 17, -1, 18, 19, 21, 20, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 22, 23, 24, 25 + }; + + public static void main(String args[]) { + int numRuns = Integer.parseInt(args[0]); + int numBytes = Integer.parseInt(args[1]); + java.util.Random rnd = new java.util.Random(); + for (int i=0; i> componentmap; + private Map componentDescriptions; + private ArgumentParser parser; + + /** + * Servlet initialization + */ + public void init(ServletConfig cfg) { + // init ticket validator + String validateURL = cfg.getInitParameter("proxyValidateUrl"); + String serviceURL = cfg.getInitParameter("proxyServiceUrl"); + TicketValidator.getInstance().initialize(validateURL, serviceURL); + // find components + String cstring = cfg.getInitParameter("Components"); + String[] components = cstring.split(","); + + componentmap = new HashMap>(); + componentDescriptions = new HashMap(); + for (int i = 0; i < components.length; i++) { + // get everything for this component and create an entry. + try { + // component class + String classname = cfg.getInitParameter("ComponentClass_" + + components[i]); + if (classname == null) + throw new NullPointerException( + "no class name found for Component " + + components[i]); + Class cls = Class.forName(classname); + componentmap.put(components[i], cls); + // description + String descr = cfg.getInitParameter("ComponentDescription_" + + components[i]); + if (descr != null) + componentDescriptions.put(components[i], descr); + else + componentDescriptions.put(components[i], ""); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + // TODO: get username, password from http session + parser = new ArgumentParser(this); + } + + /** + * set values to a given entity Object + * + * @param o + * the entity Object + * @param set + * Hashtable with names and values that sould be changed in o + */ + private void setValues(Object o, Map set, ModuleInterface module) { + Iterator keys = set.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + String[] ns = key.split("[.]", 2); + Object realO = o; + for (int i = 0; i < ns.length - 1; i++) { + Method[] m = realO.getClass().getMethods(); + boolean oFound = false; + for (int j = 0; j < m.length; j++) { + if (m[j].getName().toLowerCase().equals( + "get" + ns[i].toLowerCase())) { + try { + realO = m[j].invoke(realO, (Object[]) null); + oFound = (realO != null); + break; + } catch (Exception e) { + new TechnicalException(e); + } + } + } + if (!oFound) + break; + } + Method[] m = realO.getClass().getMethods(); + String value = set.get(key); + for (int j = 0; j < m.length; j++) { + if (!m[j].getName().toLowerCase().equals( + "set" + ns[ns.length - 1].toLowerCase())) + continue; + String type = m[j].getParameterTypes()[0].getCanonicalName(); + try { + if (type.equals("java.lang.String")) + m[j].invoke(o, value); + else if (type.equals("java.lang.Integer")) + m[j].invoke(o, Integer.parseInt(value)); + else if (type.equals("java.lang.Long")) + m[j].invoke(o, Long.parseLong(value)); + else if (type.equals("java.lang.Boolean")) + m[j].invoke(o, Boolean.valueOf(value)); + else if (type.equals("java.util.Date")) { + DateFormat df = DateFormat.getInstance(); + m[j].invoke(o, df.parse(value)); + } else { + Method m2 = module.getClass().getMethod( + "findByString", Class.class, String.class); + Object entity = + m2.invoke(module, m[j].getParameterTypes()[0], value); + if (entity != null) + m[j].invoke(o, entity); + else + throw new BusinessException( + "not object found for '" + value + "'"); + } + } catch (Exception e) { + throw new TechnicalException(e); // TODO: this needs to be + // more specific for + // some cases + } + } + } + } + + private String hasGetter(Class eType, String name) { + String rval = null; + String[] ns = name.split("[.]", 2); + String n1 = ns[0]; + Method meth = null; + + for (Method m : eType.getMethods()) { + String n = m.getName(); + if (n.startsWith("get")) { + String fn = m.getName().substring(3).toLowerCase(); + if (fn != "class" && fn.equals(n1)) { + meth = m; + rval = fn; + break; + } + } + } + if (meth != null) { + Class returnType = meth.getReturnType(); + if (rval != null && ns.length > 1 && meth != null) + return hasGetter(returnType, ns[1]); + if (returnType.getCanonicalName().startsWith("de.hsadmin.mods")) { + try { + if (returnType.getMethod("getName") != null) { + return rval + ".name"; + } + } catch (Exception e) { + // no method found + } + } + } + return rval; + } + + /** + * builds a query from a where clause and some objectIDs. + * + * @param eType + * The class of the Entity Object for which the string should be + * built. We need this to call the static method + * "createQueryFromStringKey" + * @param where + * Hashtable with where parameters. Only objects which match all + * where parameters are found + * @param oids + * Only objects with one of these object IDs are found + * + * @return queryString a query string that can be used to select the + * required Objects + */ + private String buildQuery(Class eType, Map where, + ArrayList oids) { + String rval = ""; + + boolean first = true; + Iterator wkeys = where.keySet().iterator(); + while (wkeys.hasNext()) { + String k = (String) wkeys.next(); + String kname = hasGetter(eType, k); + if (kname != null) { + rval += (first ? "" : " and ") + + "(obj." + Entity.escapeString(kname) + " = '" + Entity.escapeString(where.get(k)) + "')"; + first = false; + } + } + + String rv = ""; + if (oids != null) + try { + Method m; + m = eType.getMethod("createQueryFromStringKey", String.class); + + first = true; + for (String s : oids) { + rv += (first ? "" : " or ") + "(" + + (String) m.invoke(eType, s) + ")"; + first = false; + } + if (rv != "" && rval != "") + rval = rval + " and (" + rv + ")"; + else if (rv != "") + rval = rv; + } catch (Exception e) { + throw new TechnicalException(e); + } + + return (rval == "") ? null : rval; + } + + public class FunctionNotKnownException extends Exception { + private static final long serialVersionUID = -6330015688609717838L; + } + + public class UnknownModuleException extends Exception { + private static final long serialVersionUID = 696641072107896601L; + } + + private Object callAdd(Class cls, Map set, ModuleInterface module) { + Transaction transaction = module.getTransaction(); + transaction.beginTransaction(); + try { + Method m = module.getClass().getMethod("add", Entity.class); + Object o = cls.newInstance(); + setValues(o, set, module); + m.invoke(module, o); + transaction.commitTransaction(); + return null; + } catch (Exception e) { + transaction.rollbackTransaction(); + // TODO: this needs to be more specific, but how? + throw new TechnicalException(e); + } + } + + @SuppressWarnings("unchecked") + private List callSearch(Class cls, Map where, + ArrayList oids, ModuleInterface module) { + try { + Method m = module.getClass().getMethod("search", Class.class, + String.class, String.class); + return (List) m.invoke(module, cls, + buildQuery(cls, where, oids), null); + } catch (Exception e) { + throw new TechnicalException(e); // TODO: this needs to be more + // specific, but how? + } + } + + // / checks wheather all 'oids' are in 'list' + private void checkOids(List list, List oids) { + List oidsNotFound = new ArrayList(); + for (String id : oids) { + boolean found = false; + for (Entity e : list) { + String foundKey = e.createStringKey(); + if (foundKey.equals(id)) { + found = true; + break; + } + } + if (!found) + oidsNotFound.add(id); + } + if (oidsNotFound.size() > 0) { + throw new OidsNotFoundException(oids); + } + } + + @SuppressWarnings("unchecked") + private List callUpdate(Class cls, Map where, + ArrayList oids, Map set, + ModuleInterface module) { + // better safe than sorry - alsd hso same behavior as UNIX rm + if (where.size() == 0 && oids.size() == 0) + throw new BusinessException( + "better safe than sorry - 'update' needs a -w/--where-query or object id"); + Transaction tx = module.getTransaction(); + tx.beginTransaction(); + try { + Method m = module.getClass().getMethod("search", Class.class, + String.class, String.class); + List list = (List) m.invoke(module, cls, + buildQuery(cls, where, oids), null); + checkOids(list, oids); + Method m2 = module.getClass().getMethod("update", Entity.class); + for (int i = 0; i < list.size(); i++) { + Entity entity = list.get(i); + tx.detach(entity); + setValues(entity, set, module); + m2.invoke(module, entity); + } + tx.commitTransaction(); + } catch (Exception e) { + tx.rollbackTransaction(); + // TODO: this needs to be more specific, but how? + throw new TechnicalException(e); + } + return null; + } + + @SuppressWarnings("unchecked") + private void callDelete(Class cls, Map where, + ArrayList oids, ModuleInterface module) { + // better safe than sorry - also same behavior as UNIX rm + if (where.size() == 0 && oids.size() == 0) + throw new BusinessException( + "better safe than sorry - 'update' needs a -w/--where-query or object id"); + Transaction tx = module.getTransaction(); + tx.beginTransaction(); + try { + Method m = + module.getClass().getMethod("search", Class.class, String.class, String.class); + List list = + (List) m.invoke(module, cls, buildQuery(cls, where, oids), null); + checkOids(list, oids); + Method m2 = module.getClass().getMethod("delete", Entity.class); + for (int i = 0; i < list.size(); i++) { + Object o = list.get(i); + m2.invoke(module, o); + } + tx.commitTransaction(); + } catch (Exception e) { + tx.rollbackTransaction(); + // TODO: this needs to be more specific, but how? + throw new TechnicalException(e); + } + } + + /** + * Call one of the EntitySessions methods + * + * @param moduleName + * Defines the Entity class that will be given to the + * EntitySession + * @param function + * Defines the method that will be called. The function can be + * "add", "search", "update" and "delete". + * @param where + * Reduces the set of Entity objects which the function operates + * on to those matched by the where parameters. Only for + * "search", "update" and "delete". Will be ignored otherwise. + * @param set + * Set these values on all Objects - only for "add" and "update". + * Will be ignored otherwise. + * @param oids + * Works on Objects with these IDs. Only for "search", "update" + * and "delete". Will be ignored for "add". + * @return foundObjects + * @throws FunctionNotKnownException + */ + public List callModule(String moduleName, String function, + Map where, Map set, + ArrayList oids, ModuleInterface module) + throws FunctionNotKnownException, UnknownModuleException { + List rval = new ArrayList(); + + // handle calls to the virtual module "modules" + if (moduleName.equals("modules")) { + // only search, no manipulation is possible + if (function.equals("search")) { + Iterator m = componentDescriptions.keySet().iterator(); + while (m.hasNext()) { + String mn = (String) m.next(); + rval.add(new ModuleModel(mn, componentDescriptions.get(mn))); + } + } else if (function.equals("version")) { + rval.add(new VersionModel(version)); + } else { + throw new FunctionNotKnownException(); + } + return rval; + } + + // find the component for the named module + Class cls = componentmap.get(moduleName); + if (cls == null) + throw (new UnknownModuleException()); + + // call the appropriate methods + if (function.equals("add")) + rval.add(callAdd(cls, set, module)); + else if (function.equals("search")) { + List r = callSearch(cls, where, oids, module); + if (r != null) + return r; + } else if (function.equals("update")) { + List r = callUpdate(cls, where, oids, set, module); + if (r != null) + return callUpdate(cls, where, oids, set, module); + } else if (function.equals("delete")) { + callDelete(cls, where, oids, module); + } else + throw (new FunctionNotKnownException()); + return rval; + } + + /** + * handle put method + */ + public void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException { + try { + ModuleInterface module; + + // check login + String auth = req.getHeader("authorization"); + if (auth == null) { + // no login information at all - get it + resp.setHeader("WWW-Authenticate", + "Basic realm=\"CLIClientConnector\""); + resp.getOutputStream().println("login Failed."); + resp.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return; + } else { + // parse login information + String[] a = auth.split(" ", 2); + String ticket = a[1]; + byte[] decoded = Base64.base64ToByteArray(ticket.trim()); + StringBuffer s = new StringBuffer(); + for (int i = 0; i < decoded.length; i++) + s.append((char) decoded[i]); + a = s.toString().split(":", 2); + // try to log in + String login = a[0]; + ticket = a[1]; + module = null; + try { + if (TicketAuthentication.getInstance().login(login, ticket)) { + + // login successful + module = new GenericModuleImpl(new Transaction(login)); + + // read arguments + BufferedReader read = req.getReader(); + + String tmpbuf; + ArrayList arguments = new ArrayList(); + while ((tmpbuf = read.readLine()) != null) { + arguments.add(tmpbuf); + } + + // actually handle the request and write result to output + String output = parser.parse(arguments, module); + resp.getWriter().write(output); + } else { + resp.addHeader("X-hsadmin-error", "authentication failed"); + } + } catch (Exception e) { + resp.addHeader("X-hsadmin-error", "exception: " + e.getMessage()); + } finally { + if (module != null && module.getTransaction() != null) { + module.getTransaction().close(); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/hsarback/src/de/hsadmin/cliClientConnector/ModuleModel.java b/hsarback/src/de/hsadmin/cliClientConnector/ModuleModel.java new file mode 100644 index 0000000..b6bfc8b --- /dev/null +++ b/hsarback/src/de/hsadmin/cliClientConnector/ModuleModel.java @@ -0,0 +1,19 @@ +package de.hsadmin.cliClientConnector; + +public class ModuleModel { + private String name; + private String description; + + public ModuleModel(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return this.name; + } + + public String getDescription() { + return this.description; + } +} diff --git a/hsarback/src/de/hsadmin/cliClientConnector/OidsNotFoundException.java b/hsarback/src/de/hsadmin/cliClientConnector/OidsNotFoundException.java new file mode 100644 index 0000000..e56c87e --- /dev/null +++ b/hsarback/src/de/hsadmin/cliClientConnector/OidsNotFoundException.java @@ -0,0 +1,25 @@ +package de.hsadmin.cliClientConnector; + +import java.util.List; + + +@SuppressWarnings("serial") +public class OidsNotFoundException + extends RuntimeException +{ + static private String oidsAsString(List oids) + { + StringBuilder oidsBuilder = new StringBuilder(); + for ( String id: oids ) + oidsBuilder.append(", " + id); + if ( oidsBuilder.length() > 0 ) + return oidsBuilder.substring(2); + throw new RuntimeException( "an empty list of missing OIDS does not make sense" ); + } + + public OidsNotFoundException(List oids) + { + super("OIDS not found: " + oidsAsString(oids)); + } + +} diff --git a/hsarback/src/de/hsadmin/cliClientConnector/TechnicalException.java b/hsarback/src/de/hsadmin/cliClientConnector/TechnicalException.java new file mode 100644 index 0000000..e8fb368 --- /dev/null +++ b/hsarback/src/de/hsadmin/cliClientConnector/TechnicalException.java @@ -0,0 +1,50 @@ +package de.hsadmin.cliClientConnector; + +import java.lang.reflect.InvocationTargetException; +import java.sql.SQLException; + +import javax.persistence.RollbackException; + +public class TechnicalException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public TechnicalException(Throwable e) { + super(extractCauseMessage(e)); + } + + private static String extractCauseMessage(Throwable e) { + if (e.getMessage() != null && !(e instanceof RollbackException)) { + return e.getMessage(); + } + else if (e instanceof InvocationTargetException || e instanceof RollbackException) { + String sqlState = null; + Throwable cause = e.getCause(); + Throwable prev = null; + while (cause != null && cause != prev) { + prev = cause; + cause = prev.getCause(); + if ((cause == null || cause == prev) + && prev instanceof SQLException) { + SQLException sqlExc = (SQLException) prev; + sqlState = sqlExc.getSQLState(); + cause = sqlExc.getNextException(); + } + } + if (cause == null) + cause = prev; + if (cause != null) + return composeMessage(sqlState, cause.getMessage()); + if (e instanceof InvocationTargetException) { + return composeMessage(sqlState, ((InvocationTargetException) e).getTargetException().getMessage()); + } + } + return e.getClass() + " without detail message"; + } + + private static String composeMessage(String sqlState, String message) { + if (sqlState != null) return "SQLSTATE[" + sqlState + "] " + message; + return message; + } + +} diff --git a/hsarback/src/de/hsadmin/cliClientConnector/VersionModel.java b/hsarback/src/de/hsadmin/cliClientConnector/VersionModel.java new file mode 100644 index 0000000..ea18f79 --- /dev/null +++ b/hsarback/src/de/hsadmin/cliClientConnector/VersionModel.java @@ -0,0 +1,14 @@ +package de.hsadmin.cliClientConnector; + +public class VersionModel +{ + private String version; + + public VersionModel( String version) { + this.version = version; + } + + public String getVersion() { + return this.version; + } +} diff --git a/hsarback/src/de/hsadmin/core/model/AbstractModuleImpl.java b/hsarback/src/de/hsadmin/core/model/AbstractModuleImpl.java new file mode 100644 index 0000000..8ca6da3 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/AbstractModuleImpl.java @@ -0,0 +1,361 @@ +package de.hsadmin.core.model; + +import java.util.LinkedList; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import de.hsadmin.core.qserv.EntityProcessorFactory; +import de.hsadmin.core.qserv.Processor; +import de.hsadmin.core.qserv.QueueTask; +import de.hsadmin.mods.user.UnixUser; + +/** + * Generic implementation of EntitySession interface. + * + * @author peter + */ +public abstract class AbstractModuleImpl implements ModuleInterface { + + private static final long serialVersionUID = 2693948730004920437L; + private static Log log = LogFactory.getLog(AbstractModuleImpl.class); + + private UnixUser loginUser; + private Transaction transaction; + + public void construct(Transaction tx) { + transaction = tx; + } + + /** + * apply access restriction to JPA-QL condition. + * @param entityClass + * @param loginUser + * @param condition + * @return + */ + private String restrict(Class entityClass, UnixUser loginUser, String condition) { + String restriction = Entity.restriction(entityClass, loginUser); + if (restriction == null) + return condition; + if (condition != null && condition.length() > 0) + condition = "(" + condition + ") AND (" + restriction + ")"; + else + condition = restriction; + return condition; + } + + public Transaction getTransaction() { + return transaction; + } + + public Entity initialize(Entity newEntity) + throws AuthorisationException { + newEntity.initialize(transaction.getEntityManager(), getLoginUser()); + return newEntity; + } + + public Entity add(Entity newEntity) throws HSAdminException { + // get the user who is logged in + UnixUser loginUser = getLoginUser(); + + // create record in database + log.debug("merging?"); + newEntity.complete(transaction.getEntityManager(), loginUser); + try { + transaction.getEntityManager().persist(newEntity); + } catch (Throwable exc) { + log.error("exception: " + exc); + } finally { + log.debug("finally"); + } + log.debug("merged"); + + // check rights + if (!newEntity.isWriteAllowedFor(loginUser)) + throw new AuthorisationException(loginUser, "add", newEntity); + + // generically create the processor + EntityProcessorFactory procFact = createProcessorFactory(newEntity.getClass()); + if (procFact == null) { + log.debug("no procFact found :-("); + return newEntity; + } + log.debug("procFact found :-)"); + Processor proc = procFact.createCreateProcessor(transaction.getEntityManager(), newEntity); + + // queue the processor + queueProcessor(proc, loginUser, newEntity, "hinzugefuegt"); + + // return the added entity + return newEntity; + } + + public Entity find(Class entityClass, Object key) throws HSAdminException { + Entity entity = transaction.getEntityManager().find(entityClass, key); + + // check rights + UnixUser loginUser = getLoginUser(); + if (!entity.isReadAllowedFor(loginUser)) + throw new AuthorisationException(loginUser, "add", entity); + + return entity; + } + + public Entity findByString(Class entityClass, String key) throws HSAdminException { + // find a static method which creates the query + java.lang.reflect.Method method = null; + try { + method = entityClass.getDeclaredMethod("createQueryFromStringKey", String.class); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + method = null; + } + + Entity entity; + if (method == null) + entity = transaction.getEntityManager().find(entityClass, key); + else { + // get the query expression from the static method (query part after + // WHERE) + String query; + try { + query = (String) method.invoke(null, key); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // perform the query + List result = search(entityClass, query, null); + if (result.size() > 1) + throw new javax.persistence.NonUniqueResultException(); + if (result.size() == 0) + return null; + entity = result.get(0); + + // this was maybe thought as a fallback + // but is wrong when the above result is empty due to accessibility + // entity = em.find(entityClass, key); + } + + // return (checking rights already done in search) + return entity; + } + + public List search(Class entityClass, String condition, String orderBy) throws HSAdminException { + // restrict query + UnixUser loginUser = getLoginUser(); + condition = restrict(entityClass, loginUser, condition); + + // get the entities name (query part from FROM to WHERE) + // TODO: beware SQL injections!!! + javax.persistence.Entity entityAnnot = entityClass.getAnnotation(javax.persistence.Entity.class); + String queryString = "SELECT obj FROM " + entityAnnot.name() + " obj"; + if (condition != null && condition.length() > 0) + queryString += " WHERE " + condition; + + // Fix problem with queries WHERE .. AND (FALSE) -- pe + if (condition != null && condition.contains("AND (FALSE)")) { + return new LinkedList(); + } + + if (orderBy != null) { + queryString += " "; + queryString += orderBy; + } + + // set parameters + EntityManager entityManager = transaction.getEntityManager(); + entityManager.clear(); + + Query query = entityManager.createQuery(queryString); + setQueryParameter(query, queryString, "loginUser", loginUser); + setQueryParameter(query, queryString, "loginUserName", loginUser.getName()); + setQueryParameter(query, queryString, "loginUserPac", loginUser.getPac()); + + // do query + try { + List res = query.getResultList(); + List ret = new LinkedList(); + + // remove entities where login user has no access rights + for (Object entity : res) { + if (entity instanceof Entity) { + Entity returnedEntity = (Entity) entity; + if (returnedEntity.isReadAllowedFor(getLoginUser())) { + ret.add(returnedEntity); + } + } + } + + // return clean result + return ret; + } catch (Exception ex) { + return null; + } + } + + public Entity update(Entity existingEntity) throws HSAdminException { + // get the user who is logged in + UnixUser loginUser = getLoginUser(); + + // update record in database + log.debug("merging:"); + try { + existingEntity = existingEntity.merge(transaction.getEntityManager(), loginUser); + } catch (Throwable exc) { + log.error("exception: " + exc); + throw new RuntimeException(exc); + } finally { + log.debug("finally"); + } + log.debug("merged!"); + + // check rights + if (!existingEntity.isWriteAllowedFor(loginUser)) + throw new AuthorisationException(loginUser, "update", + existingEntity); + + // generically create the processor + EntityProcessorFactory procFact = + createProcessorFactory(existingEntity.getClass()); + if (procFact != null) { + log.debug("creating processor"); + Processor proc = procFact.createUpdateProcessor(transaction.getEntityManager(), existingEntity); + + // queue the processor + queueProcessor(proc, loginUser, existingEntity, "aktualisiert"); + } + + // return the merged entity + return existingEntity; + } + + public void delete(Entity existingEntity) throws HSAdminException { + // get the user who is logged in + UnixUser user = getLoginUser(); + + // re-attach the entity + log.debug("merging:"); + try { + existingEntity = transaction.getEntityManager().find(existingEntity.getClass(), existingEntity.id()); + } catch (Throwable exc) { + log.error("exception: " + exc); + throw new RuntimeException(exc); + } finally { + log.debug("finally"); + } + log.debug("merged!"); + + // check rights + if (!existingEntity.isWriteAllowedFor(loginUser)) + throw new AuthorisationException(loginUser, "add", existingEntity); + + // delete record in database + log.debug("deleting:"); + try { + transaction.getEntityManager().remove(existingEntity); + } catch (Throwable exc) { + log.error("exception: " + exc); + } finally { + log.debug("finally"); + } + log.debug("deleted!"); + + // generically create the processor + EntityProcessorFactory procFact = createProcessorFactory(existingEntity.getClass()); + if (procFact == null) { + log.debug("no procFact found :-("); + return; + } + log.debug("procFact found :-)"); + Processor proc = procFact.createDeleteProcessor(transaction.getEntityManager(), existingEntity); + + // queue the processor + queueProcessor(proc, user, existingEntity, "geloescht"); + } + + public EntityProcessorFactory createProcessorFactory(Class entityClass) { + try { + String procFactName = entityClass.getCanonicalName() + + "ProcessorFactory"; + Class procFactClass = Class.forName(procFactName); + if (procFactClass == null) + return null; + Object procFact = procFactClass.newInstance(); + return (EntityProcessorFactory) procFact; + } catch (Exception exc) { + log.error("exception creating instance: " + exc); + return null; + } + } + + /** + * get current login user from session context. + * @return + */ + public UnixUser getLoginUser() { + if (loginUser == null) { + log.debug("getting login user"); + Query userQuery = transaction.getEntityManager().createQuery("SELECT u FROM UnixUsers u WHERE u.name=:name"); + userQuery.setParameter("name", transaction.getLoginName()); + loginUser = (UnixUser) userQuery.getSingleResult(); + log.debug("found login user: " + loginUser.getName()); + } + return loginUser; + } + + public void queueProcessor(Processor proc, UnixUser user, Entity entity, String action) { + log.debug("queueing processor for user " + user.getId() + "/" + + user.getUserId() + "/" + user.getName()); + EntityInfo entityInfo = + entity.getClass().getAnnotation(EntityInfo.class); + String entityTypeName = + entityInfo != null ? entityInfo.name() : entity.getClass().getSimpleName(); + StringBuilder details = new StringBuilder(); + + // TODO: add properties of entity to details + + String title = + entityTypeName + " (" + entity.createStringKey() + ") " + action; + QueueTask task = new QueueTask(user, title, details.toString(), proc); + transaction.getEntityManager().persist(task); + transaction.enqueue(entity.getHiveName(), task); + log.debug("processor queued"); + } + + public String toString(StackTraceElement[] stackTrace) { + StringBuilder stack = new StringBuilder(); + for (StackTraceElement e : stackTrace) + stack.append(e.getFileName() + ":" + e.getLineNumber() + "\n"); + return stack.toString(); + } + + /** + * determines if the login user is a hostmaster. + * @return + */ + public boolean isHostmaster() { + // TODO: dummy + return false; + } + + public static void setQueryParameter(Query query, String queryString, + String argName, Object argValue) { + int argLen = argName.length(); + int iMax = queryString.length(); + int i = 0; + while ((i = queryString.indexOf(argName, i)) >= 0) { + if ((i + argLen) >= iMax || queryString.charAt(i + argLen) < 'A') { + query.setParameter(argName, argValue); + break; + } + ++i; + } + } +} diff --git a/hsarback/src/de/hsadmin/core/model/AuthenticationException.java b/hsarback/src/de/hsadmin/core/model/AuthenticationException.java new file mode 100644 index 0000000..66332f1 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/AuthenticationException.java @@ -0,0 +1,15 @@ +package de.hsadmin.core.model; + +public class AuthenticationException extends HSAdminException { + + private static final long serialVersionUID = 6242824365822822456L; + + public AuthenticationException(Exception e) { + super(e); + } + + public AuthenticationException(String msg) { + super(msg); + } + +} diff --git a/hsarback/src/de/hsadmin/core/model/AuthorisationException.java b/hsarback/src/de/hsadmin/core/model/AuthorisationException.java new file mode 100644 index 0000000..33b2dc5 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/AuthorisationException.java @@ -0,0 +1,56 @@ +package de.hsadmin.core.model; + +import de.hsadmin.mods.user.UnixUser; + +public class AuthorisationException extends HSAdminException { + + private static final long serialVersionUID = -8125905071037488732L; + + private UnixUser user; + private String method; + private Entity entity; + private String field; + + public UnixUser getUser() { + return user; + } + + public String getMethod() { + return method; + } + + public Entity getEntity() { + return entity; + } + + public String getField() { + return field; + } + + public AuthorisationException(UnixUser user, String method) { + super("nicht authorisiert fuer " + method + "()"); + + this.user = user; + this.method = method; + } + + public AuthorisationException(UnixUser user, String method, Entity entity) { + super("nicht authorisiert fuer " + method + "(" + + entity.createStringKey() + ")"); + + this.user = user; + this.method = method; + this.entity = entity; + } + + public AuthorisationException(UnixUser user, String method, Entity entity, + String field) { + super("nicht authorisiert fuer " + method + "(" + + entity.createStringKey() + "." + field + ")"); + + this.user = user; + this.method = method; + this.entity = entity; + this.field = field; + } +} diff --git a/hsarback/src/de/hsadmin/core/model/Entity.java b/hsarback/src/de/hsadmin/core/model/Entity.java new file mode 100644 index 0000000..10d9d37 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/Entity.java @@ -0,0 +1,249 @@ +package de.hsadmin.core.model; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import javax.persistence.EntityManager; + +import de.hsadmin.mods.user.UnixUser; + +public abstract class Entity { + + /** + * trims whitespace from both ends, but a null remains null. + * @param field + * @return + */ + public static String trim(String field) { + if (field == null) + return field; + return field.trim(); + } + + /** + * trims whitespace from both ends, returns null for a resulting empty + * string or null. + * @param field + * @return + */ + public static String trimToNull(String field) { + if (field == null) + return field; + field = field.trim(); + if (field.length() == 0) + return null; + return field; + } + + /** + * trims whitespace from both ends, returns an empty string for null + * @param field + * @return + */ + public static String trimToEmpty(String field) { + if (field == null) + return ""; + field = field.trim(); + return field; + } + + /** + * Creates a JPA-QL query string from a human readable string representing a + * key. + * + * Such a static method is to be implemented by every entity class which has + * a human readable key different from "id". + * + * @param humanKey + * A human readable key representing the entity. E.g. + * "info@example.com" for an E-Mail-Address. + * + * @return jpaQLQuery the matching JPA-QL query + */ + public static String createQueryFromStringKey(String humanKey) throws HSAdminException { + return "id=" + humanKey; + } + + /** + * escapes a String so that it can be used for SQL statements + * @param str a sting that should be usable as a String in an SQLStatement + * @return the escapedString + */ + public static String escapeString(String str) { + str = str.replaceAll("\"", "\\\""); + str = str.replaceAll("\'", "\\'"); + return str; + } + + /** + * Calls the static method of the Entity subclass to get a query + * restriction. + * + * @return a restricting JPA-QL expression to limit access to entities + */ + public static String restriction(Class entityClass, UnixUser loginUser) { + // hostmasters don't get any restriction + if (loginUser.hasHostmasterRole()) + return null; + + // find a static method which creates the query + java.lang.reflect.Method method = null; + try { + method = entityClass.getDeclaredMethod("restriction"); + } catch (SecurityException e) { + throw new RuntimeException( + "unexpected SecurityException in restriction()"); + } catch (NoSuchMethodException e) { + // don't grant any access + return "FALSE"; + } + + // get the restriction from a static method of the Entity class + String restriction = "FALSE"; + try { + restriction = (String) method.invoke(null); + } catch (Exception e) { + throw new RuntimeException( + "unexpected Exception in Entity-restriction() implementation"); + } + return restriction; + } + + /** + * Creates a human readable string representing a key to this entity. + * + * @return humanReadableString A human readable string which can be used as + * a key to this entity. This string can also be used with the + * static method createQueryFromStringKey which has to be supported + * by all entity + */ + public abstract String createStringKey(); + + /** + * returns true if entity is not yet stored to the database, false otherwise. + * @return + */ + public abstract boolean isNew(); + + /** + * returns the unique database if + */ + public abstract long id(); + + /** + * returns true if the argument entity has the same database id + * @param entity + * @return + */ + public boolean sameIdAs(Entity entity) { + if (entity == null) + return false; + if (getClass() != entity.getClass()) + return false; + return id() == entity.id(); + } + + /** + * returns true if entity is not yet stored to the database, false otherwise. + * @return + */ + @javax.persistence.Transient + public boolean getNew() { + return isNew(); + } + + /** + * Initializes a newly created entity. + * + * This method makes it possible to initialize fields of entities using the + * login user and database access. + * @param em + * @param loginUser + */ + public void initialize(EntityManager em, UnixUser loginUser) { + } + + /** + * completes the (new) entity before it gets stored to the database + * @param em + * @param loginUser + */ + public void complete(EntityManager em, UnixUser loginUser) { + } + + /** + * merges this entity with the database, keeping transient members which are + * otherwise nulled. + * + * Usually subclasses load the attached entity with same id and update all + * fields to which the login user has access, but no other fields. Then the + * attached entity is returned instead of this entity. + * @param em + * @param loginUser + */ + public Entity merge(EntityManager em, UnixUser loginUser) { + return em.merge(this); + } + + /** + * determines whether the given user has full read access on all merged + * fields of this entity + * @param loginUser + * @return + */ + public boolean isReadAllowedFor(UnixUser loginUser) { + return loginUser.hasHostmasterRole(); + } + + /** + * determines whether the given user has full write access on all merged + * fields of this entity + * @param loginUser + * @return + */ + public boolean isWriteAllowedFor(UnixUser loginUser) { + return loginUser.hasHostmasterRole(); + } + + /** + * returns the hive name in which this entity resists or null if entity + * is new or does not need a hive. + * @return + */ + public String getHiveName() { + return null; + } + + /** + * returns the UnixUser who owns this entity (and thus has broad access to it + * @param em + * @return + */ + public abstract UnixUser owningUser(EntityManager em); + + /** + * Clonable::clone did result in javasisst errors (duplicate method) since + * JBoss 5.1. + * This is just for testing, it does not use the proper class loader! + */ + public T replicate() { + try { + ByteArrayOutputStream baOut = new ByteArrayOutputStream(); + ObjectOutputStream oOut = new ObjectOutputStream(baOut); + oOut.writeObject(this); + byte[] buffer = baOut.toByteArray(); + + ByteArrayInputStream baIn = new ByteArrayInputStream(buffer); + ObjectInputStream oIn = new ObjectInputStream(baIn); + + @SuppressWarnings("unchecked") + T clone = (T) oIn.readObject(); + return clone; + } catch (Exception exc) { + throw new RuntimeException(exc); + } + } + +} diff --git a/hsarback/src/de/hsadmin/core/model/EntityInfo.java b/hsarback/src/de/hsadmin/core/model/EntityInfo.java new file mode 100644 index 0000000..5b58474 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/EntityInfo.java @@ -0,0 +1,12 @@ +package de.hsadmin.core.model; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(value=RetentionPolicy.RUNTIME) +public @interface EntityInfo +{ + /// human readable identifier of the entity + String name(); + +} diff --git a/hsarback/src/de/hsadmin/core/model/EntitySessionHelper.java b/hsarback/src/de/hsadmin/core/model/EntitySessionHelper.java new file mode 100644 index 0000000..036dba5 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/EntitySessionHelper.java @@ -0,0 +1,41 @@ +package de.hsadmin.core.model; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class EntitySessionHelper { + + private static Log log = LogFactory.getLog(EntitySessionHelper.class); + + /** + * helper method to create a wrapper for checking the rights according to + * the entity class. + * @param entityClass + * @return + */ + public static AbstractModuleImpl createEntitySessionWrapper(Class entityClass) { + // get in instance + AbstractModuleImpl impl = null; + try { + // determine wrapper class + ModuleImpl wrapperAnnot = entityClass.getAnnotation(ModuleImpl.class); + Class wrapperClass = null; + if (wrapperAnnot != null) { + wrapperClass = wrapperAnnot.value(); + } else { + wrapperClass = Class.forName(entityClass.getCanonicalName() + "ModuleImpl"); + } + // instantiate wrapper + impl = (AbstractModuleImpl) wrapperClass.newInstance(); + } catch (ClassNotFoundException exc) { + log.info("entity class '" + + entityClass.getCanonicalName() + + "' has no session wrapper => using restrictive default access rights"); + impl = new SecureDefaultModuleImpl(); + } catch (Exception exc) { + throw new RuntimeException(exc); + } + return impl; + } + +} diff --git a/hsarback/src/de/hsadmin/core/model/GenericModuleImpl.java b/hsarback/src/de/hsadmin/core/model/GenericModuleImpl.java new file mode 100644 index 0000000..c76a08b --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/GenericModuleImpl.java @@ -0,0 +1,99 @@ +package de.hsadmin.core.model; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class GenericModuleImpl implements ModuleInterface { + + private static final Log log = LogFactory.getLog(GenericModuleImpl.class); + + private Transaction tx; + + public GenericModuleImpl(Transaction transaction) { + tx = transaction; + } + + public Transaction getTransaction() { + return tx; + } + + @Override + public Entity initialize(Entity newEntity) throws HSAdminException { + log.trace("initialize(" + newEntity + ")"); + AbstractModuleImpl wrapper = + EntitySessionHelper.createEntitySessionWrapper(newEntity.getClass()); + wrapper.construct(tx); + return wrapper.initialize(newEntity); + } + + @Override + public Entity add(Entity newEntity) throws HSAdminException { + if (!newEntity.isNew()) + throw new HSAdminException("cannot add an already persistent entity"); + log.trace("add(" + newEntity + ")"); + AbstractModuleImpl wrapper = + EntitySessionHelper.createEntitySessionWrapper(newEntity.getClass()); + wrapper.construct(tx); + Entity result = null; + try { + result = wrapper.add(newEntity); + return result; + } catch (Exception e) { + throw new HSAdminException(e); + } + } + + @Override + public Entity find(Class entityClass, Object key) throws HSAdminException { + log.trace("find(" + entityClass + ", " + key + ")"); + AbstractModuleImpl wrapper = EntitySessionHelper.createEntitySessionWrapper(entityClass); + wrapper.construct(tx); + return wrapper.find(entityClass, key); + } + + @Override + public Entity findByString(Class entityClass, String key) throws HSAdminException { + log.trace("find(" + entityClass + ", " + key + ")"); + AbstractModuleImpl wrapper = EntitySessionHelper.createEntitySessionWrapper(entityClass); + wrapper.construct(tx); + return wrapper.findByString(entityClass, key); + } + + @Override + public List search(Class entityClass, String condition, String orderBy) throws HSAdminException { + log.trace("search(" + entityClass + ", " + condition + ")"); + AbstractModuleImpl wrapper = EntitySessionHelper.createEntitySessionWrapper(entityClass); + wrapper.construct(tx); + return wrapper.search(entityClass, condition, orderBy); + } + + @Override + public Entity update(Entity existingEntity) throws HSAdminException { + if (existingEntity.isNew()) + return add(existingEntity); + log.debug("update(" + existingEntity + ")"); + AbstractModuleImpl wrapper = EntitySessionHelper.createEntitySessionWrapper(existingEntity.getClass()); + wrapper.construct(tx); + try { + return wrapper.update(existingEntity); + } catch (Exception e) { + throw new HSAdminException(e); + } + } + + @Override + public void delete(Entity existingEntity) throws HSAdminException { + log.trace("delete(" + existingEntity + ")"); + AbstractModuleImpl wrapper = + EntitySessionHelper.createEntitySessionWrapper(existingEntity.getClass()); + wrapper.construct(tx); + try { + wrapper.delete(existingEntity); + } catch (Exception e) { + throw new HSAdminException(e); + } + } + +} diff --git a/hsarback/src/de/hsadmin/core/model/HSAdminException.java b/hsarback/src/de/hsadmin/core/model/HSAdminException.java new file mode 100644 index 0000000..8e42774 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/HSAdminException.java @@ -0,0 +1,19 @@ +package de.hsadmin.core.model; + +public class HSAdminException extends Exception { + + private static final long serialVersionUID = -5082179267383474532L; + + public HSAdminException(String message) { + super(message); + } + + public HSAdminException(Exception e) { + super(e); + } + + public HSAdminException(String message, Exception aExc) { + super(message, aExc); + } + +} diff --git a/hsarback/src/de/hsadmin/core/model/ModuleImpl.java b/hsarback/src/de/hsadmin/core/model/ModuleImpl.java new file mode 100644 index 0000000..fcd9bf1 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/ModuleImpl.java @@ -0,0 +1,10 @@ +package de.hsadmin.core.model; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface ModuleImpl +{ + Class value(); +} diff --git a/hsarback/src/de/hsadmin/core/model/ModuleInterface.java b/hsarback/src/de/hsadmin/core/model/ModuleInterface.java new file mode 100644 index 0000000..ff7ce74 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/ModuleInterface.java @@ -0,0 +1,50 @@ +package de.hsadmin.core.model; + +import java.util.List; + +/** + * Represents a CRUD (Create, Retrieve, Update, Delete) interface for generic + * entity instances. + * @author peter + */ +public interface ModuleInterface { + + public Transaction getTransaction(); + + /** + * initializes a newly created entity. + */ + public Entity initialize(Entity newEntity) throws HSAdminException; + + /** + * Adds a new entity instance to the model. + */ + public Entity add(Entity newEntity) throws HSAdminException; + + /** + * Finds an entity instance in the model, using its primary key. + */ + public Entity find(Class entityClass, Object key) throws HSAdminException; + + /** + * Finds an entity instance in the model, using its primary key in a String + * representation. + */ + public Entity findByString(Class entityClass, String key) throws HSAdminException; + + /** + * Searches entity instances in the model, using a simplified JPA-QL query. + */ + public List search(Class entityClass, String query, String orderBy) throws HSAdminException; + + /** + * Updates an existing entity in the model. + */ + public Entity update(Entity existingEntity) throws HSAdminException; + + /** + * Deletes an existing entity from the model. + */ + public void delete(Entity existingEntity) throws HSAdminException; + +} diff --git a/hsarback/src/de/hsadmin/core/model/SearchFilter.java b/hsarback/src/de/hsadmin/core/model/SearchFilter.java new file mode 100644 index 0000000..239f18d --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/SearchFilter.java @@ -0,0 +1,12 @@ +package de.hsadmin.core.model; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/// specifies pre-filters (JPAQL) for several roles +@Retention(value=RetentionPolicy.RUNTIME) +public @interface SearchFilter +{ + /// an additional JPA condition to add to the WHERE clause + String value(); +} diff --git a/hsarback/src/de/hsadmin/core/model/SecureDefaultModuleImpl.java b/hsarback/src/de/hsadmin/core/model/SecureDefaultModuleImpl.java new file mode 100644 index 0000000..dc9b156 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/SecureDefaultModuleImpl.java @@ -0,0 +1,105 @@ +package de.hsadmin.core.model; + +import java.util.LinkedList; +import java.util.List; + +/** + * allows access only for hostmasters, used as fallback wrapper. + */ +public class SecureDefaultModuleImpl extends AbstractModuleImpl { + + private static final long serialVersionUID = 4567381515459292565L; + + @Override + public Entity initialize(Entity newEntity) throws AuthorisationException { + return super.initialize(newEntity); + } + + @Override + public Entity find(Class entityClass, Object key) throws HSAdminException { + Entity entity = super.find(entityClass, key); + if (entity != null && !entity.isReadAllowedFor(getLoginUser())) + throw new AuthorisationException(getLoginUser(), "find"); + return entity; + } + + @Override + public Entity findByString(Class entityClass, String key) throws HSAdminException { + Entity entity = super.findByString(entityClass, key); + if (entity != null && !entity.isReadAllowedFor(getLoginUser())) + throw new AuthorisationException(getLoginUser(), "findByString"); + return entity; + } + + @Override + public List search(Class entityClass, String condition, String orderBy) + throws HSAdminException { + // restrict query to entities where the loginUser could have rights on + SearchFilter filterAnnot; + if (!getLoginUser().hasHostmasterRole() + && (filterAnnot = getSecurityFilterAnnotation(entityClass)) != null) { + String securityCondition = filterAnnot.value(); + if (condition != null && condition.length() > 0) + condition = "(" + condition + ")" + " AND ( " + + securityCondition + ")"; + else + condition = securityCondition; + } + + // do query + List res = super.search(entityClass, condition, orderBy); + List ret = new LinkedList(); + + // remove entities where login user has no access rights + if (res != null) { + for (Entity entity : res) { + Entity returnedEntity = entity; + if (returnedEntity.isReadAllowedFor(getLoginUser())) + ret.add(returnedEntity); + } + } + + // return clean result + return ret; + + } + + // helper method to get annotation from class or superclass + private SearchFilter getSecurityFilterAnnotation(Class entityClass) { + SearchFilter ret; + while (entityClass != null) { + ret = (SearchFilter) entityClass.getAnnotation(SearchFilter.class); + if (ret != null) + return ret; + entityClass = entityClass.getSuperclass(); + } + return null; + } + + @Override + public Entity add(Entity newEntity) throws HSAdminException { + // access rights checking is done by base class + return super.add(newEntity); + } + + @Override + public Entity update(Entity existingEntity) throws HSAdminException { + // access rights checking is done by base class + return super.update(existingEntity); + } + + @Override + public void delete(Entity detachedEntity) throws HSAdminException { + // get the entity from the database + Entity attachedEntity = getTransaction().getEntityManager().find(detachedEntity.getClass(), + detachedEntity.id()); + + // does the login user have the right to delete? + if (!attachedEntity.isWriteAllowedFor(getLoginUser())) + throw new AuthorisationException(getLoginUser(), "delete", + detachedEntity); + + super.delete(attachedEntity); + } + +} diff --git a/hsarback/src/de/hsadmin/core/model/TicketAuthentication.java b/hsarback/src/de/hsadmin/core/model/TicketAuthentication.java new file mode 100644 index 0000000..bf9f5c0 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/TicketAuthentication.java @@ -0,0 +1,17 @@ +package de.hsadmin.core.model; + +import de.hsadmin.core.model.onetier.TicketValidator; + +public class TicketAuthentication { + + private static TicketAuthentication auth = new TicketAuthentication(); + + public static TicketAuthentication getInstance() { + return auth; + } + + public boolean login(String login, String ticket) throws AuthenticationException { + return TicketValidator.getInstance().validateTicket(login, ticket); + } + +} diff --git a/hsarback/src/de/hsadmin/core/model/Transaction.java b/hsarback/src/de/hsadmin/core/model/Transaction.java new file mode 100644 index 0000000..e5f81e3 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/Transaction.java @@ -0,0 +1,179 @@ +package de.hsadmin.core.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jms.Queue; +import javax.jms.QueueConnectionFactory; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.openjpa.persistence.OpenJPAEntityManager; +//import org.hibernate.Session; + +import de.hsadmin.cliClientConnector.TechnicalException; +import de.hsadmin.core.model.onetier.PersistenceManager; +import de.hsadmin.core.qserv.QueueClient; +import de.hsadmin.core.qserv.QueueTask; + +public class Transaction { + + private static final Log log = LogFactory.getLog(Transaction.class); + + private EntityManager entityManager; + private QueueConnectionFactory queueConnectionFactory; + private String loginName; + private Map taskStores; + private boolean transactionActive; + private InitialContext ctx; + + public Transaction(String loginName) { + transactionActive = false; + this.entityManager = PersistenceManager.getEntityManager("hsadmin"); + this.loginName = loginName; + taskStores = new HashMap(); + try { + ctx = new InitialContext(); + Context env = (Context) ctx.lookup("java:comp/env"); + queueConnectionFactory = (QueueConnectionFactory) env.lookup("jms/QueueCF"); + } catch (NamingException e) { + log.fatal(e); + e.printStackTrace(); + throw new TechnicalException(e); + } + } + + public EntityManager getEntityManager() { + return entityManager; + } + + public QueueConnectionFactory getQueueConnectionFactory() { + return queueConnectionFactory; + } + + public Queue lookupJMSQueue(String queueName) { + if (ctx != null) { + try { + Context env = (Context) ctx.lookup("java:comp/env"); + return (Queue) env.lookup("jms/" + queueName); + } catch (NamingException e) { + log.fatal(e); + e.printStackTrace(); + return null; + } + } + return null; + } + + public String getLoginName() { + if (loginName != null) return loginName; + return null; + } + + public void enqueue(String hiveName, QueueTask task) { + QueueTaskStore taskStore = taskStores.get(hiveName); + if (taskStore == null) { + taskStore = new QueueTaskStore(); + taskStores.put(hiveName, taskStore); + } + taskStore.add(task); + } + + private void sendAll(EntityTransaction transaction) { + boolean firstHive = true; + for (String hive : taskStores.keySet()) { + QueueTaskStore store = taskStores.get(hive); + String queueName = "hsadminSystem-" + hive; + Queue jmsSystemQueue = lookupJMSQueue(queueName); + QueueClient qClient = null; + try { + qClient = new QueueClient(queueConnectionFactory, jmsSystemQueue); + if (firstHive) { + firstHive = false; + transaction.commit(); + } + for (QueueTask task : store.getTasks()) { + qClient.send(task); + } + } catch (Exception e) { + throw new TechnicalException(e); + } finally { + store.clear(); + if (qClient != null) qClient.close(); + } + } + if (firstHive) { + try { + transaction.commit(); + } catch (Exception e) { + throw new TechnicalException(e); + } + } + } + + public void beginTransaction() { + transactionActive = true; + entityManager.getTransaction().begin(); + } + + public void commitTransaction() { + sendAll(entityManager.getTransaction()); + transactionActive = false; + } + + public void rollbackTransaction() { + taskStores.clear(); + transactionActive = false; + try { + entityManager.getTransaction().rollback(); + } catch (IllegalStateException e) { + log.info(e); + } + } + + public void close() { + if (transactionActive) { + rollbackTransaction(); + } + entityManager.close(); + } + + /** + * Detach entities from hibernate session. + * Used to detach entities before update. Makes it possible to compare + * old and new attribute values. + */ + public void detach(Entity entity) { + // TODO: replace hibernate specific implmentation +// org.hibernate.Session hSession = (Session) entityManager.getDelegate(); +// hSession.evict(entity); + if (entityManager instanceof OpenJPAEntityManager) { + OpenJPAEntityManager openjpaEM = (OpenJPAEntityManager) entityManager; + openjpaEM.detach(entity); + } + } + + class QueueTaskStore { + private List taskList; + QueueTaskStore() { + taskList = new ArrayList(); + } + public void clear() { + taskList = new ArrayList(); + } + void add(QueueTask t) { + taskList.add(t); + } + Iterable getTasks() { + return taskList; + } + } + +} diff --git a/hsarback/src/de/hsadmin/core/model/onetier/PersistenceManager.java b/hsarback/src/de/hsadmin/core/model/onetier/PersistenceManager.java new file mode 100644 index 0000000..82e2c30 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/onetier/PersistenceManager.java @@ -0,0 +1,43 @@ +package de.hsadmin.core.model.onetier; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +public class PersistenceManager { + + private static PersistenceManager instance; + + private Map persistenceUnits; + + private PersistenceManager() { + persistenceUnits = new HashMap(); + } + + private EntityManagerFactory getEMF(String persistUnitName) { + EntityManagerFactory emf = persistenceUnits.get(persistUnitName); + if (emf == null) { + emf = Persistence.createEntityManagerFactory(persistUnitName); + persistenceUnits.put(persistUnitName, emf); + } + return emf; + } + + public static EntityManager getEntityManager(String persistUnitName) { + PersistenceManager pm = PersistenceManager.getInstance(); + EntityManagerFactory emf = pm.getEMF(persistUnitName); + return emf.createEntityManager(); + } + + private static PersistenceManager getInstance() { + if (instance == null) { + instance = new PersistenceManager(); + } + return instance; + } + + +} diff --git a/hsarback/src/de/hsadmin/core/model/onetier/TicketValidator.java b/hsarback/src/de/hsadmin/core/model/onetier/TicketValidator.java new file mode 100644 index 0000000..65685f4 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/model/onetier/TicketValidator.java @@ -0,0 +1,96 @@ +package de.hsadmin.core.model.onetier; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import de.hsadmin.core.model.AuthenticationException; + +public class TicketValidator { + + private static final Log log = LogFactory.getLog(TicketValidator.class); + + private static TicketValidator instance; + + private String proxyValidateURL; + private String proxyServiceURL; + + private TicketValidator() { + proxyServiceURL = null; + proxyValidateURL = null; + } + + public static TicketValidator getInstance() { + if (instance == null) { + instance = new TicketValidator(); + } + return instance; + } + + public void initialize(String validateURL, String serviceURL) { + proxyServiceURL = serviceURL; + proxyValidateURL = validateURL; + } + + public boolean validateTicket(String runAsUser, String ticket) throws AuthenticationException { + String ticketUser = validateTicket(ticket); + if (runAsUser != null && + (runAsUser.equals(ticketUser) // user himself + || (ticketUser.length() == 5 && runAsUser.startsWith(ticketUser)) + // pac-admin + || ticketUser.length() == 2) // hostmaster + // TODO: add test for member-account + ) { + return true; + } else { + throw new AuthenticationException("User " + ticketUser + " is not allowed to run as " + runAsUser); + } + } + + public String validateTicket(String ticket) throws AuthenticationException { + if (proxyServiceURL == null || proxyServiceURL == null) { + log.fatal("TicketValidator is not initialized."); + throw new RuntimeException("TicketValidator is not initialized."); + } + try { + URL url = new URL(proxyValidateURL + "?service=" + proxyServiceURL + "&ticket=" + ticket); + URLConnection httpConnection = url.openConnection(); + httpConnection.connect(); + InputStream inputStream = httpConnection.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + String nextLine = reader.readLine(); + while (nextLine != null) { + if (nextLine.contains("")) { + String user = extractUser(nextLine); + inputStream.close(); + return user; + } + nextLine = reader.readLine(); + } + inputStream.close(); + log.debug("Ticket validation failed: " + ticket); + throw new AuthenticationException("Invalid Ticket: " + ticket); + } catch (MalformedURLException e) { + log.fatal(e); + throw new AuthenticationException(e.getMessage()); + } catch (IOException e) { + log.fatal(e); + throw new AuthenticationException(e.getMessage()); + } + } + + private String extractUser(String nextLine) { + int start = nextLine.indexOf(""); + int end = nextLine.indexOf(""); + String user = nextLine.substring(start + 10, end); + return user; + } + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/AbstractProcessor.java b/hsarback/src/de/hsadmin/core/qserv/AbstractProcessor.java new file mode 100644 index 0000000..f88bcc7 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/AbstractProcessor.java @@ -0,0 +1,17 @@ +package de.hsadmin.core.qserv; + +import de.hsadmin.core.model.Transaction; + +abstract public class AbstractProcessor implements Processor { + + private static final long serialVersionUID = -2401718681212973276L; + + @Override + abstract public Object process() throws ProcessorException; + + @Override + public void finalize(Transaction transaction, QueueTask task) throws ProcessorException { + task.done(); + } + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/CommandShell.java b/hsarback/src/de/hsadmin/core/qserv/CommandShell.java new file mode 100644 index 0000000..273dea9 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/CommandShell.java @@ -0,0 +1,162 @@ +package de.hsadmin.core.qserv; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; + +public class CommandShell +{ + private static boolean bExecute = true; // really execute or just store command and stdin? + private static String executedCommands; // stored command and stdin + private static String[] aEnvironment; // stored environment + + /** Set mode of real execution or just storing the command and stdin. + * + * @param bExec + * specifies whether shell commands should really be executed (true) or not (false) + */ + public static void setExecute( boolean bExec ) + { + bExecute = bExec; + } + + /** Returns and clears the last command which should have been executed. + * + * @return + * Last command, plus "< 0 ) + logCommand += "< 0 && callOutput.charAt(nLen-1) == '\n' ) + // callOutput = callOutput.substring(0, nLen-1); + return callOutput.trim(); + } + return null; + } + + private static String readProcessStream(InputStream stream) throws IOException + { + BufferedReader reader = new BufferedReader( + new InputStreamReader(stream)); + StringBuffer textBuff = new StringBuffer(); + String textLine = reader.readLine(); + while (textLine != null) + { + textBuff.append(textLine); + textBuff.append('\n'); + textLine = reader.readLine(); + } + reader.close(); + return textBuff.toString(); + } +} diff --git a/hsarback/src/de/hsadmin/core/qserv/CompoundProcessor.java b/hsarback/src/de/hsadmin/core/qserv/CompoundProcessor.java new file mode 100644 index 0000000..c2ce74c --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/CompoundProcessor.java @@ -0,0 +1,30 @@ +package de.hsadmin.core.qserv; + +import java.util.ArrayList; +import java.util.List; + +public class CompoundProcessor extends AbstractProcessor { + + private static final long serialVersionUID = 5718623579674305929L; + + private List aProcessors; + + public CompoundProcessor(Processor... aProcessor) { + aProcessors = new ArrayList(); + for (Processor aP : aProcessor) { + aProcessors.add(aP); + } + } + + public Object process() throws ProcessorException { + for (Processor aProcessor : aProcessors) { + aProcessor.process(); + } + return null; + } + + public void appendProcessor(Processor aProcessor) { + aProcessors.add(aProcessor); + } + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/CopyFileProcessor.java b/hsarback/src/de/hsadmin/core/qserv/CopyFileProcessor.java new file mode 100644 index 0000000..1628d7c --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/CopyFileProcessor.java @@ -0,0 +1,22 @@ +package de.hsadmin.core.qserv; + +public class CopyFileProcessor extends AbstractProcessor { + + private static final long serialVersionUID = 1741419610410712714L; + + private Processor internal; + + public CopyFileProcessor(String source, String target, String owner, String group, String modeMask) { + internal = new ShellProcessor( + "cp " + source + " " + target + " && " + + "chown " + owner + ":" + group + " " + target + " && " + + "chmod " + modeMask + " " + target + ); + } + + @Override + public Object process() throws ProcessorException { + return internal.process(); + } + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/CreateFileProcessor.java b/hsarback/src/de/hsadmin/core/qserv/CreateFileProcessor.java new file mode 100644 index 0000000..c613c6c --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/CreateFileProcessor.java @@ -0,0 +1,25 @@ +package de.hsadmin.core.qserv; + +import java.util.Map; + +public class CreateFileProcessor extends AbstractProcessor { + + private static final long serialVersionUID = -2947609532975712444L; + + private CompoundProcessor compoundProcessor; + + public CreateFileProcessor(String templateName, Map templateValues, String targetPath, String owner, String group, String modeMask) throws ProcessorException { + TemplateProcessor templateProcessor = new TemplateProcessor(templateName, templateValues, targetPath, false); + ShellProcessor shellProcessor = + new ShellProcessor( + "chown " + owner + ":" + group + " " + targetPath + " && " + + "chmod " + modeMask + " " + targetPath); + compoundProcessor = new CompoundProcessor(templateProcessor, shellProcessor); + } + + @Override + public Object process() throws ProcessorException { + return compoundProcessor.process(); + } + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/EntityProcessorFactory.java b/hsarback/src/de/hsadmin/core/qserv/EntityProcessorFactory.java new file mode 100644 index 0000000..45e84f8 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/EntityProcessorFactory.java @@ -0,0 +1,22 @@ +package de.hsadmin.core.qserv; + +import javax.persistence.EntityManager; + +import de.hsadmin.core.model.Entity; + +/** + * Most processor factories need only these methods. + * + * @author peter + */ +public interface EntityProcessorFactory { + public Processor createCreateProcessor(EntityManager em, + T entity) throws ProcessorException; + + public Processor createUpdateProcessor(EntityManager em, + T newEntity) throws ProcessorException; + + public Processor createDeleteProcessor(EntityManager em, + T entity) throws ProcessorException; + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/JDBCProcessor.java b/hsarback/src/de/hsadmin/core/qserv/JDBCProcessor.java new file mode 100644 index 0000000..556b15b --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/JDBCProcessor.java @@ -0,0 +1,89 @@ +package de.hsadmin.core.qserv; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import de.hsadmin.core.qserv.AbstractProcessor; +import de.hsadmin.core.qserv.ProcessorException; +import de.hsadmin.core.util.Config; + +public class JDBCProcessor extends AbstractProcessor { + + private static final long serialVersionUID = 7702753017749022325L; + + private String driver; + private String url; + private String user; + private String password; + private List sql; + + public JDBCProcessor(String driver, String url, String user, String password) { + this.driver = driver; + this.url = url; + this.user = user; + this.password = password; + } + + public JDBCProcessor(String driver, String url) { + this.driver = driver; + this.url = url; + } + + public void addSQL(String sqlStatement) { + if (sql == null) + sql = new ArrayList(); + sql.add(sqlStatement); + } + + public Object process() throws ProcessorException { + Connection c = null; + Config config = Config.getInstance(); + if ("com.mysql.jdbc.Driver".equals(driver)) { + if (user == null) + user = config.getProperty("mysqladmin.user", "root"); + if (password == null) + password = config.getProperty("mysqladmin.password"); + } + if ("org.postgresql.Driver".equals(driver)) { + if (user == null) + user = config.getProperty("pgsqladmin.user", "postgres"); + if (password == null) + password = config.getProperty("pgsqladmin.password"); + } + if (user == null || password == null) { + throw new ProcessorException("database admin-user configuration failed"); + } + try { + Class.forName(driver); + c = DriverManager.getConnection(url, user, password); + if (c == null) + throw new ProcessorException("cannot connect to '" + url + "'"); + Statement s = c.createStatement(); + for (String sqlStatement : sql) { + s.addBatch(sqlStatement); + System.out.println("SQL: " + sqlStatement); + } + return s.executeBatch(); + } catch (SQLException aSqlExc) { + Exception exc = aSqlExc.getNextException(); + if (exc == null) + exc = aSqlExc; + System.out.println("ERR: " + exc.getMessage()); + throw new ProcessorException(aSqlExc.getMessage() + ", reason: " + + exc.getMessage()); + } catch (Exception aExc) { + throw new ProcessorException(aExc.getMessage()); + } finally { + if (c != null) { + try { + c.close(); + } catch (Exception exc) { + } + } + } + } +} diff --git a/hsarback/src/de/hsadmin/core/qserv/MailerProcessor.java b/hsarback/src/de/hsadmin/core/qserv/MailerProcessor.java new file mode 100644 index 0000000..5c636c4 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/MailerProcessor.java @@ -0,0 +1,41 @@ +package de.hsadmin.core.qserv; + +import java.util.Properties; + +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; + +public class MailerProcessor extends AbstractProcessor { + + private static final long serialVersionUID = -8900943664576420041L; + + private String aTo; + private String aSubject; + private String aBody; + + public MailerProcessor(String aTo, String aSubject, String aBody) { + this.aTo = aTo; + this.aSubject = aSubject; + this.aBody = aBody; + } + + public Object process() throws ProcessorException { + Properties aProps = new Properties(); + aProps.setProperty("mail.smtp.host", "localhost"); + + Session aSess = Session.getInstance(aProps); + MimeMessage aMsg = new MimeMessage(aSess); + try { + aMsg.setFrom(new InternetAddress("robot@hostsharing.net")); + aMsg.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(aTo)); + aMsg.setSubject(aSubject); + aMsg.setText(aBody); + MailerShell.send(aMsg); + return null; + } catch (MessagingException aExc) { + throw new ProcessorException(aExc); + } + } +} diff --git a/hsarback/src/de/hsadmin/core/qserv/MailerShell.java b/hsarback/src/de/hsadmin/core/qserv/MailerShell.java new file mode 100644 index 0000000..d036b6a --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/MailerShell.java @@ -0,0 +1,41 @@ +package de.hsadmin.core.qserv; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; + +public class MailerShell { + + private static boolean bExecute = true; // really execute or just store + // command and stdin? + private static MimeMessage aMessage; // stored message + + /** + * Set mode of real mailer or just storing the messages for testing + * purposes. + * + * @param bExec + * specifies whether messages are really send (true) or just + * stored (false) + */ + public static void setExecute(boolean bExec) { + bExecute = bExec; + } + + /** + * Returns and clears the last command which should have been executed. + * + * @return Last command, plus "<"; + } + } + + // frees all ressources + public void close() { + try { + if (jmsSender != null) jmsSender.close(); + } catch (Exception exc) { } + try { + if (jmsSession != null) jmsSession.close(); + } catch (Exception exc) { } + try { + if (jmsConnection != null) jmsConnection.close(); + } catch (Exception exc) { } + } +} diff --git a/hsarback/src/de/hsadmin/core/qserv/QueueCommons.java b/hsarback/src/de/hsadmin/core/qserv/QueueCommons.java new file mode 100644 index 0000000..41305af --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/QueueCommons.java @@ -0,0 +1,14 @@ +package de.hsadmin.core.qserv; + +public class QueueCommons +{ + protected static final boolean TRANSACTED = true; + protected static final boolean IMMEDIATE = false; + protected static final boolean DEFAULT = IMMEDIATE; + + public QueueCommons() + { + super(); + } + +} \ No newline at end of file diff --git a/hsarback/src/de/hsadmin/core/qserv/QueueServer.java b/hsarback/src/de/hsadmin/core/qserv/QueueServer.java new file mode 100644 index 0000000..c709096 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/QueueServer.java @@ -0,0 +1,254 @@ +package de.hsadmin.core.qserv; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.Properties; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.QueueReceiver; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.naming.Context; +import javax.naming.InitialContext; + +public class QueueServer extends QueueCommons implements + MessageListener, ExceptionListener { + + private static final String VERSION_NO = "1.0"; + + private int nMessagesProcessed = 0; + private QueueConnection conn; + private QueueSession queueSession; + private String jmsStatusQueue; + private String jmsPassWord; + private String jmsUserName; + private String jmsSystemQueue; + private String jmsFactory; + + /** Runs the QueueServer, using the arguments as ConnectionFactory + * and Topic names. + */ + public static void main(String[] args) throws Exception { + File propFile = new File(System.getProperty("user.dir"), + "conf/qserv.properties"); + if (args.length == 1) + propFile = new File(args[0]); + else if (args.length != 0) { + throw new Exception( + "Wrong number of arguments.\n" + + "With no arguments '" + + propFile + + "' will be used as config file.\n" + + "Or give a properties file as single argument.\n\n" + + "Example config file:\n\n" + + "hsadmin.jms.factory=QueueCF\n" + + "hsadmin.jms.system-queue=hive-h01\n" + + "hsadmin.jms.status-queue=queue/hsadminStatus\n" + + "hsadmin.jms.username=hive-h01\n" + + "hsadmin.jms.password=geheimeskennwort\n"); + } + + FileInputStream propStream = new FileInputStream(propFile); + Properties props = new Properties(System.getProperties()); + props.load(propStream); + propStream.close(); + String stdout = System.getProperty("hsadmin.stdout"); + if (stdout != null && stdout.length() > 0) + System.setOut(new PrintStream(new FileOutputStream(stdout))); + String stderr = System.getProperty("hsadmin.stderr"); + if (stderr != null && stderr.length() > 0) + System.setErr(new PrintStream(new FileOutputStream(stderr))); + + final QueueServer qServ = new QueueServer(); + qServ.setJmsFactory(props.getProperty("hsadmin.jms.factory")); + qServ.setJmsSystemQueue(props.getProperty("hsadmin.jms.system-queue")); + qServ.setJmsStatusQueue(props.getProperty("hsadmin.jms.status-queue")); + qServ.setJmsUserName(props.getProperty("hsadmin.jms.username")); + qServ.setJmsPassWord(props.getProperty("hsadmin.jms.password")); + System.out.println("=================================================="); + System.out.println("hsadmin-qserv " + VERSION_NO + + " started using:"); + System.out.println("queue server: " + + props.getProperty("hsadmin.jms.factory")); + System.out.println("system queue: " + + props.getProperty("hsadmin.jms.system-queue")); + System.out.println("status queue: " + + props.getProperty("hsadmin.jms.status-queue")); + System.out.println("queue user: " + + props.getProperty("hsadmin.jms.username")); + System.out.println("=================================================="); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + qServ.close(); + } + }); + while (!qServ.connect()) { + Thread.sleep(10000); + } + while (true) { + Thread.sleep(10000); + } + } + + private boolean connect() { + // create JMS connection and session + try { + Context ctx = new InitialContext(); + QueueConnectionFactory connectionFactory = + (QueueConnectionFactory) ctx.lookup(jmsFactory); + conn = connectionFactory.createQueueConnection(jmsUserName, jmsPassWord); + conn.setExceptionListener(this); + queueSession = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = (Queue) ctx.lookup(jmsSystemQueue); + conn.start(); + QueueReceiver receiver = queueSession.createReceiver(queue); + receiver.setMessageListener(this); + return true; + } catch (Exception e) { + System.out.println(e.getMessage()); + return false; + } + } + + // / Is called when a message arrives; processes Processor within message. + public synchronized void onMessage(Message jmsMessage) { + // TODO logging + System.out.println("\nonMessage(" + jmsMessage); + QueueTask task = null; + try { + // expect ObjectMessage + ObjectMessage jmsObjectMessage = (ObjectMessage) jmsMessage; + + // get QueueTask + task = (QueueTask) jmsObjectMessage.getObject(); + + // TODO: logging + System.out.println("processing (" + task.getTitle() + " | started(" + + task.getStarted() + ") |" + task.getDetails() + "|" + + task.getProcessor() + ")"); + System.out.println("with " + task.getProcessor()); + // execute processor within the message + task.getProcessor().process(); + System.out.println("done"); + + // mark done + sendStatus(task); + } catch (Exception receiveException) { + System.err.println("exception " + receiveException); // TODO: + // logging + receiveException.printStackTrace(System.err); + if (receiveException.getCause() != null) { + System.err.println("caused by exception " + + receiveException.getCause()); // TODO: logging + receiveException.getCause().printStackTrace(System.err); + } else + System.err.println("no further cause available"); + task.setException(receiveException); + sendStatus(task); + } catch (Throwable t) { + System.err.println("severe exception " + t); // TODO: logging + } finally { + setNMessagesProcessed(getNMessagesProcessed() + 1); + notifyAll(); + } + } + + @Override + public void onException(JMSException e) { + System.out.println("Exception: " + e.getMessage()); + close(); + while (!connect()) { + try { + Thread.sleep(10000); + } catch (InterruptedException e1) { } + } + } + + public void close() { + if (queueSession != null) { + try { + queueSession.close(); + } catch (JMSException e1) { } + } + if (conn != null) { + try { + conn.close(); + } catch (JMSException e1) { } + } + } + + // / send the status information back to the hsadmin server + protected void sendStatus(QueueTask queueMessage) { + MessageProducer producer = null; + Session statusSession = null; + Connection statusConnection = null; + try { + System.out.println("sendStatus( " + queueMessage + ")"); + Context ctx = new InitialContext(); + System.out.println("Created InitialContext"); + ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup(jmsFactory); + System.out.println("connectionFactory= " + + connectionFactory.toString()); + Destination queue = (Destination) ctx.lookup(jmsStatusQueue); + statusConnection = connectionFactory.createConnection(jmsUserName, jmsPassWord); + statusSession = statusConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + producer = statusSession.createProducer(queue); + ObjectMessage statusMessage = statusSession.createObjectMessage(queueMessage); + System.out.println("send( " + statusMessage + ")"); + producer.send(statusMessage); + } catch (Exception statusException) { + // TODO: log exception + System.out.println("failed"); + statusException.printStackTrace(System.err); + } finally { + // close JMS + try { producer.close(); } catch (Exception exc) { } + try { statusSession.close(); } catch (Exception exc) { } + try { statusConnection.close(); } catch (Exception exc) { } + } + + } + + public void setNMessagesProcessed(int nMessagesProcessed) { + this.nMessagesProcessed = nMessagesProcessed; + } + + public int getNMessagesProcessed() { + return nMessagesProcessed; + } + + public void setJmsStatusQueue(String property) { + jmsStatusQueue = property; + } + + public void setJmsPassWord(String property) { + jmsPassWord = property; + } + + public void setJmsUserName(String property) { + jmsUserName = property; + } + + public void setJmsSystemQueue(String property) { + jmsSystemQueue = property; + } + + public void setJmsFactory(String property) { + jmsFactory = property; + } + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/QueueStatusReceiverServlet.java b/hsarback/src/de/hsadmin/core/qserv/QueueStatusReceiverServlet.java new file mode 100644 index 0000000..4918fd0 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/QueueStatusReceiverServlet.java @@ -0,0 +1,141 @@ +package de.hsadmin.core.qserv; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.QueueReceiver; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.persistence.EntityManager; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import de.hsadmin.cliClientConnector.TechnicalException; +import de.hsadmin.core.model.Transaction; +import de.hsadmin.core.util.Config; + +public class QueueStatusReceiverServlet extends HttpServlet + implements MessageListener, ExceptionListener { + + private static final long serialVersionUID = -5701350884034782083L; + + private String jmsUser; + private String jmsPass; + private QueueConnectionFactory queueConnectionFactory; + private QueueConnection queueConnection; + private QueueSession queueSession; + private boolean isConnected; + private int messageCount; + private int errorCount; + + @Override + public void init() throws ServletException { + isConnected = false; + messageCount = 0; + errorCount = 0; + try { + connect(); + } catch (NamingException e) { + throw new ServletException(e); + } + } + + private void connect() throws NamingException { + Config config = Config.getInstance(); + jmsUser = config.getProperty("hsadmin.jms.username", "hsadmin"); + jmsPass = config.getProperty("hsadmin.jms.password", "hsadmin-pw"); + InitialContext ctx = new InitialContext(); + Context env = (Context) ctx.lookup("java:comp/env"); + queueConnectionFactory = (QueueConnectionFactory) env.lookup("jms/QueueCF"); + while (!isConnected) { + try { + queueConnection = queueConnectionFactory.createQueueConnection(jmsUser, jmsPass); + queueConnection.setExceptionListener(this); + queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = (Queue) env.lookup("jms/hsadminStatus"); + queueConnection.start(); + QueueReceiver receiver = queueSession.createReceiver(queue); + receiver.setMessageListener(this); + isConnected = true; + } catch (JMSException e) { + close(); + try { + Thread.sleep(2000); + } catch (InterruptedException e1) { } + } + } + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.setContentType("text/plain"); + PrintWriter writer = resp.getWriter(); + writer.println("Verbindungsstatus: " + (isConnected ? "OK" : "ERROR")); + writer.println("Verarbeitete Nachrichten: " + messageCount); + writer.println("Verarbeitungsfehler: " + errorCount); + writer.close(); + } + + @Override + public void destroy() { + close(); + } + + private void close() { + try { if (queueSession != null) queueSession.close(); } catch (JMSException e) { } + try { if (queueConnection != null) queueConnection.close(); } catch (JMSException e) { } + isConnected = false; + } + + @Override + public void onMessage(Message jmsMessage) { + Transaction transaction = null; + messageCount++; + try { + ObjectMessage objMessage = (ObjectMessage) jmsMessage; + QueueTask detachedQT = (QueueTask) objMessage.getObject(); + transaction = new Transaction("statusreceiver"); + transaction.beginTransaction(); + EntityManager em = transaction.getEntityManager(); + // merging detachedQT directly makes Hibernate run into an endless recursion (stack overflow) + em.clear(); + QueueTask persistentQT = em.find(QueueTask.class, detachedQT.getId()); + persistentQT.assign(detachedQT); + Processor processor = persistentQT.getProcessor(); + if (processor != null) { + processor.finalize(transaction, persistentQT); + } + em.persist(persistentQT); + em.flush(); + transaction.commitTransaction(); + } catch (Exception e) { + errorCount++; + if (transaction != null) transaction.rollbackTransaction(); + throw new TechnicalException(e); + } finally { + if (transaction != null) transaction.close(); + } + } + + @Override + public void onException(JMSException exception) { + close(); + try { Thread.sleep(10000); } catch (InterruptedException e) { } + try { connect(); } catch (NamingException e) { } + } + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/QueueTask.java b/hsarback/src/de/hsadmin/core/qserv/QueueTask.java new file mode 100644 index 0000000..bdcbb68 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/QueueTask.java @@ -0,0 +1,232 @@ +package de.hsadmin.core.qserv; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.EntityManager; +import javax.persistence.FetchType; +import javax.persistence.GenerationType; + +import de.hsadmin.core.model.Entity; +import de.hsadmin.core.model.EntityInfo; +import de.hsadmin.core.model.ModuleImpl; +import de.hsadmin.mods.qstat.QTaskModuleImpl; +import de.hsadmin.mods.user.UnixUser; + +@javax.persistence.Entity(name = "QueueTasks") +@javax.persistence.Table(name = "queue_task") +@javax.persistence.SequenceGenerator(name = "QueueTaskSeqGen", sequenceName = "queue_task_id_seq") +@EntityInfo(name = "Systemauftrag") +@ModuleImpl(QTaskModuleImpl.class) +public class QueueTask extends Entity implements Serializable { + + private static final long serialVersionUID = 2171870783383767875L; + + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "QueueTaskSeqGen") + @javax.persistence.Column(name = "task_id", columnDefinition = "integer") + private long id; + + @javax.persistence.JoinColumn(name="user_id", columnDefinition="integer", nullable=true) + @javax.persistence.ManyToOne(fetch=FetchType.EAGER) + private UnixUser user; + + @javax.persistence.Column(name = "started", columnDefinition = "date") + @javax.persistence.Temporal(javax.persistence.TemporalType.TIMESTAMP) + private Date started; + + @javax.persistence.Column(name = "finished", columnDefinition = "date", nullable = true) + @javax.persistence.Temporal(javax.persistence.TemporalType.TIMESTAMP) + private Date finished; + + @javax.persistence.Column(name = "title", columnDefinition = "character varying(192)") + private String title; + + @javax.persistence.Column(name = "details", columnDefinition = "text", nullable = true) + private String details; + + private Processor proc; + + @javax.persistence.Column(name = "exception", columnDefinition = "text", nullable = true) + private String exception; + + public QueueTask() { + } + + public QueueTask(UnixUser user, String title, String details, Processor proc) { + this.user = user; + this.title = title; + this.details = details; + this.started = new Date(); + this.finished = null; + this.proc = proc; + this.exception = null; + } + + @Override + public UnixUser owningUser(EntityManager em) { + return user; + } + + /** + * determines whether the given user has full read access + * on all merged fields of this entity + */ + @Override + public boolean isReadAllowedFor(UnixUser loginUser) { + return loginUser.hasPacAdminRoleFor(getUser().getPac()) + || loginUser.id() == getUser().id(); + } + + /** + * JPA-Query restriction for these entities + * @return + */ + public static String restriction() { + return + // customers can see all entries of all users of their pacs + "obj.user.pac.customer.name=:loginUserName OR " + + // packet admins can access all entries of their users + // TODO: Hostsharing convention + "obj.user.pac.name=:loginUserName OR " + + // otherwise only their own tasks + "obj.user=:loginUser"; + } + + @Override + public String createStringKey() { + return Long.toString(id); + } + + @Override + public long id() { + return id; + } + + public long getId() { + return id; + } + + protected void setId(long id) { + this.id = id; + } + + public UnixUser getUser() { + return user; + } + + public void setUser(UnixUser user) { + this.user = user; + } + + public Date getStarted() { + return started; + } + + public void setStarted(Date started) { + this.finished = started; + } + + public Date getFinished() { + return finished; + } + + /** + * virtual attribute done + * @return + */ + @javax.persistence.Transient + public boolean isDone() { + return finished != null; + } + + public void done() { + finished = new java.util.Date(); + } + + /** + * virtual attribute status + * @return + */ + @javax.persistence.Transient + public QueueTaskStatus getStatus() { + return isDone() ? (exception == null ? QueueTaskStatus.DONE + : QueueTaskStatus.ERROR) : QueueTaskStatus.PENDING; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDetails() { + return details; + } + + public void setDetails(String details) { + this.details = details; + } + + public Processor getProcessor() { + return proc; + } + + public void setProcessor(Processor proc) { + this.proc = proc; + } + + public String getException() { + return exception; + }; + + public void setException(String exception) { + this.exception = exception; + } + + public void setException(Exception exception) { + this.exception = exception.toString(); + // TODO: stack+cause + } + + @Override + public boolean isNew() { + return id == 0; + } + + /** + * assigns all data field of qt to this instance, with + * the exception of the id + */ + public void assign(QueueTask qt) { + this.user = qt.user; + this.title = qt.title; + this.details = qt.details; + this.started = qt.started; + this.finished = qt.finished; + this.proc = qt.proc; + this.exception = qt.exception; + } + + @Override + public String getHiveName() { + return null; + } + + public enum QueueTaskStatus { + + PENDING("pending"), ERROR("error"), DONE("done"); + + private final String color; + + QueueTaskStatus(final String color) { + this.color = color; + } + + public String toString() { + return color; + } + } +} diff --git a/hsarback/src/de/hsadmin/core/qserv/ShellException.java b/hsarback/src/de/hsadmin/core/qserv/ShellException.java new file mode 100644 index 0000000..1970b54 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/ShellException.java @@ -0,0 +1,19 @@ +package de.hsadmin.core.qserv; + +public class ShellException + extends Exception +{ + private static final long serialVersionUID = 8335020360721047849L; + + int nExitCode; + + public ShellException() + { + } + + public ShellException( int exitCode, String message ) + { + super( message ); + nExitCode = exitCode; + } +} diff --git a/hsarback/src/de/hsadmin/core/qserv/ShellProcessor.java b/hsarback/src/de/hsadmin/core/qserv/ShellProcessor.java new file mode 100644 index 0000000..e4dca5c --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/ShellProcessor.java @@ -0,0 +1,76 @@ +package de.hsadmin.core.qserv; + + +/** + * A ShellProcessor encapsulates a shell command as a Processor. + * + * As such, a client can prepare shell commands to be processed on a server. + * + * @author mi + */ +public class ShellProcessor extends AbstractProcessor { + + private static final long serialVersionUID = -649045174380048818L; + + private String aSystemCall; + private String[] aEnv; + private String aInput; + private String aOutput; + private String aErrors; + + /** + * Constructor for a queue entry which executes a system call. + * + * @param aSystemCall + * the system call to be executed + */ + public ShellProcessor(String aSystemCall) { + this(aSystemCall, null, null); + } + + /** + * Constructor for a queue entry which executes a system call with stdin + * data. + * + * @param aSystemCall + * the system call to be executed + * @param aInput + * data for stdin of the system call + */ + public ShellProcessor(String aSystemCall, String aInput) { + this.aSystemCall = aSystemCall; + this.aInput = aInput; + } + + public ShellProcessor(String aSystemCall, String[] aEnv, String aInput) { + this.aSystemCall = aSystemCall; + this.aEnv = aEnv; + this.aInput = aInput; + } + + public Object process() throws ProcessorException { + try { + CommandShell.setEnvironment(aEnv); + aOutput = CommandShell.execute(aSystemCall, aInput); + return aOutput; + } catch (ShellException aExc) { + aExc.printStackTrace(System.err); // Logging + throw new ProcessorException(aExc); + } + } + + /** + * @return Returns stderr as a String. + */ + public String getErrors() { + return aErrors; + } + + /** + * @return Returns stdout as a String. + */ + public String getOutput() { + return aOutput; + } + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/TemplateProcessor.java b/hsarback/src/de/hsadmin/core/qserv/TemplateProcessor.java new file mode 100644 index 0000000..08b312b --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/TemplateProcessor.java @@ -0,0 +1,57 @@ +package de.hsadmin.core.qserv; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Map; + +import net.sf.jtpl.Template; + +public class TemplateProcessor extends AbstractProcessor { + + private static final long serialVersionUID = 4520635523274792876L; + + private String content = null; + private String targetPath = null; + private boolean overwriteTarget = false; + + public TemplateProcessor(String templateName, Map templateValues, String targetPath, boolean overwriteTarget) throws ProcessorException { + this.targetPath = targetPath; + this.overwriteTarget = overwriteTarget; + try { + InputStream stream = TemplateProcessor.class.getClassLoader().getResourceAsStream(templateName); + Template template = new Template(new InputStreamReader(stream)); + for (String key : templateValues.keySet()) { + template.assign(key, templateValues.get(key)); + } + template.parse("main"); + content = template.out(); + } catch (IOException e) { + throw new ProcessorException(e); + } + } + + @Override + public Object process() throws ProcessorException { + try { + File file = new File(targetPath); + if (file.exists() && !overwriteTarget) { + return null; + } + if (!file.exists() || file.canWrite()) { + BufferedWriter writer = new BufferedWriter(new FileWriter(file)); + writer.append(content); + writer.close(); + } else { + throw new ProcessorException("could not write file " + targetPath); + } + return null; + } catch (IOException e) { + throw new ProcessorException(e); + } + } + +} diff --git a/hsarback/src/de/hsadmin/core/qserv/WaitingTasksProcessor.java b/hsarback/src/de/hsadmin/core/qserv/WaitingTasksProcessor.java new file mode 100644 index 0000000..bace182 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/qserv/WaitingTasksProcessor.java @@ -0,0 +1,64 @@ +package de.hsadmin.core.qserv; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import de.hsadmin.core.model.Transaction; + +public class WaitingTasksProcessor extends AbstractProcessor { + + private static final long serialVersionUID = 1198528557513957546L; + + private Processor main; + private List waitingTasks; + + public WaitingTasksProcessor(Processor mainProcessor) { + this.main = mainProcessor; + this.waitingTasks = new ArrayList(); + } + + @Override + public Object process() throws ProcessorException { + return main.process(); + } + + @Override + public void finalize(Transaction transaction, QueueTask task) + throws ProcessorException { + if (task.getException() == null) { + for (WaitingProcessor p : waitingTasks) { + QueueTask wTask = + new QueueTask(task.getUser(), task.getTitle() + " / " + p.getTitle(), task.getTitle() + " / " + p.getTitle(), p.getProc()); + transaction.getEntityManager().persist(wTask); + transaction.enqueue(p.getHost(), wTask); + } + } + super.finalize(transaction, task); + } + + public void appendProcessor(String hostName, Processor waitingTask, String title) { + waitingTasks.add(new WaitingProcessor(hostName, waitingTask, title)); + } + + class WaitingProcessor implements Serializable { + private static final long serialVersionUID = -5205925170344385765L; + private String host; + private Processor proc; + private String title; + public WaitingProcessor(String hostName, Processor processor, String procTitle) { + host = hostName; + proc = processor; + title = procTitle; + } + public Processor getProc() { + return proc; + } + public String getHost() { + return host; + } + public String getTitle() { + return title; + } + } +} diff --git a/hsarback/src/de/hsadmin/core/util/Config.java b/hsarback/src/de/hsadmin/core/util/Config.java new file mode 100644 index 0000000..e917db4 --- /dev/null +++ b/hsarback/src/de/hsadmin/core/util/Config.java @@ -0,0 +1,53 @@ +package de.hsadmin.core.util; + +import java.io.File; +import java.io.FileReader; +import java.util.Properties; + +public class Config { + + private static Config instance; + + private Properties props; + + private Config() { + props = new Properties(); + File file = new File(System.getProperty("user.dir") + "/hsadmin.properties"); + if (!file.canRead()) { + file = new File(System.getProperty("user.dir") + "/conf/hsadmin.properties"); + } + if (!file.canRead()) { + file = new File(System.getProperty("user.home") + "/.hsadmin.properties"); + } + if (!file.canRead()) { + file = new File("/etc/hsadmin.properties"); + } + if (!file.canRead()) { + file = new File("/etc/hsadmin/hsadmin.properties"); + } + if (file.canRead()) { + try { + props.load(new FileReader(file)); + } catch (Exception e) { + // should not happen + e.printStackTrace(); + } + } + } + + public static Config getInstance() { + if (instance == null) { + instance = new Config(); + } + return instance; + } + + public String getProperty(String propertyName) { + return props.getProperty(propertyName).trim(); + } + + public String getProperty(String propertyName, String defaultValue) { + return props.getProperty(propertyName, defaultValue).trim(); + } + +} diff --git a/hsarback/src/de/hsadmin/mods/cust/BankAccount.java b/hsarback/src/de/hsadmin/mods/cust/BankAccount.java new file mode 100644 index 0000000..6329954 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/cust/BankAccount.java @@ -0,0 +1,164 @@ +package de.hsadmin.mods.cust; + +import static javax.persistence.FetchType.EAGER; + +import javax.persistence.EntityManager; + +import de.hsadmin.mods.user.UnixUser; + +@javax.persistence.Entity(name = "BankAccounts") +@javax.persistence.Table(name = "bank_account") +public class BankAccount extends de.hsadmin.core.model.Entity implements + java.io.Serializable { + private static final long serialVersionUID = 2965368183976686458L; + + public static String createQueryFromStringKey(String humanKey) { + return "customer.name = " + humanKey; + } + + @Override + public String createStringKey() { + return getCustomer().getName(); + } + + @Override + public long id() { + return id; + } + + // attribute id + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.SEQUENCE) + @javax.persistence.SequenceGenerator(name = "bank_account_id_seq_gen", sequenceName = "bank_account_bank_account_i_seq", allocationSize = 20) + @javax.persistence.Column(name = "bank_account_id", columnDefinition = "integer") + private long id; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + // attribute customer + @javax.persistence.JoinColumn(name = "bp_id", columnDefinition = "integer") + @javax.persistence.OneToOne(fetch = EAGER) + private Customer customer; + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + // attribute autoDebitGA + @javax.persistence.Column(name = "autodebit_ga", columnDefinition = "boolean", nullable = true) + private Boolean autoDebitGA; + + public Boolean isAutoDebitGA() { + return autoDebitGA; + } + + public Boolean getAutoDebitGA() { + return autoDebitGA; + } + + public void setAutoDebitGA(Boolean autoDebitGA) { + this.autoDebitGA = autoDebitGA; + } + + // attribute autoDebitAR + @javax.persistence.Column(name = "autodebit_ar", columnDefinition = "boolean", nullable = true) + private Boolean autoDebitAR; + + public Boolean isAutoDebitAR() { + return autoDebitAR; + } + + public Boolean getAutoDebitAR() { + return autoDebitAR; + } + + public void setAutoDebitAR(Boolean autoDebitAR) { + this.autoDebitAR = autoDebitAR; + } + + // attribute autoDebitOP + @javax.persistence.Column(name = "autodebit_op", columnDefinition = "boolean", nullable = true) + private Boolean autoDebitOP; + + public Boolean isAutoDebitOP() { + return autoDebitOP; + } + + public Boolean getAutoDebitOP() { + return autoDebitOP; + } + + public void setAutoDebitOP(Boolean autoDebitOP) { + this.autoDebitOP = autoDebitOP; + } + + // attribute bankCustomer + @javax.persistence.Column(name = "bank_customer", columnDefinition = "character varying(50)", nullable = true) + private String bankCustomer; + + public String getBankCustomer() { + return bankCustomer; + } + + public void setBankCustomer(String bankCustomer) { + this.bankCustomer = bankCustomer; + } + + // attribute bankAccount + @javax.persistence.Column(name = "bank_account", columnDefinition = "character varying(10)", nullable = true) + private String bankAccount; + + public String getBankAccount() { + return bankAccount; + } + + public void setBankAccount(String bankAccount) { + this.bankAccount = bankAccount; + } + + // attribute bankCode + @javax.persistence.Column(name = "bank_code", columnDefinition = "character varying(8)", nullable = true) + private String bankCode; + + public String getBankCode() { + return bankCode; + } + + public void setBankCode(String bankCode) { + this.bankCode = bankCode; + } + + // attribute bankName + @javax.persistence.Column(name = "bank_name", columnDefinition = "character varying(50)", nullable = true) + private String bankName; + + public String getBankName() { + return bankName; + } + + public void setBankName(String bankName) { + this.bankName = bankName; + } + + // / {$inheritDoc} + @Override + public boolean isNew() { + return id == 0; + } + + // / {@inheritDoc} + @Override + public UnixUser owningUser(EntityManager em) { + return customer.owningUser(em); + } +} diff --git a/hsarback/src/de/hsadmin/mods/cust/Contact.java b/hsarback/src/de/hsadmin/mods/cust/Contact.java new file mode 100644 index 0000000..4640f56 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/cust/Contact.java @@ -0,0 +1,282 @@ +package de.hsadmin.mods.cust; + +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.GenerationType.SEQUENCE; + +import javax.persistence.EntityManager; + +import de.hsadmin.mods.user.UnixUser; + +@javax.persistence.Entity(name = "Contacts") +@javax.persistence.Table(name = "contact") +@javax.persistence.SequenceGenerator(name = "ContactsSeqGen", sequenceName = "contact_contact_id_seq") +public class Contact extends de.hsadmin.core.model.Entity implements + java.io.Serializable { + + private static final long serialVersionUID = 9119607911598098558L; + + // attribute id + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy = SEQUENCE, generator = "ContactsSeqGen") + @javax.persistence.Column(name = "contact_id", columnDefinition = "integer") + private long id; + + // attribute customer + @javax.persistence.JoinColumn(name = "bp_id", columnDefinition = "integer") + @javax.persistence.ManyToOne(fetch = EAGER) + private Customer customer; + + // attribute salut + @javax.persistence.Column(name = "salut", columnDefinition = "character varying(30)") + private String salut; + + // attribute firstName + @javax.persistence.Column(name = "first_name", columnDefinition = "character varying(40)") + private String firstName; + + // attribute lastName + @javax.persistence.Column(name = "last_name", columnDefinition = "character varying(40)") + private String lastName; + + // attribute title + @javax.persistence.Column(name = "title", columnDefinition = "character varying(20)") + private String title; + + // attribute firma + @javax.persistence.Column(name = "firma", columnDefinition = "character varying(120)") + private String firma; + + // attribute co + @javax.persistence.Column(name = "co", columnDefinition = "character varying(50)") + private String co; + + // attribute street + @javax.persistence.Column(name = "street", columnDefinition = "character varying(50)") + private String street; + + // attribute zipCode + @javax.persistence.Column(name = "zipcode", columnDefinition = "character varying(10)") + private String zipCode; + + // attribute city + @javax.persistence.Column(name = "city", columnDefinition = "character varying(40)") + private String city; + + // attribute country + @javax.persistence.Column(name = "country", columnDefinition = "character varying(30)") + private String country; + + // attribute phonePrivate + @javax.persistence.Column(name = "phone_private", columnDefinition = "character varying(30)") + private String phonePrivate; + + // attribute phoneOffice + @javax.persistence.Column(name = "phone_office", columnDefinition = "character varying(30)") + private String phoneOffice; + + // attribute phoneMobile + @javax.persistence.Column(name = "phone_mobile", columnDefinition = "character varying(30)") + private String phoneMobile; + + // attribute fax + @javax.persistence.Column(name = "fax", columnDefinition = "character varying(30)") + private String fax; + + // attribute email + @javax.persistence.Column(name = "email", columnDefinition = "character varying(50)") + private String email; + + public Contact() { + } + + public Contact(Customer cust) { + this.customer = cust; + } + + public Contact(Customer cust, String salut, String title, String firstName, + String lastName, String firma, String co, String street, + String zipCode, String city, String country, String phonePrivate, + String phoneOffice, String phoneMobile, String fax, String email) { + this.customer = cust; + this.salut = salut; + this.title = title; + this.firstName = firstName; + this.lastName = lastName; + this.firma = firma; + this.co = co; + this.street = street; + this.zipCode = zipCode; + this.city = city; + this.country = country; + this.phonePrivate = phonePrivate; + this.phoneOffice = phoneOffice; + this.phoneMobile = phoneMobile; + this.fax = fax; + this.email = email; + } + + // / {@inheritDoc} + public static String createQueryFromStringKey(String humanKey) { + return "name='" + humanKey + "'"; + } + + // / {@inheritDoc} + @Override + public String createStringKey() { + return getCustomer().getName(); + } + + // / {@inheritDoc} + @Override + public long id() { + return id; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + public String getSalut() { + return salut; + } + + public void setSalut(String salut) { + this.salut = salut; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getFirma() { + return firma; + } + + public void setFirma(String firma) { + this.firma = firma; + } + + public String getCo() { + return co; + } + + public void setCo(String co) { + this.co = co; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getZipCode() { + return zipCode; + } + + public void setZipCode(String zipCode) { + this.zipCode = zipCode; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getPhonePrivate() { + return phonePrivate; + } + + public void setPhonePrivate(String phonePrivate) { + this.phonePrivate = phonePrivate; + } + + public String getPhoneOffice() { + return phoneOffice; + } + + public void setPhoneOffice(String phoneOffice) { + this.phoneOffice = phoneOffice; + } + + public String getPhoneMobile() { + return phoneMobile; + } + + public void setPhoneMobile(String phoneMobile) { + this.phoneMobile = phoneMobile; + } + + public String getFax() { + return fax; + } + + public void setFax(String fax) { + this.fax = fax; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + // / {$inheritDoc} + @Override + public boolean isNew() { + return id == 0; + } + + // / {@inheritDoc} + @Override + public UnixUser owningUser(EntityManager em) { + return null; // TODO: no access yet + } +} diff --git a/hsarback/src/de/hsadmin/mods/cust/Customer.java b/hsarback/src/de/hsadmin/mods/cust/Customer.java new file mode 100644 index 0000000..086b527 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/cust/Customer.java @@ -0,0 +1,267 @@ +package de.hsadmin.mods.cust; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.FetchType.LAZY; +import static javax.persistence.GenerationType.SEQUENCE; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.EntityManager; + +import de.hsadmin.mods.pac.Pac; +import de.hsadmin.mods.user.UnixUser; + +@javax.persistence.Entity(name = "Customers") +@javax.persistence.Table(name = "business_partner") +@javax.persistence.SequenceGenerator(name = "CustomersSeqGen", sequenceName = "business_partner_bp_id_seq") +public class Customer extends de.hsadmin.core.model.Entity implements Serializable { + + private static final long serialVersionUID = -7450594652238392616L; + + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy = SEQUENCE, generator = "CustomersSeqGen") + @javax.persistence.Column(name = "bp_id", columnDefinition = "integer") + private long id; + + @javax.persistence.Column(name = "member_id", columnDefinition = "integer") + private int memberNo; + + @javax.persistence.Column(name = "member_code", columnDefinition = "character varying(20)") + private String name; + + @javax.persistence.Column(name = "member_since", columnDefinition = "date", nullable = true) + private Date memberSince; + + @javax.persistence.Column(name = "member_until", columnDefinition = "date", nullable = true) + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date memberUntil; + + @javax.persistence.Column(name = "member_role", columnDefinition = "character varying(100)", nullable = true) + private String memberRole; + + @javax.persistence.Column(name = "author_contract", columnDefinition = "date", nullable = true) + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date authorContract; + + @javax.persistence.Column(name = "nondisc_contract", columnDefinition = "date", nullable = true) + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date nonDiscContract; + + @javax.persistence.Column(name = "shares_updated", columnDefinition = "date", nullable = true) + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date sharesUpdated; + + @javax.persistence.Column(name = "shares_signed", columnDefinition = "integer") + private int sharesSigned; + + @javax.persistence.Column(name = "uid_vat", columnDefinition = "character varying(20)", nullable = true) + private String uidVAT; + + @javax.persistence.OneToMany(fetch = EAGER, cascade = ALL, mappedBy = "customer") + private Set contacts; + + // virtual attribute bankAccount + @javax.persistence.OneToOne(fetch = EAGER, cascade = ALL, mappedBy = "customer") + private BankAccount bankAccount; + + // virtual attribute billData + @javax.persistence.OneToOne(fetch = EAGER, cascade = ALL, mappedBy = "customer") + private CustomersTariff billData; + + // virtual attribute pacs + @javax.persistence.OneToMany(fetch = LAZY, cascade = ALL, mappedBy = "customer") + @javax.persistence.OrderBy("name") + private Set pacs; + + public Customer() { + } + + public Customer(int memberNo, String memberCode) { + this.memberNo = memberNo; + this.name = memberCode; + } + + public static String createQueryFromStringKey(String humanKey) { + return "name='" + humanKey + "'"; + } + + @Override + public String createStringKey() { + return getName(); + } + + @Override + public long id() { + return id; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getMemberNo() { + return memberNo; + } + + public void setMemberNo(int memberNo) { + this.memberNo = memberNo; + } + + public String getName() { + return name; + } + + public void setName(String memberCode) { + this.name = memberCode; + } + + public Date getMemberSince() { + return memberSince; + } + + public void setMemberSince(Date memberSince) { + this.memberSince = memberSince; + } + + public Date getMemberUntil() { + return memberUntil; + } + + public void setMemberUntil(Date memberUntil) { + this.memberUntil = memberUntil; + } + + public String getMemberRole() { + return memberRole; + } + + public void setMemberRole(String memberRole) { + this.memberRole = memberRole; + } + + public Date getAuthorContract() { + return authorContract; + } + + public void setAuthorContract(Date authorContract) { + this.authorContract = authorContract; + } + + public Date getNonDiscContract() { + return nonDiscContract; + } + + public void setNonDiscContract(Date nonDiscContract) { + this.nonDiscContract = nonDiscContract; + } + + public Date getSharesUpdated() { + return sharesUpdated; + } + + public void setSharesUpdated(Date sharesUpdated) { + this.sharesUpdated = sharesUpdated; + } + + public int getSharesSigned() { + return sharesSigned; + } + + public void setSharesSigned(int sharesSigned) { + this.sharesSigned = sharesSigned; + } + + public String getUidVAT() { + return uidVAT; + } + + public void setUidVAT(String uidVAT) { + this.uidVAT = uidVAT; + } + + public Set getContacts() { + if (contacts == null) { + contacts = new HashSet(); + contacts.add(new Contact(this)); + } + return contacts; + } + + public void setContacts(Set contacts) { + this.contacts = contacts; + } + + // virtual attribute contractualContact + // TODO: currently only a single contact can be managed + @javax.persistence.Transient + public Contact getContractualContact() { + return getContacts().iterator().next(); + } + + public void setContractualContact(Contact contact) { + contacts = new HashSet(); + contacts.add(contact); + } + + public BankAccount getBankAccount() { + return bankAccount; + } + + public void setBankAccount(BankAccount bankAccount) { + this.bankAccount = bankAccount; + } + + public CustomersTariff getBillData() { + return billData; + } + + public void setBillData(CustomersTariff tariff) { + this.billData = tariff; + } + + public Set getPacs() { + return pacs; + } + + public void setPacs(Set pacs) { + this.pacs = pacs; + } + + @Override + public boolean isNew() { + return id == 0; + } + + @Override + public UnixUser owningUser(EntityManager em) { + return null; // TODO: no access yet + } + + /** + * determines whether the given user has full read access on all merged fields of this entity + */ + @Override + public boolean isReadAllowedFor(UnixUser loginUser) { + return loginUser.hasCustomerRoleFor(this); + } + + /** + * determines whether the given user has full write access on all merged fields of this entity + */ + @Override + public boolean isWriteAllowedFor(UnixUser loginUser) { + return loginUser.hasCustomerRoleFor(this); + } + + public static String restriction() { + return "name=:loginUserName"; + } +} diff --git a/hsarback/src/de/hsadmin/mods/cust/CustomersTariff.java b/hsarback/src/de/hsadmin/mods/cust/CustomersTariff.java new file mode 100644 index 0000000..a596727 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/cust/CustomersTariff.java @@ -0,0 +1,188 @@ +package de.hsadmin.mods.cust; + +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.GenerationType.SEQUENCE; + +import java.util.Date; + +import javax.persistence.EntityManager; + +import de.hsadmin.mods.user.UnixUser; + +@javax.persistence.Entity(name = "CustomersTariffs") +@javax.persistence.Table(name = "billdata") +@javax.persistence.SequenceGenerator(name = "CustomersTariffsSeqGen", sequenceName = "billdata_billdata_id_seq") +public class CustomersTariff extends de.hsadmin.core.model.Entity implements + java.io.Serializable { + + private static final long serialVersionUID = -3628577459027111705L; + + // / bean ctor + public CustomersTariff() { + } + + public CustomersTariff(Customer cust) { + this.customer = cust; + } + + // / {@inheritDoc} + public static String createQueryFromStringKey(String humanKey) { + return "customer.name = '" + humanKey + "'"; + } + + // / {@inheritDoc} + @Override + public String createStringKey() { + return getCustomer().getName(); + } + + // / {@inheritDoc} + @Override + public long id() { + return id; + } + + // attribute id + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy = SEQUENCE, generator = "CustomersTariffsSeqGen") + @javax.persistence.Column(name = "billdata_id", columnDefinition = "integer") + private long id; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + // attribute customer + @javax.persistence.JoinColumn(name = "bp_id", columnDefinition = "integer") + @javax.persistence.OneToOne(fetch = EAGER) + // TODO: das will ich gar nicht, geht aber nicht ohne?!? + private Customer customer; + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + // attribute domainDiscountSince + @javax.persistence.Column(name = "tariff_domain_discount_since", columnDefinition = "date") + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date domainDiscountSince; + + public Date getDomainDiscountSince() { + return domainDiscountSince; + } + + public void setDomainDiscountSince(Date domainDiscountSince) { + this.domainDiscountSince = domainDiscountSince; + } + + // attribute domainDiscountUntil + @javax.persistence.Column(name = "tariff_domain_discount_until", columnDefinition = "date") + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date domainDiscountUntil; + + public Date getDomainDiscountUntil() { + return domainDiscountUntil; + } + + public void setDomainDiscountUntil(Date domainDiscountUntil) { + this.domainDiscountUntil = domainDiscountUntil; + } + + // attribute trafficDiscountSince + @javax.persistence.Column(name = "tariff_traffic_discount_since", columnDefinition = "date") + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date trafficDiscountSince; + + public Date getTrafficDiscountSince() { + return trafficDiscountSince; + } + + public void setTrafficDiscountSince(Date trafficDiscountSince) { + this.trafficDiscountSince = trafficDiscountSince; + } + + // attribute trafficDiscountUntil + @javax.persistence.Column(name = "tariff_traffic_discount_until", columnDefinition = "date") + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date trafficDiscountUntil; + + public Date getTrafficDiscountUntil() { + return trafficDiscountUntil; + } + + public void setTrafficDiscountUntil(Date trafficDiscountUntil) { + this.trafficDiscountUntil = trafficDiscountUntil; + } + + // attribute quotaDiscountSince + @javax.persistence.Column(name = "tariff_quota_discount_since", columnDefinition = "date") + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date quotaDiscountSince; + + public Date getQuotaDiscountSince() { + return quotaDiscountSince; + } + + public void setQuotaDiscountSince(Date quotaDiscountSince) { + this.quotaDiscountSince = quotaDiscountSince; + } + + // attribute quotaDiscountUntil + @javax.persistence.Column(name = "tariff_quota_discount_until", columnDefinition = "date") + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date quotaDiscountUntil; + + public Date getQuotaDiscountUntil() { + return quotaDiscountUntil; + } + + public void setQuotaDiscountUntil(Date quotaDiscountUntil) { + this.quotaDiscountUntil = quotaDiscountUntil; + } + + // attribute discountSince + @javax.persistence.Column(name = "tariff_discount_since", columnDefinition = "date") + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date discountSince; + + public Date getDiscountSince() { + return discountSince; + } + + public void setDiscountSince(Date discountSince) { + this.discountSince = discountSince; + } + + // attribute discountUntil + @javax.persistence.Column(name = "tariff_discount_until", columnDefinition = "date") + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date discountUntil; + + public Date getDiscountUntil() { + return discountUntil; + } + + public void setDiscountUntil(Date discountUntil) { + this.discountUntil = discountUntil; + } + + // / {$inheritDoc} + @Override + public boolean isNew() { + return id == 0; + } + + // / {@inheritDoc} + @Override + public UnixUser owningUser(EntityManager em) { + return customer.owningUser(em); + } +} diff --git a/hsarback/src/de/hsadmin/mods/pac/BaseComponent.java b/hsarback/src/de/hsadmin/mods/pac/BaseComponent.java new file mode 100644 index 0000000..d8eb378 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/pac/BaseComponent.java @@ -0,0 +1,108 @@ +package de.hsadmin.mods.pac; + +import static javax.persistence.GenerationType.SEQUENCE; + +import java.io.Serializable; + +import javax.persistence.EntityManager; + +import de.hsadmin.mods.user.UnixUser; + +@javax.persistence.Entity(name = "BaseComponents") +@javax.persistence.Table(name = " basecomponent") +@javax.persistence.SequenceGenerator(name = "BaseComponentsSeqGen", sequenceName = "basecomponent_basecomponent_seq") +public class BaseComponent extends de.hsadmin.core.model.Entity implements Serializable { + + private static final long serialVersionUID = -8161827018235142603L; + + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy = SEQUENCE, generator = "BaseComponentsSeqGen") + @javax.persistence.Column(name = "basecomponent_id", columnDefinition = "integer") + private long id; + + @javax.persistence.Column(name = "basecomponent_code", columnDefinition = "character varying(10)") + private String feature; + + @javax.persistence.Column(name = "description", columnDefinition = "character varying(100)") + private String description; + + @javax.persistence.Column(name = "sorting", columnDefinition = "integer") + private int sorting; + + @javax.persistence.Column(name = "valid", columnDefinition = "boolean") + private boolean valid; + + public BaseComponent() { + } + + public BaseComponent(String feature, String desc, int sortPos, boolean valid) { + this.feature = feature; + this.description = desc; + this.sorting = sortPos; + this.valid = valid; + } + + public static String createQueryFromStringKey(String humanKey) { + return "feature='" + humanKey + "'"; + } + + @Override + public String createStringKey() { + return getFeature(); + } + + @Override + public long id() { + return id; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getFeature() { + return feature; + } + + public void setFeature(String code) { + this.feature = code; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getSorting() { + return sorting; + } + + public void setSorting(int sorting) { + this.sorting = sorting; + } + + public boolean getValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + + @Override + public boolean isNew() { + return id == 0; + } + + @Override + public UnixUser owningUser(EntityManager em) { + return null; // TODO: kinda somebody like root needed + } +} diff --git a/hsarback/src/de/hsadmin/mods/pac/BasePac.java b/hsarback/src/de/hsadmin/mods/pac/BasePac.java new file mode 100644 index 0000000..91a9801 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/pac/BasePac.java @@ -0,0 +1,141 @@ +package de.hsadmin.mods.pac; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.FetchType.LAZY; +import static javax.persistence.GenerationType.SEQUENCE; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +@javax.persistence.Entity(name = "BasePacs") +@javax.persistence.Table(name = "basepacket") +@javax.persistence.SequenceGenerator(name = "BasePacsSeqGen", sequenceName = "basepacket_basepacket_id_seq") +public class BasePac implements Serializable { + + private static final long serialVersionUID = 1491121619195513435L; + + public BasePac() { + } + + public BasePac(String name, String desc, int sortPos) { + this.name = name; + this.description = desc; + this.sorting = sortPos; + this.valid = true; + } + + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy = SEQUENCE, generator = "BasePacsSeqGen") + @javax.persistence.Column(name = "basepacket_id", columnDefinition = "integer") + private long id; + + @javax.persistence.Column(name = "basepacket_code", columnDefinition = "character varying(10)") + private String name; + + @javax.persistence.Column(name = "description", columnDefinition = "character varying(100)") + private String description; + + @javax.persistence.Column(name = "sorting", columnDefinition = "integer") + private int sorting; + + @javax.persistence.Column(name = "valid", columnDefinition = "boolean") + private boolean valid; + + @javax.persistence.OneToMany(fetch = LAZY, cascade = ALL) + private Set components; + + @javax.persistence.OneToMany(fetch = LAZY, cascade = ALL) + @javax.persistence.JoinTable(name = "packet_component", joinColumns = @javax.persistence.JoinColumn(name = "packet_id"), inverseJoinColumns = @javax.persistence.JoinColumn(name = "basepacket_id")) + private Set pacs; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getSorting() { + return sorting; + } + + public void setSorting(int sorting) { + this.sorting = sorting; + } + + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + + public Set getComponents() { + return components != null ? components + : (components = new HashSet()); + } + + public void setComponents(Set components) { + this.components = components; + } + + public Component getComponent(String feature) { + for (Component comp : getComponents()) + if (feature.equals(comp.getBaseComponent().getFeature())) + return comp; + return null; + } + + public void addComponent(Component comp) { + getComponents().add(comp); + } + + public void removeComponent(Component comp) { + getComponents().remove(comp); + } + + public Set getPacs() { + return pacs; + } + + public void setPacs(Set pacs) { + this.pacs = pacs; + } + + // generic + public boolean equals(Object aOther) { + if (aOther == null || aOther.getClass() != getClass()) + return false; + BasePac aOtherBP = (BasePac) aOther; + if (id != aOtherBP.id) + return false; + if (name != null && !name.equals(aOtherBP.name)) + return false; + if (description != null && description.equals(aOtherBP.description)) + return false; + if (sorting != aOtherBP.sorting) + return false; + if (valid != aOtherBP.valid) + return false; + return true; + } +} diff --git a/hsarback/src/de/hsadmin/mods/pac/Component.java b/hsarback/src/de/hsadmin/mods/pac/Component.java new file mode 100644 index 0000000..fd4d6be --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/pac/Component.java @@ -0,0 +1,117 @@ +package de.hsadmin.mods.pac; + +import java.io.Serializable; + +import javax.persistence.IdClass; + +@javax.persistence.Entity(name = "Components") +@javax.persistence.Table(name = "component") +@IdClass(ComponentId.class) +public class Component implements Serializable { + + private static final long serialVersionUID = 970709621200712794L; + + @javax.persistence.Id + private BasePac basePac; + + @javax.persistence.Id + private BaseComponent baseComponent; + + @javax.persistence.Column(name = "min_quantity", columnDefinition = "integer") + private int minimumQuantity; + + @javax.persistence.Column(name = "max_quantity", columnDefinition = "integer") + private int maximimumQuantity; + + @javax.persistence.Column(name = "default_quantity", columnDefinition = "integer") + private int defaultQuantity; + + @javax.persistence.Column(name = "increment_quantity", columnDefinition = "integer") + private int incrementQuantity; + + @javax.persistence.Column(name = "include_quantity", columnDefinition = "integer") + private int includedQuantity; + + @javax.persistence.Column(name = "admin_only", columnDefinition = "boolean") + private boolean adminOnly; + + public Component() { + } + + public Component(BasePac basePac, BaseComponent baseComp, int min, int max, + int def, int incr, int incl, boolean adminOnly) { + this.basePac = basePac; + this.baseComponent = baseComp; + this.minimumQuantity = min; + this.maximimumQuantity = max; + this.defaultQuantity = def; + this.incrementQuantity = incr; + this.includedQuantity = incl; + this.adminOnly = adminOnly; + } + + public BasePac getBasePac() { + return basePac; + } + + public void setBasePac(BasePac basePac) { + this.basePac = basePac; + } + + public BaseComponent getBaseComponent() { + return baseComponent; + } + + public void setBaseComponent(BaseComponent baseComponent) { + this.baseComponent = baseComponent; + } + + public int getMinimumQuantity() { + return minimumQuantity; + } + + public void setMinimumQuantity(int minimumQuantity) { + this.minimumQuantity = minimumQuantity; + } + + public int getMaximimumQuantity() { + return maximimumQuantity; + } + + public void setMaximimumQuantity(int maximimumQuantity) { + this.maximimumQuantity = maximimumQuantity; + } + + public int getDefaultQuantity() { + return defaultQuantity; + } + + public void setDefaultQuantity(int defiautoQuantity) { + this.defaultQuantity = defiautoQuantity; + } + + public int getIncrementQuantity() { + return incrementQuantity; + } + + public void setIncrementQuantity(int incrementQuantity) { + this.incrementQuantity = incrementQuantity; + } + + public int getIncludedQuantity() { + return includedQuantity; + } + + public void setIncludedQuantity(int includedQuantity) { + this.includedQuantity = includedQuantity; + } + + public boolean isAdminOnly() { + return adminOnly; + } + + public void setAdminOnly(boolean adminOnly) { + this.adminOnly = adminOnly; + } + +} diff --git a/hsarback/src/de/hsadmin/mods/pac/ComponentId.java b/hsarback/src/de/hsadmin/mods/pac/ComponentId.java new file mode 100644 index 0000000..3f961b9 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/pac/ComponentId.java @@ -0,0 +1,22 @@ +package de.hsadmin.mods.pac; + +public class ComponentId { + + public long basePac; + public long baseComponent; + + @Override + public boolean equals(Object obj) { + if (obj != null && obj instanceof ComponentId) { + ComponentId other = (ComponentId) obj; + return basePac == other.basePac && baseComponent == other.baseComponent; + } + return false; + } + + @Override + public int hashCode() { + return (new Long(basePac ^ baseComponent % Integer.MAX_VALUE)).intValue(); + } + +} diff --git a/hsarback/src/de/hsadmin/mods/pac/Hive.java b/hsarback/src/de/hsadmin/mods/pac/Hive.java new file mode 100644 index 0000000..cf7512e --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/pac/Hive.java @@ -0,0 +1,109 @@ +package de.hsadmin.mods.pac; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.FetchType.LAZY; +import static javax.persistence.GenerationType.SEQUENCE; + +import java.util.Set; + +import javax.persistence.EntityManager; + +import de.hsadmin.mods.user.UnixUser; + +@javax.persistence.Entity(name="Hives") +@javax.persistence.Table(name="hive") +@javax.persistence.SequenceGenerator(name="HivesSeqGen", sequenceName="hive_hive_id_seq") +public class Hive + extends de.hsadmin.core.model.Entity + implements java.io.Serializable +{ + private static final long serialVersionUID = -2270234313165009590L; + + /// bean ctor + public Hive() + { + } + + public Hive( String name, INetAddress inetAddr ) + { + this.name = name; + this.inetAddr = inetAddr; + } + + public Hive( String name, INetAddress inetAddr, String desc ) + { + this(name, inetAddr); + this.description = desc; + } + + /// {@inheritDoc} + public static String createQueryFromStringKey(String humanKey) + { + return "hiveName='" + humanKey + "'"; + } + + /// {@inheritDoc} + @Override + public String createStringKey() + { + return getHiveName(); + } + + /// {@inheritDoc} + @Override + public long id() + { + return id; + } + + // attribute id + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy=SEQUENCE, generator="HivesSeqGen") + @javax.persistence.Column(name="hive_id") + private long id; + public long getId() { return id; } + public void setId( long id ) { this.id = id; } + + // attribute hiveName + @javax.persistence.Column(name="hive_name", columnDefinition="character varying(3)", unique=true) + private String name; + public String getName() { return name; } + @Override + public String getHiveName() { return name; } + public void setName( String hiveName ) { this.name = hiveName; } + + // attribute inetAddr + @javax.persistence.JoinColumn(name="inet_addr_id") + @javax.persistence.ManyToOne(fetch=EAGER) + private INetAddress inetAddr; + public INetAddress getInetAddr() { return inetAddr; } + public void setInetAddr( INetAddress inetAddr ) { this.inetAddr = inetAddr; } + + // attribute description + @javax.persistence.Column(name="description", columnDefinition="character varying(100)") + private String description; + public String getDescription() { return description; } + public void setDescription( String description ) { this.description = description; } + + // virtual attribute pacs + @javax.persistence.OneToMany(fetch=LAZY, cascade=ALL, mappedBy="hive") + @javax.persistence.OrderBy("name") + private Set pacs; + public Set getPacs() { return pacs; } + public void setPacs( Set pacs) { this.pacs = pacs; } + + /// {$inheritDoc} + @Override + public boolean isNew() + { + return id == 0; + } + + /// {@inheritDoc} + @Override + public UnixUser owningUser( EntityManager em ) + { + return null; // TODO: kinda somebody like root needed + } +} diff --git a/hsarback/src/de/hsadmin/mods/pac/INetAddress.java b/hsarback/src/de/hsadmin/mods/pac/INetAddress.java new file mode 100644 index 0000000..0f28fc7 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/pac/INetAddress.java @@ -0,0 +1,87 @@ +package de.hsadmin.mods.pac; + +import static javax.persistence.GenerationType.SEQUENCE; + +import javax.persistence.EntityManager; + +import de.hsadmin.mods.user.UnixUser; + +@javax.persistence.Entity(name="INetAddresses") +@javax.persistence.Table(name="inet_addr") +@javax.persistence.SequenceGenerator(name="INetAddressesSeqGen", sequenceName="inet_addr_inet_addr_id_seq") +public class INetAddress + extends de.hsadmin.core.model.Entity + implements java.io.Serializable +{ + private static final long serialVersionUID = -5792279453911426607L; + + /// bean ctor + public INetAddress() + { + } + + public INetAddress( String inetAddr ) + { + this.inetAddr = inetAddr; + } + + public INetAddress( String inetAddr, String desc ) + { + this(inetAddr); + description = desc; + } + + /// {@inheritDoc} + public static String createQueryFromStringKey(String humanKey) + { + return "inetAddr='" + humanKey + "'"; + } + + /// {@inheritDoc} + @Override + public String createStringKey() + { + return getInetAddr(); + } + + /// {@inheritDoc} + @Override + public long id() + { + return id; + } + + // attribute id + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy=SEQUENCE, generator="INetAddressesSeqGen") + @javax.persistence.Column(name="inet_addr_id") + private long id; + public long getId() { return id; } + public void setId( long id ) { this.id = id; } + + // attribute inetAddr + @javax.persistence.Column(name="inet_addr", columnDefinition="inet", unique=true) + private String inetAddr; + public String getInetAddr() { return inetAddr; } + public void setInetAddr( String inetAddr ) { this.inetAddr = inetAddr; } + + // attribute description + @javax.persistence.Column(name="description", columnDefinition="character varying(100)") + private String description; + public String getDescription() { return description; } + public void setDescription( String description ) { this.description = description; } + + /// {$inheritDoc} + @Override + public boolean isNew() + { + return id == 0; + } + + /// {@inheritDoc} + @Override + public UnixUser owningUser( EntityManager em ) + { + return null; // TODO: kinda somebody like root needed + } +} diff --git a/hsarback/src/de/hsadmin/mods/pac/Pac.java b/hsarback/src/de/hsadmin/mods/pac/Pac.java new file mode 100644 index 0000000..0b8c86a --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/pac/Pac.java @@ -0,0 +1,280 @@ +package de.hsadmin.mods.pac; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.FetchType.LAZY; +import static javax.persistence.GenerationType.SEQUENCE; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.EntityManager; + +import de.hsadmin.mods.cust.Customer; +import de.hsadmin.mods.user.UnixUser; + +@javax.persistence.Entity(name = "Pacs") +@javax.persistence.Table(name = "packet") +@javax.persistence.SequenceGenerator(name = "PacsSeqGen", sequenceName = "packet_packet_id_seq") +@de.hsadmin.core.model.EntityInfo(name = "Paket") +public class Pac extends de.hsadmin.core.model.Entity implements Serializable { + + private static final long serialVersionUID = 1201899873300190132L; + + @javax.persistence.Id + @javax.persistence.GeneratedValue(strategy = SEQUENCE, generator = "PacsSeqGen") + @javax.persistence.Column(name = "packet_id") + private long id; + + @javax.persistence.Column(name = "packet_name", unique = true) + private String name; + + @javax.persistence.JoinColumn(name = "bp_id") + @javax.persistence.ManyToOne(fetch = EAGER) + private Customer customer; + + @javax.persistence.JoinColumn(name = "hive_id") + @javax.persistence.ManyToOne(fetch = EAGER) + private Hive hive; + + @javax.persistence.Column(name = "created") + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date created; + + @javax.persistence.Column(name = "cancelled", nullable = true) + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date cancelled; + + @javax.persistence.Column(name = "order_number", nullable = true) + private String orderNo; + + @javax.persistence.Column(name = "webserver_group", nullable = true) + private String webserverGroup; + + @javax.persistence.JoinColumn(name = "cur_inet_addr_id", nullable = true) + @javax.persistence.ManyToOne(fetch = EAGER) + private INetAddress curINetAddr; + + @javax.persistence.JoinColumn(name = "old_inet_addr_id", nullable = true) + @javax.persistence.ManyToOne(fetch = EAGER) + private INetAddress oldINetAddr; + + @javax.persistence.OneToMany(fetch = LAZY, cascade = ALL) + private java.util.Set pacComponents; + + @javax.persistence.OneToMany(fetch = LAZY, cascade = ALL, mappedBy = "pac") + private java.util.Set unixUser; + + public Pac() { + } + + public Pac(String name, Customer cust, BasePac basePac, Hive hive) { + this.name = name; + this.customer = cust; + this.hive = hive; + + this.created = new java.util.Date(); + this.webserverGroup = "httpd"; + this.curINetAddr = hive.getInetAddr(); + + pacComponents = new java.util.HashSet(); + java.util.Date today = new java.util.Date(); + for (Component comp : basePac.getComponents()) { + PacComponent pacComp = new PacComponent(basePac, + comp.getBaseComponent(), this, comp.getDefaultQuantity(), + today, null); + pacComponents.add(pacComp); + System.err.println("TRACE [Pac]: added pacComp " + pacComp + " to " + + getName()); + } + } + + // / {@inheritDoc} + public static String createQueryFromStringKey(String humanKey) { + return "obj.name='" + humanKey + "'"; + } + + // / {@inheritDoc} + @Override + public String createStringKey() { + return getName(); + } + + // / {@inheritDoc} + @Override + public long id() { + return id; + } + + public long getId() { + return id; + } + + protected void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + public Hive getHive() { + return hive; + } + + public void setHive(Hive hive) { + this.hive = hive; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getCancelled() { + return cancelled; + } + + public void setCancelled(Date cancelled) { + this.cancelled = cancelled; + } + + public String getOrderNo() { + return orderNo; + } + + public void setOrderNo(String orderNo) { + this.orderNo = orderNo; + } + + public String getwebserverGroup() { + return webserverGroup; + } + + public void setwebserverGroup(String webserverGroup) { + this.webserverGroup = webserverGroup; + } + + public INetAddress getCurINetAddr() { + return curINetAddr; + } + + public void setCurINetAddr(INetAddress curINetAddr) { + this.curINetAddr = curINetAddr; + } + + public INetAddress getOldINetAddr() { + return oldINetAddr; + } + + public void setOldINetAddr(INetAddress oldINetAddr) { + this.oldINetAddr = oldINetAddr; + } + + // virtual attribute basePac + /* + * This does not work, JPA/Hibernate always wants to create an invalid row + * in packet_component + * + * @javax.persistence.ManyToOne(fetch=EAGER, optional=true) // optional + * should be default anyway + * + * @javax.persistence.JoinTable( name="packet_component", + * joinColumns=@javax.persistence.JoinColumn(name="packet_id"), + * inverseJoinColumns=@javax.persistence.JoinColumn(name="basepacket_id") ) + */ + @javax.persistence.Transient + public BasePac getBasePac() { + return getPacComponents().iterator().next().getBasePac(); + } + + public void setBasePac(BasePac basePac) { + // TODO: needs code to change basePac in all pacComponents + } + + public java.util.Set getPacComponents() { + return pacComponents; + } + + public void setPacComponents(java.util.Set pacComponents) { + this.pacComponents = pacComponents; + } + + public PacComponent getPacComponent(String feature) { + for (PacComponent pc : getPacComponents()) + if (feature.equals(pc.getBaseComponent().getFeature())) + return pc; + return null; + } + + public java.util.Set getUnixUser() { + return unixUser; + } + + public void setUnixUser(java.util.Set unixUser) { + this.unixUser = unixUser; + } + + // generic + public String toString() { + return super.toString() + "{ name=" + name + " }"; + } + + // / {$inheritDoc} + @Override + public boolean isNew() { + return id == 0; + } + + // / {$inheritDoc} + @Override + public String getHiveName() { + if (isNew()) + return null; + else + return getHive().getHiveName(); + } + + // / {@inheritDoc} + @Override + public UnixUser owningUser(EntityManager em) { + return customer.owningUser(em); + } + + public UnixUser getAdminUser(EntityManager em) { + // TODO Auto-generated method stub + return null; + } + + // / determines whether the given user has full read access on all merged + // fields of this entity + public boolean isReadAllowedFor(UnixUser loginUser) { + return getName().equals(loginUser.getName()) + || super.isReadAllowedFor(loginUser); + } + + // / query restriction for access control + public static String restriction() { + return + // all pacs of customer + "obj.customer.name=:loginUserName OR " + + + // pac of packet admin + "obj.name=:loginUserName"; + } +} diff --git a/hsarback/src/de/hsadmin/mods/pac/PacComponent.java b/hsarback/src/de/hsadmin/mods/pac/PacComponent.java new file mode 100644 index 0000000..fbb82a4 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/pac/PacComponent.java @@ -0,0 +1,99 @@ +package de.hsadmin.mods.pac; + +import static javax.persistence.FetchType.EAGER; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.IdClass; + +@javax.persistence.Entity(name = "PacComponents") +@javax.persistence.Table(name = "packet_component") +@IdClass(PacComponentId.class) +public class PacComponent implements Serializable { + + private static final long serialVersionUID = 5359873462163274873L; + + @javax.persistence.Id + private Pac pac; + + @javax.persistence.Id + private BaseComponent baseComponent; + + @javax.persistence.JoinColumn(name = "basepacket_id", columnDefinition = "integer", nullable = false) + @javax.persistence.ManyToOne(fetch = EAGER) + private BasePac basePac; + + @javax.persistence.Column(name = "quantity", columnDefinition = "integer") + private int quantity; + + @javax.persistence.Column(name = "created", columnDefinition = "date", nullable = true) + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date created; + + @javax.persistence.Column(name = "cancelled", columnDefinition = "date", nullable = true) + @javax.persistence.Temporal(javax.persistence.TemporalType.DATE) + private Date cancelled; + + public PacComponent() { + } + + public PacComponent(BasePac basePac, BaseComponent baseComp, Pac pac, + int quantity, Date created, Date cancelled) { + this.basePac = basePac; + this.pac = pac; + this.baseComponent = baseComp; + this.quantity = quantity; + this.created = created; + this.cancelled = cancelled; + } + + public BasePac getBasePac() { + return basePac; + } + + public void setbasePac(BasePac basePac) { + this.basePac = basePac; + } + + public BaseComponent getBaseComponent() { + return baseComponent; + } + + public void setBaseComponent(BaseComponent baseComponent) { + this.baseComponent = baseComponent; + } + + public Pac getPac() { + return pac; + } + + public void setPac(Pac pac) { + this.pac = pac; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getCancelled() { + return cancelled; + } + + public void setCancelled(Date cancelled) { + this.cancelled = cancelled; + } + +} diff --git a/hsarback/src/de/hsadmin/mods/pac/PacComponentId.java b/hsarback/src/de/hsadmin/mods/pac/PacComponentId.java new file mode 100644 index 0000000..14a5403 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/pac/PacComponentId.java @@ -0,0 +1,23 @@ +package de.hsadmin.mods.pac; + + +public class PacComponentId { + + public long pac; + public long baseComponent; + + @Override + public boolean equals(Object obj) { + if (obj != null && obj instanceof PacComponentId) { + PacComponentId other = (PacComponentId) obj; + return pac == other.pac && baseComponent == other.baseComponent; + } + return false; + } + + @Override + public int hashCode() { + return (new Long(pac ^ baseComponent % Integer.MAX_VALUE)).intValue(); + } + +} diff --git a/hsarback/src/de/hsadmin/mods/qstat/QTaskModuleImpl.java b/hsarback/src/de/hsadmin/mods/qstat/QTaskModuleImpl.java new file mode 100644 index 0000000..ea23346 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/qstat/QTaskModuleImpl.java @@ -0,0 +1,38 @@ +package de.hsadmin.mods.qstat; + +import java.util.List; + +import de.hsadmin.core.model.AbstractModuleImpl; +import de.hsadmin.core.model.Entity; +import de.hsadmin.core.model.HSAdminException; +import de.hsadmin.mods.user.UnixUser; + +public class QTaskModuleImpl extends AbstractModuleImpl { + + private static final long serialVersionUID = 3942424688570077274L; + + public QTaskModuleImpl() { + } + + @Override + public List search(Class entityClass, String condition, String orderBy) throws HSAdminException { + // do query and return result + if (orderBy == null || orderBy.length() == 0) { + orderBy = "ORDER BY obj.started DESC"; + } + return super.search(entityClass, condition, orderBy); + } + + // throws an AuthorisationException if the login user has no write acess + // on the pac of the given UnixUser + @SuppressWarnings("unused") + private boolean hasAccessOnPacOf(UnixUser user) { + // only pac admins (same name as pac) and the owner (customer) have + // write access to the pac + boolean isPacAdmin = getLoginUser().getName().equals( + user.getPac().getName()); + boolean isCustomer = getLoginUser().getName().equals( + user.getPac().getCustomer().getName()); + return isPacAdmin || isCustomer; + } +} diff --git a/hsarback/src/de/hsadmin/mods/user/UnixUser.java b/hsarback/src/de/hsadmin/mods/user/UnixUser.java new file mode 100644 index 0000000..76619c5 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/user/UnixUser.java @@ -0,0 +1,323 @@ +package de.hsadmin.mods.user; + +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.GenerationType.SEQUENCE; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; +import javax.persistence.Transient; + +import de.hsadmin.core.model.EntityInfo; +import de.hsadmin.mods.pac.Pac; + +@Entity(name = "UnixUsers") +@Table(name = "unixuser") +@SequenceGenerator(name = "UnixUsersSeqGen", sequenceName = "unixuser_unixuser_id_seq") +@EntityInfo(name = "UNIX-Zugang/Mailbox") +public class UnixUser extends de.hsadmin.core.model.Entity implements Serializable { + + private static final long serialVersionUID = 7823071611805642906L; + + @Id + @GeneratedValue(strategy = SEQUENCE, generator = "UnixUsersSeqGen") + @Column(name = "unixuser_id", columnDefinition = "integer", updatable=false, insertable=false) + private long id; + + // attribute userid - really NOT unique! + @Column(name = "userid", columnDefinition = "integer", nullable = false, updatable=false) + private long userId; + + @Column(name = "name", columnDefinition = "character varying(24)", unique = true, updatable=false) + private String name; + + @Transient + private String password; + + @JoinColumn(name = "packet_id", columnDefinition = "integer", updatable=false) + @ManyToOne(fetch = EAGER) + private Pac pac; + + @Column(name = "comment", columnDefinition = "character varying(128)") + private String comment; + + @Column(name = "shell", columnDefinition = "character varying(32)") + private String shell; + + @Column(name = "homedir", columnDefinition = "character varying(48)", updatable=false) + private String homedir; + + @Column(name = "locked", columnDefinition = "boolean") + private boolean locked; + + @Column(name = "quota_softlimit", columnDefinition = "integer") + private Integer quotaSoftlimit; + + @Column(name = "quota_hardlimit", columnDefinition = "integer") + private Integer quotaHardlimit; + + public UnixUser() { + } + + public UnixUser(Pac pac, String name, int userId, String comment, + String homeDir, String shell) { + this.pac = pac; + this.name = name; + this.userId = userId; + this.comment = comment; + this.shell = shell; + init(); + } + + /** + * fixes some fields if null + */ + public void init() { + if (comment == null) { + comment = name; + } + if (homedir == null) { + this.homedir = getDefaultHomedir(); + } + if (shell == null) { + shell = "/bin/false"; + } + if (quotaSoftlimit == null) { + quotaSoftlimit = new Integer(0); + } + if (quotaHardlimit == null) { + quotaHardlimit = new Integer(0); + } + } + + public static String createQueryFromStringKey(String humanKey) { + return "obj.name='" + humanKey + "'"; + } + + @Override + public String createStringKey() { + return getName(); + } + + @Override + public long id() { + return id; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPassword() { + // TODO: should NOT be an external property + return password; + } + public void setPassword(String password) { + this.password = password; + } + + public Pac getPac() { + return pac; + } + + public void setPac(Pac pac) { + this.pac = pac; + pac.getUnixUser().add(this); + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public String getShell() { + return shell; + } + + public void setShell(String shell) { + this.shell = shell; + } + + public String getHomedir() { + return homedir; + } + + public void setHomedir(String homedir) { + this.homedir = homedir; + } + + /** + * returns true if the user has the default homedir path + */ + public boolean isDefaultHomedir() { + return getHomedir().equals(getDefaultHomedir()); + } + + /** + * returns the default homedir path + */ + private String getDefaultHomedir() { + String pacName = pac.getName(); + if (name.equals(pacName)) + return "/home/pacs/" + pacName; + else + return "/home/pacs/" + pacName + "/users/" + + name.substring(pacName.length() + 1); + } + + public boolean isLocked() { + return locked; + } + + public void setLocked(boolean locked) { + this.locked = locked; + } + + public Integer getQuotaSoftlimit() { + return quotaSoftlimit; + } + + public void setQuotaSoftlimit(Integer quota) { + this.quotaSoftlimit = quota; + } + + public void setQuotaHardlimit(Integer quotaLimit) { + this.quotaHardlimit = quotaLimit; + } + + public Integer getQuotaHardlimit() { + return quotaHardlimit; + } + + @Override + public boolean isNew() { + return id == 0; + } + + @Override + public void initialize(EntityManager em, UnixUser loginUser) { + pac = loginUser.getPac(); // a default useful for the pac admin + // TODO should not be hardcoded, but how? + homedir = "/home/pacs/" + pac.getName() + "/users/..."; + } + + @Override + public UnixUser merge(EntityManager em, UnixUser loginUser) { + if (homedir == null) + homedir = "/home/pacs/" + pac.getName() + "/users/" + + getName().substring(6); // TODO: Hack + + UnixUser dbEntity = (UnixUser) super.merge(em, loginUser); + dbEntity.setPassword(this.getPassword()); // because this is not in database + return dbEntity; + } + + @Override + public String getHiveName() { + if (isNew()) + return null; + else + return getPac().getHiveName(); + } + + @Override + public UnixUser owningUser(EntityManager em) { + return getPac().getAdminUser(em); + } + + /** + * determines whether this user account is a hostmaster account + */ + public boolean hasHostmasterRole() { + // TODO: hardcoded Hostsharing conventions + return getName().length() == 2; + } + + /** + * determines whether this user account has rights on the given customer + */ + public boolean hasCustomerRoleFor(de.hsadmin.mods.cust.Customer cust) { + // TODO: hardcoded Hostsharing conventions + return getName().equals(cust.getName()) || hasHostmasterRole(); + } + + /** + * determines whether this user account has admin rights on the given pac + */ + public boolean hasPacAdminRoleFor(de.hsadmin.mods.pac.Pac pac) { + // TODO: hardcoded Hostsharing conventions + return pac.getName().equals(getName()) + || hasCustomerRoleFor(pac.getCustomer()); + } + + /** + * determines whether this user account has admin rights on the given dom + */ +// public boolean hasDomAdminRoleFor(de.hsadmin.mods.dom.Domain dom) { +// // TODO: hardcoded Hostsharing conventions +// return this.getId() == dom.getUser().getId() +// || hasPacAdminRoleFor(dom.getUser().getPac()); +// } + + @Override + public boolean isWriteAllowedFor(UnixUser loginUser) { + String pacName = pac.getName(); + if (!name.equals(pacName) && !name.startsWith(pacName + "-")) + return false; + + if (super.isWriteAllowedFor(loginUser)) + return true; + return this == loginUser || loginUser.hasPacAdminRoleFor(getPac()); + } + + @Override + public boolean isReadAllowedFor(UnixUser loginUser) { + if (super.isReadAllowedFor(loginUser)) + return true; + return this.getId() == loginUser.getId() || loginUser.hasPacAdminRoleFor(getPac()); + } + + /** + * JPA-Query restriction for these entities + */ + public static String restriction() { + return + // customers can access all users in their pacs + "obj.pac.customer.name = :loginUserName OR " + // packet admins can access all users in their pacs + + "obj.pac.name = :loginUserName OR " + // all users can access their own account + + "obj.name = :loginUserName"; + } +} diff --git a/hsarback/src/de/hsadmin/mods/user/UnixUserModuleImpl.java b/hsarback/src/de/hsadmin/mods/user/UnixUserModuleImpl.java new file mode 100644 index 0000000..8cb217f --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/user/UnixUserModuleImpl.java @@ -0,0 +1,279 @@ +package de.hsadmin.mods.user; + +import java.util.LinkedList; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import de.hsadmin.core.model.AuthorisationException; +import de.hsadmin.core.model.Entity; +import de.hsadmin.core.model.AbstractModuleImpl; +import de.hsadmin.core.model.HSAdminException; +import de.hsadmin.mods.pac.Pac; + +public class UnixUserModuleImpl extends AbstractModuleImpl { + + private static final long serialVersionUID = 7480353553685400790L; + + public UnixUserModuleImpl() { + } + + @Override + public Entity initialize(Entity newEntity) throws AuthorisationException { + UnixUser newUnixUser = (UnixUser) super.initialize(newEntity); + newUnixUser.setName(getLoginUser().getPac().getName() + '-'); + return newUnixUser; + } + + @Override + public Entity find(Class entityClass, Object key) throws HSAdminException { + // do query + UnixUser res = (UnixUser) super.find(entityClass, key); + + // check access rights + needsPartialAccessOnPacOf(res, "find"); + + // return clean result + return res; + } + + @Override + public Entity findByString(Class entityClass, String key) throws HSAdminException { + // do query + UnixUser res = (UnixUser) super.findByString(entityClass, key); + + // return directly (checking rights already done in search within + // findByString) + return res; + } + + @Override + public List search(Class entityClass, String condition, String orderBy) throws HSAdminException { + // do query + if (orderBy == null || orderBy.length() == 0) { + orderBy = "ORDER BY obj.name ASC"; + } + List res = super.search(entityClass, condition, orderBy); + List ret = new LinkedList(); + + // remove entities where login user has no access rights + if (res != null) { + for (Entity entity : res) { + try { + UnixUser returnedUnixUser = (UnixUser) entity; + needsPartialAccessOnPacOf(returnedUnixUser, "search"); + ret.add(returnedUnixUser); + } catch (AuthorisationException exc) { + } // ignore + } + } + + // return clean result + return ret; + } + + @Override + public Entity add(Entity newEntity) throws HSAdminException { + EntityManager em = getEntityManager(); + // only allow pac which matches the username (TODO: hard coded + // Hostsharing convention) + UnixUser newUnixUser = (UnixUser) newEntity; + // validation of username and password + String name = newUnixUser.getName(); + if (name == null) { + throw new HSAdminException("username is required"); + } + String userName = name.toLowerCase().trim(); + for (char c : userName.toCharArray()) { + if (!(Character.isLetterOrDigit(c) || c == '.' || c == '-' || c == '_')) { + throw new AuthorisationException(getLoginUser(), "add", newUnixUser, "userId"); + } + } + if (userName.length() < 7 || userName.charAt(5) != '-' || userName.lastIndexOf('-') > 5) { + throw new AuthorisationException(getLoginUser(), "add", newUnixUser, "userId"); + } + String passWord = newUnixUser.getPassword(); + if (passWord == null || passWord.length() == 0) { + throw new HSAdminException("password is required"); + } + if (passWord.indexOf(':') >= 0) { + throw new AuthorisationException(getLoginUser(), "add", newUnixUser, "userId"); + } + Query qPac = em.createQuery("SELECT obj FROM Pacs obj WHERE obj.name = :pacName"); + qPac.setParameter("pacName", userName.substring(0, 5)); + Pac pac = (Pac) qPac.getSingleResult(); + newUnixUser.setName(userName); + newUnixUser.setHomedir("/home/pacs/" + userName.substring(0, 5) + "/users/" + userName.substring(6)); + + // no appropriate uid? + if (newUnixUser.getUserId() < 1000) { + // determine next free uid + long nUID = 20000; + Query qUID = em.createQuery("SELECT MAX(u.userId) FROM UnixUsers u"); + Long maxUid = (Long) qUID.getSingleResult(); + if (maxUid >= nUID) + nUID = maxUid + 1; + newUnixUser.setUserId(nUID); + } else { + // given uid belongs to same pac? + Query q = em.createQuery("SELECT u.userId FROM UnixUsers u WHERE u.userId = :userId AND u.pac = :pac"); + q.setParameter("userId", newUnixUser.getUserId()); + q.setParameter("pac", pac); + List idOfSamePac = q.getResultList(); + if (idOfSamePac.size() == 0) { + throw new AuthorisationException(getLoginUser(), "add", newUnixUser, "userId"); + } + } + + // don't move this up, it will update the new entity still with wrong + // userid! + newUnixUser.setPac(pac); + newUnixUser.init(); + + // authorisation check + needsFullAccessOnPacOf(newUnixUser, "add"); + + // add new entity + return super.add(newEntity); + } + + private EntityManager getEntityManager() { + return getTransaction().getEntityManager(); + } + + @Override + public Entity update(Entity existingEntity) throws HSAdminException { + // get the entity from the database + UnixUser detachedUnixUser = (UnixUser) existingEntity; + UnixUser attachedUnixUser = getEntityManager().find(detachedUnixUser.getClass(), + detachedUnixUser.getId()); + + // authorisation check + needsFullAccessOnUser(attachedUnixUser, "update"); + + // update fields where the login user has write access + if (attachedUnixUser.getUserId() != detachedUnixUser.getUserId()) + throw new AuthorisationException(getLoginUser(), "update", + detachedUnixUser, "id"); + attachedUnixUser.setName(detachedUnixUser.getName()); + attachedUnixUser.setPassword(detachedUnixUser.getPassword()); + if (hasFullAccessOnPacOf(attachedUnixUser)) + attachedUnixUser.setComment(detachedUnixUser.getComment()); + else if (!attachedUnixUser.getComment().equals( + detachedUnixUser.getComment())) + throw new AuthorisationException(getLoginUser(), "update", + detachedUnixUser, "comment"); + if (hasFullAccessOnPacOf(attachedUnixUser)) + attachedUnixUser.setHomedir(detachedUnixUser.getHomedir()); + else if (!attachedUnixUser.getHomedir().equals( + detachedUnixUser.getHomedir())) + throw new AuthorisationException(getLoginUser(), "update", + detachedUnixUser, "homedir"); + if (!attachedUnixUser.getShell().equals(detachedUnixUser.getShell())) + if (hasFullAccessOnPacOf(attachedUnixUser) + || isLoginShell(attachedUnixUser.getShell()) + || !isLoginShell(detachedUnixUser.getShell())) + attachedUnixUser.setShell(detachedUnixUser.getShell()); + else + throw new AuthorisationException(getLoginUser(), "update", + detachedUnixUser, "shell"); + if (attachedUnixUser.isLocked() != detachedUnixUser.isLocked()) + throw new AuthorisationException(getLoginUser(), "update", + detachedUnixUser, "locked"); + if (detachedUnixUser.getQuotaSoftlimit() != null) { + if (hasFullAccessOnPacOf(attachedUnixUser)) { + attachedUnixUser.setQuotaSoftlimit(detachedUnixUser.getQuotaSoftlimit()); + if (detachedUnixUser.getQuotaHardlimit() != null) { + attachedUnixUser.setQuotaHardlimit(detachedUnixUser.getQuotaHardlimit()); + } + } + else { + Integer oldQuota = attachedUnixUser.getQuotaSoftlimit(); + Integer newQuota = detachedUnixUser.getQuotaSoftlimit(); + if (oldQuota != newQuota && !oldQuota.equals(newQuota)) + throw new AuthorisationException(getLoginUser(), "update", + detachedUnixUser, "quota"); + } + } + + // update entity + return super.update(attachedUnixUser); + } + + @Override + public void delete(Entity existingEntity) throws HSAdminException { + // get the entity from the database + UnixUser detachedUnixUser = (UnixUser) existingEntity; + UnixUser attachedUnixUser = getEntityManager().find(detachedUnixUser.getClass(), + detachedUnixUser.getId()); + + // authorisation check + if (attachedUnixUser.getName().length() < 7) { + throw new AuthorisationException(attachedUnixUser, "delete"); + } + needsFullAccessOnPacOf(attachedUnixUser, "delete"); + + // delete entity + super.delete(attachedUnixUser); + } + + // throws an AuthorisationException if the login user has no write acess + // on the pac of the given UnixUser + private boolean hasFullAccessOnPacOf(UnixUser user) { + // only pac admins (same name as pac) and the owner (customer) have + // write access to the pac + boolean isPacAdmin = getLoginUser().getName().equals( + user.getPac().getName()); + boolean isCustomer = getLoginUser().getName().equals( + user.getPac().getCustomer().getName()); + boolean isHostmaster = getLoginUser().hasHostmasterRole(); + return isPacAdmin || isCustomer || isHostmaster; + } + + // throws an AuthorisationException if the login user has no write acess + // on the pac of the given UnixUser + private void needsFullAccessOnPacOf(UnixUser user, String method) + throws AuthorisationException { + if (!hasFullAccessOnPacOf(user)) + throw new AuthorisationException(getLoginUser(), method, user); + } + + // throws an AuthorisationException if the login user has no read acess on + // the pac of the given UnixUser + private void needsPartialAccessOnPacOf(UnixUser user, String method) + throws AuthorisationException { + if (!hasFullAccessOnPacOf(user) + && getLoginUser().getPac().id() != user.getPac().id()) + throw new AuthorisationException(getLoginUser(), method, user); + } + + // throws an AuthorisationException if the login user has not even partial + // write access on the given UnixUser + private void needsFullAccessOnUser(UnixUser user, String method) + throws AuthorisationException { + // neither pac admin (same name as pac), pac owner (customer) nor the + // user itself? + if (!hasFullAccessOnPacOf(user) && !getLoginUser().sameIdAs(user)) + throw new AuthorisationException(getLoginUser(), method, user); + } + + // returns true if the given shell is a login shell + private static boolean isLoginShell(String shell) { + // TODO: list of login shells should not be hardcoded + if (shell.equals("/bin/sh")) + return true; + if (shell.equals("/bin/bash")) + return true; + if (shell.equals("/bin/csh")) + return true; + if (shell.equals("/bin/tcsh")) + return true; + if (shell.equals("/bin/zsh")) + return true; + if (shell.equals("/bin/ksh")) + return true; + return false; + } + +} diff --git a/hsarback/src/de/hsadmin/mods/user/UnixUserProcessorFactory.java b/hsarback/src/de/hsadmin/mods/user/UnixUserProcessorFactory.java new file mode 100644 index 0000000..c9ded82 --- /dev/null +++ b/hsarback/src/de/hsadmin/mods/user/UnixUserProcessorFactory.java @@ -0,0 +1,105 @@ +package de.hsadmin.mods.user; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.persistence.EntityManager; + +import de.hsadmin.core.model.Entity; +import de.hsadmin.core.qserv.CompoundProcessor; +import de.hsadmin.core.qserv.EntityProcessorFactory; +import de.hsadmin.core.qserv.Processor; +import de.hsadmin.core.qserv.ShellProcessor; + +/** + * Factory class which creates Processor instances for dealing with UNIX user + * accounts. + * + * @author mi + */ +public class UnixUserProcessorFactory implements EntityProcessorFactory { + + /** + * @return a Processor which creates a new UNIX user account + */ + public Processor createCreateProcessor(EntityManager em, + T entity) { + UnixUser user = (UnixUser) entity; + CompoundProcessor aCP = new CompoundProcessor(new ShellProcessor( + "newusers", user.getName() + ":" + user.getPassword() + ":" + + user.getUserId() + ":" + user.getPac().getName() + + ":" + user.getComment() + ":" + user.getHomedir() + + ":" + user.getShell() + "\n")); + appendSetQuotaProcessor(aCP, user); + appendMakeMaildirProcessor(aCP, user); + return aCP; + } + + /** + * @return a Processor which updates an existing UNIX user account + */ + public Processor createUpdateProcessor(EntityManager em, + T entity) { + UnixUser user = (UnixUser) entity; + CompoundProcessor aCP = new CompoundProcessor(new ShellProcessor( + "usermod -c '" + user.getComment() + "'" + " -d '" + + user.getHomedir() + "'" + " -s '" + user.getShell() + + "' " + user.getName())); + if (user.getPassword() != null && user.getPassword().length() > 0) + aCP.appendProcessor(new ShellProcessor("chpasswd ", user.getName() + + ":" + user.getPassword() + "\n")); + appendSetQuotaProcessor(aCP, user); + return aCP; + } + + /** + * @return a Processor which deletes an existing UNIX user account + */ + public Processor createDeleteProcessor(EntityManager em, + T entity) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyMMdd-HHmm-"); + String trashPrefix = "/home/trash/" + sdf.format(new Date()); + + UnixUser user = (UnixUser) entity; + CompoundProcessor aCP = new CompoundProcessor(); + if (user.isDefaultHomedir()) + aCP.appendProcessor(new ShellProcessor("mv '" + user.getHomedir() + + "' '" + trashPrefix + user.getName() + "'")); + aCP.appendProcessor(new ShellProcessor("userdel " + user.getName())); + return aCP; + } + + private void appendSetQuotaProcessor(CompoundProcessor aCP, UnixUser user) { + Integer quotaSoft = user.getQuotaSoftlimit(); + if (quotaSoft == null || quotaSoft.intValue() == 0) { + aCP.appendProcessor(new ShellProcessor("setquota -u " + + user.getName() + " 0 0 0 0 " + + "`df /home/pacs/ | tail -n1 | cut -d' ' -f1`")); + return; + } + Integer userSoftQuota = quotaSoft * 1024; + Integer quotaHard = user.getQuotaHardlimit(); + if (quotaHard == null) { + quotaHard = new Integer(0); + } + Integer userHardQuota = quotaHard * 1024; + if (userHardQuota.intValue() < userSoftQuota.intValue()) { + // set default value + userHardQuota = ((Double) (userSoftQuota * 1.5 + 32)).intValue(); + } + aCP.appendProcessor(new ShellProcessor("setquota -u " + + user.getName() + " " + userSoftQuota + " " + + userHardQuota + " 0 0 " + + "`df /home/pacs/ | tail -n1 | cut -d' ' -f1`")); + } + + private void appendMakeMaildirProcessor(CompoundProcessor aCP, UnixUser user) { + aCP.appendProcessor( + new ShellProcessor( + "su -l " + user.getName() + " -s \"/bin/bash\" -c \"maildirmake " + + user.getHomedir() + "/Maildir" + + "\"" + )); + } + +} diff --git a/hsarback/src/de/hsadmin/remote/AbstractRemote.java b/hsarback/src/de/hsadmin/remote/AbstractRemote.java new file mode 100644 index 0000000..3150fd8 --- /dev/null +++ b/hsarback/src/de/hsadmin/remote/AbstractRemote.java @@ -0,0 +1,229 @@ +package de.hsadmin.remote; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import de.hsadmin.core.model.AuthenticationException; +import de.hsadmin.core.model.AuthorisationException; +import de.hsadmin.core.model.Entity; +import de.hsadmin.core.model.GenericModuleImpl; +import de.hsadmin.core.model.HSAdminException; +import de.hsadmin.core.model.ModuleInterface; +import de.hsadmin.core.model.TicketAuthentication; +import de.hsadmin.core.model.Transaction; +import de.hsadmin.mods.user.UnixUser; + +public abstract class AbstractRemote implements IRemote { + + private TicketAuthentication authentication; + + public AbstractRemote() { + authentication = new TicketAuthentication(); + } + + protected abstract Class getEntityClass(); + + protected abstract void entity2map(Entity entity, Map resultMap); + + protected abstract void map2entity(Map setParams, Entity entity); + + protected abstract void regularizeKeys(Map whereParams); + + /* (non-Javadoc) + * @see de.hsadmin.remote.IRemote#search(java.lang.String, java.lang.String, java.util.Map) + */ + public List> search(String runAsUser, String ticket, + Map whereParams) throws HSAdminException { + String user = runAsUser; + Transaction transaction = new Transaction(user); + try { + if (authentication.login(user, ticket)) { + ModuleInterface module = new GenericModuleImpl(transaction); + UnixUser unixUser = null; + unixUser = (UnixUser) module.findByString(UnixUser.class, user); + List list = module.search(getEntityClass(), + buildQueryCondition(whereParams), null); + ArrayList> result = new ArrayList>(); + for (Entity e : list) { + HashMap entry = new HashMap(); + entity2map(e, entry); + if (e.isReadAllowedFor(unixUser)) { + result.add(entry); + } + } + return result; + } else { + throw new AuthenticationException("authentication failed"); + } + } catch (SecurityException e) { + throw new HSAdminException(e); + } catch (IllegalArgumentException e) { + throw new HSAdminException(e); + } finally { + transaction.close(); + } + } + + /* (non-Javadoc) + * @see de.hsadmin.remote.IRemote#add(java.lang.String, java.lang.String, java.util.Map) + */ + public Map add(String runAsUser, String ticket, + Map setParams) throws HSAdminException { + String user = runAsUser; + Transaction transaction = new Transaction(user); + try { + if (authentication.login(user, ticket)) { + ModuleInterface module = new GenericModuleImpl(transaction); + Constructor constructor = + getEntityClass().getConstructor(); + Entity entity = constructor.newInstance(); + map2entity(setParams, entity); + transaction.beginTransaction(); + Entity insertedEntity = module.add(entity); + transaction.commitTransaction(); + HashMap entry = new HashMap(); + entity2map(insertedEntity, entry); + return entry; + } else { + throw new AuthenticationException("authentication failed"); + } + } catch (Exception e) { + throw new HSAdminException(e); + } finally { + transaction.close(); + } + } + + /* (non-Javadoc) + * @see de.hsadmin.remote.IRemote#delete(java.lang.String, java.lang.String, java.util.Map) + */ + public void delete(String runAsUser, String ticket, + Map whereParams) throws HSAdminException { + String user = runAsUser; + Transaction transaction = new Transaction(user); + try { + if (authentication.login(user, ticket)) { + ModuleInterface module = new GenericModuleImpl(transaction); + UnixUser unixUser = null; + unixUser = (UnixUser) module.findByString(UnixUser.class, user); + String queryCondition = buildQueryCondition(whereParams); + if (queryCondition == null || queryCondition.length() == 0) { + throw new HSAdminException( + "better safe than sorry: no where parameter found"); + } + List list = module.search(getEntityClass(), + queryCondition, null); + transaction.beginTransaction(); + for (Entity e : list) { + if (e.isWriteAllowedFor(unixUser)) { + module.delete(e); + } else { + throw new AuthorisationException(unixUser, "delete", e); + } + } + transaction.commitTransaction(); + } else { + throw new AuthenticationException("authentication failed"); + } + } catch (SecurityException e) { + throw new HSAdminException(e); + } catch (IllegalArgumentException e) { + throw new HSAdminException(e); + } finally { + transaction.close(); + } + } + + /* (non-Javadoc) + * @see de.hsadmin.remote.IRemote#update(java.lang.String, java.lang.String, java.util.Map, java.util.Map) + */ + public List> update(String runAsUser, String ticket, + Map setParams, Map whereParams) + throws HSAdminException { + String user = runAsUser; + Transaction transaction = new Transaction(user); + try { + if (authentication.login(user, ticket)) { + ModuleInterface module = new GenericModuleImpl(transaction); + UnixUser unixUser = null; + unixUser = (UnixUser) module.findByString(UnixUser.class, user); + ArrayList> result = new ArrayList>(); + String queryCondition = buildQueryCondition(whereParams); + if (queryCondition == null || queryCondition.length() == 0) { + throw new HSAdminException( + "better safe than sorry: no where parameter found"); + } + List list = module.search(getEntityClass(), + queryCondition, "ORDER BY name ASC"); + transaction.beginTransaction(); + for (Entity update : list) { + if (update.isWriteAllowedFor(unixUser)) { + transaction.detach(update); + map2entity(setParams, update); + update = module.update(update); + HashMap entry = new HashMap(); + entity2map(update, entry); + result.add(entry); + } else { + throw new AuthorisationException(unixUser, "update", update); + } + } + transaction.commitTransaction(); + return result; + } else { + throw new AuthenticationException("authentication failed"); + } + } catch (SecurityException e) { + throw new HSAdminException(e); + } catch (IllegalArgumentException e) { + throw new HSAdminException(e); + } finally { + transaction.close(); + } + } + + protected boolean assertNotNull(String string) { + return string != null && string.length() > 0; + } + + protected boolean assertNotNull(Integer integ) { + return integ != null; + } + + private String buildQueryCondition(Map whereParams) { + regularizeKeys(whereParams); + StringBuffer cond = new StringBuffer(); + Iterator keyIterator = whereParams.keySet().iterator(); + while (keyIterator.hasNext()) { + if (cond.length() > 0) { + cond.append(" AND "); + } + String field = keyIterator.next(); + String value = whereParams.get(field).replaceAll("'", "\'"); + cond.append("obj."); + cond.append(field); + cond.append(" = '"); + cond.append(value); + cond.append("'"); + } + return cond.toString(); + } + + protected void replaceKey(Map whereParams, String shortKey, String regularKey) { + if (whereParams.containsKey(shortKey)) { + String value = whereParams.get(shortKey); + whereParams.remove(shortKey); + whereParams.put(regularKey, value); + } + } + + protected boolean assertNotNull(Date aDate) { + return aDate != null; + } + +} diff --git a/hsarback/src/de/hsadmin/remote/IRemote.java b/hsarback/src/de/hsadmin/remote/IRemote.java new file mode 100644 index 0000000..32ca056 --- /dev/null +++ b/hsarback/src/de/hsadmin/remote/IRemote.java @@ -0,0 +1,35 @@ +package de.hsadmin.remote; + +import java.util.List; +import java.util.Map; + +import de.hsadmin.core.model.HSAdminException; + +public interface IRemote { + + public abstract List> search( + String runAsUser, + String ticket, + Map whereParams + ) throws HSAdminException; + + public abstract Map add( + String runAsUser, + String ticket, + Map setParams + ) throws HSAdminException; + + public abstract void delete( + String runAsUser, + String ticket, + Map whereParams + ) throws HSAdminException; + + public abstract List> update( + String runAsUser, + String ticket, + Map setParams, + Map whereParams + ) throws HSAdminException; + +} \ No newline at end of file diff --git a/hsarback/src/de/hsadmin/remote/QueueTaskRemote.java b/hsarback/src/de/hsadmin/remote/QueueTaskRemote.java new file mode 100644 index 0000000..7b2136e --- /dev/null +++ b/hsarback/src/de/hsadmin/remote/QueueTaskRemote.java @@ -0,0 +1,49 @@ +package de.hsadmin.remote; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; + +import de.hsadmin.core.model.Entity; +import de.hsadmin.core.qserv.QueueTask; + +public class QueueTaskRemote extends AbstractRemote { + + private static final DateFormat df = SimpleDateFormat.getDateInstance(DateFormat.SHORT); + + @Override + protected Class getEntityClass() { + return QueueTask.class; + } + + @Override + protected void entity2map(Entity entity, Map resultMap) { + QueueTask task = (QueueTask) entity; + resultMap.put("id", Long.toString(task.getId())); + resultMap.put("status", task.getStatus().toString()); + resultMap.put("title", task.getTitle()); + resultMap.put("details", task.getDetails()); + resultMap.put("exception", task.getException()); + resultMap.put("user", task.getUser().getName()); + Date started = task.getStarted(); + if (assertNotNull(started)) { + resultMap.put("started", df.format(started)); + } + Date finished = task.getFinished(); + if (assertNotNull(finished)) { + resultMap.put("finished", df.format(finished)); + } + } + + @Override + protected void map2entity(Map setParams, Entity entity) { + // never used + } + + @Override + protected void regularizeKeys(Map whereParams) { + replaceKey(whereParams, "user", "user.name"); + } + +} diff --git a/hsarback/src/de/hsadmin/remote/UnixUserRemote.java b/hsarback/src/de/hsadmin/remote/UnixUserRemote.java new file mode 100644 index 0000000..1b3c849 --- /dev/null +++ b/hsarback/src/de/hsadmin/remote/UnixUserRemote.java @@ -0,0 +1,81 @@ +package de.hsadmin.remote; + +import java.util.Map; + +import de.hsadmin.core.model.Entity; +import de.hsadmin.mods.user.UnixUser; + +public class UnixUserRemote extends AbstractRemote { + + @Override + protected Class getEntityClass() { + return UnixUser.class; + } + + @Override + protected void entity2map(Entity entity, Map map) { + UnixUser user = (UnixUser) entity; + map.put("id", Long.toString(user.getId())); + map.put("name", user.getName()); + map.put("comment", user.getComment()); + map.put("userid", Long.toString(user.getUserId())); + map.put("pac", user.getPac().getName()); + map.put("shell", user.getShell()); + map.put("homedir", user.getHomedir()); + Integer quotaSoft = user.getQuotaSoftlimit(); + if (assertNotNull(quotaSoft)) { + map.put("quota_softlimit", quotaSoft.toString()); + } + Integer quotaHard = user.getQuotaHardlimit(); + if (assertNotNull(quotaHard)) { + map.put("quota_hardlimit", quotaHard.toString()); + } + } + + @Override + protected void map2entity(Map map, Entity entity) { + UnixUser user = (UnixUser) entity; + String id = map.get("id"); + if (assertNotNull(id)) { + user.setId(Long.parseLong(id)); + } + String name = map.get("name"); + if (assertNotNull(name)) { + user.setName(name); + } + String password = map.get("password"); + if (assertNotNull(password)) { + user.setPassword(password); + } + String comment = map.get("comment"); + if (assertNotNull(comment)) { + user.setComment(comment); + } + String userid = map.get("userid"); + if (assertNotNull(userid)) { + user.setUserId(Long.parseLong(userid)); + } + String shell = map.get("shell"); + if (assertNotNull(shell)) { + user.setShell(shell); + } + String homedir = map.get("homedir"); + if (assertNotNull(homedir)) { + user.setHomedir(homedir); + } + String quota = map.get("quota_softlimit"); + if (assertNotNull(quota)) { + user.setQuotaSoftlimit(Integer.parseInt(quota)); + } + String quotaLimit = map.get("quota_hardlimit"); + if (assertNotNull(quotaLimit)) { + user.setQuotaHardlimit(Integer.parseInt(quotaLimit)); + } + } + + @Override + protected void regularizeKeys(Map whereParams) { + replaceKey(whereParams, "pac", "pac.name"); + } + +} diff --git a/hsarback/src/net/sf/jtpl/Jtpl.java b/hsarback/src/net/sf/jtpl/Jtpl.java new file mode 100644 index 0000000..f9b0057 --- /dev/null +++ b/hsarback/src/net/sf/jtpl/Jtpl.java @@ -0,0 +1,307 @@ +/* + Copyright 2009 jtpl.sourceforge.net + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package net.sf.jtpl; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Jtpl: a very simple template engine for Java
+ * Contact: emmanuel.alliel@gmail.com
+ * Web: http://jtpl.sourceforge.net
+ * + * @version $LastChangedRevision: 51 $ + * @author Emmanuel ALLIEL + * @author Staffan Olsson + * + *

+ * Template syntax:
+ *    Variables:
+ *       {VARIABLE_NAME}
+ *    Blocks:
+ *       <!-- BEGIN: BlockName -->
+ *           <!-- BEGIN: SubBlockName -->
+ *           <!-- END: SubBlockName -->
+ *       <!-- END: BlockName -->
+ *

+ * License: Apache 2.0
+*/ + +public class Jtpl implements Serializable +{ + private static final long serialVersionUID = -7175035307912229580L; + + private HashMap blocks = new HashMap(); + private HashMap parsedBlocks = new HashMap(); + private HashMap subBlocks = new HashMap(); + private HashMap vars = new HashMap(); + // flag for backwards compatibility, will probably be reomved in future releases + private boolean failSilently = false; + private boolean implicitMain = false; + + /** + * Constructs a Jtpl object and reads the template from a file. + * For backwards compatibility this constructor enables 'failSilently'. + * @param fileName the file name of the template, exemple: "java/folder/index.tpl" + * @throws IOException when an i/o error occurs while reading the template. + */ + public Jtpl(String fileName) throws IOException + { + this(new File(fileName)); + // this is the old constructor so it enables the old (pre-1.3) behavior on errors + this.failSilently = true; + } + + /** + * Constructs a Jtpl object and reads the template from a file. + * @param file readable file containing template source + * @throws IOException when an i/o error occurs while reading the template. + */ + public Jtpl(File file) throws IOException + { + FileReader fr = new FileReader(file); + String fileText = readFile(fr); + makeTree(fileText); + } + + /** + * Constructs a Jtpl object and reads the template from arbitrary input source. + * @param template the template source + * @throws IOException when an i/o error occurs while reading the template. + */ + public Jtpl(Reader template) throws IOException + { + String fileText = readFile(template); + makeTree(fileText); + } + + /** + * Assign a template variable. + * For variables that are used in blocks, the variable value + * must be set before parse is called. + * @param varName the name of the variable to be set. + * @param varData the new value of the variable. + */ + public void assign(String varName, String varData) + { + vars.put(varName, varData); + } + + /** + * Generates the HTML page and return it into a String. + */ + public String out() + { + if (this.implicitMain) { + this.parse("main"); + } + Object main = parsedBlocks.get("main"); + if (main == null) { + throw new IllegalStateException("'main' block not parsed"); + } + return(main.toString()); + } + + /** + * Parse a template block. + * If the block contains variables, these variables must be set + * before the block is added. + * If the block contains subblocks, the subblocks + * must be parsed before this block. + * @param blockName the name of the block to be parsed. + * @throws IllegalArgumentException if the block name is not found (and failSiletly==false) + */ + public void parse(String blockName) throws IllegalArgumentException + { + String copy = ""; + if (implicitMain && !"main".equals(blockName) && !blockName.startsWith("main.")) { + blockName = "main." + blockName; + } + try { + copy = blocks.get(blockName).toString(); + } catch (NullPointerException e) { + if (!this.failSilently) throw new IllegalArgumentException( + "Block '" + blockName + "' not found." + + " Matches " + locateBlock(blockName)); + } + Pattern pattern = Pattern.compile("\\{([\\w\\.]+)\\}"); + Matcher matcher = pattern.matcher(copy); + pattern = Pattern.compile("_BLOCK_\\.(.+)"); + for (Matcher matcher2; matcher.find();) + { + String match = matcher.group(1); + matcher2 = pattern.matcher(match); + if (matcher2.find()) + { + if (parsedBlocks.containsKey(matcher2.group(1))) + { + copy = copy.replaceFirst("\\{"+match+"\\}", escape( + parsedBlocks.get(matcher2.group(1)).toString())); + } + else + { + copy = copy.replaceFirst("\\{"+match+"\\}", ""); + } + } + else + { + if (vars.containsKey(match)) + { + copy = copy.replaceFirst("\\{"+match+"\\}", escape( + vars.get(match).toString())); + } + else + { + // Leave unchanged because it might be wanted in output. + // Can always be removed by assigning empty value. + //copy = copy.replaceFirst("\\{"+match+"\\}", ""); + } + } + } + if (parsedBlocks.containsKey(blockName)) + { + parsedBlocks.put(blockName, parsedBlocks.get(blockName) + copy); + } + else + { + parsedBlocks.put(blockName, copy); + } + if (subBlocks.containsKey(blockName)) + { + parsedBlocks.put(subBlocks.get(blockName), ""); + } + } + + /** + * Template parsing uses regex replace to insert result text, + * which means that special characters in replacement string must be escaped. + * @param replacement The text that should appear in output. + * @return Text escaped so that it works as String.replaceFirst replacement. + */ + protected String escape(String replacement) { + return replacement.replace("\\", "\\\\").replace("$", "\\$"); + } + + /** + * Lists the blocks that end with the given blockName. + * @param blockName The name as used in parse + * @return Blocks where blockName is the child + * (the Set's toString lists the full names) + */ + protected Set locateBlock(final String blockName) { + Set matches = new java.util.HashSet(); + for (Iterator it = blocks.keySet().iterator(); it.hasNext(); ) { + Object b = it.next(); + if (b.toString().endsWith('.' + blockName)) matches.add(b); + } + return matches; + } + + private String readFile(Reader fr) throws IOException + { + StringBuffer content = new StringBuffer(); + for (int c; (c = fr.read()) != -1; content.append((char)c)); + fr.close(); + return content.toString(); + } + + private void makeTree(String fileText) + { + // BEGIN: implicit main + if (!Pattern.compile(".*.*", Pattern.DOTALL) + .matcher(fileText).matches()) { + this.implicitMain = true; // affects parse(block) and out() + fileText = "" + fileText + ""; + } + // END: implicit main + Pattern pattern = Pattern.compile("(.*?)(?=(?:)|(?:\\s*$))", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + Matcher matcher = pattern.matcher(fileText); + ArrayList blockNames = new ArrayList(); + String parentName = ""; + int lastlength = 0; // used in newline trimming to see if one block immediately follows the previous + while (matcher.find()) + { + // BEGIN: newline trimming + String after = matcher.group(3); // contents after tag + if (lastlength == 0 || fileText.charAt(matcher.start() - 1) == '\n') { + after = after.replaceFirst("^\\r?\\n", ""); + } + lastlength = after.length(); + // END: newline trimming + if (matcher.group(1).toUpperCase().equals("BEGIN")) + { + parentName = implode(blockNames); + blockNames.add(matcher.group(2)); + String currentBlockName = implode(blockNames); + if (blocks.containsKey(currentBlockName)) + { + blocks.put(currentBlockName, blocks.get(currentBlockName) + after); + } + else + { + blocks.put(currentBlockName, after); + } + if (blocks.containsKey(parentName)) + { + blocks.put(parentName, blocks.get(parentName) + "{_BLOCK_." + currentBlockName + "}"); + } + else + { + blocks.put(parentName, "{_BLOCK_." + currentBlockName + "}"); + } + subBlocks.put(parentName, currentBlockName); + subBlocks.put(currentBlockName, ""); + } + else if (matcher.group(1).toUpperCase().equals("END")) + { + blockNames.remove(blockNames.size()-1); + parentName = implode(blockNames); + if (blocks.containsKey(parentName)) + { + blocks.put(parentName, blocks.get(parentName) + after); + } + else + { + blocks.put(parentName, after); + } + } + } + } + + private String implode(ArrayList al) + { + String ret = ""; + for (int i = 0; al.size() > i; i++) + { + if (i != 0) + { + ret += "."; + } + ret += al.get(i); + } + return (ret); + } + +} diff --git a/hsarback/src/net/sf/jtpl/Template.java b/hsarback/src/net/sf/jtpl/Template.java new file mode 100644 index 0000000..2c23fa7 --- /dev/null +++ b/hsarback/src/net/sf/jtpl/Template.java @@ -0,0 +1,136 @@ +/* + Copyright 2009 jtpl.sourceforge.net + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package net.sf.jtpl; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringReader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.sf.jtpl.Jtpl; + +/** + * The forward compatible template engine interface, + * replacing {@link Jtpl}. + */ +public class Template implements Serializable { + + private static final long serialVersionUID = 3916690583929137140L; + + private Jtpl t; + + public Template(Reader template) throws IOException { + t = new Jtpl(template); + } + + public Template(String templateSource) { + try { + t = new Jtpl(new StringReader(templateSource)); + } catch (IOException e) { + throw new RuntimeException(e); // should be impossible with StringReader + } + } + + public Template(File templateFile) throws FileNotFoundException { + FileReader r = new FileReader(templateFile); + try { + t = new Jtpl(templateFile); + } catch (IOException e) { + try { + r.close(); + } catch (Exception nothingToDoAboutIt) {} + } + } + + // proxying methods from Jtpl1 + + public Template assign(String varName, String varData) { + t.assign(varName, varData); + return this; + } + + public Template parse(String blockName) throws IllegalArgumentException { + t.parse(blockName); + return this; + } + + public String out() { + return t.out(); + } + + // bean properties support + + public Template parse(String blockName, Object bean) { + assignAll(bean); + return parse(blockName); + } + + public String out(Object bean) { + assignAll(bean); + return out(); + } + + protected void assignAll(Object bean) { + Map p = getBeanProperties(bean); + Iterator i = p.keySet().iterator(); + while (i.hasNext()) { + String key = (String) i.next(); + assign(key.toUpperCase(), "" + p.get(key)); + } + } + + /** + * @param bean The instance that has properties named according to JavaBean standard. + * @return Map that should be considered immutable + */ + protected Map getBeanProperties(Object bean) { + HashMap values = new HashMap(); + if (bean == null) return values; + Method[] m = bean.getClass().getMethods(); + + Pattern p = Pattern.compile("get([A-Z]\\w+)"); + + for (int i = 0; i < m.length; i++) { + if (m[i].getName().equals("getClass")) continue; + if (m[i].getParameterTypes().length > 0) continue; + Matcher r = p.matcher(m[i].getName()); + if (r.matches()) { + try { + values.put(r.group(1).toLowerCase(), m[i].invoke(bean, new Object[0])); + } catch (IllegalArgumentException e) { + throw e; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + } + return values; + } + +} + diff --git a/hsarback/src/org/apache/xmlrpc/webserver/XmlRpcServlet.properties b/hsarback/src/org/apache/xmlrpc/webserver/XmlRpcServlet.properties new file mode 100644 index 0000000..fcef01f --- /dev/null +++ b/hsarback/src/org/apache/xmlrpc/webserver/XmlRpcServlet.properties @@ -0,0 +1,2 @@ +user=de.hsadmin.remote.UnixUserRemote +q=de.hsadmin.remote.QueueTaskRemote