diff options
author | Simon Rettberg | 2015-09-08 17:38:07 +0200 |
---|---|---|
committer | Simon Rettberg | 2015-09-08 17:38:07 +0200 |
commit | 3aa85cd588b745fb2537dbe748580d9c3b980133 (patch) | |
tree | 42d2c622b089c40b856d9ef09ed50eaa68407b68 /src | |
parent | Start adapting to new DB/Thrift model (diff) | |
download | masterserver-3aa85cd588b745fb2537dbe748580d9c3b980133.tar.gz masterserver-3aa85cd588b745fb2537dbe748580d9c3b980133.tar.xz masterserver-3aa85cd588b745fb2537dbe748580d9c3b980133.zip |
Compilable...
Diffstat (limited to 'src')
31 files changed, 1015 insertions, 1476 deletions
diff --git a/src/main/java/org/openslx/imagemaster/App.java b/src/main/java/org/openslx/imagemaster/App.java index 24dfffc..6c834b0 100644 --- a/src/main/java/org/openslx/imagemaster/App.java +++ b/src/main/java/org/openslx/imagemaster/App.java @@ -1,12 +1,14 @@ package org.openslx.imagemaster; +import java.net.InetAddress; +import java.net.SocketException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.apache.thrift.transport.TTransportException; -import org.openslx.imagemaster.serverconnection.CrcScheduler; +import org.openslx.imagemaster.localrpc.NetworkHandler; import org.openslx.imagemaster.thrift.server.BinaryListener; import org.slf4j.LoggerFactory; @@ -26,7 +28,7 @@ public class App LoggerFactory.getLogger( "ROOT" ); } - public static void main( String[] args ) throws TTransportException, NoSuchAlgorithmException + public static void main( String[] args ) throws TTransportException, NoSuchAlgorithmException, SocketException { // Init logging log.info( "Starting Application" ); @@ -36,6 +38,13 @@ public class App t = new Thread( new BinaryListener( 9090, false ), "Thrift PLAIN" ); servers.add( t ); t.start(); + + // Create UDP RPC local interface + t = new Thread( new NetworkHandler( 1333, InetAddress.getLoopbackAddress() ) ); + servers.add( t ); + t.start(); + + // Create SSL binary listener try { t = new Thread( new BinaryListener( 9091, true ), "Thrift TLS" ); servers.add( t ); @@ -44,9 +53,6 @@ public class App log.warn( "No TLS available:", e ); } - // start the crc checking scheduler - CrcScheduler.startScheduling(); - // Run more servers // ... // Wait for all servers to die @@ -57,7 +63,8 @@ public class App wait.join(); success = true; } catch ( InterruptedException e ) { - // Do nothing... + if ( wait.isInterrupted() || !wait.isAlive() ) + break; } } } diff --git a/src/main/java/org/openslx/imagemaster/Globals.java b/src/main/java/org/openslx/imagemaster/Globals.java index c33f3fe..eb83f4d 100644 --- a/src/main/java/org/openslx/imagemaster/Globals.java +++ b/src/main/java/org/openslx/imagemaster/Globals.java @@ -183,4 +183,9 @@ public class Globals { return Boolean.valueOf( properties.getProperty( "ldap_ssl" ) ); } + + public static int getPlainSocketPort() + { + return Util.tryToParseInt( properties.getProperty( "filetransfer.port.plain" ) ); + } } diff --git a/src/main/java/org/openslx/imagemaster/db/MysqlConnection.java b/src/main/java/org/openslx/imagemaster/db/MysqlConnection.java index dcfa713..10c263b 100644 --- a/src/main/java/org/openslx/imagemaster/db/MysqlConnection.java +++ b/src/main/java/org/openslx/imagemaster/db/MysqlConnection.java @@ -24,11 +24,15 @@ public class MysqlConnection implements AutoCloseable { MysqlConnection(Connection rawConnection) { this.rawConnection = rawConnection; } - + public MysqlStatement prepareStatement(String sql) throws SQLException { + return prepareStatement( sql, false ); + } + + public MysqlStatement prepareStatement(String sql, boolean getKeys) throws SQLException { if (!sql.startsWith("SELECT")) hasPendingQueries = true; - MysqlStatement statement = new MysqlStatement(rawConnection, sql); + MysqlStatement statement = new MysqlStatement(rawConnection, sql, getKeys); openStatements.add(statement); return statement; } diff --git a/src/main/java/org/openslx/imagemaster/db/MysqlStatement.java b/src/main/java/org/openslx/imagemaster/db/MysqlStatement.java index 391aed0..3dda36a 100644 --- a/src/main/java/org/openslx/imagemaster/db/MysqlStatement.java +++ b/src/main/java/org/openslx/imagemaster/db/MysqlStatement.java @@ -18,7 +18,8 @@ import java.util.Map; * "http://www.javaworld.com/article/2077706/core-java/named-parameters-for-preparedstatement.html?page=2" * >Named Parameters for PreparedStatement</a> */ -public class MysqlStatement implements Closeable { +public class MysqlStatement implements Closeable +{ private static final QueryCache cache = new QueryCache(); @@ -28,19 +29,24 @@ public class MysqlStatement implements Closeable { private final List<ResultSet> openResultSets = new ArrayList<>(); - MysqlStatement(Connection con, String sql) throws SQLException { + MysqlStatement( Connection con, String sql, boolean getKeys ) throws SQLException + { PreparsedQuery query; - synchronized (cache) { - query = cache.get(sql); + synchronized ( cache ) { + query = cache.get( sql ); } - if (query == null) { - query = parse(sql); - synchronized (cache) { - cache.put(sql, query); + if ( query == null ) { + query = parse( sql ); + synchronized ( cache ) { + cache.put( sql, query ); } } this.query = query; - this.statement = con.prepareStatement(query.sql); + if ( getKeys ) { + this.statement = con.prepareStatement( query.sql, Statement.RETURN_GENERATED_KEYS ); + } else { + this.statement = con.prepareStatement( query.sql ); + } } /** @@ -50,10 +56,11 @@ public class MysqlStatement implements Closeable { * @return parameter indexes * @throws IllegalArgumentException if the parameter does not exist */ - private List<Integer> getIndexes(String name) { - List<Integer> indexes = query.indexMap.get(name); - if (indexes == null) { - throw new IllegalArgumentException("Parameter not found: " + name); + private List<Integer> getIndexes( String name ) + { + List<Integer> indexes = query.indexMap.get( name ); + if ( indexes == null ) { + throw new IllegalArgumentException( "Parameter not found: " + name ); } return indexes; } @@ -67,10 +74,11 @@ public class MysqlStatement implements Closeable { * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setObject(int, java.lang.Object) */ - public void setObject(String name, Object value) throws SQLException { - List<Integer> indexes = getIndexes(name); - for (Integer index : indexes) { - statement.setObject(index, value); + public void setObject( String name, Object value ) throws SQLException + { + List<Integer> indexes = getIndexes( name ); + for ( Integer index : indexes ) { + statement.setObject( index, value ); } } @@ -83,10 +91,11 @@ public class MysqlStatement implements Closeable { * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setString(int, java.lang.String) */ - public void setString(String name, String value) throws SQLException { - List<Integer> indexes = getIndexes(name); - for (Integer index : indexes) { - statement.setString(index, value); + public void setString( String name, String value ) throws SQLException + { + List<Integer> indexes = getIndexes( name ); + for ( Integer index : indexes ) { + statement.setString( index, value ); } } @@ -99,10 +108,11 @@ public class MysqlStatement implements Closeable { * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setInt(int, int) */ - public void setInt(String name, int value) throws SQLException { - List<Integer> indexes = getIndexes(name); - for (Integer index : indexes) { - statement.setInt(index, value); + public void setInt( String name, int value ) throws SQLException + { + List<Integer> indexes = getIndexes( name ); + for ( Integer index : indexes ) { + statement.setInt( index, value ); } } @@ -115,10 +125,11 @@ public class MysqlStatement implements Closeable { * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setLong(int, long) */ - public void setLong(String name, long value) throws SQLException { - List<Integer> indexes = getIndexes(name); - for (Integer index : indexes) { - statement.setLong(index, value); + public void setLong( String name, long value ) throws SQLException + { + List<Integer> indexes = getIndexes( name ); + for ( Integer index : indexes ) { + statement.setLong( index, value ); } } @@ -131,10 +142,11 @@ public class MysqlStatement implements Closeable { * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setBoolean(int, boolean) */ - public void setBoolean(String name, boolean value) throws SQLException { - List<Integer> indexes = getIndexes(name); - for (Integer index : indexes) { - statement.setBoolean(index, value); + public void setBoolean( String name, boolean value ) throws SQLException + { + List<Integer> indexes = getIndexes( name ); + for ( Integer index : indexes ) { + statement.setBoolean( index, value ); } } @@ -147,10 +159,11 @@ public class MysqlStatement implements Closeable { * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setBoolean(int, boolean) */ - public void setBinary(String name, byte[] value) throws SQLException { - List<Integer> indexes = getIndexes(name); - for (Integer index : indexes) { - statement.setBytes(index, value); + public void setBinary( String name, byte[] value ) throws SQLException + { + List<Integer> indexes = getIndexes( name ); + for ( Integer index : indexes ) { + statement.setBytes( index, value ); } } @@ -161,7 +174,8 @@ public class MysqlStatement implements Closeable { * @throws SQLException if an error occurred * @see PreparedStatement#execute() */ - public boolean execute() throws SQLException { + public boolean execute() throws SQLException + { return statement.execute(); } @@ -172,9 +186,10 @@ public class MysqlStatement implements Closeable { * @throws SQLException if an error occurred * @see PreparedStatement#executeQuery() */ - public ResultSet executeQuery() throws SQLException { + public ResultSet executeQuery() throws SQLException + { ResultSet rs = statement.executeQuery(); - openResultSets.add(rs); + openResultSets.add( rs ); return rs; } @@ -187,7 +202,8 @@ public class MysqlStatement implements Closeable { * @throws SQLException if an error occurred * @see PreparedStatement#executeUpdate() */ - public int executeUpdate() throws SQLException { + public int executeUpdate() throws SQLException + { return statement.executeUpdate(); } @@ -197,17 +213,18 @@ public class MysqlStatement implements Closeable { * @see Statement#close() */ @Override - public void close() { - for (ResultSet rs : openResultSets) { + public void close() + { + for ( ResultSet rs : openResultSets ) { try { rs.close(); - } catch (SQLException e) { + } catch ( SQLException e ) { // } } try { statement.close(); - } catch (SQLException e) { + } catch ( SQLException e ) { // Nothing to do } } @@ -217,7 +234,8 @@ public class MysqlStatement implements Closeable { * * @throws SQLException if something went wrong */ - public void addBatch() throws SQLException { + public void addBatch() throws SQLException + { statement.addBatch(); } @@ -229,91 +247,115 @@ public class MysqlStatement implements Closeable { * @return update counts for each statement * @throws SQLException if something went wrong */ - public int[] executeBatch() throws SQLException { + public int[] executeBatch() throws SQLException + { return statement.executeBatch(); } + /** + * Get the generated key from the last insert. Assumes that one row was inserted, and the + * generated key is an int. + * + * @return the generated key + * @throws SQLException if no key was generated by this statement + */ + public int getGeneratedKeys() throws SQLException + { + try ( ResultSet generatedKeys = statement.getGeneratedKeys() ) { + if ( generatedKeys.next() ) { + return generatedKeys.getInt( 1 ); + } + throw new SQLException( "Could not obtain generated key" ); + } + } + // static methods - private static PreparsedQuery parse(String query) { + private static PreparsedQuery parse( String query ) + { int length = query.length(); - StringBuffer parsedQuery = new StringBuffer(length); + StringBuffer parsedQuery = new StringBuffer( length ); Map<String, List<Integer>> paramMap = new HashMap<>(); boolean inSingleQuote = false; boolean inDoubleQuote = false; boolean hasBackslash = false; int index = 1; - for (int i = 0; i < length; i++) { - char c = query.charAt(i); - if (hasBackslash) { + for ( int i = 0; i < length; i++ ) { + char c = query.charAt( i ); + if ( hasBackslash ) { // Last char was a backslash, so we ignore the current char hasBackslash = false; - } else if (c == '\\') { + } else if ( c == '\\' ) { // This is a backslash, next char will be escaped hasBackslash = true; - } else if (inSingleQuote) { + } else if ( inSingleQuote ) { // End of quoted string - if (c == '\'') { + if ( c == '\'' ) { inSingleQuote = false; } - } else if (inDoubleQuote) { + } else if ( inDoubleQuote ) { // End of quoted string - if (c == '"') { + if ( c == '"' ) { inDoubleQuote = false; } } else { // Not in string, look for named params - if (c == '\'') { + if ( c == '\'' ) { inSingleQuote = true; - } else if (c == '"') { + } else if ( c == '"' ) { inDoubleQuote = true; - } else if (c == ':' && i + 1 < length && Character.isJavaIdentifierStart(query.charAt(i + 1))) { + } else if ( c == ':' && i + 1 < length && Character.isJavaIdentifierStart( query.charAt( i + 1 ) ) ) { int j = i + 2; - while (j < length && Character.isJavaIdentifierPart(query.charAt(j))) { + while ( j < length && Character.isJavaIdentifierPart( query.charAt( j ) ) ) { j++; } - String name = query.substring(i + 1, j); + String name = query.substring( i + 1, j ); c = '?'; // replace the parameter with a question mark i += name.length(); // skip past the end of the parameter - List<Integer> indexList = paramMap.get(name); - if (indexList == null) { + List<Integer> indexList = paramMap.get( name ); + if ( indexList == null ) { indexList = new ArrayList<>(); - paramMap.put(name, indexList); + paramMap.put( name, indexList ); } - indexList.add(new Integer(index)); + indexList.add( new Integer( index ) ); index++; } } - parsedQuery.append(c); + parsedQuery.append( c ); } - return new PreparsedQuery(parsedQuery.toString(), paramMap); + return new PreparsedQuery( parsedQuery.toString(), paramMap ); } // private helper classes - private static class PreparsedQuery { + private static class PreparsedQuery + { private final Map<String, List<Integer>> indexMap; private final String sql; - public PreparsedQuery(String sql, Map<String, List<Integer>> indexMap) { + public PreparsedQuery( String sql, Map<String, List<Integer>> indexMap ) + { this.sql = sql; this.indexMap = indexMap; } } - private static class QueryCache extends LinkedHashMap<String, PreparsedQuery> { + private static class QueryCache extends LinkedHashMap<String, PreparsedQuery> + { private static final long serialVersionUID = 1L; - public QueryCache() { - super(30, (float) 0.75, true); + public QueryCache() + { + super( 30, (float)0.75, true ); } @Override - protected boolean removeEldestEntry(Map.Entry<String, PreparsedQuery> eldest) { + protected boolean removeEldestEntry( Map.Entry<String, PreparsedQuery> eldest ) + { return size() > 40; } } diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java index 027ddf6..2f5394c 100644 --- a/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java +++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java @@ -1,11 +1,7 @@ package org.openslx.imagemaster.db.mappers; -import java.util.List; - import org.openslx.bwlp.thrift.iface.ImagePublishData; -import org.openslx.imagemaster.Globals; -import org.openslx.imagemaster.serverconnection.UploadingImage; -import org.openslx.imagemaster.util.Util; + /** * Representing an image in the database. @@ -14,221 +10,10 @@ import org.openslx.imagemaster.util.Util; public class DbImage { - public final String uuid; - public final int revision; - public final String title; - /** - * Relative path of image file (relative to Globals.getImageDir()) - */ - public final String relativePath; - public final long createTime; - public final long updateTime; - public final int ownerId; - public final String ownerLogin; - public final int operatingSystem; - public final boolean isValid; - public final boolean isDeleted; - public final String longDescription; - public final long fileSize; - public final int[] blockStatus; - - public DbImage( String uuid ) - { - this.uuid = uuid; - this.revision = 0; - this.title = null; - this.relativePath = null; - this.createTime = 0; - this.updateTime = 0; - this.ownerId = 0; - this.ownerLogin = null; - this.operatingSystem = 0; - this.isValid = false; - this.isDeleted = false; - this.longDescription = null; - this.fileSize = 0; - this.blockStatus = null; - } - - public DbImage( String uuid, int imageVersion, String imageName, String imagePath, - long imageCreateTime, long imageUpdateTime, int imageOwnerId, String imageOwnerLogin, int contentOperatingSystem, - boolean isValid, boolean isDeleted, String longDescription, - long fileSize, String missingBlocksList ) - { - this.uuid = uuid; - this.revision = imageVersion; - this.title = imageName; - this.relativePath = imagePath; - this.createTime = imageCreateTime; - this.updateTime = imageUpdateTime; - this.ownerId = imageOwnerId; - this.ownerLogin = imageOwnerLogin; - this.operatingSystem = contentOperatingSystem; - this.isValid = isValid; - this.isDeleted = isDeleted; - this.longDescription = longDescription; - this.fileSize = fileSize; - - String[] parts = missingBlocksList.split( ";" ); - blockStatus = new int[ Util.getNumberOfBlocks( fileSize, Globals.blockSize ) ]; // initialize array to ones - for ( int i = 0; i < blockStatus.length; ++i ) { - blockStatus[i] = UploadingImage.VALID; - } - for ( String block : parts ) { // Now mark missing blocks (if any) - int i = Util.tryToParseInt( block, -1 ); - if ( i >= 0 && i < blockStatus.length ) - blockStatus[i] = UploadingImage.MISSING; - } - } - - /** - * Check if image with imageData already exists. (Only checks the UUID.) - * - * @param imageData - * @return - */ - public static boolean exists( String uuid ) - { - return getImageByUuid( uuid ) != null; - } - - /** - * Insert a new image into database - * - * @param imageData - * The metadata of the image - * @param filepath - * Local storage path of image - * @return Affected rows - */ - public static int insert( ImagePublishData imageData, String filepath ) - { - int numBlocks = Util.getNumberOfBlocks( imageData.fileSize, Globals.blockSize ); - String missingBlocksList = ""; - for ( int i = 0; i < numBlocks; i++ ) { - missingBlocksList = missingBlocksList + String.valueOf( i ) + ";"; - } - DbUser user = DbUser.forLogin( imageData.ownerLogin ); - int owner = 0; - if ( user != null ) - owner = user.userId; - - return MySQL - .update( - "INSERT IGNORE INTO image (uuid, revision, title, path, createtime, updatetime, ownerid, operatingsystem, isvalid, isdeleted, description, filesize, missingblocks) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - imageData.uuid, imageData.revision, imageData.title, filepath, - imageData.createTime, imageData.updateTime, owner, - imageData.operatingSystem, imageData.isValid, - imageData.isDeleted, imageData.description, imageData.fileSize, - missingBlocksList ); - } - - /** - * Updates the missing blocks of an uploading image. - * - * @param missingBlocks - * @return - */ - public int updateMissingBlocks( List<Integer> missingBlocks ) - { - String missingBlocksList = ""; - if ( missingBlocks != null ) { - for ( Integer block : missingBlocks ) { - missingBlocksList = missingBlocksList + String.valueOf( block ) + ";"; - } - } - return MySQL.update( "UPDATE image SET image.missingblocks = ? WHERE image.uuid = ?", missingBlocksList, uuid ); - } - - /** - * Marks an image as _deleted_ in the database. - * - * @return - */ - public int delete() - { - return MySQL.update( "UPDATE image SET image.isdeleted = 1 WHERE image.uuid = ?", this.uuid ); - } - - /** - * Returns all images from database where blocks are still missing. - * - * @return - */ - public static List<DbImage> getUploadingImages() - { - return MySQL - .findAll( - DbImage.class, - "SELECT image.uuid, image.revision, image.title, image.path, image.createtime, image.updatetime, image.ownerid, user.login, image.operatingsystem, image.isvalid, image.isdeleted, image.description, image.filesize, image.missingblocks" - + " FROM image" - + " INNER JOIN user ON (image.ownerid = user.userid)" - + " WHERE missingBlocks != ''" ); - } - - /** - * Returns the image that is corrsponding to a specified uuid. - * - * @param uuid - * @return - */ - public static DbImage getImageByUuid( String uuid ) - { - return MySQL - .findUniqueOrNull( - DbImage.class, - "SELECT image.uuid, image.revision, image.title, image.path, image.createtime, image.updatetime, image.ownerid, user.login, image.operatingsystem, image.isvalid, image.isdeleted, image.description, image.filesize, image.missingblocks" - + " FROM image" - + " INNER JOIN user ON (image.ownerid = user.userid)" - + " WHERE uuid = ?", - uuid ); - } - - /** - * Return all public images as ImageData list, which can be directly - * used by the thrift API. - * - * @param start - * @param limit - * @return - */ - public static List<ImageData> asImageDataList( int start, int limit ) - { - return MySQL - .findAll( - ImageData.class, - "SELECT image.uuid, image.revision, image.title, image.createtime, image.updatetime, user.login, image.operatingsystem, image.isvalid, image.isdeleted, image.description, image.filesize" - + " FROM image" - + " INNER JOIN user ON (image.ownerid = user.userid)" - + " ORDER BY uuid, revision" - + " LIMIT " + start + ", " + limit ); - } - - /** - * Creates an instance of the thrift ImageData class of this DbImage object. - * - * @return The corresponding image data - */ - public ImageData getImageData() - { - String owner = "unknown"; - DbUser user = DbUser.forLogin( this.ownerId ); - if ( user != null ) - owner = user.getLogin(); - return new ImageData( - this.uuid, this.revision, this.title, this.createTime, - this.updateTime, owner, this.operatingSystem, this.isValid, - this.isDeleted, this.longDescription, this.fileSize ); - } - - /** - * Get absolute path of this image - * - * @return absolute path - */ - public String getAbsolutePath() + public static ImagePublishData getImageVersion( String imageVersionId ) { - return Globals.getImageDir() + "/" + this.relativePath; + // TODO Auto-generated method stub + return null; } } diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbOperatingSystem.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbOperatingSystem.java deleted file mode 100644 index 1504e50..0000000 --- a/src/main/java/org/openslx/imagemaster/db/mappers/DbOperatingSystem.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.openslx.imagemaster.db.mappers; - -import java.util.List; - -import org.openslx.bwlp.thrift.iface.OperatingSystem; -import org.openslx.imagemaster.db.MySQL; -import org.openslx.util.TimeoutReference; - -public class DbOperatingSystem -{ - - private static TimeoutReference<List<OperatingSystem>> cached = new TimeoutReference<List<OperatingSystem>>( - 30000, null ); - - private DbOperatingSystem() - { - } - - public static List<OperatingSystem> getAll() - { - List<OperatingSystem> list = cached.get(); - if ( list != null ) - return list; - list = MySQL.findAll( - OperatingSystem.class, - "SELECT osid, displayname, NULL, architecture" - + " FROM operatingsystem" ); - for ( OperatingSystem os : list ) { - os.virtualizerOsId = MySQL.findMap( String.class, String.class, - "SELECT virtid, virtoskeyword FROM os_x_virt WHERE osid = ?", os.osId ); - } - cached.set( list ); - return list; - } - -} diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbOsVirt.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbOsVirt.java new file mode 100644 index 0000000..ee8a39a --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbOsVirt.java @@ -0,0 +1,108 @@ +package org.openslx.imagemaster.db.mappers; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.OperatingSystem; +import org.openslx.bwlp.thrift.iface.Virtualizer; +import org.openslx.imagemaster.db.Database; +import org.openslx.imagemaster.db.MysqlConnection; +import org.openslx.imagemaster.db.MysqlStatement; +import org.openslx.imagemaster.util.Util; + +public class DbOsVirt +{ + + private static final Logger LOGGER = Logger.getLogger( DbOsVirt.class ); + + public static List<OperatingSystem> getOsList() throws SQLException + { + try ( MysqlConnection connection = Database.getConnection() ) { + // Query OSs + MysqlStatement stmt = connection.prepareStatement( "SELECT" + + " osid, displayname, architecture, maxmem, maxcpu" + " FROM operatingsystem" ); + ResultSet rs = stmt.executeQuery(); + List<OperatingSystem> list = new ArrayList<>(); + Map<Integer, Map<String, String>> osVirtMappings = getOsVirtMappings( connection ); + while ( rs.next() ) { + int osId = rs.getInt( "osid" ); + list.add( new OperatingSystem( osId, rs.getString( "displayname" ), osVirtMappings.get( osId ), + rs.getString( "architecture" ), rs.getInt( "maxmem" ), rs.getInt( "maxcpu" ) ) ); + } + return list; + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbOsVirt.getOsList()", e ); + throw e; + } + } + + private static Map<Integer, Map<String, String>> getOsVirtMappings( MysqlConnection connection ) + throws SQLException + { + MysqlStatement stmt = connection.prepareStatement( "SELECT osid, virtid, virtoskeyword FROM os_x_virt" ); + ResultSet rs = stmt.executeQuery(); + Map<Integer, Map<String, String>> map = new HashMap<>(); + while ( rs.next() ) { + Integer osId = rs.getInt( "osid" ); + Map<String, String> osMap = map.get( osId ); + if ( osMap == null ) { + osMap = new HashMap<>(); + map.put( osId, osMap ); + } + osMap.put( rs.getString( "virtid" ), rs.getString( "virtoskeyword" ) ); + } + return map; + } + + public static List<Virtualizer> getVirtualizerList() throws SQLException + { + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "SELECT virtid, virtname" + " FROM virtualizer" ); + ResultSet rs = stmt.executeQuery(); + List<Virtualizer> list = new ArrayList<>(); + while ( rs.next() ) { + list.add( new Virtualizer( rs.getString( "virtid" ), rs.getString( "virtname" ) ) ); + } + return list; + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbOsVirt.getVirtualizerList()", e ); + throw e; + } + } + + public static boolean osExists( int osId ) + { + if ( osId <= 0 ) + return false; + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "SELECT osid FROM operatingsystem WHERE osid = :osid" ); + stmt.setInt( "osid", osId ); + ResultSet rs = stmt.executeQuery(); + return rs.next(); + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbOsVirt.exists()", e ); + return false; + } + } + + public static boolean virtExists( String virtId ) + { + if ( Util.isEmpty( virtId ) ) + return false; + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "SELECT virtid FROM virtualizer WHERE virtid = :virtid" ); + stmt.setString( "virtid", virtId ); + ResultSet rs = stmt.executeQuery(); + return rs.next(); + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbOsVirt.virtExists()", e ); + return false; + } + } + +} diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbPendingSatellite.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbPendingSatellite.java index 15cdbb9..906d49e 100644 --- a/src/main/java/org/openslx/imagemaster/db/mappers/DbPendingSatellite.java +++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbPendingSatellite.java @@ -1,17 +1,54 @@ package org.openslx.imagemaster.db.mappers; +import java.sql.SQLException; +import java.util.List; + import org.apache.log4j.Logger; -import org.openslx.imagemaster.db.MySQL; +import org.openslx.bwlp.thrift.iface.UserInfo; +import org.openslx.imagemaster.db.Database; +import org.openslx.imagemaster.db.MysqlConnection; +import org.openslx.imagemaster.db.MysqlStatement; +import org.openslx.util.Json; public class DbPendingSatellite { - private static final Logger LOG = Logger.getLogger( DbPendingSatellite.class ); + private static final Logger LOGGER = Logger.getLogger( DbPendingSatellite.class ); + + public static int add( UserInfo user, String displayName, List<String> address, String modulus, String exponent ) + throws SQLException + { + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "INSERT INTO pending_satellite" + + " (dateline, userid, organizationid, satellitename, addresses, publickey)" + + " VALUES (UNIX_TIMESTAMP(), :userid, :organizationid, :satellitename, :addresses, :pubkey)", true ); + stmt.setString( "userid", user.userId ); + stmt.setString( "organizationid", user.organizationId ); + stmt.setString( "satellitename", displayName ); + stmt.setString( "addresses", Json.serialize( address ) ); + stmt.setString( "pubkey", Json.serialize( new KeyWrapper( modulus, exponent ) ) ); + stmt.executeUpdate(); + int key = stmt.getGeneratedKeys(); + connection.commit(); + return key; + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbPendingSatellite.add()", e ); + throw e; + } + } - public static boolean add( String organizationId, String address, String modulus, String exponent ) + private static class KeyWrapper { - String publickey = "mod:" + modulus + " exp:" + exponent; - return MySQL.update( "INSERT INTO pending_satellite (dateline, organizationid, address, publickey)" - + " VALUES (UNIX_TIMESTAMP(), ?, ?, ?)", organizationId, address, publickey ) != 0; + public String type; + public String modulus; + public String exponent; + + public KeyWrapper( String modulus, String exponent ) + { + this.type = "RSA"; + this.modulus = modulus; + this.exponent = exponent; + } } + } diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbSatellite.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbSatellite.java new file mode 100644 index 0000000..811ac67 --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbSatellite.java @@ -0,0 +1,62 @@ +package org.openslx.imagemaster.db.mappers; + +import java.nio.ByteBuffer; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.Satellite; +import org.openslx.bwlp.thrift.iface.UserInfo; +import org.openslx.imagemaster.db.Database; +import org.openslx.imagemaster.db.MysqlConnection; +import org.openslx.imagemaster.db.MysqlStatement; +import org.openslx.imagemaster.db.models.LocalSatellite; +import org.openslx.util.Json; + +public class DbSatellite +{ + + private static final Logger LOGGER = Logger.getLogger( DbSatellite.class ); + + public static LocalSatellite get( int satelliteId ) + { + return null; + } + + public static List<Satellite> getSatellites( UserInfo ui ) throws SQLException + { + if ( ui == null ) + return null; + return getSatellites( ui.organizationId ); + } + + public static List<Satellite> getSatellites( String organizationId ) throws SQLException + { + if ( organizationId == null ) + return null; + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "SELECT satellitename, addresses, certsha256" + + " FROM satellite WHERE organizationid = :organizationid" ); + stmt.setString( "organizationid", organizationId ); + ResultSet rs = stmt.executeQuery(); + List<Satellite> list = new ArrayList<>(); + while ( rs.next() ) { + List<String> al = Arrays.asList( Json.deserialize( rs.getString( "addresses" ), String[].class ) ); + list.add( new Satellite( al, rs.getString( "satellitename" ), ByteBuffer.wrap( rs.getBytes( "certsha256" ) ) ) ); + } + return list; + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbSatellite.getSatellites()", e ); + throw e; + } + } + + public static LocalSatellite get( String organizationId, String displayName ) + { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java index ed55d8a..349b023 100644 --- a/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java +++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java @@ -105,4 +105,22 @@ public class DbUser return new ArrayList<>( 0 ); } + public static boolean exists( UserInfo user ) + { + if ( user == null ) + return false; + return exists( user.userId ); + } + + private static boolean exists( String userId ) + { + if ( userId == null ) + return false; + try { + return forLogin( userId ) != null; + } catch ( SQLException e ) { + return false; + } + } + } diff --git a/src/main/java/org/openslx/imagemaster/db/models/LocalSatellite.java b/src/main/java/org/openslx/imagemaster/db/models/LocalSatellite.java new file mode 100644 index 0000000..75f0f94 --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/db/models/LocalSatellite.java @@ -0,0 +1,22 @@ +package org.openslx.imagemaster.db.models; + +import java.security.Key; +import java.util.List; + +public class LocalSatellite +{ + // TODO + public final int satelliteId = 0; + public final String satelliteName = ""; + public final String organizationId = ""; + public final List<String> addresses = null; + + // TODO + + public Key getPubkey() + { + // TODO + return null; + } + +} diff --git a/src/main/java/org/openslx/imagemaster/localrpc/JsonUser.java b/src/main/java/org/openslx/imagemaster/localrpc/JsonUser.java index 8f13084..2504a3d 100644 --- a/src/main/java/org/openslx/imagemaster/localrpc/JsonUser.java +++ b/src/main/java/org/openslx/imagemaster/localrpc/JsonUser.java @@ -1,23 +1,32 @@ package org.openslx.imagemaster.localrpc; -import org.openslx.imagemaster.session.User; +import org.openslx.bwlp.thrift.iface.Role; +import org.openslx.bwlp.thrift.iface.UserInfo; +import org.openslx.imagemaster.util.Util; public class JsonUser { - private String login = null; - private String organizationid = null; + private String userId = null; + private String organizationId = null; private String firstName = null; private String lastName = null; private String mail = null; private String role = null; - private int userId = -1; - public User toUser() + public UserInfo toUser() { - if ( userId <= 0 || firstName == null || lastName == null || firstName.isEmpty() || lastName.isEmpty() ) + Role role; + try { + role = Role.valueOf( this.role ); + } catch ( Exception e ) { return null; - return new ShibUser( userId, login, organizationid, firstName, lastName, mail, role ); + } + if ( Util.isEmpty( userId ) || Util.isEmpty( organizationId ) || Util.isEmpty( lastName ) || Util.isEmpty( mail ) ) + return null; + UserInfo ui = new UserInfo( userId, firstName, lastName, mail, organizationId ); + ui.role = role; + return ui; } } diff --git a/src/main/java/org/openslx/imagemaster/localrpc/NetworkHandler.java b/src/main/java/org/openslx/imagemaster/localrpc/NetworkHandler.java index 96b1212..7223435 100644 --- a/src/main/java/org/openslx/imagemaster/localrpc/NetworkHandler.java +++ b/src/main/java/org/openslx/imagemaster/localrpc/NetworkHandler.java @@ -12,9 +12,9 @@ import java.util.concurrent.LinkedBlockingQueue; import org.apache.log4j.Logger; import org.openslx.bwlp.thrift.iface.ClientSessionData; +import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.imagemaster.session.Session; import org.openslx.imagemaster.session.SessionManager; -import org.openslx.imagemaster.session.User; import com.google.gson.Gson; @@ -124,7 +124,7 @@ public class NetworkHandler implements Runnable { try { JsonUser ju = gson.fromJson( payload, JsonUser.class ); - User u = ju.toUser(); + UserInfo u = ju.toUser(); if ( u == null ) { log.warn( "Invalid or inomplete RPC data (" + payload + ")" ); return "Invalid or incomplete RPC data"; diff --git a/src/main/java/org/openslx/imagemaster/localrpc/ShibUser.java b/src/main/java/org/openslx/imagemaster/localrpc/ShibUser.java deleted file mode 100644 index 4cba3a4..0000000 --- a/src/main/java/org/openslx/imagemaster/localrpc/ShibUser.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.openslx.imagemaster.localrpc; - -import org.openslx.imagemaster.session.User; - -public class ShibUser extends User -{ - - protected ShibUser( int userId, String login, String organization, String firstName, String lastName, String eMail, String role ) - { - super( userId, login, "", organization, firstName, lastName, eMail, "", role ); - } - -} diff --git a/src/main/java/org/openslx/imagemaster/server/ApiServer.java b/src/main/java/org/openslx/imagemaster/server/ApiServer.java index ebd222e..57c3944 100644 --- a/src/main/java/org/openslx/imagemaster/server/ApiServer.java +++ b/src/main/java/org/openslx/imagemaster/server/ApiServer.java @@ -1,43 +1,5 @@ package org.openslx.imagemaster.server; -import java.nio.ByteBuffer; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.log4j.Logger; -import org.openslx.encryption.AsymKeyHolder; -import org.openslx.imagemaster.db.DbImage; -import org.openslx.imagemaster.db.DbPendingSatellite; -import org.openslx.imagemaster.db.DbSatellite; -import org.openslx.imagemaster.db.DbUser; -import org.openslx.imagemaster.serverconnection.ImageProcessor; -import org.openslx.imagemaster.serversession.ServerAuthenticator; -import org.openslx.imagemaster.serversession.ServerSession; -import org.openslx.imagemaster.serversession.ServerSessionManager; -import org.openslx.imagemaster.serversession.ServerUser; -import org.openslx.imagemaster.session.Authenticator; -import org.openslx.imagemaster.session.Session; -import org.openslx.imagemaster.session.SessionManager; -import org.openslx.imagemaster.session.User; -import org.openslx.imagemaster.thrift.iface.AuthenticationError; -import org.openslx.imagemaster.thrift.iface.AuthenticationException; -import org.openslx.imagemaster.thrift.iface.AuthorizationError; -import org.openslx.imagemaster.thrift.iface.AuthorizationException; -import org.openslx.imagemaster.thrift.iface.DownloadData; -import org.openslx.imagemaster.thrift.iface.ImageData; -import org.openslx.imagemaster.thrift.iface.ImageDataError; -import org.openslx.imagemaster.thrift.iface.ImageDataException; -import org.openslx.imagemaster.thrift.iface.InvalidTokenException; -import org.openslx.imagemaster.thrift.iface.OrganizationData; -import org.openslx.imagemaster.thrift.iface.ServerSessionData; -import org.openslx.imagemaster.thrift.iface.SessionData; -import org.openslx.imagemaster.thrift.iface.UploadData; -import org.openslx.imagemaster.thrift.iface.UploadException; -import org.openslx.imagemaster.thrift.iface.UserInfo; -import org.openslx.imagemaster.util.Util; /** * API Server This is where all the requests from the outside arrive. We don't @@ -52,201 +14,4 @@ import org.openslx.imagemaster.util.Util; */ public class ApiServer { - - private static final Logger LOG = Logger.getLogger( ApiServer.class ); - - /** - * Request for authentication - * - * @param login The user's login in the form "user@organization.com" - * @param password user's password - * @return SessionData struct with session id/token iff login successful - * @throws AuthenticationException if login not successful - */ - public static SessionData authenticate( String login, String password ) - throws AuthenticationException - { - if ( login == null || password == null ) { - throw new AuthenticationException( - AuthenticationError.INVALID_CREDENTIALS, - "Empty username or password!" ); - } - final User user = Authenticator.authenticate( login, password ); - - final Session session = new Session( user ); - return SessionManager.addSession( session ); - } - - /** - * Request information about user for given token - * - * @param token a user's token - * @return UserInfo struct for given token's user - * @throws InvalidTokenException if no user matches the given token - */ - public static UserInfo getUserFromToken( String token ) - throws InvalidTokenException - { - final Session session = SessionManager.getSessionFromToken( token ); - if ( session == null ) - throw new InvalidTokenException(); - return new UserInfo( session.getLogin(), session.getFirstName(), - session.getLastName(), session.getEMail(), session.getOrgenizationId() ); - } - - public static UploadData submitImage( String serverSessionId, ImageData imageDescription, List<Integer> crcSums ) - throws AuthorizationException, ImageDataException, UploadException - { - // first check session of server - if ( ServerSessionManager.getSession( serverSessionId ) == null ) { - throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "No valid serverSessionId" ); - } - // then let the image processor decide what to do - return ImageProcessor.getUploadInfos( imageDescription, crcSums ); - } - - public static DownloadData getImage( String uuid, String serverSessionId ) throws AuthorizationException, ImageDataException - { - // first check session of server - if ( ServerSessionManager.getSession( serverSessionId ) == null ) { - throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "No valid serverSessionId" ); - } - - if ( !DbImage.exists( uuid ) ) { - throw new ImageDataException( ImageDataError.UNKNOWN_IMAGE, "UUID is not known by this server." ); - } - - // then let the image processor decide what to do - return ImageProcessor.getDownloadInfos( uuid ); - } - - /** - * Start the server authentication of a uni/hs satellite server. - * - * @param organization the organization that the server belongs to - * @return a random string that needs to be encrypted with the private - * key of the requesting satellite server - * @throws ServerAuthenticationException when organization is invalid/unknown - */ - public static ByteBuffer startServerAuthentication( String organizationId ) - throws AuthenticationException - { - if ( organizationId == null || organizationId.isEmpty() ) - throw new AuthenticationException( AuthenticationError.INVALID_ORGANIZATION, "Empty organization" ); - - DbSatellite satellite = DbSatellite.fromOrganizationId( organizationId ); - if ( satellite == null ) - throw new AuthenticationException( AuthenticationError.INVALID_ORGANIZATION, "Unknown organization: '" + organizationId + "'" ); - if ( satellite.getPubkey() == null ) - throw new AuthenticationException( AuthenticationError.INVALID_KEY, "There is no public key known for your organization." ); - return ServerAuthenticator.startServerAuthentication( organizationId ); - } - - /** - * Authenticate the uni/hs satellite server with the encrypted string. - * - * @param organization the organization that the server belongs to - * @param challengeResponse the encrypted string - * @return session data iff the authentication was successful - * @throws AuthenticationException - */ - public static ServerSessionData serverAuthenticate( String organizationId, - ByteBuffer challengeResponse ) throws AuthenticationException - { - if ( organizationId == null || challengeResponse == null ) { - throw new AuthenticationException( AuthenticationError.INVALID_ORGANIZATION, "Empty organization or challengeResponse" ); - } - DbSatellite satellite = DbSatellite.fromOrganizationId( organizationId ); - if ( satellite == null ) - throw new AuthenticationException( AuthenticationError.INVALID_ORGANIZATION, "Unknown organization" ); - if ( satellite.getPubkey() == null ) - throw new AuthenticationException( AuthenticationError.INVALID_KEY, "There is no public key known for your organization." ); - - final ServerUser serverUser = ServerAuthenticator.serverAuthenticate( satellite, challengeResponse ); - - final ServerSession session = new ServerSession( serverUser ); - return ServerSessionManager.addSession( session ); - } - - public static boolean isServerAuthenticated( String serverSessionId ) - { - return ( ServerSessionManager.getSession( serverSessionId ) != null ); - } - - public static boolean publishUser( String serverSessionId, UserInfo user ) throws AuthorizationException - { - // Check session. - if ( ServerSessionManager.getSession( serverSessionId ) == null ) { - throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" ); - } - if ( DbUser.forLogin( user.userId ) == null ) { - // User not known by server. Insert into server database. - return DbUser.insertOrUpdate( user ); - } - // Else user is already known by server. Do nothing. - return true; - } - - public static List<UserInfo> findUser( String sessionId, String organizationId, String searchTerm ) throws AuthorizationException - { - // Needs to be a logged in user - if ( SessionManager.getSessionFromSessionId( sessionId ) == null ) - throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" ); - // Search string needs to be at least 2 characters (FIXME: quick and dirty ignoring LIKE chars) - if ( searchTerm == null || searchTerm.length() < 2 || searchTerm.replaceAll( "[%_]", "" ).length() < 2 ) - return new ArrayList<>( 0 ); - return DbUser.findUser( organizationId, searchTerm ); - } - - public static List<OrganizationData> getOrganizations() - { - return DbSatellite.asOrganizationDataList(); - } - - public static List<ImageData> getPublicImages( String sessionId, int page ) throws AuthorizationException - { - if ( SessionManager.getSessionFromSessionId( sessionId ) == null ) - throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" ); - return DbImage.asImageDataList( page * 100, ( page + 1 ) * 100 ); - } - - public static boolean registerSatellite( String organizationId, String address, String modulus, String exponent ) - { - if ( organizationId == null || address == null || exponent == null || modulus == null ) - return false; - Key newKey; - try { - newKey = new AsymKeyHolder( null, Util.tryToParseBigInt( exponent ), Util.tryToParseBigInt( modulus ) ).getPublicKey(); - } catch ( NoSuchAlgorithmException | InvalidKeySpecException e ) { - LOG.warn( "Invalid public key in registerOrganization for " + organizationId + " (" + address + ")", e ); - return false; - } - if ( newKey == null ) { - LOG.warn( "Uninstantiable public key in registerOrganization for " + organizationId + " (" + address + ")" ); - return false; - } - DbSatellite existing = DbSatellite.fromSuffix( organizationId ); - if ( existing != null ) { - Key existingKey = existing.getPubkey(); - if ( existingKey != null && Util.keysEqual( newKey, existingKey ) ) - return true; - } - return DbPendingSatellite.add( organizationId, address, modulus, exponent ); - } - - public static boolean updateSatelliteAddress( String serverSessionId, String address ) throws AuthorizationException - { - ServerSession session = ServerSessionManager.getSession( serverSessionId ); - if ( session == null ) - throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "No valid serverSessionId" ); - String organization = session.getOrganization(); - DbSatellite currentSatellite = DbSatellite.fromOrganizationId( organization ); - if (currentSatellite == null) { - // TODO: error. - } else { - currentSatellite.updateAddress(address, organization); - return true; - } - return false; - } } diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/AbstractTransfer.java b/src/main/java/org/openslx/imagemaster/serverconnection/AbstractTransfer.java new file mode 100644 index 0000000..3acac5b --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/serverconnection/AbstractTransfer.java @@ -0,0 +1,92 @@ +package org.openslx.imagemaster.serverconnection; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +public abstract class AbstractTransfer { + + /** + * How long to keep this transfer information when the transfer is + * (potentially) done + */ + private static final long FINISH_TIMEOUT = TimeUnit.MINUTES.toMillis(5); + + /** + * How long to keep this transfer information when there are no active + * connections and the transfer seems unfinished + */ + private static final long IDLE_TIMEOUT = TimeUnit.HOURS.toMillis(4); + + /** + * Time stamp of when (we think) the transfer finished. Clients can/might + * not tell us they're done, and simply taking "no active connection" as a + * sign the download is done might have unwanted effects if the user's + * connection drops for a minute. If this time stamp (plus a FINISH_TIMEOUT) + * passed, + * we consider the download done and flag it for removal. + * If set to zero, the transfer is not finished, or not assumed to have + * finished. + */ + protected final AtomicLong potentialFinishTime = new AtomicLong(0); + + /** + * Time of last activity on this transfer. + */ + protected final AtomicLong lastActivityTime = new AtomicLong(System.currentTimeMillis()); + + private final String transferId; + + public AbstractTransfer(String transferId) { + this.transferId = transferId; + } + + /** + * Returns true if the transfer is considered completed. + * + * @param now pass System.currentTimeMillis() + * @return true if the transfer is considered completed + */ + public boolean isComplete(long now) { + long val = potentialFinishTime.get(); + return val != 0 && val + FINISH_TIMEOUT < now; + } + + /** + * Returns true if there has been no activity on this transfer for a certain + * amount of time. + * + * @param now pass System.currentTimeMillis() + * @return true if the transfer reached its idle timeout + */ + public final boolean hasReachedIdleTimeout(long now) { + return getActiveConnectionCount() == 0 && lastActivityTime.get() + IDLE_TIMEOUT < now; + } + + public final String getId() { + return transferId; + } + + /** + * Returns true if this transfer would potentially accept new connections. + * This should NOT return false if there are too many concurrent + * connections, as this is used to signal the client whether to keep trying + * to connect. + * + * @return true if this transfer would potentially accept new connections + */ + public abstract boolean isActive(); + + /** + * Cancel this transfer, aborting all active connections and rejecting + * further incoming ones. + */ + public abstract void cancel(); + + /** + * Returns number of active transfer connections. + * + * @return number of active transfer connections + */ + public abstract int getActiveConnectionCount(); + +} diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java b/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java index 12b5701..9340706 100644 --- a/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java +++ b/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java @@ -1,17 +1,11 @@ package org.openslx.imagemaster.serverconnection; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; -import java.security.KeyManagementException; import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -20,16 +14,22 @@ import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; -import org.apache.commons.lang.mutable.MutableInt; import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.ImagePublishData; +import org.openslx.bwlp.thrift.iface.InvocationError; +import org.openslx.bwlp.thrift.iface.TInvocationException; +import org.openslx.bwlp.thrift.iface.TTransferRejectedException; +import org.openslx.bwlp.thrift.iface.TransferInformation; import org.openslx.filetransfer.Downloader; -import org.openslx.filetransfer.FileRange; import org.openslx.filetransfer.IncomingEvent; import org.openslx.filetransfer.Listener; import org.openslx.filetransfer.Uploader; -import org.openslx.filetransfer.WantRangeCallback; import org.openslx.imagemaster.Globals; -import org.openslx.imagemaster.db.DbImage; +import org.openslx.imagemaster.crcchecker.CrcFile; +import org.openslx.imagemaster.db.mappers.DbOsVirt; +import org.openslx.imagemaster.db.mappers.DbUser; +import org.openslx.imagemaster.util.RandomString; +import org.openslx.imagemaster.util.Util; /** * Class to handle all incoming and outgoing connections. @@ -41,8 +41,8 @@ public class ConnectionHandler implements IncomingEvent private static Logger log = Logger.getLogger( ConnectionHandler.class ); private static SSLContext sslContext; - private static Map<String, UploadingImage> pendingIncomingUploads = new ConcurrentHashMap<>(); - private static Map<String, DbImage> pendingIncomingDownloads = new ConcurrentHashMap<>(); + private static Map<String, AbstractTransfer> pendingIncomingUploads = new ConcurrentHashMap<>(); + private static Map<String, AbstractTransfer> pendingIncomingDownloads = new ConcurrentHashMap<>(); private static IncomingEvent eventHandler = new ConnectionHandler(); private static ThreadPoolExecutor uploadPool = new ThreadPoolExecutor( 0, 5, 6, TimeUnit.MINUTES, new SynchronousQueue<Runnable>() ); private static ThreadPoolExecutor downloadPool = new ThreadPoolExecutor( 0, 5, 6, TimeUnit.MINUTES, new SynchronousQueue<Runnable>() ); @@ -61,40 +61,75 @@ public class ConnectionHandler implements IncomingEvent sslContext = SSLContext.getInstance( "SSLv3" ); KeyManager[] keyManagers = kmf.getKeyManagers(); sslContext.init( keyManagers, null, null ); - listener = new Listener( eventHandler, sslContext, Globals.getSslSocketPort() ); + listener = new Listener( eventHandler, sslContext, Globals.getSslSocketPort(), 15000 ); listener.start(); - } catch ( FileNotFoundException e ) { - log.error( "Could not find keystore." ); - System.exit( 2 ); - } catch ( KeyStoreException e ) { - log.error( "KeyStore implemenation not supported." ); - System.exit( 2 ); - } catch ( NoSuchAlgorithmException e ) { - log.error( "Could not find such Algorithm" ); - System.exit( 2 ); - } catch ( CertificateException e ) { - log.error( "Certificate unvalid." ); - System.exit( 2 ); - } catch ( IOException e ) { - log.error( "Could not read keyfile" ); - System.exit( 2 ); - } catch ( UnrecoverableKeyException e ) { - log.error( "Key in keystore is not valid" ); - System.exit( 2 ); - } catch ( KeyManagementException e ) { - log.error( "Context initialization failed." ); + } catch ( Exception e ) { + log.error( "Initialization failed.", e ); System.exit( 2 ); } } /** + * Checks if this image is already uploading and returns a new list with missing blocks if so. + * Puts the new image into processing list else. + * + * @param serverSessionId The uploading server + * @param imageData The data of the image + * @return + * @throws UploadException If some error occurred during the process + */ + public static TransferInformation getUploadInfos( ImagePublishData imageData, List<Integer> crcSums ) + throws TTransferRejectedException, TInvocationException + { + // check image data + if ( Util.isEmpty( imageData.imageName ) ) + throw new TInvocationException( InvocationError.INVALID_DATA, "Image name not set" ); + if ( !DbUser.exists( imageData.user ) ) + throw new TInvocationException( InvocationError.INVALID_DATA, "Invalid or missing image owner" ); + if ( DbOsVirt.osExists( imageData.osId ) ) + throw new TInvocationException( InvocationError.INVALID_DATA, "Content operating system not set" ); + if ( DbOsVirt.virtExists( imageData.virtId ) ) + throw new TInvocationException( InvocationError.INVALID_DATA, "Content virtualizer system not set" ); + if ( imageData.fileSize <= 0 ) + throw new TInvocationException( InvocationError.INVALID_DATA, "File size is too small" ); + + log.debug( "A satellite is submitting " + imageData.imageVersionId ); + + final String uuid = imageData.imageVersionId; + final String filepathRelative; + final CrcFile crcFile; + if ( crcSums == null ) { + crcFile = null; + } else { + crcFile = new CrcFile( crcSums ); + } + ImagePublishData image; + + synchronized ( pendingIncomingUploads ) { + /* + // check if image is already uploading + if ( ( image = uploadingImages.get( uuid ) ) == null ) { + // TODO insert new image to DB + uploadingImages.put( uuid, image ); + } + */ + } + + final String token = RandomString.generate( 50, false ); + + // TODO addUpload( token, image ); + // TODO Set crc file on image - if there is already a crc file assigned, this does nothing + return new TransferInformation( token, Globals.getPlainSocketPort(), Globals.getSslSocketPort() ); + } + + /** * Add a new allowed incoming upload connection * for the given token and image. * * @param token The unique token * @param image Image being uploaded */ - public static void addUpload( String token, UploadingImage image ) + public static void addUpload( String token, AbstractTransfer image ) { pendingIncomingUploads.put( token, image ); log.debug( "Added upload" ); @@ -107,7 +142,7 @@ public class ConnectionHandler implements IncomingEvent * @param token The unique token * @param image Image being uploaded */ - public static void addDownload( String token, DbImage image ) + public static void addDownload( String token, AbstractTransfer image ) { pendingIncomingDownloads.put( token, image ); log.debug( "Added download" ); @@ -117,94 +152,21 @@ public class ConnectionHandler implements IncomingEvent * Server is uploading - client is downloading! */ @Override - public void incomingUploader( final Uploader uploader ) + public void incomingDownloadRequest( final Uploader uploader ) { - String token = uploader.getToken(); - // check token to identify the client - if ( token == null ) { - uploader.sendErrorCode( "No token available." ); - uploader.close( null ); - return; - } - - final DbImage image = pendingIncomingDownloads.remove( token ); - if ( image == null ) { - uploader.sendErrorCode( "Token not accepted." ); - uploader.close( null ); - return; - } - - try { - uploadPool.execute( new Runnable() { - @Override - public void run() - { - uploader.upload( image.getAbsolutePath() ); - } - } ); - } catch ( RejectedExecutionException e ) { - uploader.sendErrorCode( "Too many concurrent uploads." ); - uploader.close( null ); - } + // TODO + uploader.sendErrorCode( "Too many concurrent uploads." ); + uploader.cancel(); } /** * Server is downloading - client is uploading! */ @Override - public void incomingDownloader( final Downloader downloader ) throws IOException + public void incomingUploadRequest( final Downloader downloader ) throws IOException { - log.debug( "Client wants to upload" ); - String token = downloader.getToken(); - if ( token == null ) - { - downloader.sendErrorCode( "No token available." ); - downloader.close( null ); - return; - } - - final UploadingImage image = pendingIncomingUploads.remove( token ); - if ( image == null ) { - downloader.sendErrorCode( "Token not accepted." ); - downloader.close( null ); - return; - } - final MutableInt lastBlock = new MutableInt( -1 ); - - try { - downloadPool.execute( new Runnable() { - @Override - public void run() - { - downloader.download( image.getAbsolutePath(), new WantRangeCallback() { - @Override - public FileRange get() - { - if ( lastBlock.intValue() != -1 ) { - image.setNeedsCheck( lastBlock.intValue() ); - image.increaseTransmittedTimes( lastBlock.intValue() ); - } - // get start of range. - int blockNumber = image.getNextMissingBlock(); - if ( blockNumber == -1 ) { - log.debug( "Download complete." ); - return null; - } - lastBlock.setValue( blockNumber ); - log.debug( "Block " + blockNumber + " was transmitted " + image.getTimesTransmitted( blockNumber ) + " time(s)." ); - - long startOfRange = image.getNextMissingBlock() * Globals.blockSize; - long endOfRange = Math.min( startOfRange + Globals.blockSize, image.getFileSize() ); - FileRange range = new FileRange( startOfRange, endOfRange ); - return range; - } - } ); - image.updateDb(); - } - } ); - } catch ( RejectedExecutionException e ) { - downloader.sendErrorCode( "Too many concurrent downloads." ); - downloader.close( null ); - } + // TODO + downloader.sendErrorCode( "Too many concurrent downloads." ); + downloader.cancel(); } } diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/CrcScheduler.java b/src/main/java/org/openslx/imagemaster/serverconnection/CrcScheduler.java deleted file mode 100644 index 031a807..0000000 --- a/src/main/java/org/openslx/imagemaster/serverconnection/CrcScheduler.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.openslx.imagemaster.serverconnection; - -import java.io.IOException; -import java.util.Iterator; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; - -import org.apache.log4j.Logger; -import org.openslx.imagemaster.Globals; -import org.openslx.imagemaster.crcchecker.CrcChecker; - -/** - * Class to schedule CRC checks. - * CRC checks of uploading images are so done parallel to the connection. - */ -public class CrcScheduler extends TimerTask -{ - - private static Logger log = Logger.getLogger( CrcScheduler.class ); - - @Override - public void run() - { - List<UploadingImage> list = ImageProcessor.getImagesToCheck(); - if ( list == null || list.isEmpty() ) - return; - log.debug( "Starting checks: " + list ); - Iterator<UploadingImage> iter = list.iterator(); - // iterate over the uploading images that need to be checked - while ( iter.hasNext() ) { - UploadingImage image = iter.next(); - log.debug( "Checking blocks of " + image.getUuid() ); - CrcChecker crcChecker = new CrcChecker( image.getImageFile(), image.getCrcFile() ); - log.debug( "CRCFile is valid: " + crcChecker.hasValidCrcFile() ); - if ( !crcChecker.hasValidCrcFile() ) { - image.setCrcFile( null ); // set crc file to null, so that the image processor will try to write it again. - crcChecker.done(); - continue; - } - for ( int block = 0; block < image.getNumberOfBlocks(); block++ ) { - if ( image.needsCheck( block ) ) { - try { - if ( crcChecker.checkBlock( block ) ) { - image.setValid( block ); - log.debug( block + " was valid" ); - } else { - image.setNeedsRequest( block ); - log.debug( block + " was NOT valid" ); - } - } catch ( IOException e ) { - if ( e.getMessage().equalsIgnoreCase( "crc" ) ) { - image.setCrcFile( null ); // set crc file to null, so that the imageprocessor will try to write it again. - image.updateDb(); - crcChecker.done(); - break; - } else { - // could not read image file - log.error( "Could not read from image file on disk. Pleas contact the server administrator. Image: '" + image.getImageFile().toString() + "'" ); - } - } - } - } - image.updateDb(); // writes valid blocks to database - crcChecker.done(); // closes image file - } - log.debug( "... done" ); - } - - public static void startScheduling() - { - Timer timer = new Timer( "CRCScheduler" ); - - // start now and fire every 60 s - timer.schedule( new CrcScheduler(), 0, Globals.getCrcSchedulingInterval() * 1000L ); - } - -} diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/ImageProcessor.java b/src/main/java/org/openslx/imagemaster/serverconnection/ImageProcessor.java deleted file mode 100644 index 15de77b..0000000 --- a/src/main/java/org/openslx/imagemaster/serverconnection/ImageProcessor.java +++ /dev/null @@ -1,191 +0,0 @@ -package org.openslx.imagemaster.serverconnection; - -import java.io.File; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.log4j.Logger; -import org.openslx.imagemaster.Globals; -import org.openslx.imagemaster.crcchecker.CrcFile; -import org.openslx.imagemaster.db.DbImage; -import org.openslx.imagemaster.db.DbUser; -import org.openslx.imagemaster.thrift.iface.DownloadData; -import org.openslx.imagemaster.thrift.iface.ImageData; -import org.openslx.imagemaster.thrift.iface.ImageDataError; -import org.openslx.imagemaster.thrift.iface.ImageDataException; -import org.openslx.imagemaster.thrift.iface.UploadData; -import org.openslx.imagemaster.thrift.iface.UploadError; -import org.openslx.imagemaster.thrift.iface.UploadException; -import org.openslx.imagemaster.util.RandomString; -import org.openslx.imagemaster.util.Util; - -/** - * Processing the up- and download of images. - * Handles who is authorized and knows which blocks are missing / need to be sent - */ -public class ImageProcessor -{ - - private static final Logger log = Logger.getLogger( ImageProcessor.class ); - - /** - * The uploading images. - * Key: imageUUID, - * Value: uploadingImageInfos - */ - private static Map<String, UploadingImage> uploadingImages = new ConcurrentHashMap<>(); - - /** - * The UUIDs of the images that need to be checked by the crc checker. - */ - private static List<String> imagesToCheck = new ArrayList<>(); - - /** - * Checks if this image is already uploading and returns a new list with missing blocks if so. - * Puts the new image into processing list else. - * - * @param serverSessionId The uploading server - * @param imageData The data of the image - * @return - * @throws UploadException If some error occurred during the process - */ - public static UploadData getUploadInfos( ImageData imageData, List<Integer> crcSums ) - throws UploadException, ImageDataException - { - // check image data - if ( imageData.title == null || imageData.title.isEmpty() ) - throw new ImageDataException( ImageDataError.INVALID_DATA, "Image name not set." ); - if ( imageData.ownerLogin == null || !DbUser.exists( imageData.ownerLogin ) ) - throw new ImageDataException( ImageDataError.INVALID_DATA, "Invalid image owner: " + imageData.ownerLogin ); - if ( !imageData.isSetOperatingSystem() ) - throw new ImageDataException( ImageDataError.INVALID_DATA, "Content operating system not set." ); - if ( imageData.fileSize <= 0 ) - throw new ImageDataException( ImageDataError.INVALID_DATA, "File size is too small." ); - - log.debug( "Satellite is submitting " + imageData.uuid ); - - final String uuid = imageData.uuid; - final String filepathRelative; - final CrcFile crcFile; - if ( crcSums == null ) { - crcFile = null; - } else { - crcFile = new CrcFile( crcSums ); - } - UploadingImage image; - - synchronized ( uploadingImages ) { - // check if image is already uploading - if ( ( image = uploadingImages.get( uuid ) ) == null ) { - // insert new image - if ( crcSums != null && !crcFile.isValid() ) - throw new UploadException( UploadError.INVALID_CRC, "CRC sums were invalid." ); - filepathRelative = generateFilepathOfImage( imageData ); - DbImage.insert( imageData, filepathRelative ); // Make sure it exists in DB - try { - image = new UploadingImage( uuid ); - } catch ( Exception e ) { - throw new UploadException( UploadError.GENERIC_ERROR, "Internal error" ); - } - uploadingImages.put( uuid, image ); - } - } - - final String token = RandomString.generate( 50, false ); - - ConnectionHandler.addUpload( token, image ); - // Set crc file on image - if there is already a crc file assigned, this does nothing - image.setCrcFile( crcFile ); - if ( image.allBlocksValid() ) - removeFinishedUploads(); - return new UploadData( token, Globals.getSslSocketPort() ); - } - - public static DownloadData getDownloadInfos( String uuid ) throws ImageDataException - { - DbImage image = DbImage.getImageByUuid( uuid ); - if ( image == null ) - throw new ImageDataException( ImageDataError.UNKNOWN_IMAGE, "UUID '" + uuid + "' does not map to a known image." ); - // server was downloading another image and now gets a new connection for this new download - String token = RandomString.generate( 50, false ); - ConnectionHandler.addDownload( token, image ); - return new DownloadData( token, Globals.getSslSocketPort(), null ); // TODO: Return crc list - } - - /** - * Go though list of active uploading images and remove - * those that are finished. - */ - public static void removeFinishedUploads() - { - - for ( Iterator<UploadingImage> it = uploadingImages.values().iterator(); it.hasNext(); ) { - UploadingImage image = it.next(); - if ( image.allBlocksValid() ) { - synchronized ( imagesToCheck ) { - imagesToCheck.remove( image.getUuid() ); - image.updateMissingBlocks( null ); - } - it.remove(); - } - } - } - - public static List<UploadingImage> getImagesToCheck() - { - synchronized ( imagesToCheck ) { - if ( imagesToCheck.isEmpty() ) - return null; - List<UploadingImage> result = new ArrayList<>( imagesToCheck.size() ); - for ( String uuid : imagesToCheck ) { - result.add( uploadingImages.get( uuid ) ); - } - return result; - } - } - - public static List<Integer> getRequestedBlocks( String token ) - { - // for the downloader - // TODO - return null; - } - - /** - * Generates the filePath of an image. - * And creates the folder if wanted. - * The crc file is found under filePath + ".crc" - * - * @param imageData The data of the image - * @return The filePath of the given image - */ - public static String generateFilepathOfImage( ImageData imageData ) - { - return generateFilePathOfImage( imageData.uuid, imageData.title, imageData.revision ); - } - - public static String generateFilePathOfImage( String uuid, String imageName, int imageVersion ) - { - String result = Util.sanitizeFileName( uuid ) + "/"; - File dir = new File( Globals.getImageDir() + "/" + result ); - if ( !dir.exists() ) { - dir.mkdirs(); - } - result += imageName + "-rev" + String.valueOf( imageVersion ); - return result; - } - - public static String generateFilepathOfCrcFile( ImageData imageData ) - { - return generateFilepathOfImage( imageData ) + ".crc"; - } - - public static String generateFilepathOfCrcFile( String uuid, String imageName, int imageVersion ) - { - return generateFilePathOfImage( uuid, imageName, imageVersion ) + ".crc"; - } - -} diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/UploadingImage.java b/src/main/java/org/openslx/imagemaster/serverconnection/UploadingImage.java deleted file mode 100644 index 46e6c91..0000000 --- a/src/main/java/org/openslx/imagemaster/serverconnection/UploadingImage.java +++ /dev/null @@ -1,243 +0,0 @@ -package org.openslx.imagemaster.serverconnection; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.log4j.Logger; -import org.openslx.imagemaster.Globals; -import org.openslx.imagemaster.crcchecker.CrcFile; -import org.openslx.imagemaster.crcchecker.ImageFile; -import org.openslx.imagemaster.db.DbImage; - -/** - * Helper class for ImageProcessor and ConnectionHandler to save some infos about the images in the - * process list. - */ -public class UploadingImage -{ - - public static final Logger log = Logger.getLogger( UploadingImage.class ); - - /** - * The status list of the blocks. - * x = 0 block is missing - * x = 200 block arrived and is valid - * x > 0 block is invalid and was transmitted x times (needs request) - * x < 0 block is invalid and was transmitted x times (needs check) - */ - private final int[] blockStatus; - /** - * Remember last position in blockStatus array that was returned, so we don't always - * iterate from the beginning. - */ - private int lastStatusPos = 0; - - public static final int VALID = 200; - public static final int MISSING = 0; - - private DbImage dbImage = null; // the DB representation of this image - /** - * Class for accessing the file (read blocks from it) - */ - private ImageFile imageFile = null; - private CrcFile crcFile = null; - - protected UploadingImage( String uuid ) - { - this.dbImage = DbImage.getImageByUuid( uuid ); - if ( this.dbImage == null ) - throw new RuntimeException( "Unknown image " + uuid + " on UploadingImage creation" ); - this.blockStatus = this.dbImage.blockStatus; - } - - protected void setValid( int index ) - { - synchronized ( blockStatus ) { - blockStatus[index] = VALID; - } - } - - protected void updateDb() - { - List<Integer> missingBlocks = new ArrayList<>(); - synchronized ( blockStatus ) { - for ( int block = 0; block < blockStatus.length; block++ ) { - if ( blockStatus[block] != VALID ) { - missingBlocks.add( block ); - } - } - } - dbImage.updateMissingBlocks( missingBlocks ); - } - - protected void setMissing( int index ) - { - synchronized ( blockStatus ) { - blockStatus[index] = MISSING; - } - } - - protected void setNeedsRequest( int index ) - { - synchronized ( blockStatus ) { - blockStatus[index] = Math.abs( blockStatus[index] ); // switch to positive value if needed - } - } - - protected void setNeedsCheck( int index ) - { - synchronized ( blockStatus ) { - if ( crcFile == null ) { - blockStatus[index] = VALID; - } else { - blockStatus[index] = -Math.abs( blockStatus[index] ); // switch to negative value if needed - } - } - } - - protected void increaseTransmittedTimes( int index ) - { - synchronized ( blockStatus ) { - if ( blockStatus[index] == VALID ) - return; - blockStatus[index] += ( blockStatus[index] <= MISSING ) ? -1 : 1; // increase in both directions - } - } - - protected int getTimesTransmitted( int index ) - { - synchronized ( blockStatus ) { - return Math.abs( blockStatus[index] ); - } - } - - protected boolean needsRequest( int index ) - { - synchronized ( blockStatus ) { - return ( ( blockStatus[index] >= MISSING ) && ( blockStatus[index] != VALID ) ); - } - } - - protected boolean needsCheck( int index ) - { - synchronized ( blockStatus ) { - return ( blockStatus[index] < MISSING ); - } - } - - protected int getNumberOfBlocks() - { - return blockStatus.length; - } - - protected int getNextMissingBlock() - { - synchronized ( blockStatus ) { - for ( int i = 0; i < blockStatus.length; i++ ) { - int index = ( i + lastStatusPos ) % blockStatus.length; - if ( blockStatus[index] == MISSING ) - return lastStatusPos = index; - } - for ( int index = 0; index < blockStatus.length; index++ ) { - if ( blockStatus[index] > MISSING && blockStatus[index] < VALID ) - return lastStatusPos = index; - } - for ( int index = 0; index < blockStatus.length; index++ ) { - if ( blockStatus[index] < MISSING ) - return lastStatusPos = index; - } - } - return -1; - } - - /* - protected long getTimestamp() - { - return this.timestamp; - } - */ - - protected ImageFile getImageFile() - { - if ( imageFile == null ) { - imageFile = new ImageFile( dbImage.getAbsolutePath(), Globals.blockSize ); - } - return imageFile; - } - - protected CrcFile getCrcFile() - { - if ( crcFile == null ) { - try { - crcFile = new CrcFile( dbImage.getAbsolutePath() + ".crc" ); - } catch ( IOException e ) { - // Not found... return null - } - } - return crcFile; - } - - protected void setCrcFile( CrcFile crcFile ) - { - if ( crcFile == null ) - return; - if ( getCrcFile() == null && crcFile.isValid() ) { - this.crcFile = crcFile; - crcFile.writeCrcFile( dbImage.getAbsolutePath() + ".crc" ); - } - } - - public int getAmountOfBlocksNeedingRequest() - { - if ( blockStatus == null ) - return 0; - int result = 0; - for ( int i = 0; i < blockStatus.length; i++ ) { - if ( needsRequest( i ) ) - result++; - } - return result; - } - - public boolean allBlocksValid() - { - if ( blockStatus == null ) - return false; - synchronized ( blockStatus ) { - for ( int i : blockStatus ) { - if ( i != 200 ) - return false; - } - } - return true; - } - - @Override - public String toString() - { - return "UUID: " + dbImage.uuid + ", filename " + dbImage.relativePath + "\nmissing blocks " + getAmountOfBlocksNeedingRequest() + - ", number of blocks " + getNumberOfBlocks(); - - } - - public String getAbsolutePath() - { - return dbImage.getAbsolutePath(); - } - - public long getFileSize() - { - return dbImage.fileSize; - } - - public String getUuid() - { - return dbImage.uuid; - } - - public void updateMissingBlocks( List<Integer> missingBlocks ) - { - dbImage.updateMissingBlocks( missingBlocks ); - } -} diff --git a/src/main/java/org/openslx/imagemaster/serversession/ServerAuthenticator.java b/src/main/java/org/openslx/imagemaster/serversession/ServerAuthenticator.java index 88697bf..a7c7fde 100644 --- a/src/main/java/org/openslx/imagemaster/serversession/ServerAuthenticator.java +++ b/src/main/java/org/openslx/imagemaster/serversession/ServerAuthenticator.java @@ -6,10 +6,10 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; import org.apache.thrift.TException; +import org.openslx.bwlp.thrift.iface.AuthorizationError; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; import org.openslx.encryption.AsymEncryptionHandler; -import org.openslx.imagemaster.db.DbSatellite; -import org.openslx.imagemaster.thrift.iface.AuthenticationError; -import org.openslx.imagemaster.thrift.iface.AuthenticationException; +import org.openslx.imagemaster.db.models.LocalSatellite; import org.openslx.imagemaster.util.RandomString; /** @@ -23,20 +23,20 @@ public class ServerAuthenticator /** * Servers currently doing authentication. Maps from organization to the challenge we sent. */ - private static Map<String, byte[]> authenticatingServers = new ConcurrentHashMap<>(); + private static Map<Integer, byte[]> authenticatingServers = new ConcurrentHashMap<>(); /** * Start the server authentification. * - * @param organization + * @param satelliteId * the organization of the server * @return encrypted random string */ - public static ByteBuffer startServerAuthentication( String organization ) + public static ByteBuffer startServerAuthentication( int satelliteId ) { byte[] secret = RandomString.generateBinary( 100 ); - authenticatingServers.put( organization, secret ); - log.info( "Server of organinzation '" + organization + authenticatingServers.put( satelliteId, secret ); + log.info( "Server of organinzation '" + satelliteId + "' starts to authenticate. And got string: '" + secret.length + "'" ); return ByteBuffer.wrap( secret ); @@ -48,25 +48,24 @@ public class ServerAuthenticator * @param organizationId Is already verified. * @param address * @param challengeResponse - * @return * @throws ServerAuthenticationException * @throws TException */ - public static ServerUser serverAuthenticate( DbSatellite satellite, ByteBuffer challengeResponse ) - throws AuthenticationException + public static void serverAuthenticate( LocalSatellite satellite, ByteBuffer challengeResponse ) + throws TAuthorizationException { byte[] encryptedBytes = new byte[ challengeResponse.remaining() ]; challengeResponse.get( encryptedBytes ); AsymEncryptionHandler verifier = new AsymEncryptionHandler( satellite.getPubkey() ); - if ( !verifier.verifyMessage( encryptedBytes, authenticatingServers.get( satellite.getOrganizationId() ) ) ) - throw new AuthenticationException( AuthenticationError.CHALLENGE_FAILED, "You failed the encryption challenge. private and public key don't seem to match." ); + if ( !verifier.verifyMessage( encryptedBytes, authenticatingServers.get( satellite.satelliteId ) ) ) { + throw new TAuthorizationException( AuthorizationError.CHALLENGE_FAILED, + "You failed the encryption challenge. private and public key don't seem to match." ); + } - log.info( "Server of organinzation " + satellite.getName() + " (" + satellite.getOrganizationId() + ") authenticated." ); + log.info( "Server '" + satellite.satelliteName + "' (" + satellite.organizationId + ") authenticated." ); - authenticatingServers.remove( satellite.getOrganizationId() ); - - return new ServerUser( satellite.getOrganizationId(), satellite.getAddress() ); + authenticatingServers.remove( satellite.organizationId ); } } diff --git a/src/main/java/org/openslx/imagemaster/serversession/ServerSession.java b/src/main/java/org/openslx/imagemaster/serversession/ServerSession.java index 28b143e..3b79c5d 100644 --- a/src/main/java/org/openslx/imagemaster/serversession/ServerSession.java +++ b/src/main/java/org/openslx/imagemaster/serversession/ServerSession.java @@ -1,6 +1,9 @@ package org.openslx.imagemaster.serversession; +import java.util.List; + import org.openslx.imagemaster.Globals; +import org.openslx.imagemaster.db.models.LocalSatellite; /** * Holds the session id of the server and manages the timeout. @@ -10,9 +13,9 @@ public class ServerSession private static final long TIMEOUT = Long.valueOf( Globals.getSessionTimeoutServer() ) * 1000L; private long timeOut = 0; - private final ServerUser serverUser; + private final LocalSatellite serverUser; - public ServerSession(final ServerUser serverUser) + public ServerSession(final LocalSatellite serverUser) { this.serverUser = serverUser; this.timeOut = System.currentTimeMillis() + TIMEOUT; @@ -30,13 +33,13 @@ public class ServerSession return System.currentTimeMillis() > this.timeOut; } - public String getOrganization() + public String getOrganizationId() { - return serverUser.organization; + return serverUser.organizationId; } - public String getAddress() + public List<String> getAddresses() { - return serverUser.address; + return serverUser.addresses; } } diff --git a/src/main/java/org/openslx/imagemaster/serversession/ServerSessionManager.java b/src/main/java/org/openslx/imagemaster/serversession/ServerSessionManager.java index 49e9ebe..4171ea4 100644 --- a/src/main/java/org/openslx/imagemaster/serversession/ServerSessionManager.java +++ b/src/main/java/org/openslx/imagemaster/serversession/ServerSessionManager.java @@ -4,22 +4,20 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.TimeUnit; -import org.apache.log4j.Logger; -import org.openslx.imagemaster.session.SessionManager; -import org.openslx.imagemaster.thrift.iface.ServerSessionData; +import org.openslx.bwlp.thrift.iface.ServerSessionData; import org.openslx.imagemaster.util.Hash; +import org.openslx.util.QuickTimer; +import org.openslx.util.QuickTimer.Task; /** * Manages all server sessions and kicks timed out sessions. */ public class ServerSessionManager { - private static Logger log = Logger.getLogger( SessionManager.class ); - // Map of currently known sessions private static final Map<String, ServerSession> serverSessions = new LinkedHashMap<String, ServerSession>(); - private static final Thread gcThread; public static ServerSessionData addSession( ServerSession serverSession ) { @@ -45,30 +43,21 @@ public class ServerSessionManager } static { - gcThread = new Thread( new Runnable() { - + QuickTimer.scheduleAtFixedDelay( new Task() { @Override - public void run() + public void fire() { - for ( ;; ) { - try { - Thread.sleep( 1800L * 1000L ); - } catch ( InterruptedException e ) { - } - synchronized ( serverSessions ) { - Iterator<ServerSession> it = serverSessions.values().iterator(); - while ( it.hasNext() ) { - final ServerSession s = it.next(); - if ( s.timedOut() ) { - log.debug( "Removing old session of " + s.getOrganization() ); - it.remove(); - } + synchronized ( serverSessions ) { + Iterator<ServerSession> it = serverSessions.values().iterator(); + while ( it.hasNext() ) { + final ServerSession s = it.next(); + if ( s.timedOut() ) { + it.remove(); } } } } - } ); - gcThread.start(); + }, 911, TimeUnit.MINUTES.toMillis( 14 ) ); } } diff --git a/src/main/java/org/openslx/imagemaster/serversession/ServerUser.java b/src/main/java/org/openslx/imagemaster/serversession/ServerUser.java deleted file mode 100644 index 3549621..0000000 --- a/src/main/java/org/openslx/imagemaster/serversession/ServerUser.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.openslx.imagemaster.serversession; - -/** - * The ServerUser that is hold in a ServerSession - */ -public class ServerUser -{ - - public final String organization, address; - - public ServerUser(String organization, String address) - { - this.organization = organization; - this.address = address; - } - -} diff --git a/src/main/java/org/openslx/imagemaster/session/Authenticator.java b/src/main/java/org/openslx/imagemaster/session/Authenticator.java index 19891b9..91f3127 100644 --- a/src/main/java/org/openslx/imagemaster/session/Authenticator.java +++ b/src/main/java/org/openslx/imagemaster/session/Authenticator.java @@ -1,9 +1,13 @@ package org.openslx.imagemaster.session; +import java.sql.SQLException; + import org.apache.log4j.Logger; -import org.openslx.imagemaster.db.DbUser; -import org.openslx.imagemaster.thrift.iface.AuthenticationError; -import org.openslx.imagemaster.thrift.iface.AuthenticationException; +import org.openslx.bwlp.thrift.iface.AuthorizationError; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; +import org.openslx.bwlp.thrift.iface.UserInfo; +import org.openslx.imagemaster.db.mappers.DbUser; +import org.openslx.imagemaster.db.models.LocalUser; /** * Authenticates a user against a backend (ldap here) @@ -19,38 +23,27 @@ public class Authenticator * @param username * @param password * @return - * @throws AuthenticationException + * @throws TAuthenticationException */ - public static User authenticate( String username, String password ) throws AuthenticationException + public static UserInfo authenticate( String username, String password ) throws TAuthorizationException { String login = username; - /* - if ( username.split( "@" ).length == 2 ) { - log.info( "username is in username@organization format" ); - // we are in username@organization format - DbSatellite satellite = DbSatellite.fromOrganization( username.split( "@" )[1] ); - if ( satellite == null ) - throw new AuthenticationException( AuthenticationError.INVALID_CREDENTIALS, "Unkown Organization." ); - login = satellite.getPrefix() + "_" + username.split( "@" )[0]; - } else if ( username.split( "_" ).length != 2 ) { - log.info( "username is not in a valid format." ); - throw new AuthenticationException( AuthenticationError.INVALID_CREDENTIALS, "Credentials must be in (username@organization) or (prefix@username)" ); - } - */ log.info( "Logging in with: " + login ); - User user = DbUser.forLogin( login, password ); // throws exception if credentials are invalid + LocalUser user; + try { + user = DbUser.forLogin( login, password ); + } catch ( SQLException e ) { + user = null; + } // throws exception if credentials are invalid if ( user == null ) { log.debug( "Login failed: " + username ); - throw new AuthenticationException( AuthenticationError.GENERIC_ERROR, "Something went wrong." ); + throw new TAuthorizationException( AuthorizationError.GENERIC_ERROR, "Something went wrong." ); } log.debug( "Login succesful: " + username ); - // if successfull: update/insert into db - DbUser.insertOrUpdate( user ); - - return user; + return user.toUserInfo(); } // } diff --git a/src/main/java/org/openslx/imagemaster/session/Session.java b/src/main/java/org/openslx/imagemaster/session/Session.java index f91a3ae..ed36274 100644 --- a/src/main/java/org/openslx/imagemaster/session/Session.java +++ b/src/main/java/org/openslx/imagemaster/session/Session.java @@ -1,5 +1,6 @@ package org.openslx.imagemaster.session; +import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.imagemaster.Globals; /** @@ -12,9 +13,9 @@ public class Session private static final long TIMEOUT = Long.valueOf( Globals.getSessionTimeoutUser() ) * 1000L; private long timeOut = 0; - private final User user; + private final UserInfo user; - public Session(final User dbuser) + public Session( final UserInfo dbuser ) { this.user = dbuser; this.timeOut = System.currentTimeMillis() + TIMEOUT; @@ -29,17 +30,17 @@ public class Session public synchronized boolean timedOut() { - return System.currentTimeMillis() > this.timeOut; + return this.timeOut == 0 || System.currentTimeMillis() > this.timeOut; } - public String getSatelliteAddress() + public synchronized void invalidate() { - return user.satelliteAddress; + this.timeOut = 0; } public String getLogin() { - return user.login; + return user.userId; } public String getFirstName() @@ -56,10 +57,15 @@ public class Session { return user.eMail; } - + public String getOrgenizationId() { return user.organizationId; } + public UserInfo getUserInfo() + { + return user; + } + } diff --git a/src/main/java/org/openslx/imagemaster/session/SessionManager.java b/src/main/java/org/openslx/imagemaster/session/SessionManager.java index 7dcb754..c12334a 100644 --- a/src/main/java/org/openslx/imagemaster/session/SessionManager.java +++ b/src/main/java/org/openslx/imagemaster/session/SessionManager.java @@ -1,13 +1,21 @@ package org.openslx.imagemaster.session; +import java.sql.SQLException; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; -import org.openslx.imagemaster.thrift.iface.SessionData; +import org.openslx.bwlp.thrift.iface.ClientSessionData; +import org.openslx.bwlp.thrift.iface.Satellite; +import org.openslx.bwlp.thrift.iface.UserInfo; +import org.openslx.imagemaster.db.mappers.DbSatellite; import org.openslx.imagemaster.util.Hash; +import org.openslx.util.QuickTimer; +import org.openslx.util.QuickTimer.Task; /** * Class for managing active user sessions. This class and all its function are @@ -19,9 +27,8 @@ public class SessionManager // Map of currently known sessions private static final Map<String, Session> sessions = new LinkedHashMap<>(); - private static final Thread gcThread; - public static SessionData addSession( Session session ) + public static ClientSessionData addSession( Session session ) { final String authToken = Hash.md5( UUID.randomUUID().toString() ); final String sessionId = Hash.sha256( UUID.randomUUID().toString() ); @@ -30,13 +37,20 @@ public class SessionManager sessions.put( authToken, session ); sessions.put( sessionId, session ); } - return new SessionData( sessionId, authToken, session.getSatelliteAddress() ); + UserInfo ui = session.getUserInfo(); + List<Satellite> sats; + try { + sats = DbSatellite.getSatellites( ui ); + } catch ( SQLException e ) { + sats = null; + } + return new ClientSessionData( sessionId, authToken, sats, ui ); } public static Session getSessionFromToken( String token ) { if ( token == null || token.length() != 32 ) { - log.debug("invalid token format: " + token); + log.debug( "invalid token format: " + token ); return null; } final Session session; @@ -50,35 +64,27 @@ public class SessionManager } static { - gcThread = new Thread( new Runnable() { - + QuickTimer.scheduleAtFixedDelay( new Task() { @Override - public void run() + public void fire() { - for ( ;; ) { - try { - Thread.sleep( 1800L * 1000L ); - } catch ( InterruptedException e ) { - } - synchronized ( sessions ) { - Iterator<Session> it = sessions.values().iterator(); - while ( it.hasNext() ) { - final Session s = it.next(); - if ( s.timedOut() ) { - it.remove(); - } + synchronized ( sessions ) { + Iterator<Session> it = sessions.values().iterator(); + while ( it.hasNext() ) { + final Session s = it.next(); + if ( s.timedOut() ) { + it.remove(); } } } } - } ); - gcThread.start(); + }, 123, TimeUnit.MINUTES.toMillis( 13 ) ); } public static Object getSessionFromSessionId( String sessionId ) { if ( sessionId == null || sessionId.length() != 64 ) { - log.debug("invalid sessionid format: " + sessionId); + log.debug( "invalid sessionid format: " + sessionId ); return null; } final Session session; @@ -91,4 +97,18 @@ public class SessionManager return session; } + public static void invalidate( String sessionId ) + { + if ( sessionId == null || sessionId.length() != 64 ) { + log.debug( "invalidate: invalid sessionid format: " + sessionId ); + return; + } + synchronized ( sessions ) { + Session session = sessions.get( sessionId ); + if ( session != null ) { + session.invalidate(); + } + } + } + } diff --git a/src/main/java/org/openslx/imagemaster/thrift/server/BinaryListener.java b/src/main/java/org/openslx/imagemaster/thrift/server/BinaryListener.java index d7a3c12..d18e8a8 100644 --- a/src/main/java/org/openslx/imagemaster/thrift/server/BinaryListener.java +++ b/src/main/java/org/openslx/imagemaster/thrift/server/BinaryListener.java @@ -18,14 +18,16 @@ import org.apache.thrift.transport.TSSLTransportFactory; import org.apache.thrift.transport.TSSLTransportFactory.TSSLTransportParameters; import org.apache.thrift.transport.TServerTransport; import org.apache.thrift.transport.TTransportException; +import org.openslx.bwlp.thrift.iface.MasterServer; import org.openslx.imagemaster.Globals; -import org.openslx.imagemaster.thrift.iface.ImageServer; +import org.openslx.thrifthelper.TBinaryProtocolSafe; public class BinaryListener implements Runnable { private static final int MAX_MSG_LEN = 30 * 1000 * 1000; - private final ImageServer.Processor<ImageServerHandler> processor = new ImageServer.Processor<ImageServerHandler>( new ImageServerHandler() ); + private final MasterServer.Processor<MasterServerHandler> processor = new MasterServer.Processor<MasterServerHandler>( + new MasterServerHandler() ); final TProtocolFactory protFactory = new TBinaryProtocolSafe.Factory( true, true ); private static Logger log = Logger.getLogger( BinaryListener.class ); @@ -49,7 +51,7 @@ public class BinaryListener implements Runnable } /** - * Listen with TLS wrapping - has to use the threadpool server, since encrypted + * Listen with TLS wrapping - has to use the thread pool server, since encrypted * servers cannot use nonblocking sockets :( * * @param port listen port @@ -76,7 +78,7 @@ public class BinaryListener implements Runnable args.protocolFactory( protFactory ); args.processor( processor ); args.minWorkerThreads( 4 ).maxWorkerThreads( 256 ); - args.requestTimeout( 30 ).requestTimeoutUnit( TimeUnit.SECONDS ); + args.requestTimeout( 2 ).requestTimeoutUnit( TimeUnit.MINUTES ); args.transportFactory( new TFramedTransport.Factory( MAX_MSG_LEN ) ); return new TThreadPoolServer( args ); } diff --git a/src/main/java/org/openslx/imagemaster/thrift/server/ImageServerHandler.java b/src/main/java/org/openslx/imagemaster/thrift/server/ImageServerHandler.java deleted file mode 100644 index f2f88d0..0000000 --- a/src/main/java/org/openslx/imagemaster/thrift/server/ImageServerHandler.java +++ /dev/null @@ -1,121 +0,0 @@ -package org.openslx.imagemaster.thrift.server; - -import java.nio.ByteBuffer; -import java.util.List; - -import org.apache.thrift.TException; -import org.openslx.imagemaster.server.ApiServer; -import org.openslx.imagemaster.thrift.iface.AuthenticationException; -import org.openslx.imagemaster.thrift.iface.AuthorizationException; -import org.openslx.imagemaster.thrift.iface.DownloadData; -import org.openslx.imagemaster.thrift.iface.ImageData; -import org.openslx.imagemaster.thrift.iface.ImageDataException; -import org.openslx.imagemaster.thrift.iface.ImageServer; -import org.openslx.imagemaster.thrift.iface.InvalidTokenException; -import org.openslx.imagemaster.thrift.iface.OrganizationData; -import org.openslx.imagemaster.thrift.iface.ServerSessionData; -import org.openslx.imagemaster.thrift.iface.SessionData; -import org.openslx.imagemaster.thrift.iface.UploadData; -import org.openslx.imagemaster.thrift.iface.UploadException; -import org.openslx.imagemaster.thrift.iface.UserInfo; - -public class ImageServerHandler implements ImageServer.Iface -{ - - @Override - public boolean ping() - { - return true; - } - - @Override - public SessionData authenticate( String username, String password ) - throws AuthenticationException - { - return ApiServer.authenticate( username, password ); - } - - @Override - public UserInfo getUserFromToken( String token ) - throws InvalidTokenException - { - return ApiServer.getUserFromToken( token ); - } - - @Override - public ByteBuffer startServerAuthentication( String organization ) - throws AuthenticationException - { - return ApiServer.startServerAuthentication( organization ); - } - - @Override - public ServerSessionData serverAuthenticate( String organization, - ByteBuffer challengeResponse ) throws AuthenticationException - { - return ApiServer.serverAuthenticate( organization, challengeResponse ); - } - - @Override - public UploadData submitImage( String serverSessionId, ImageData imageDescription, List<Integer> crcSums ) - throws AuthorizationException, ImageDataException, UploadException - { - return ApiServer.submitImage( serverSessionId, imageDescription, crcSums ); - } - - @Override - public DownloadData getImage( String serverSessionId, String uuid ) throws AuthorizationException, ImageDataException - { - return ApiServer.getImage( uuid, serverSessionId ); - } - - @Override - public boolean isServerAuthenticated( String serverSessionId ) - { - return ApiServer.isServerAuthenticated( serverSessionId ); - } - - @Override - public List<OrganizationData> getOrganizations() - { - return ApiServer.getOrganizations(); - } - - @Override - public List<UserInfo> findUser( String sessionId, String organizationId, String searchTerm ) throws AuthorizationException - { - return ApiServer.findUser( sessionId, organizationId, searchTerm ); - } - - @Override - public boolean publishUser( String serverSessionId, UserInfo user ) - throws AuthorizationException - { - return ApiServer.publishUser( serverSessionId, user ); - } - - @Override - public List<ImageData> getPublicImages( String sessionId, int page ) throws AuthorizationException - { - return ApiServer.getPublicImages( sessionId, page ); - } - - @Override - public boolean registerSatellite( String organizationId, String address, String modulus, String exponent ) - { - return ApiServer.registerSatellite( organizationId, address, modulus, exponent ); - } - - @Override - public boolean updateSatelliteAddress( String serverSessionId, String address ) throws TException - { - return ApiServer.updateSatelliteAddress( serverSessionId, address ); - } - - @Override - public ServerSessionData addSession( String localPassword, UserInfo userInfo ) throws TException - { - // TODO Should be called from local web authenticator doing the ECP stuff - return null; - } -} diff --git a/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java b/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java new file mode 100644 index 0000000..ec13a6f --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java @@ -0,0 +1,305 @@ +package org.openslx.imagemaster.thrift.server; + +import java.nio.ByteBuffer; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.AuthorizationError; +import org.openslx.bwlp.thrift.iface.ClientSessionData; +import org.openslx.bwlp.thrift.iface.ImagePublishData; +import org.openslx.bwlp.thrift.iface.InvocationError; +import org.openslx.bwlp.thrift.iface.MasterServer; +import org.openslx.bwlp.thrift.iface.MasterSoftware; +import org.openslx.bwlp.thrift.iface.MasterTag; +import org.openslx.bwlp.thrift.iface.OperatingSystem; +import org.openslx.bwlp.thrift.iface.Organization; +import org.openslx.bwlp.thrift.iface.Satellite; +import org.openslx.bwlp.thrift.iface.ServerSessionData; +import org.openslx.bwlp.thrift.iface.SessionData; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; +import org.openslx.bwlp.thrift.iface.TInvalidTokenException; +import org.openslx.bwlp.thrift.iface.TInvocationException; +import org.openslx.bwlp.thrift.iface.TNotFoundException; +import org.openslx.bwlp.thrift.iface.TTransferRejectedException; +import org.openslx.bwlp.thrift.iface.TransferInformation; +import org.openslx.bwlp.thrift.iface.UserInfo; +import org.openslx.bwlp.thrift.iface.Virtualizer; +import org.openslx.encryption.AsymKeyHolder; +import org.openslx.imagemaster.db.mappers.DbOrganization; +import org.openslx.imagemaster.db.mappers.DbOsVirt; +import org.openslx.imagemaster.db.mappers.DbPendingSatellite; +import org.openslx.imagemaster.db.mappers.DbSatellite; +import org.openslx.imagemaster.db.mappers.DbUser; +import org.openslx.imagemaster.db.models.LocalSatellite; +import org.openslx.imagemaster.serversession.ServerAuthenticator; +import org.openslx.imagemaster.serversession.ServerSession; +import org.openslx.imagemaster.serversession.ServerSessionManager; +import org.openslx.imagemaster.session.Authenticator; +import org.openslx.imagemaster.session.Session; +import org.openslx.imagemaster.session.SessionManager; +import org.openslx.imagemaster.util.Util; + +public class MasterServerHandler implements MasterServer.Iface +{ + + private static final Logger LOGGER = Logger.getLogger( MasterServerHandler.class ); + + @Override + public boolean ping() + { + return true; + } + + @Override + public SessionData authenticate( String login, String password ) throws TAuthorizationException, TInvocationException + { + ClientSessionData csd = localAccountLogin( login, password ); + String serverAddress = null; + if ( csd.satellites != null && !csd.satellites.isEmpty() ) { + for ( Satellite sat : csd.satellites ) { + if ( sat.addressList == null || sat.addressList.isEmpty() ) + continue; + if ( serverAddress == null || ( sat.displayName != null && sat.displayName.equals( "default" ) ) ) { + serverAddress = sat.addressList.get( 0 ); + } + } + } + return new SessionData( csd.sessionId, csd.authToken, serverAddress ); + } + + /** + * Request for authentication + * + * @param login The user's login in the form "user@organization.com" + * @param password user's password + * @return SessionData struct with session id/token iff login successful + * @throws TAuthorizationException if login not successful + */ + @Override + public ClientSessionData localAccountLogin( String login, String password ) + throws TAuthorizationException, TInvocationException + { + if ( login == null || password == null ) { + throw new TAuthorizationException( + AuthorizationError.INVALID_CREDENTIALS, + "Empty username or password!" ); + } + final UserInfo user = Authenticator.authenticate( login, password ); + + final Session session = new Session( user ); + return SessionManager.addSession( session ); + } + + @Override + public List<UserInfo> findUser( String sessionId, String organizationId, String searchTerm ) + throws TAuthorizationException, TInvocationException + { + // Needs to be a logged in user + if ( SessionManager.getSessionFromSessionId( sessionId ) == null ) + throw new TAuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" ); + // Search string needs to be at least 2 characters (FIXME: quick and dirty ignoring LIKE chars) + if ( searchTerm == null || searchTerm.length() < 2 || searchTerm.replaceAll( "[%_]", "" ).length() < 2 ) + return new ArrayList<>( 0 ); + return DbUser.findUser( organizationId, searchTerm ); + } + + @Override + public List<ImagePublishData> getPublicImages( String sessionId, int page ) + throws TAuthorizationException, TInvocationException + { + // TODO Auto-generated method stub + return null; + } + + @Override + public void invalidateSession( String sessionId ) throws TInvalidTokenException + { + SessionManager.invalidate( sessionId ); + } + + /** + * Request information about user for given token + * + * @param token a user's token + * @return UserInfo struct for given token's user + * @throws InvalidTokenException if no user matches the given token + */ + @Override + public UserInfo getUserFromToken( String token ) throws TInvalidTokenException + { + final Session session = SessionManager.getSessionFromToken( token ); + if ( session == null ) + throw new TInvalidTokenException(); + return new UserInfo( session.getLogin(), session.getFirstName(), + session.getLastName(), session.getEMail(), session.getOrgenizationId() ); + } + + @Override + public boolean isServerAuthenticated( String serverSessionId ) + { + return ( ServerSessionManager.getSession( serverSessionId ) != null ); + } + + /** + * Start the server authentication of a uni/hs satellite server. + * + * @param organization the organization that the server belongs to + * @return a random string that needs to be encrypted with the private + * key of the requesting satellite server + * @throws ServerAuthenticationException when organization is invalid/unknown + */ + @Override + public ByteBuffer startServerAuthentication( int satelliteId ) throws TAuthorizationException, TInvocationException + { + LocalSatellite satellite = DbSatellite.get( satelliteId ); + if ( satellite == null ) + throw new TAuthorizationException( AuthorizationError.INVALID_ORGANIZATION, "Unknown satellite id: " + satelliteId ); + if ( satellite.getPubkey() == null ) + throw new TAuthorizationException( AuthorizationError.INVALID_KEY, "There is no public key known for your satellite." ); + return ServerAuthenticator.startServerAuthentication( satelliteId ); + } + + /** + * Authenticate the uni/hs satellite server with the encrypted string. + * + * @param organization the organization that the server belongs to + * @param challengeResponse the encrypted string + * @return session data iff the authentication was successful + */ + @Override + public ServerSessionData serverAuthenticate( int satelliteId, ByteBuffer challengeResponse ) + throws TAuthorizationException, TInvocationException + { + if ( challengeResponse == null ) + throw new TAuthorizationException( AuthorizationError.INVALID_ORGANIZATION, "Empty organization or challengeResponse" ); + LocalSatellite satellite = DbSatellite.get( satelliteId ); + if ( satellite == null ) + throw new TAuthorizationException( AuthorizationError.INVALID_ORGANIZATION, "Unknown satellite id: " + satelliteId ); + if ( satellite.getPubkey() == null ) + throw new TAuthorizationException( AuthorizationError.INVALID_KEY, "There is no public key known for your satellite." ); + + ServerAuthenticator.serverAuthenticate( satellite, challengeResponse ); + + final ServerSession session = new ServerSession( satellite ); + return ServerSessionManager.addSession( session ); + } + + @Override + public ImagePublishData getImageData( String serverSessionId, String imageVersionId ) + throws TAuthorizationException, TInvocationException, TNotFoundException + { + // TODO Auto-generated method stub + return null; + } + + @Override + public TransferInformation submitImage( String serverSessionId, ImagePublishData imageDescription, List<ByteBuffer> blockHashes ) + throws TAuthorizationException, TInvocationException, TTransferRejectedException + { + // TODO Auto-generated method stub + return null; + } + + @Override + public TransferInformation downloadImage( String sessionId, String imageVersionId ) + throws TAuthorizationException, TInvocationException, TNotFoundException + { + // TODO Auto-generated method stub + return null; + } + + @Override + public List<Organization> getOrganizations() throws TInvocationException + { + try { + return DbOrganization.getAll(); + } catch ( SQLException e ) { + throw new TInvocationException(); + } + } + + @Override + public List<OperatingSystem> getOperatingSystems() throws TInvocationException + { + try { + return DbOsVirt.getOsList(); + } catch ( SQLException e ) { + throw new TInvocationException(); + } + } + + @Override + public List<Virtualizer> getVirtualizers() throws TInvocationException + { + try { + return DbOsVirt.getVirtualizerList(); + } catch ( SQLException e ) { + throw new TInvocationException(); + } + } + + @Override + public List<MasterTag> getTags( long startDate ) throws TInvocationException + { + // TODO Auto-generated method stub + return null; + } + + @Override + public List<MasterSoftware> getSoftware( long startDate ) throws TInvocationException + { + // TODO Auto-generated method stub + return null; + } + + @Override + public int registerSatellite( String userToken, String displayName, List<String> addresses, String modulus, + String exponent, ByteBuffer certSha256 ) throws TInvocationException + { + if ( userToken == null || exponent == null || modulus == null ) + throw new TInvocationException( InvocationError.MISSING_DATA, "A required parameter is null" ); + final Session session = SessionManager.getSessionFromToken( userToken ); + if ( session == null || session.getUserInfo() == null ) + throw new TInvocationException( InvocationError.UNKNOWN_USER, "Not a valid user token" ); + String organizationId = session.getUserInfo().organizationId; + Key newKey; + try { + newKey = new AsymKeyHolder( null, Util.tryToParseBigInt( exponent ), Util.tryToParseBigInt( modulus ) ).getPublicKey(); + } catch ( NoSuchAlgorithmException | InvalidKeySpecException e ) { + LOGGER.warn( "Invalid public key in registerOrganization for " + organizationId + " (By " + session.getLogin() + ")", e ); + throw new TInvocationException( InvocationError.INVALID_DATA, "Cannot reconstruct public key" ); + } + if ( newKey == null ) { + LOGGER.warn( "Uninstantiable public key in registerOrganization for " + organizationId + " (By " + session.getLogin() + ")" ); + throw new TInvocationException( InvocationError.INVALID_DATA, "Cannot reconstruct public key" ); + } + LocalSatellite existing = DbSatellite.get( organizationId, displayName ); + if ( existing != null ) { + Key existingKey = existing.getPubkey(); + if ( existingKey != null && Util.keysEqual( newKey, existingKey ) ) + return existing.satelliteId; + } + try { + return DbPendingSatellite.add( session.getUserInfo(), displayName, addresses, modulus, exponent ); + } catch ( SQLException e ) { + throw new TInvocationException(); + } + } + + @Override + public boolean updateSatellite( String serverSessionId, String displayName, List<String> addresses ) + throws TAuthorizationException, TInvocationException + { + ServerSession session = ServerSessionManager.getSession( serverSessionId ); + if ( session == null ) + throw new TAuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "No valid serverSessionId" ); + // TODO + return false; + } + +} diff --git a/src/main/java/org/openslx/imagemaster/util/Util.java b/src/main/java/org/openslx/imagemaster/util/Util.java index a524953..ca595b5 100644 --- a/src/main/java/org/openslx/imagemaster/util/Util.java +++ b/src/main/java/org/openslx/imagemaster/util/Util.java @@ -35,8 +35,7 @@ public class Util { if ( something == null ) { if ( message != null ) - log.fatal( "[NOTNULL] " + message ); - log.warn( Thread.currentThread().getStackTrace().toString() ); + log.fatal( "[NOTNULL] " + message, new NullPointerException() ); System.exit( 2 ); } } @@ -54,8 +53,7 @@ public class Util { if ( something == null || something.isEmpty() ) { if ( message != null ) - log.fatal( "[NOTNULL] " + message ); - log.warn( Thread.currentThread().getStackTrace().toString() ); + log.fatal( "[NOTNULL] " + message, new NullPointerException() ); System.exit( 2 ); } } @@ -98,6 +96,11 @@ public class Util folder.delete(); } + public static boolean isEmpty( String str ) + { + return str != null && !str.isEmpty(); + } + /** * Tries to parse an int. Returns 0 on error. * |