From f45886abed5f04728561d5c8f97423a8036806fc Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 30 Aug 2016 18:02:43 +0200 Subject: Implement global image exchange with satellite servers --- src/main/java/org/openslx/imagemaster/Globals.java | 6 + .../java/org/openslx/imagemaster/db/Paginator.java | 10 +- .../openslx/imagemaster/db/mappers/DbImage.java | 196 ++++++++++++++++++++- .../imagemaster/db/mappers/DbImageBlock.java | 67 +++++++ .../org/openslx/imagemaster/db/mappers/DbUser.java | 15 +- .../openslx/imagemaster/db/models/LocalUser.java | 4 +- .../serverconnection/ConnectionHandler.java | 167 ++++++++++++++++-- .../serverconnection/IncomingTransfer.java | 45 ++++- .../serverconnection/OutgoingTransfer.java | 22 +++ .../imagemaster/session/SessionManager.java | 45 +++-- .../thrift/server/MasterServerHandler.java | 160 +++++++++++++---- .../org/openslx/imagemaster/util/Sha512Crypt.java | 2 +- .../org/openslx/imagemaster/util/UserUtil.java | 62 +++++++ 13 files changed, 713 insertions(+), 88 deletions(-) create mode 100644 src/main/java/org/openslx/imagemaster/serverconnection/OutgoingTransfer.java create mode 100644 src/main/java/org/openslx/imagemaster/util/UserUtil.java diff --git a/src/main/java/org/openslx/imagemaster/Globals.java b/src/main/java/org/openslx/imagemaster/Globals.java index aebc198..7b634f0 100644 --- a/src/main/java/org/openslx/imagemaster/Globals.java +++ b/src/main/java/org/openslx/imagemaster/Globals.java @@ -126,6 +126,12 @@ public class Globals return 86400l * 500; } + public static long getOldImageExpireTimeSeconds() + { + // TODO Auto-generated method stub + return 86400l * 30; + } + /* STRINGS */ public static String getImageDir() diff --git a/src/main/java/org/openslx/imagemaster/db/Paginator.java b/src/main/java/org/openslx/imagemaster/db/Paginator.java index 267a1a3..ad6bb9f 100644 --- a/src/main/java/org/openslx/imagemaster/db/Paginator.java +++ b/src/main/java/org/openslx/imagemaster/db/Paginator.java @@ -1,11 +1,13 @@ package org.openslx.imagemaster.db; -public class Paginator { +public class Paginator +{ - public static final int PER_PAGE = 200; + public static final int PER_PAGE = 400; - public static String limitStatement(int page) { - return " LIMIT " + (page * PER_PAGE) + ", " + PER_PAGE; + public static String limitStatement( int page ) + { + return " LIMIT " + ( page * PER_PAGE ) + ", " + PER_PAGE; } } 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 f4c3ddc..6fddf76 100644 --- a/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java +++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbImage.java @@ -1,16 +1,27 @@ 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.List; import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.ImageDetailsRead; +import org.openslx.bwlp.thrift.iface.ImagePermissions; import org.openslx.bwlp.thrift.iface.ImagePublishData; +import org.openslx.bwlp.thrift.iface.ImageSummaryRead; +import org.openslx.bwlp.thrift.iface.ImageVersionDetails; import org.openslx.bwlp.thrift.iface.InvocationError; +import org.openslx.bwlp.thrift.iface.ShareMode; import org.openslx.bwlp.thrift.iface.TInvocationException; +import org.openslx.bwlp.thrift.iface.TNotFoundException; import org.openslx.imagemaster.Globals; import org.openslx.imagemaster.db.Database; import org.openslx.imagemaster.db.MysqlConnection; import org.openslx.imagemaster.db.MysqlStatement; +import org.openslx.imagemaster.db.Paginator; +import org.openslx.thrifthelper.ImagePublishDataEx; import org.openslx.util.Util; /** @@ -22,9 +33,40 @@ public class DbImage private static final Logger LOGGER = Logger.getLogger( DbImage.class ); - public static ImagePublishData getImageVersion( String imageVersionId ) + private static final ImagePermissions emptyPermissions = new ImagePermissions(); + + public static ImagePublishDataEx getImageVersion( String imageVersionId ) throws SQLException { - return null; + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "SELECT v.imageversionid, v.imagebaseid, v.createtime, v.filesize, v.filepath," + + " v.uploaderid, v.isvalid, v.virtualizerconfig, b.displayname, b.description, b.osid, b.virtid, b.ownerid, b.istemplate" + + " FROM imageversion v INNER JOIN imagebase b USING (imagebaseid)" + + " WHERE v.imageversionid = :imageversionid" ); + stmt.setString( "imageversionid", imageVersionId ); + ResultSet rs = stmt.executeQuery(); + if ( !rs.next() ) + return null; + ImagePublishDataEx img = new ImagePublishDataEx(); + img.imageVersionId = rs.getString( "imageversionid" ); + img.imageBaseId = rs.getString( "imagebaseid" ); + img.createTime = rs.getLong( "createtime" ); + img.fileSize = rs.getLong( "filesize" ); + img.uploader = DbUser.getUserInfoOrNull( connection, rs.getString( "uploaderid" ) ); + img.imageName = rs.getString( "displayname" ); + img.description = rs.getString( "description" ); + img.osId = rs.getInt( "osid" ); + img.virtId = rs.getString( "virtid" ); + img.owner = DbUser.getUserInfoOrNull( connection, rs.getString( "ownerid" ) ); + img.isTemplate = rs.getBoolean( "istemplate" ); + img.machineDescription = ByteBuffer.wrap( rs.getBytes( "virtualizerconfig" ) ); + // Ex + img.exImagePath = rs.getString( "filepath" ); + img.exIsValid = rs.getBoolean( "isvalid" ); + return img; + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbImage.getImageVersion()", e ); + throw e; + } } public static void createImageBase( ImagePublishData img ) throws TInvocationException @@ -44,7 +86,7 @@ public class DbImage + " istemplate = :istemplate WHERE imagebaseid = :baseid" ); stmt2.setString( "baseid", img.imageBaseId ); stmt2.setString( "displayname", img.imageName ); - stmt2.setString( "updaterid", img.user.userId ); + stmt2.setString( "updaterid", img.uploader.userId ); stmt2.setString( "description", img.description ); stmt2.setInt( "osid", img.osId ); stmt2.setBoolean( "istemplate", img.isTemplate ); @@ -62,8 +104,8 @@ public class DbImage stmt2.setInt( "osid", img.osId ); stmt2.setString( "virtid", img.virtId ); stmt2.setLong( "createtime", img.createTime ); - stmt2.setString( "ownerid", img.user.userId ); - stmt2.setString( "updaterid", img.user.userId ); + stmt2.setString( "ownerid", img.owner.userId ); + stmt2.setString( "updaterid", img.uploader.userId ); stmt2.setBoolean( "istemplate", img.isTemplate ); stmt2.executeUpdate(); } @@ -83,14 +125,15 @@ public class DbImage + " filepath, uploaderid, isvalid, isprocessed, mastersha1, virtualizerconfig)" + " VALUES " + " (:imageversionid, :imagebaseid, :createtime, :expiretime, :filesize," - + " :filepath, :uploaderid, 0, 0, NULL, NULL)" ); + + " :filepath, :uploaderid, 0, 0, NULL, :virtualizerconfig)" ); verStmt.setString( "imageversionid", img.imageVersionId ); verStmt.setString( "imagebaseid", img.imageBaseId ); verStmt.setLong( "createtime", img.createTime ); verStmt.setLong( "expiretime", Util.unixTime() + Globals.getImageValiditySeconds() ); verStmt.setLong( "filesize", img.fileSize ); verStmt.setString( "filepath", relLocalPath ); - verStmt.setString( "uploaderid", img.user.userId ); + verStmt.setString( "uploaderid", img.uploader.userId ); + verStmt.setBinary( "virtualizerconfig", img.getMachineDescription() ); verStmt.execute(); connection.commit(); } catch ( SQLException e ) { @@ -99,4 +142,143 @@ public class DbImage } } + public static void markValid( String imageVersionId, boolean isValid ) throws SQLException + { + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "UPDATE imageversion" + + " SET isvalid = :isvalid WHERE imageversionid = :imageversionid" ); + stmt.setBoolean( "isvalid", isValid ); + stmt.setString( "imageversionid", imageVersionId ); + stmt.executeUpdate(); + updateLatestForVersion( connection, imageVersionId ); + connection.commit(); + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbImage.markValid()", e ); + throw e; + } + } + + private static void updateLatestForVersion( MysqlConnection connection, String imageVersionId ) throws SQLException + { + MysqlStatement stmt = connection.prepareStatement( "SELECT imagebaseid FROM imageversion" + + " WHERE imageversionid = :imageversionid" ); + stmt.setString( "imageversionid", imageVersionId ); + ResultSet rs = stmt.executeQuery(); + if ( !rs.next() ) + return; + updateLatestForBase( connection, rs.getString( "imagebaseid" ) ); + } + + private static void updateLatestForBase( MysqlConnection connection, String imageBaseId ) throws SQLException + { + MysqlStatement stmt = connection.prepareStatement( "SELECT imageversionid FROM imageversion" + + " WHERE isvalid <> 0 AND expiretime > UNIX_TIMESTAMP() AND imagebaseid = :imagebaseid" + + " ORDER BY createtime DESC LIMIT 1" ); + stmt.setString( "imagebaseid", imageBaseId ); + ResultSet rs = stmt.executeQuery(); + String latestVersionId = null; + if ( rs.next() ) { + latestVersionId = rs.getString( "imageversionid" ); + } + MysqlStatement updateLatestRef = connection.prepareStatement( "UPDATE imagebase SET latestversionid = :latestversionid" + + " WHERE imagebaseid = :imagebaseid" ); + updateLatestRef.setString( "latestversionid", latestVersionId ); + updateLatestRef.setString( "imagebaseid", imageBaseId ); + updateLatestRef.executeUpdate(); + MysqlStatement expireStmt = connection.prepareStatement( "UPDATE imageversion SET expiretime = :expiretime" + + " WHERE imagebaseid = :imagebaseid AND imageversionid <> :latestversionid" ); + expireStmt.setString( "imagebaseid", imageBaseId ); + expireStmt.setString( "latestversionid", latestVersionId ); + expireStmt.setLong( "expiretime", Util.unixTime() + Globals.getOldImageExpireTimeSeconds() ); + expireStmt.executeUpdate(); + } + + public static List getPublicList( int page ) throws SQLException, TInvocationException + { + if ( page < 0 ) + throw new TInvocationException( InvocationError.INVALID_DATA, "page must be >= 0" ); + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "SELECT v.imageversionid, v.imagebaseid, v.createtime, v.filesize," + + " v.uploaderid, b.displayname, b.description, b.osid, b.virtid, b.ownerid, b.istemplate FROM imagebase b" + + " INNER JOIN imageversion v ON (b.latestversionid = v.imageversionid)" + + " WHERE v.isvalid = 1" + + " ORDER BY imageversionid " + Paginator.limitStatement( page ) ); + ResultSet rs = stmt.executeQuery(); + List list = new ArrayList<>(); + while ( rs.next() ) { + ImageSummaryRead img = new ImageSummaryRead(); + img.createTime = rs.getLong( "createtime" ); + //img.description = rs.getString( "description" ); + img.fileSize = rs.getLong( "filesize" ); + img.imageBaseId = rs.getString( "imagebaseid" ); + img.imageName = rs.getString( "displayname" ); + img.latestVersionId = rs.getString( "imageversionid" ); + img.isTemplate = rs.getBoolean( "istemplate" ); + img.osId = rs.getInt( "osid" ); + img.ownerId = rs.getString( "ownerid" ); + //img.software = rs.get( "" ); TODO + //img.tags = rs.get( "" ); TODO + img.uploaderId = rs.getString( "uploaderid" ); + img.virtId = rs.getString( "virtid" ); + img.userPermissions = emptyPermissions; + img.defaultPermissions = emptyPermissions; + list.add( img ); + } + return list; + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbImage.getPublicList()", e ); + throw e; + } + } + + protected static List getImageVersions( MysqlConnection connection, String imageBaseId ) + throws SQLException + { + List versionList = new ArrayList<>(); + MysqlStatement stmt = connection.prepareStatement( "SELECT" + + " imageversionid, createtime, expiretime, filesize, uploaderid," + + " isprocessed FROM imageversion" + + " WHERE imagebaseid = :imagebaseid AND isvalid = 1" ); + stmt.setString( "imagebaseid", imageBaseId ); + ResultSet rs = stmt.executeQuery(); + while ( rs.next() ) { + List software = null; // DbSoftwareTag.getImageVersionSoftwareList(connection, imageVersionId); + String imageVersionId = rs.getString( "imageversionid" ); + versionList.add( new ImageVersionDetails( imageVersionId, rs.getLong( "createtime" ), + rs.getLong( "expiretime" ), rs.getLong( "filesize" ), rs.getString( "uploaderid" ), + true, true, + rs.getBoolean( "isprocessed" ), software ) ); + } + stmt.close(); + return versionList; + } + + public static ImageDetailsRead getImageDetails( String imageBaseId ) throws TNotFoundException, SQLException + { + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "SELECT i.imagebaseid, i.latestversionid, i.displayname," + + " i.description, i.osid, i.virtid, i.createtime, i.updatetime, i.ownerid, i.updaterid, i.istemplate" + + " FROM imagebase i" + + " WHERE i.imagebaseid = :imagebaseid" ); + stmt.setString( "imagebaseid", imageBaseId ); + ResultSet rs = stmt.executeQuery(); + if ( !rs.next() ) + throw new TNotFoundException(); + // Exists: + List tags = null; // DbSoftwareTag.getImageTags(connection, imageBaseId); + List versions = getImageVersions( connection, imageBaseId ); + ImageDetailsRead image = new ImageDetailsRead( rs.getString( "imagebaseid" ), + rs.getString( "latestversionid" ), versions, rs.getString( "displayname" ), + rs.getString( "description" ), tags, rs.getInt( "osid" ), rs.getString( "virtid" ), + rs.getLong( "createtime" ), rs.getLong( "updatetime" ), rs.getString( "ownerid" ), + rs.getString( "updaterid" ), ShareMode.FROZEN, + rs.getBoolean( "istemplate" ), emptyPermissions ); + image.userPermissions = emptyPermissions; + return image; + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbImage.getImageDetails()", e ); + throw e; + } + } + } diff --git a/src/main/java/org/openslx/imagemaster/db/mappers/DbImageBlock.java b/src/main/java/org/openslx/imagemaster/db/mappers/DbImageBlock.java index 7986d87..155f522 100644 --- a/src/main/java/org/openslx/imagemaster/db/mappers/DbImageBlock.java +++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbImageBlock.java @@ -1,6 +1,9 @@ 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.List; import java.util.concurrent.ArrayBlockingQueue; @@ -109,4 +112,68 @@ public class DbImageBlock } } + public static List getMissingStatusList( String imageVersionId ) throws SQLException + { + try ( MysqlConnection connection = Database.getConnection() ) { + MysqlStatement stmt = connection.prepareStatement( "SELECT startbyte, ismissing FROM imageblock" + + " WHERE imageversionid = :imageversionid ORDER BY startbyte ASC" ); + stmt.setString( "imageversionid", imageVersionId ); + ResultSet rs = stmt.executeQuery(); + List list = new ArrayList<>(); + long expectedOffset = 0; + while ( rs.next() ) { + long currentOffset = rs.getLong( "startbyte" ); + if ( currentOffset < expectedOffset ) + continue; + while ( currentOffset > expectedOffset ) { + list.add( Boolean.TRUE ); + expectedOffset += FileChunk.CHUNK_SIZE; + } + if ( currentOffset == expectedOffset ) { + list.add( rs.getBoolean( "ismissing" ) ); + expectedOffset += FileChunk.CHUNK_SIZE; + } + } + return list; + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbImageBlock.getBlockStatuses()", e ); + throw e; + } + } + + public static List getBlockHashes( String imageVersionId ) throws SQLException + { + try ( MysqlConnection connection = Database.getConnection() ) { + return getBlockHashes( connection, imageVersionId ); + } catch ( SQLException e ) { + LOGGER.error( "Query failed in DbImageBlock.getBlockHashes()", e ); + throw e; + } + } + + private static List getBlockHashes( MysqlConnection connection, String imageVersionId ) + throws SQLException + { + MysqlStatement stmt = connection.prepareStatement( "SELECT startbyte, blocksha1 FROM imageblock" + + " WHERE imageversionid = :imageversionid ORDER BY startbyte ASC" ); + stmt.setString( "imageversionid", imageVersionId ); + ResultSet rs = stmt.executeQuery(); + List list = new ArrayList<>(); + long expectedOffset = 0; + while ( rs.next() ) { + long currentOffset = rs.getLong( "startbyte" ); + if ( currentOffset < expectedOffset ) + continue; + while ( currentOffset > expectedOffset ) { + list.add( null ); + expectedOffset += FileChunk.CHUNK_SIZE; + } + if ( currentOffset == expectedOffset ) { + list.add( ByteBuffer.wrap( rs.getBytes( "blocksha1" ) ) ); + expectedOffset += FileChunk.CHUNK_SIZE; + } + } + return list; + } + } 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 b6040e7..00ec45e 100644 --- a/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java +++ b/src/main/java/org/openslx/imagemaster/db/mappers/DbUser.java @@ -73,7 +73,18 @@ public class DbUser throw new TNotFoundException(); return user.toUserInfo(); } - + + static UserInfo getUserInfoOrNull( MysqlConnection connection, String userId ) throws SQLException + { + MysqlStatement stmt = connection.prepareStatement( localUserSql + + " WHERE user.userid = :userid" ); + stmt.setString( "userid", userId ); + ResultSet rs = stmt.executeQuery(); + if ( !rs.next() ) + return null; + return localFromRs( rs ).toUserInfo(); + } + public static List findUser( String organizationId, String searchTerm ) { // TODO Implement @@ -84,7 +95,7 @@ public class DbUser { return exists( user, false ); } - + public static boolean exists( UserInfo user, boolean withIdentity ) { if ( user == null ) diff --git a/src/main/java/org/openslx/imagemaster/db/models/LocalUser.java b/src/main/java/org/openslx/imagemaster/db/models/LocalUser.java index bc9289a..39468c4 100644 --- a/src/main/java/org/openslx/imagemaster/db/models/LocalUser.java +++ b/src/main/java/org/openslx/imagemaster/db/models/LocalUser.java @@ -18,7 +18,7 @@ public class LocalUser public final Role role; public LocalUser( String login, String password, String organizationId, String firstName, String lastName, String eMail, - Role tutor ) + Role role ) { this.login = login; this.organizationId = organizationId; @@ -26,7 +26,7 @@ public class LocalUser this.firstName = firstName; this.lastName = lastName; this.eMail = eMail; - this.role = tutor; + this.role = role; } @Override diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java b/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java index 141e17f..f32c655 100644 --- a/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java +++ b/src/main/java/org/openslx/imagemaster/serverconnection/ConnectionHandler.java @@ -1,10 +1,13 @@ package org.openslx.imagemaster.serverconnection; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.security.KeyStore; +import java.sql.SQLException; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -21,13 +24,17 @@ 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.IncomingEvent; import org.openslx.filetransfer.Listener; import org.openslx.filetransfer.Uploader; -import org.openslx.filetransfer.util.AbstractTransfer; import org.openslx.imagemaster.Globals; +import org.openslx.imagemaster.db.mappers.DbImage; +import org.openslx.thrifthelper.ImagePublishDataEx; import org.openslx.util.GrowingThreadPoolExecutor; +import org.openslx.util.QuickTimer; +import org.openslx.util.QuickTimer.Task; /** * Class to handle all incoming and outgoing connections. @@ -40,8 +47,10 @@ public class ConnectionHandler implements IncomingEvent private static final int MAX_TRANSFERS = 12; - private static Map incomingTransfers = new ConcurrentHashMap<>(); - private static Map outgoingTransfers = new ConcurrentHashMap<>(); + private static Map incomingTransfersByTransferId = new ConcurrentHashMap<>(); + private static final Map incomingTransfersByVersionId = new ConcurrentHashMap<>(); + + private static Map outgoingTransfers = new ConcurrentHashMap<>(); private static IncomingEvent eventHandler = new ConnectionHandler(); private final ExecutorService transferPool = new GrowingThreadPoolExecutor( 1, MAX_TRANSFERS * 2, 1, TimeUnit.MINUTES, new SynchronousQueue(), @@ -51,7 +60,7 @@ public class ConnectionHandler implements IncomingEvent private static final Listener sslListener; static { - LOGGER.debug( "Starting listener on port " + Globals.getFiletransferPortSsl() ); + LOGGER.debug( "Starting BFTP on port " + Globals.getFiletransferPortSsl() + "+ and " + Globals.getFiletransferPortPlain() ); Listener ssl = null; Listener plain = null; try { @@ -68,32 +77,89 @@ public class ConnectionHandler implements IncomingEvent ssl.start(); plain = new Listener( eventHandler, null, Globals.getFiletransferPortPlain(), Globals.getFiletransferTimeout() * 1000 ); plain.start(); + // TODO: Bail out/retry if failed, getters for ports } catch ( Exception e ) { LOGGER.error( "Initialization failed.", e ); System.exit( 2 ); } sslListener = ssl; plainListener = plain; + QuickTimer.scheduleAtFixedDelay( new Task() { + + @Override + public void fire() + { + long now = System.currentTimeMillis(); + for ( Iterator it = incomingTransfersByTransferId.values().iterator(); it.hasNext(); ) { + IncomingTransfer t = it.next(); + if ( t.isComplete( now ) || t.hasReachedIdleTimeout( now ) ) { + LOGGER.debug( "Removing transfer " + t.getId() ); + it.remove(); + } + } + for ( Iterator it = incomingTransfersByVersionId.values().iterator(); it.hasNext(); ) { + IncomingTransfer t = it.next(); + if ( t.isComplete( now ) || t.hasReachedIdleTimeout( now ) ) { + it.remove(); + } + } + } + }, 10000, 301000 ); + } + + public static int getSslPort() + { + if ( sslListener.isRunning() ) + return sslListener.getPort(); + return 0; + } + + public static int getPlainPort() + { + if ( plainListener.isRunning() ) + return plainListener.getPort(); + return 0; } - public static IncomingTransfer registerUpload( ImagePublishData img, List blockHashes ) + /** + * Register new incoming transfer from a satellite server. + * + * @param img the desired meta data for the new image/version, as supplied by the satellite + * @param blockHashes list of block hashes for the image + * @param existing OPTIONAL if the image version already exists on the server, this is given so + * we can repair/complete the local file, if necessary + * @return The assigned transfer object + */ + public static IncomingTransfer registerUpload( ImagePublishData img, List blockHashes, ImagePublishDataEx existing ) throws TTransferRejectedException, TInvocationException { IncomingTransfer transfer; - synchronized ( incomingTransfers ) { - transfer = incomingTransfers.get( img.imageVersionId ); + synchronized ( incomingTransfersByTransferId ) { + transfer = incomingTransfersByVersionId.get( img.imageVersionId ); if ( transfer == null ) { if ( getUploadConnectionCount() >= MAX_TRANSFERS ) { throw new TTransferRejectedException( "Too many active transfers" ); } + File absDestination; + if ( existing == null ) { + absDestination = new File( new File( Globals.getImageDir(), img.imageBaseId ), img.imageVersionId ); + } else { + absDestination = new File( Globals.getImageDir(), existing.exImagePath ); + } + plainListener.start(); + sslListener.start(); try { - transfer = new IncomingTransfer( img, blockHashes ); + transfer = new IncomingTransfer( img, blockHashes, absDestination, getPlainPort(), getSslPort() ); } catch ( FileNotFoundException e ) { - LOGGER.warn( "Cannot init download", e ); + LOGGER.warn( "Cannot init download to " + absDestination.toString(), e ); throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "File access error" ); } - incomingTransfers.put( transfer.getId(), transfer ); - incomingTransfers.put( img.imageVersionId, transfer ); + LOGGER.info( "New incoming upload: " + transfer.getId() + " for " + img.imageVersionId + " (" + img.imageName + ")" ); + incomingTransfersByTransferId.put( transfer.getId(), transfer ); + incomingTransfersByVersionId.put( img.imageVersionId, transfer ); + } else { + LOGGER.info( "Another request for existing upload: " + transfer.getId() + " for " + img.imageVersionId + " (" + img.imageName + + ")" ); } } return transfer; @@ -102,7 +168,7 @@ public class ConnectionHandler implements IncomingEvent public static IncomingTransfer getExistingUpload( ImagePublishData imageData, List crcSums ) throws TTransferRejectedException { - IncomingTransfer transfer = incomingTransfers.get( imageData.imageVersionId ); + IncomingTransfer transfer = incomingTransfersByVersionId.get( imageData.imageVersionId ); if ( transfer == null ) return null; if ( transfer.getFileSize() != imageData.fileSize ) @@ -112,15 +178,31 @@ public class ConnectionHandler implements IncomingEvent return transfer; } + public static IncomingTransfer getUploadByToken( String uploadToken ) + { + return incomingTransfersByTransferId.get( uploadToken ); + } + /** * Server is uploading - client is downloading! */ @Override public void incomingDownloadRequest( final Uploader uploader ) { - // TODO - uploader.sendErrorCode( "Too many concurrent uploads." ); - uploader.cancel(); + OutgoingTransfer transfer = outgoingTransfers.get( uploader.getToken() ); + if ( transfer == null ) { + LOGGER.debug( "Unknown download token received" ); + uploader.sendErrorCode( "Unknown download token." ); + uploader.cancel(); + return; + } + if ( getDownloadConnectionCount() >= MAX_TRANSFERS ) { + uploader.sendErrorCode( "Too many concurrent uploads." ); + uploader.cancel(); + } + if ( !transfer.addConnection( uploader, transferPool ) ) { + uploader.cancel(); + } } /** @@ -129,7 +211,7 @@ public class ConnectionHandler implements IncomingEvent @Override public void incomingUploadRequest( final Downloader downloader ) throws IOException { - IncomingTransfer transfer = incomingTransfers.get( downloader.getToken() ); + IncomingTransfer transfer = incomingTransfersByTransferId.get( downloader.getToken() ); if ( transfer == null ) { downloader.sendErrorCode( "Unknown upload token." ); downloader.cancel(); @@ -149,7 +231,7 @@ public class ConnectionHandler implements IncomingEvent { final long now = System.currentTimeMillis(); int active = 0; - for ( IncomingTransfer t : incomingTransfers.values() ) { + for ( IncomingTransfer t : incomingTransfersByTransferId.values() ) { if ( t.countsTowardsConnectionLimit( now ) ) { active += t.getActiveConnectionCount(); } @@ -157,4 +239,55 @@ public class ConnectionHandler implements IncomingEvent return active; } + public static int getDownloadConnectionCount() + { + final long now = System.currentTimeMillis(); + int active = 0; + for ( OutgoingTransfer t : outgoingTransfers.values() ) { + if ( t.countsTowardsConnectionLimit( now ) ) { + active += t.getActiveConnectionCount(); + } + } + return active; + } + + public static void removeUpload( IncomingTransfer transfer ) + { + incomingTransfersByTransferId.remove( transfer.getId() ); + incomingTransfersByVersionId.remove( transfer.getImageVersionId() ); + } + + public static TransferInformation registerDownload( ImagePublishDataEx img ) throws TTransferRejectedException, TInvocationException + { + OutgoingTransfer transfer; + File absSource; + absSource = new File( Globals.getImageDir(), img.exImagePath ); + if ( !absSource.exists() ) { + LOGGER.error( absSource.toString() + " missing!" ); + try { + DbImage.markValid( img.imageVersionId, false ); + } catch ( SQLException e ) { + } + throw new TTransferRejectedException( "File missing on server" ); + } + if ( absSource.length() != img.fileSize ) { + LOGGER.error( absSource.toString() + " has unexpected size (is: " + absSource.length() + ", should: " + img.fileSize + ")" ); + try { + DbImage.markValid( img.imageVersionId, false ); + } catch ( SQLException e ) { + } + throw new TTransferRejectedException( "File corrupted on server" ); + } + synchronized ( outgoingTransfers ) { + if ( getDownloadConnectionCount() >= MAX_TRANSFERS ) { + throw new TTransferRejectedException( "Too many active transfers" ); + } + plainListener.start(); + sslListener.start(); + transfer = new OutgoingTransfer( absSource, getPlainPort(), getSslPort() ); + outgoingTransfers.put( transfer.getId(), transfer ); + } + return transfer.getTransferInfo(); + } + } diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/IncomingTransfer.java b/src/main/java/org/openslx/imagemaster/serverconnection/IncomingTransfer.java index bfc65e1..53842be 100644 --- a/src/main/java/org/openslx/imagemaster/serverconnection/IncomingTransfer.java +++ b/src/main/java/org/openslx/imagemaster/serverconnection/IncomingTransfer.java @@ -3,9 +3,11 @@ package org.openslx.imagemaster.serverconnection; import java.io.File; import java.io.FileNotFoundException; import java.nio.ByteBuffer; +import java.sql.SQLException; import java.util.List; import java.util.UUID; +import org.apache.log4j.Logger; import org.openslx.bwlp.thrift.iface.ImagePublishData; import org.openslx.bwlp.thrift.iface.TInvocationException; import org.openslx.bwlp.thrift.iface.TransferInformation; @@ -13,6 +15,7 @@ import org.openslx.filetransfer.util.ChunkStatus; import org.openslx.filetransfer.util.FileChunk; import org.openslx.filetransfer.util.IncomingTransferBase; import org.openslx.imagemaster.Globals; +import org.openslx.imagemaster.db.mappers.DbImage; import org.openslx.imagemaster.db.mappers.DbImageBlock; import org.openslx.imagemaster.util.Util; import org.openslx.util.ThriftUtil; @@ -20,16 +23,33 @@ import org.openslx.util.ThriftUtil; public class IncomingTransfer extends IncomingTransferBase { + private static final Logger LOGGER = Logger.getLogger( IncomingTransfer.class ); + private static final long MIN_FREE_SPACE_BYTES = FileChunk.CHUNK_SIZE * 10; private final String imageVersionId; - public IncomingTransfer( ImagePublishData img, List blockHashes ) + private final TransferInformation transferInfo; + + public IncomingTransfer( ImagePublishData img, List blockHashes, File absDestination, int plainPort, int sslPort ) throws TInvocationException, FileNotFoundException { - super( UUID.randomUUID().toString(), new File( new File( Globals.getImageDir(), img.imageBaseId ), img.imageVersionId ), - img.fileSize, ThriftUtil.unwrapByteBufferList( blockHashes ) ); + super( UUID.randomUUID().toString(), absDestination, img.fileSize, ThriftUtil.unwrapByteBufferList( blockHashes ) ); this.imageVersionId = img.imageVersionId; + this.transferInfo = new TransferInformation( getId(), plainPort, sslPort ); + // If the file already exists, see if any chunks are already complete + if ( absDestination.exists() && absDestination.length() > 0 ) { + try { + List statusList = DbImageBlock.getMissingStatusList( img.imageVersionId ); + if ( !statusList.isEmpty() ) { + getChunks().resumeFromStatusList( statusList, absDestination.length() ); + for ( int i = 0; i < 3; ++i ) { + queueUnhashedChunk( false ); + } + } + } catch ( SQLException e ) { + } + } } @Override @@ -56,18 +76,30 @@ public class IncomingTransfer extends IncomingTransferBase protected boolean finishIncomingTransfer() { potentialFinishTime.set( System.currentTimeMillis() ); + try { + DbImage.markValid( this.imageVersionId, true ); + } catch ( SQLException e ) { + // Nothing to do + } return true; } @Override public TransferInformation getTransferInfo() { - return new TransferInformation( getId(), Globals.getFiletransferPortPlain(), Globals.getFiletransferPortSsl() ); + return transferInfo; } @Override protected void chunkStatusChanged( FileChunk chunk ) { + if ( chunk.getFailCount() > 6 ) { + cancel(); + LOGGER.warn( "Server is cancelling upload of Version " + imageVersionId + + ": Hash check for block " + chunk.getChunkIndex() + + " failed " + chunk.getFailCount() + + " times." ); + } ChunkStatus status = chunk.getStatus(); if ( status == ChunkStatus.MISSING || status == ChunkStatus.COMPLETE ) { try { @@ -78,4 +110,9 @@ public class IncomingTransfer extends IncomingTransferBase } } + public Object getImageVersionId() + { + return imageVersionId; + } + } diff --git a/src/main/java/org/openslx/imagemaster/serverconnection/OutgoingTransfer.java b/src/main/java/org/openslx/imagemaster/serverconnection/OutgoingTransfer.java new file mode 100644 index 0000000..a6f80b2 --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/serverconnection/OutgoingTransfer.java @@ -0,0 +1,22 @@ +package org.openslx.imagemaster.serverconnection; + +import java.io.File; +import java.util.UUID; + +import org.openslx.filetransfer.util.OutgoingTransferBase; + +public class OutgoingTransfer extends OutgoingTransferBase +{ + + public OutgoingTransfer( File sourceFile, int plainPort, int sslPort ) + { + super( UUID.randomUUID().toString(), sourceFile, plainPort, sslPort ); + } + + @Override + public String getRelativePath() + { + return null; + } + +} diff --git a/src/main/java/org/openslx/imagemaster/session/SessionManager.java b/src/main/java/org/openslx/imagemaster/session/SessionManager.java index c141d24..e9f133e 100644 --- a/src/main/java/org/openslx/imagemaster/session/SessionManager.java +++ b/src/main/java/org/openslx/imagemaster/session/SessionManager.java @@ -47,22 +47,6 @@ public class SessionManager 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 ); - return null; - } - final Session session; - synchronized ( sessions ) { - session = sessions.get( token ); - } - if ( session == null || session.timedOut() ) { - return null; - } - return session; - } - static { QuickTimer.scheduleAtFixedDelay( new Task() { @Override @@ -81,12 +65,41 @@ public class SessionManager }, 123, TimeUnit.MINUTES.toMillis( 13 ) ); } + public static Session getSessionFromToken( String token ) + { + if ( token == null || token.length() != 32 ) { + log.debug( "invalid token format: " + token ); + return null; + } + final Session session; + synchronized ( sessions ) { + session = sessions.get( token ); + } + if ( session == null || session.timedOut() ) { + return null; + } + return session; + } + public static Session getSessionFromSessionId( String sessionId ) { if ( sessionId == null || sessionId.length() != 64 ) { log.debug( "invalid sessionid format: " + sessionId ); return null; } + final Session session; + synchronized ( sessions ) { + session = sessions.get( sessionId ); + } + if ( session == null || session.timedOut() ) { + return null; + } + session.refresh(); + return session; + } + + public static Session getSessionFromSessionIdOrToken( String sessionId ) + { final Session session; synchronized ( sessions ) { session = sessions.get( sessionId ); diff --git a/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java b/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java index 60f4ccb..3bdbd3f 100644 --- a/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java +++ b/src/main/java/org/openslx/imagemaster/thrift/server/MasterServerHandler.java @@ -13,7 +13,9 @@ import org.apache.log4j.Logger; import org.apache.thrift.TException; import org.openslx.bwlp.thrift.iface.AuthorizationError; import org.openslx.bwlp.thrift.iface.ClientSessionData; +import org.openslx.bwlp.thrift.iface.ImageDetailsRead; import org.openslx.bwlp.thrift.iface.ImagePublishData; +import org.openslx.bwlp.thrift.iface.ImageSummaryRead; import org.openslx.bwlp.thrift.iface.InvocationError; import org.openslx.bwlp.thrift.iface.MasterServer; import org.openslx.bwlp.thrift.iface.MasterSoftware; @@ -29,9 +31,11 @@ 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.TransferStatus; import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.bwlp.thrift.iface.Virtualizer; import org.openslx.encryption.AsymKeyHolder; +import org.openslx.filetransfer.util.ChunkList; import org.openslx.filetransfer.util.FileChunk; import org.openslx.imagemaster.Globals; import org.openslx.imagemaster.db.Database; @@ -43,7 +47,6 @@ 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.db.models.LocalUser; import org.openslx.imagemaster.serverconnection.ConnectionHandler; import org.openslx.imagemaster.serverconnection.IncomingTransfer; import org.openslx.imagemaster.serversession.ServerAuthenticator; @@ -52,7 +55,9 @@ 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.UserUtil; import org.openslx.imagemaster.util.Util; +import org.openslx.thrifthelper.ImagePublishDataEx; public class MasterServerHandler implements MasterServer.Iface { @@ -131,11 +136,33 @@ public class MasterServerHandler implements MasterServer.Iface } @Override - public List getPublicImages( String sessionId, int page ) + public List getPublicImages( String sessionId, int page ) throws TAuthorizationException, TInvocationException { - // TODO Auto-generated method stub - return null; + Session session = SessionManager.getSessionFromSessionId( sessionId ); + if ( session == null ) + throw new TAuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" ); + UserUtil.assertTutor( session.getUserInfo() ); + try { + return DbImage.getPublicList( page ); + } catch ( SQLException e ) { + throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database failure" ); + } + } + + @Override + public ImageDetailsRead getImageDetails( String sessionId, String imageBaseId ) + throws TAuthorizationException, TNotFoundException, TInvocationException + { + Session session = SessionManager.getSessionFromSessionId( sessionId ); + if ( session == null ) + throw new TAuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" ); + UserUtil.assertTutor( session.getUserInfo() ); + try { + return DbImage.getImageDetails( imageBaseId ); + } catch ( SQLException e ) { + throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database failure" ); + } } @Override @@ -214,8 +241,15 @@ public class MasterServerHandler implements MasterServer.Iface public ImagePublishData getImageData( String serverSessionId, String imageVersionId ) throws TAuthorizationException, TInvocationException, TNotFoundException { - // TODO Auto-generated method stub - return null; + Session session = SessionManager.getSessionFromSessionIdOrToken( serverSessionId ); + if ( session == null ) + throw new TAuthorizationException( AuthorizationError.INVALID_TOKEN, "Unknown session id/token" ); + UserUtil.assertTutor( session.getUserInfo() ); + try { + return DbImage.getImageVersion( imageVersionId ); + } catch ( SQLException e ) { + throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" ); + } } @Override @@ -226,6 +260,9 @@ public class MasterServerHandler implements MasterServer.Iface Session session = SessionManager.getSessionFromToken( userToken ); if ( session == null ) throw new TAuthorizationException( AuthorizationError.INVALID_TOKEN, "Given user token not known to the server" ); + UserUtil.assertTutor( session.getUserInfo() ); + img.owner = UserUtil.getFirstPublishingUser( img.owner, img.uploader, session.getUserInfo() ); + img.uploader = UserUtil.getFirstPublishingUserOrDummy( img.uploader, session.getUserInfo() ); // check image data if ( Util.isEmpty( img.imageName ) ) throw new TInvocationException( InvocationError.INVALID_DATA, "Image name not set" ); @@ -235,8 +272,10 @@ public class MasterServerHandler implements MasterServer.Iface throw new TInvocationException( InvocationError.MISSING_DATA, "ImagePublishData has invalid imageBaseId" ); if ( !Util.isUUID( img.imageVersionId ) ) throw new TInvocationException( InvocationError.MISSING_DATA, "ImagePublishData has invalid imageVersionId" ); - if ( img.user == null || img.user.userId == null ) - throw new TInvocationException( InvocationError.MISSING_DATA, "Missing user id" ); + if ( img.owner == null || img.owner.userId == null ) + throw new TInvocationException( InvocationError.MISSING_DATA, "Missing owner or owner is anonymous" ); + if ( img.uploader == null || img.uploader.userId == null ) + throw new TInvocationException( InvocationError.MISSING_DATA, "Missing uploader or uploader is anonymous" ); // check for complete block hash list boolean listComplete = false; if ( blockHashes != null && blockHashes.size() == FileChunk.fileSizeToChunkCount( img.fileSize ) ) { @@ -253,48 +292,53 @@ public class MasterServerHandler implements MasterServer.Iface // Check if an upload is already assigned IncomingTransfer existingUpload = ConnectionHandler.getExistingUpload( img, blockHashes ); if ( existingUpload != null ) { + LOGGER.info( "Satellite tried to register already existing upload for version " + img.imageVersionId + " - is " + + existingUpload.getId() ); return existingUpload.getTransferInfo(); } + // Check if version already exists + ImagePublishDataEx existing; + try { + existing = DbImage.getImageVersion( img.imageVersionId ); + if ( existing != null ) { + if ( existing.fileSize != img.fileSize ) + throw new TInvocationException( InvocationError.INVALID_DATA, "Image already exists; file size mismatch" ); + List existingHashes = DbImageBlock.getBlockHashes( img.imageVersionId ); + if ( !ChunkList.hashListsEqualBbBb( blockHashes, existingHashes ) ) + throw new TInvocationException( InvocationError.INVALID_DATA, "Image already exists; block hashes mismatch" ); + } + } catch ( SQLException e ) { + throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Internal database error" ); + } // No existing upload - create new one // checks that hit the db if ( !DbOsVirt.osExists( img.osId ) ) throw new TInvocationException( InvocationError.INVALID_DATA, "Content operating system not set" ); if ( !DbOsVirt.virtExists( img.virtId ) ) throw new TInvocationException( InvocationError.INVALID_DATA, "Content virtualizer system not set" ); - try { - LocalUser user = DbUser.forUserId( img.user.userId ); - if ( user == null ) { - user = DbUser.forUserId( session.getUserInfo().userId ); - if ( user != null ) { - img.user = user.toUserInfo(); - } - } - if ( user == null ) - throw new TInvocationException( InvocationError.UNKNOWN_USER, "Unknown user id " + img.user.userId ); - if ( user.isAnonymous() ) - throw new TInvocationException( InvocationError.UNKNOWN_USER, "The owner of the image does not participate in image exchange" ); - } catch ( SQLException e ) { - throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" ); - } // Make sure we have a destination to write to if ( !new File( Globals.getImageDir() ).isDirectory() ) throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Storage offline" ); // Try to register an upload - IncomingTransfer transfer = ConnectionHandler.registerUpload( img, blockHashes ); + IncomingTransfer transfer = ConnectionHandler.registerUpload( img, blockHashes, existing ); try { DbImage.createImageBase( img ); } catch ( TException t ) { transfer.cancel(); + ConnectionHandler.removeUpload( transfer ); throw t; } - try { - DbImage.createImageVersion( img, transfer.getRelativePath() ); - } catch ( SQLException e1 ) { - transfer.cancel(); - if ( Database.isDuplicateKeyException( e1 ) ) { - throw new TInvocationException( InvocationError.INVALID_DATA, "The image already exists on the server" ); - } else { - throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" ); + if ( existing == null ) { + try { + DbImage.createImageVersion( img, transfer.getRelativePath() ); + } catch ( SQLException e1 ) { + transfer.cancel(); + ConnectionHandler.removeUpload( transfer ); + if ( Database.isDuplicateKeyException( e1 ) ) { + throw new TInvocationException( InvocationError.INVALID_DATA, "The image already exists on the server" ); + } else { + throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" ); + } } } try { @@ -307,10 +351,27 @@ public class MasterServerHandler implements MasterServer.Iface @Override public TransferInformation downloadImage( String sessionId, String imageVersionId ) - throws TAuthorizationException, TInvocationException, TNotFoundException + throws TAuthorizationException, TInvocationException, TNotFoundException, TTransferRejectedException { - // TODO Auto-generated method stub - return null; + // Valid submit session? + Session session = SessionManager.getSessionFromToken( sessionId ); + if ( session == null ) + throw new TAuthorizationException( AuthorizationError.INVALID_TOKEN, "Given user token not known to the server" ); + UserUtil.assertTutor( session.getUserInfo() ); + ImagePublishDataEx img; + List blockHashes; + try { + img = DbImage.getImageVersion( imageVersionId ); + blockHashes = DbImageBlock.getBlockHashes( img.imageVersionId ); + } catch ( SQLException e ) { + throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database error" ); + } + if ( img == null || !img.exIsValid ) + throw new TNotFoundException(); + TransferInformation ti = ConnectionHandler.registerDownload( img ); + ti.machineDescription = img.machineDescription; + ti.blockHashes = blockHashes; + return ti; } @Override @@ -398,4 +459,33 @@ public class MasterServerHandler implements MasterServer.Iface return false; } + @Override + public TransferStatus queryUploadStatus( String uploadToken ) throws TInvalidTokenException + { + IncomingTransfer upload = ConnectionHandler.getUploadByToken( uploadToken ); + if ( upload == null ) + throw new TInvalidTokenException(); + return upload.getStatus(); + } + + @Override + public UserInfo getUser( String userToken, String userId ) + throws TAuthorizationException, TNotFoundException, TInvocationException + { + Session session = SessionManager.getSessionFromToken( userToken ); + if ( session == null ) + throw new TAuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "No valid user token" ); + UserInfo userInfo = session.getUserInfo(); + UserUtil.assertTutor( userInfo ); + UserInfo queriedUser; + try { + queriedUser = DbUser.getUserInfo( userToken ); + } catch ( SQLException e ) { + throw new TInvocationException( InvocationError.INTERNAL_SERVER_ERROR, "Database broken" ); + } + if ( UserUtil.getFirstPublishingUser( queriedUser ) == null ) + throw new TNotFoundException( "Unknown userid" ); + return queriedUser; + } + } diff --git a/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java b/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java index bcd4b99..c4a4b61 100644 --- a/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java +++ b/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java @@ -416,7 +416,7 @@ public final class Sha512Crypt try { - int srounds = Integer.valueOf(num).intValue(); + Integer.valueOf(num).intValue(); } catch (NumberFormatException ex) { diff --git a/src/main/java/org/openslx/imagemaster/util/UserUtil.java b/src/main/java/org/openslx/imagemaster/util/UserUtil.java new file mode 100644 index 0000000..2e447f3 --- /dev/null +++ b/src/main/java/org/openslx/imagemaster/util/UserUtil.java @@ -0,0 +1,62 @@ +package org.openslx.imagemaster.util; + +import java.sql.SQLException; + +import org.openslx.bwlp.thrift.iface.AuthorizationError; +import org.openslx.bwlp.thrift.iface.Role; +import org.openslx.bwlp.thrift.iface.TAuthorizationException; +import org.openslx.bwlp.thrift.iface.TNotFoundException; +import org.openslx.bwlp.thrift.iface.UserInfo; +import org.openslx.imagemaster.db.mappers.DbUser; + +public class UserUtil +{ + + /** + * Given a list of users, return the first one that isn't anonymous, which means they opted in + * for global image sharing. + */ + public static UserInfo getFirstPublishingUser( UserInfo... user ) + { + if ( user == null ) + return null; + for ( UserInfo u : user ) { + if ( Util.isEmpty( u.userId ) ) + continue; + try { + u = DbUser.getUserInfo( u.userId ); + } catch ( SQLException | TNotFoundException e ) { + continue; + } + if ( !Util.isEmpty( u.eMail ) + && ( !Util.isEmpty( u.firstName ) || !Util.isEmpty( u.lastName ) ) ) { + return u; + } + } + return null; + } + + /** + * Given a list of users, return the first one that isn't anonymous, which means they opted in + * for global image sharing. If none matches, return the dummy user. + */ + public static UserInfo getFirstPublishingUserOrDummy( UserInfo... user ) + { + UserInfo ret = getFirstPublishingUser( user ); + if ( ret != null ) + return ret; + try { + return DbUser.getUserInfo( "dummy" ); + } catch ( TNotFoundException | SQLException e ) { + return null; + } + } + + public static void assertTutor( UserInfo userInfo ) throws TAuthorizationException + { + if ( userInfo == null || userInfo.role != Role.TUTOR ) { + throw new TAuthorizationException( AuthorizationError.NO_PERMISSION, "Permission denied!" ); + } + } + +} -- cgit v1.2.3-55-g7522